From 96d9c64752e1447ab109bfe8baf26020403612ae Mon Sep 17 00:00:00 2001 From: Seigo Nonaka Date: Wed, 3 Feb 2021 13:18:18 -0800 Subject: [PATCH] Reduce the memory usage in system_server SystemFonts and related classes are now aggressively used in system server. To reduce the memory impact, use ArrayMap/Set instead of HashMap/Set. Also, the language tags are duplicated in two places, one in Java and one in Native object. Remove Java reference. Bug: 178472803 Test: N/A Change-Id: Id94b68bd34a1958fda67512dcdc683b20b12003f --- graphics/java/android/graphics/Typeface.java | 6 ++-- .../android/graphics/fonts/FontFamily.java | 31 ++++++++++++------- .../android/graphics/fonts/SystemFonts.java | 15 +++++---- libs/hwui/jni/fonts/FontFamily.cpp | 26 +++++++++++++++- .../graphics/fonts/UpdatableFontDir.java | 8 ++--- 5 files changed, 58 insertions(+), 28 deletions(-) diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 005a72661091e..35e6b8595c2a1 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -42,6 +42,7 @@ import android.provider.FontsContract; import android.system.ErrnoException; import android.system.OsConstants; import android.text.FontConfig; +import android.util.ArrayMap; import android.util.Base64; import android.util.LongSparseArray; import android.util.LruCache; @@ -67,7 +68,6 @@ import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -147,7 +147,7 @@ public class Typeface { */ @GuardedBy("SYSTEM_FONT_MAP_LOCK") @UnsupportedAppUsage(trackingBug = 123769347) - static final Map sSystemFontMap = new HashMap<>(); + static final Map sSystemFontMap = new ArrayMap<>(); // DirectByteBuffer object to hold sSystemFontMap's backing memory mapping. static ByteBuffer sSystemFontMapBuffer = null; @@ -1231,7 +1231,7 @@ public class Typeface { /** @hide */ @VisibleForTesting public static Map deserializeFontMap(ByteBuffer buffer) throws IOException { - Map fontMap = new HashMap<>(); + Map fontMap = new ArrayMap<>(); int typefacesBytesCount = buffer.getInt(); long[] nativePtrs = nativeReadTypefaces(buffer.slice()); if (nativePtrs == null) { diff --git a/graphics/java/android/graphics/fonts/FontFamily.java b/graphics/java/android/graphics/fonts/FontFamily.java index c29c194861f12..77f86fe726f3b 100644 --- a/graphics/java/android/graphics/fonts/FontFamily.java +++ b/graphics/java/android/graphics/fonts/FontFamily.java @@ -20,15 +20,16 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.text.FontConfig; +import android.util.SparseIntArray; import com.android.internal.util.Preconditions; import dalvik.annotation.optimization.CriticalNative; +import dalvik.annotation.optimization.FastNative; import libcore.util.NativeAllocationRegistry; import java.util.ArrayList; -import java.util.HashSet; /** * A font family class can be used for creating Typeface. @@ -68,7 +69,9 @@ public final class FontFamily { nGetReleaseNativeFamily()); private final ArrayList mFonts = new ArrayList<>(); - private final HashSet mStyleHashSet = new HashSet<>(); + // Most FontFamily only has regular, bold, italic, bold-italic. Thus 4 should be good for + // initial capacity. + private final SparseIntArray mStyles = new SparseIntArray(4); /** * Constructs a builder. @@ -77,7 +80,7 @@ public final class FontFamily { */ public Builder(@NonNull Font font) { Preconditions.checkNotNull(font, "font can not be null"); - mStyleHashSet.add(makeStyleIdentifier(font)); + mStyles.append(makeStyleIdentifier(font), 0); mFonts.add(font); } @@ -97,9 +100,11 @@ public final class FontFamily { */ public @NonNull Builder addFont(@NonNull Font font) { Preconditions.checkNotNull(font, "font can not be null"); - if (!mStyleHashSet.add(makeStyleIdentifier(font))) { + int key = makeStyleIdentifier(font); + if (mStyles.indexOfKey(key) >= 0) { throw new IllegalArgumentException(font + " has already been added"); } + mStyles.append(key, 0); mFonts.add(font); return this; } @@ -120,7 +125,7 @@ public final class FontFamily { nAddFont(builderPtr, mFonts.get(i).getNativePtr()); } final long ptr = nBuild(builderPtr, langTags, variant, isCustomFallback); - final FontFamily family = new FontFamily(mFonts, langTags, variant, ptr); + final FontFamily family = new FontFamily(mFonts, ptr); sFamilyRegistory.registerNativeAllocation(family, ptr); return family; } @@ -139,15 +144,11 @@ public final class FontFamily { } private final ArrayList mFonts; - private final String mLangTags; - private final int mVariant; private final long mNativePtr; // Use Builder instead. - private FontFamily(@NonNull ArrayList fonts, String langTags, int variant, long ptr) { + private FontFamily(@NonNull ArrayList fonts, long ptr) { mFonts = fonts; - mLangTags = langTags; - mVariant = variant; mNativePtr = ptr; } @@ -157,7 +158,7 @@ public final class FontFamily { * @return a BCP-47 compliant language tag. */ public @Nullable String getLangTags() { - return mLangTags; + return nGetLangTags(mNativePtr); } /** @@ -165,7 +166,7 @@ public final class FontFamily { * @return a family variant */ public int getVariant() { - return mVariant; + return nGetVariant(mNativePtr); } /** @@ -191,4 +192,10 @@ public final class FontFamily { public long getNativePtr() { return mNativePtr; } + + @FastNative + private static native String nGetLangTags(long family); + + @CriticalNative + private static native int nGetVariant(long family); } diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java index c166e12fc6bff..904085feb8cb4 100644 --- a/graphics/java/android/graphics/fonts/SystemFonts.java +++ b/graphics/java/android/graphics/fonts/SystemFonts.java @@ -22,6 +22,7 @@ import android.graphics.FontListParser; import android.graphics.Typeface; import android.text.FontConfig; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Log; import com.android.internal.annotations.GuardedBy; @@ -36,8 +37,6 @@ import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -76,7 +75,7 @@ public final class SystemFonts { if (Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) { sAvailableFonts = collectAllFonts(); } else { - Set set = new HashSet<>(); + Set set = new ArraySet<>(); for (FontFamily[] items : sFamilyMap.values()) { for (FontFamily family : items) { for (int i = 0; i < family.getSize(); ++i) { @@ -96,7 +95,7 @@ public final class SystemFonts { FontConfig fontConfig = getSystemPreinstalledFontConfig(); Map map = buildSystemFallback(fontConfig); - Set res = new HashSet<>(); + Set res = new ArraySet<>(); for (FontFamily[] families : map.values()) { for (FontFamily family : families) { for (int i = 0; i < family.getSize(); ++i) { @@ -218,7 +217,7 @@ public final class SystemFonts { } private static void appendNamedFamily(@NonNull FontConfig.FontFamily xmlFamily, - @NonNull HashMap bufferCache, + @NonNull ArrayMap bufferCache, @NonNull ArrayMap> fallbackListMap) { final String familyName = xmlFamily.getName(); final FontFamily family = createFontFamily( @@ -284,8 +283,8 @@ public final class SystemFonts { */ @VisibleForTesting public static Map buildSystemFallback(FontConfig fontConfig) { - final Map fallbackMap = new HashMap<>(); - final HashMap bufferCache = new HashMap<>(); + final Map fallbackMap = new ArrayMap<>(); + final ArrayMap bufferCache = new ArrayMap<>(); final List xmlFamilies = fontConfig.getFontFamilies(); final ArrayMap> fallbackListMap = new ArrayMap<>(); @@ -326,7 +325,7 @@ public final class SystemFonts { public static Map buildSystemTypefaces( FontConfig fontConfig, Map fallbackMap) { - final HashMap result = new HashMap<>(); + final ArrayMap result = new ArrayMap<>(); Typeface.initSystemDefaultTypefaces(fallbackMap, fontConfig.getAliases(), result); return result; } diff --git a/libs/hwui/jni/fonts/FontFamily.cpp b/libs/hwui/jni/fonts/FontFamily.cpp index 37e52766f2ef1..a07723f39b0cc 100644 --- a/libs/hwui/jni/fonts/FontFamily.cpp +++ b/libs/hwui/jni/fonts/FontFamily.cpp @@ -83,6 +83,23 @@ static jlong FontFamily_Builder_GetReleaseFunc(CRITICAL_JNI_PARAMS) { return reinterpret_cast(releaseFontFamily); } +// FastNative +static jstring FontFamily_getLangTags(JNIEnv* env, jobject, jlong familyPtr) { + FontFamilyWrapper* family = reinterpret_cast(familyPtr); + uint32_t localeListId = family->family->localeListId(); + if (localeListId == 0) { + return nullptr; + } + std::string langTags = minikin::getLocaleString(localeListId); + return env->NewStringUTF(langTags.c_str()); +} + +// CriticalNative +static jint FontFamily_getVariant(jlong familyPtr) { + FontFamilyWrapper* family = reinterpret_cast(familyPtr); + return static_cast(family->family->variant()); +} + /////////////////////////////////////////////////////////////////////////////// static const JNINativeMethod gFontFamilyBuilderMethods[] = { @@ -93,9 +110,16 @@ static const JNINativeMethod gFontFamilyBuilderMethods[] = { { "nGetReleaseNativeFamily", "()J", (void*) FontFamily_Builder_GetReleaseFunc }, }; +static const JNINativeMethod gFontFamilyMethods[] = { + {"nGetLangTags", "(J)Ljava/lang/String;", (void*)FontFamily_getLangTags}, + {"nGetVariant", "(J)I", (void*)FontFamily_getVariant}, +}; + int register_android_graphics_fonts_FontFamily(JNIEnv* env) { return RegisterMethodsOrDie(env, "android/graphics/fonts/FontFamily$Builder", - gFontFamilyBuilderMethods, NELEM(gFontFamilyBuilderMethods)); + gFontFamilyBuilderMethods, NELEM(gFontFamilyBuilderMethods)) + + RegisterMethodsOrDie(env, "android/graphics/fonts/FontFamily", gFontFamilyMethods, + NELEM(gFontFamilyMethods)); } } diff --git a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java index afc97c7fd8038..dac94f6aa9d22 100644 --- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java +++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java @@ -27,6 +27,7 @@ import android.os.FileUtils; import android.system.ErrnoException; import android.system.Os; import android.text.FontConfig; +import android.util.ArrayMap; import android.util.Base64; import android.util.Slog; @@ -39,7 +40,6 @@ import java.io.FileOutputStream; import java.io.IOException; import java.security.SecureRandom; import java.time.Instant; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -116,7 +116,7 @@ final class UpdatableFontDir { * FontFileInfo}. All files in this map are validated, and have higher revision numbers than * corresponding font files in {@link #mPreinstalledFontDirs}. */ - private final Map mFontFileInfoMap = new HashMap<>(); + private final ArrayMap mFontFileInfoMap = new ArrayMap<>(); UpdatableFontDir(File filesDir, List preinstalledFontDirs, FontFileParser parser, FsverityUtil fsverityUtil) { @@ -205,7 +205,7 @@ final class UpdatableFontDir { */ public void update(List requests) throws SystemFontException { // Backup the mapping for rollback. - HashMap backupMap = new HashMap<>(mFontFileInfoMap); + ArrayMap backupMap = new ArrayMap<>(mFontFileInfoMap); long backupLastModifiedDate = mLastModifiedDate; boolean success = false; try { @@ -464,7 +464,7 @@ final class UpdatableFontDir { } Map getFontFileMap() { - Map map = new HashMap<>(); + Map map = new ArrayMap<>(); for (Map.Entry entry : mFontFileInfoMap.entrySet()) { map.put(entry.getKey(), entry.getValue().getFile()); }