Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 48230317 authored by Seigo Nonaka's avatar Seigo Nonaka
Browse files

Decouple font collection logic from fallback building logic

Bug: 173752727
Test: atest SystemFontsTest
Change-Id: I87030852a31c48a702becd406a56d7af6a101c48
parent d6b491aa
Loading
Loading
Loading
Loading
+3 −6
Original line number Diff line number Diff line
@@ -23,7 +23,6 @@ import static org.junit.Assert.assertTrue;

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;
@@ -49,7 +48,6 @@ import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Locale;

@SmallTest
@@ -133,14 +131,14 @@ public class TypefaceSystemFallbackTest {
    private static void buildSystemFallback(String xml,
            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")));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(TEST_FONTS_XML,
                TEST_FONT_DIR, oemCustomization, fallbackMap, availableFonts);
                TEST_FONT_DIR, oemCustomization, fallbackMap);
        Typeface.initSystemDefaultTypefaces(fontMap, fallbackMap, aliases);
    }

@@ -156,12 +154,11 @@ public class TypefaceSystemFallbackTest {
    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, oemCustomization, fallbackMap, availableFonts);
                SYSTEM_FONT_DIR, oemCustomization, fallbackMap);

        assertNotNull(aliases);
        assertFalse(fallbackMap.isEmpty());
+6 −2
Original line number Diff line number Diff line
@@ -23,8 +23,11 @@ import static org.junit.Assert.assertTrue;
import android.content.Context;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.graphics.fonts.FontFamily;
import android.graphics.fonts.SystemFonts;
import android.os.SharedMemory;
import android.text.FontConfig;
import android.util.Pair;

import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
@@ -195,8 +198,9 @@ public class TypefaceTest {
    @Test
    public void testSerialize() throws Exception {
        HashMap<String, Typeface> systemFontMap = new HashMap<>();
        Typeface.initSystemDefaultTypefaces(systemFontMap, SystemFonts.getRawSystemFallbackMap(),
                SystemFonts.getAliases());
        Pair<FontConfig.Alias[], Map<String, FontFamily[]>> res =
                SystemFonts.initializePreinstalledFonts();
        Typeface.initSystemDefaultTypefaces(systemFontMap, res.second, res.first);
        SharedMemory sharedMemory = Typeface.serializeFontMap(systemFontMap);
        Map<String, Typeface> copiedFontMap =
                Typeface.deserializeFontMap(sharedMemory.mapReadOnly().order(ByteOrder.BIG_ENDIAN));
+1 −4
Original line number Diff line number Diff line
@@ -20,7 +20,6 @@ import android.annotation.NonNull;
import android.content.Context;
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;
@@ -35,7 +34,6 @@ import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;

public class FontFallbackSetup implements AutoCloseable {
    private final String[] mTestFontFiles;
@@ -78,11 +76,10 @@ public class FontFallbackSetup implements AutoCloseable {
        }

        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, oemCustomization, fallbackMap, availableFonts);
                mTestFontsDir, oemCustomization, fallbackMap);
        Typeface.initSystemDefaultTypefaces(mFontMap, fallbackMap, aliases);
    }

+4 −2
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ import android.text.FontConfig;
import android.util.Base64;
import android.util.LongSparseArray;
import android.util.LruCache;
import android.util.Pair;
import android.util.SparseArray;

import com.android.internal.annotations.GuardedBy;
@@ -1296,8 +1297,9 @@ public class Typeface {

    static {
        final HashMap<String, Typeface> systemFontMap = new HashMap<>();
        initSystemDefaultTypefaces(systemFontMap, SystemFonts.getRawSystemFallbackMap(),
                SystemFonts.getAliases());
        Pair<FontConfig.Alias[], Map<String, FontFamily[]>> pair =
                SystemFonts.initializePreinstalledFonts();
        initSystemDefaultTypefaces(systemFontMap, pair.second, pair.first);
        setSystemFontMap(systemFontMap);
    }

+41 −47
Original line number Diff line number Diff line
@@ -22,7 +22,9 @@ import android.graphics.FontListParser;
import android.text.FontConfig;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;

@@ -51,9 +53,9 @@ public final class SystemFonts {

    private SystemFonts() {}  // Do not instansiate.

    private static final Map<String, FontFamily[]> sSystemFallbackMap;
    private static final FontConfig.Alias[] sAliases;
    private static final List<Font> sAvailableFonts;
    private static final Object LOCK = new Object();
    private static @GuardedBy("sLock") Set<Font> sAvailableFonts;
    private static @GuardedBy("sLock") Map<String, FontFamily[]> sFamilyMap;

    /**
     * Returns all available font files in the system.
@@ -61,29 +63,24 @@ public final class SystemFonts {
     * @return a set of system fonts
     */
    public static @NonNull Set<Font> getAvailableFonts() {
        HashSet<Font> set = new HashSet<>();
        set.addAll(sAvailableFonts);
        return set;
        synchronized (LOCK) {
            if (sAvailableFonts != null) {
                return sAvailableFonts;
            }

    /**
     * Returns raw system fallback map.
     *
     * This method is intended to be used only by Typeface static initializer.
     * @hide
     */
    public static @NonNull Map<String, FontFamily[]> getRawSystemFallbackMap() {
        return sSystemFallbackMap;
            Set<Font> set = new HashSet<>();

            for (FontFamily[] items : sFamilyMap.values()) {
                for (FontFamily family : items) {
                    for (int i = 0; i < family.getSize(); ++i) {
                        set.add(family.getFont(i));
                    }
                }
            }

    /**
     * Returns a list of aliases.
     *
     * This method is intended to be used only by Typeface static initializer.
     * @hide
     */
    public static @NonNull FontConfig.Alias[] getAliases() {
        return sAliases;
            sAvailableFonts = Collections.unmodifiableSet(set);
            return sAvailableFonts;
        }
    }

    private static @Nullable ByteBuffer mmap(@NonNull String fullPath) {
@@ -98,8 +95,7 @@ public final class SystemFonts {

    private static void pushFamilyToFallback(@NonNull FontConfig.Family xmlFamily,
            @NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackMap,
            @NonNull Map<String, ByteBuffer> cache,
            @NonNull ArrayList<Font> availableFonts) {
            @NonNull Map<String, ByteBuffer> cache) {

        final String languageTags = xmlFamily.getLanguages();
        final int variant = xmlFamily.getVariant();
@@ -123,7 +119,7 @@ public final class SystemFonts {
        }

        final FontFamily defaultFamily = defaultFonts.isEmpty() ? null : createFontFamily(
                xmlFamily.getName(), defaultFonts, languageTags, variant, cache, availableFonts);
                xmlFamily.getName(), defaultFonts, languageTags, variant, cache);

        // Insert family into fallback map.
        for (int i = 0; i < fallbackMap.size(); i++) {
@@ -135,8 +131,7 @@ public final class SystemFonts {
                }
            } else {
                final FontFamily family = createFontFamily(
                        xmlFamily.getName(), fallback, languageTags, variant, cache,
                        availableFonts);
                        xmlFamily.getName(), fallback, languageTags, variant, cache);
                if (family != null) {
                    fallbackMap.valueAt(i).add(family);
                } else if (defaultFamily != null) {
@@ -152,8 +147,7 @@ public final class SystemFonts {
            @NonNull List<FontConfig.Font> fonts,
            @NonNull String languageTags,
            @FontConfig.Family.Variant int variant,
            @NonNull Map<String, ByteBuffer> cache,
            @NonNull ArrayList<Font> availableFonts) {
            @NonNull Map<String, ByteBuffer> cache) {
        if (fonts.size() == 0) {
            return null;
        }
@@ -187,7 +181,6 @@ public final class SystemFonts {
                throw new RuntimeException(e);  // Never reaches here
            }

            availableFonts.add(font);
            if (b == null) {
                b = new FontFamily.Builder(font);
            } else {
@@ -199,12 +192,11 @@ public final class SystemFonts {

    private static void appendNamedFamily(@NonNull FontConfig.Family xmlFamily,
            @NonNull HashMap<String, ByteBuffer> bufferCache,
            @NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackListMap,
            @NonNull ArrayList<Font> availableFonts) {
            @NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackListMap) {
        final String familyName = xmlFamily.getName();
        final FontFamily family = createFontFamily(
                familyName, Arrays.asList(xmlFamily.getFonts()),
                xmlFamily.getLanguages(), xmlFamily.getVariant(), bufferCache, availableFonts);
                xmlFamily.getLanguages(), xmlFamily.getVariant(), bufferCache);
        if (family == null) {
            return;
        }
@@ -227,8 +219,7 @@ public final class SystemFonts {
    public static FontConfig.Alias[] buildSystemFallback(@NonNull String xmlPath,
            @NonNull String fontDir,
            @NonNull FontCustomizationParser.Result oemCustomization,
            @NonNull ArrayMap<String, FontFamily[]> fallbackMap,
            @NonNull ArrayList<Font> availableFonts) {
            @NonNull Map<String, FontFamily[]> fallbackMap) {
        try {
            final FileInputStream fontsIn = new FileInputStream(xmlPath);
            final FontConfig fontConfig = FontListParser.parse(fontsIn, fontDir);
@@ -243,12 +234,12 @@ public final class SystemFonts {
                if (familyName == null) {
                    continue;
                }
                appendNamedFamily(xmlFamily, bufferCache, fallbackListMap, availableFonts);
                appendNamedFamily(xmlFamily, bufferCache, fallbackListMap);
            }

            for (int i = 0; i < oemCustomization.mAdditionalNamedFamilies.size(); ++i) {
                appendNamedFamily(oemCustomization.mAdditionalNamedFamilies.get(i),
                        bufferCache, fallbackListMap, availableFonts);
                        bufferCache, fallbackListMap);
            }

            // Then, add fallback fonts to the each fallback map.
@@ -257,7 +248,7 @@ public final class SystemFonts {
                // 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, availableFonts);
                    pushFamilyToFallback(xmlFamily, fallbackListMap, bufferCache);
                }
            }

@@ -292,14 +283,17 @@ public final class SystemFonts {
        }
    }

    static {
        final ArrayMap<String, FontFamily[]> systemFallbackMap = new ArrayMap<>();
        final ArrayList<Font> availableFonts = new ArrayList<>();
    /** @hide */
    public static @NonNull Pair<FontConfig.Alias[], Map<String, FontFamily[]>>
            initializePreinstalledFonts() {
        final FontCustomizationParser.Result oemCustomization =
                readFontCustomization("/product/etc/fonts_customization.xml", "/product/fonts/");
        sAliases = buildSystemFallback("/system/etc/fonts.xml", "/system/fonts/",
                oemCustomization, systemFallbackMap, availableFonts);
        sSystemFallbackMap = Collections.unmodifiableMap(systemFallbackMap);
        sAvailableFonts = Collections.unmodifiableList(availableFonts);
        Map<String, FontFamily[]> map = new ArrayMap<>();
        FontConfig.Alias[] aliases = buildSystemFallback("/system/etc/fonts.xml", "/system/fonts/",
                oemCustomization, map);
        synchronized (LOCK) {
            sFamilyMap = map;
        }
        return new Pair(aliases, map);
    }
}