Loading core/tests/coretests/src/android/graphics/TypefaceTest.java +64 −12 Original line number Diff line number Diff line Loading @@ -23,6 +23,8 @@ import static org.junit.Assert.assertTrue; import android.content.Context; import android.content.res.AssetManager; import android.content.res.Resources; import android.graphics.fonts.SystemFonts; import android.os.SharedMemory; import androidx.test.InstrumentationRegistry; import androidx.test.filters.LargeTest; Loading @@ -35,9 +37,9 @@ import com.android.frameworks.coretests.R; import org.junit.Test; import org.junit.runner.RunWith; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.List; import java.nio.ByteOrder; import java.util.HashMap; import java.util.Map; import java.util.Random; @RunWith(AndroidJUnit4.class) Loading @@ -54,6 +56,10 @@ public class TypefaceTest { Typeface.create(Typeface.MONOSPACE, 0) }; private static final int[] STYLES = { Typeface.NORMAL, Typeface.BOLD, Typeface.ITALIC, Typeface.BOLD_ITALIC, }; @SmallTest @Test public void testBasic() throws Exception { Loading @@ -64,6 +70,16 @@ public class TypefaceTest { assertTrue("basic", Typeface.MONOSPACE != null); } @SmallTest @Test public void testDefaults() { for (int style : STYLES) { String msg = "style = " + style; assertNotNull(msg, Typeface.defaultFromStyle(style)); assertEquals(msg, style, Typeface.defaultFromStyle(style).getStyle()); } } @SmallTest @Test public void testUnique() throws Exception { Loading Loading @@ -178,21 +194,57 @@ public class TypefaceTest { @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); HashMap<String, Typeface> systemFontMap = new HashMap<>(); Typeface.initSystemDefaultTypefaces(systemFontMap, SystemFonts.getRawSystemFallbackMap(), SystemFonts.getAliases()); SharedMemory sharedMemory = Typeface.serializeFontMap(systemFontMap); Map<String, Typeface> copiedFontMap = Typeface.deserializeFontMap(sharedMemory.mapReadOnly().order(ByteOrder.BIG_ENDIAN)); assertEquals(systemFontMap.size(), copiedFontMap.size()); for (String key : systemFontMap.keySet()) { assertTrue(copiedFontMap.containsKey(key)); Typeface original = systemFontMap.get(key); Typeface copied = copiedFontMap.get(key); assertEquals(original.getStyle(), copied.getStyle()); assertEquals(original.getWeight(), copied.getWeight()); assertEquals(measureText(original, "hello"), measureText(copied, "hello"), 1e-6); } } @SmallTest @Test public void testSetSystemFontMap() throws Exception { Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); Resources res = context.getResources(); Map<String, Typeface> fontMap = Map.of( "sans-serif", Typeface.create(res.getFont(R.font.samplefont), Typeface.NORMAL), "serif", Typeface.create(res.getFont(R.font.samplefont2), Typeface.NORMAL), "monospace", Typeface.create(res.getFont(R.font.samplefont3), Typeface.NORMAL), "sample", Typeface.create(res.getFont(R.font.samplefont4), Typeface.NORMAL), "sample-italic", Typeface.create(res.getFont(R.font.samplefont4), Typeface.ITALIC)); Typeface.setSystemFontMap(fontMap); // Test public static final fields assertEquals(fontMap.get("sans-serif"), Typeface.DEFAULT); assertEquals(Typeface.BOLD, Typeface.DEFAULT_BOLD.getStyle()); assertEquals(fontMap.get("sans-serif"), Typeface.SANS_SERIF); assertEquals(fontMap.get("serif"), Typeface.SERIF); assertEquals(fontMap.get("monospace"), Typeface.MONOSPACE); // Test defaults assertEquals(fontMap.get("sans-serif"), Typeface.defaultFromStyle(Typeface.NORMAL)); for (int style : STYLES) { String msg = "style = " + style; assertNotNull(msg, Typeface.defaultFromStyle(style)); assertEquals(msg, style, Typeface.defaultFromStyle(style).getStyle()); } // Test create() assertEquals(fontMap.get("sample"), Typeface.create("sample", Typeface.NORMAL)); assertEquals( fontMap.get("sample-italic"), Typeface.create("sample-italic", Typeface.ITALIC)); } private static float measureText(Typeface typeface, String text) { Paint paint = new Paint(); paint.setTypeface(typeface); Loading graphics/java/android/graphics/Typeface.java +175 −72 Original line number Diff line number Diff line Loading @@ -34,8 +34,12 @@ import android.graphics.fonts.FontVariationAxis; import android.graphics.fonts.SystemFonts; import android.os.Build; import android.os.ParcelFileDescriptor; import android.os.SharedMemory; import android.os.Trace; import android.provider.FontRequest; import android.provider.FontsContract; import android.system.ErrnoException; import android.system.OsConstants; import android.text.FontConfig; import android.util.Base64; import android.util.LongSparseArray; Loading @@ -50,6 +54,7 @@ import dalvik.annotation.optimization.CriticalNative; import libcore.util.NativeAllocationRegistry; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileDescriptor; import java.io.IOException; Loading @@ -57,6 +62,7 @@ import java.io.InputStream; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; Loading @@ -79,19 +85,19 @@ public class Typeface { Typeface.class.getClassLoader(), nativeGetReleaseFunc()); /** The default NORMAL typeface object */ public static final Typeface DEFAULT; public static final Typeface DEFAULT = null; /** * The default BOLD typeface object. Note: this may be not actually be * bold, depending on what fonts are installed. Call getStyle() to know * for sure. */ public static final Typeface DEFAULT_BOLD; public static final Typeface DEFAULT_BOLD = null; /** The NORMAL style of the default sans serif typeface. */ public static final Typeface SANS_SERIF; public static final Typeface SANS_SERIF = null; /** The NORMAL style of the default serif typeface. */ public static final Typeface SERIF; public static final Typeface SERIF = null; /** The NORMAL style of the default monospace typeface. */ public static final Typeface MONOSPACE; public static final Typeface MONOSPACE = null; /** * The default {@link Typeface}s for different text styles. Loading @@ -99,6 +105,7 @@ public class Typeface { * It shouldn't be changed for app wide typeface settings. Please use theme and font XML for * the same purpose. */ @GuardedBy("SYSTEM_FONT_MAP_LOCK") @UnsupportedAppUsage(trackingBug = 123769446) static Typeface[] sDefaults; Loading @@ -125,16 +132,28 @@ public class Typeface { private static final LruCache<String, Typeface> sDynamicTypefaceCache = new LruCache<>(16); private static final Object sDynamicCacheLock = new Object(); @GuardedBy("SYSTEM_FONT_MAP_LOCK") static Typeface sDefaultTypeface; // Following two fields are not used but left for hiddenapi private list /** * sSystemFontMap is read only and unmodifiable. * Use public API {@link #create(String, int)} to get the typeface for given familyName. */ @GuardedBy("SYSTEM_FONT_MAP_LOCK") @UnsupportedAppUsage(trackingBug = 123769347) static final Map<String, Typeface> sSystemFontMap; static final Map<String, Typeface> sSystemFontMap = new HashMap<>(); // DirectByteBuffer object to hold sSystemFontMap's backing memory mapping. @GuardedBy("SYSTEM_FONT_MAP_LOCK") static ByteBuffer sSystemFontMapBuffer = null; // Lock to guard sSystemFontMap and derived default or public typefaces. // sStyledCacheLock may be held while this lock is held. Holding them in the reverse order may // introduce deadlock. private static final Object SYSTEM_FONT_MAP_LOCK = new Object(); // This field is used but left for hiddenapi private list // We cannot support sSystemFallbackMap since we will migrate to public FontFamily API. /** * @deprecated Use {@link android.graphics.fonts.FontFamily} instead. Loading Loading @@ -187,9 +206,17 @@ public class Typeface { */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) private static void setDefault(Typeface t) { synchronized (SYSTEM_FONT_MAP_LOCK) { sDefaultTypeface = t; nativeSetDefault(t.native_instance); } } private static Typeface getDefault() { synchronized (SYSTEM_FONT_MAP_LOCK) { return sDefaultTypeface; } } /** Returns the typeface's weight value */ public @IntRange(from = 0, to = 1000) int getWeight() { Loading Loading @@ -833,7 +860,7 @@ public class Typeface { style = NORMAL; } if (family == null) { family = sDefaultTypeface; family = getDefault(); } // Return early if we're asked for the same face/style Loading Loading @@ -902,7 +929,7 @@ public class Typeface { @IntRange(from = 1, to = 1000) int weight, boolean italic) { Preconditions.checkArgumentInRange(weight, 0, 1000, "weight"); if (family == null) { family = sDefaultTypeface; family = getDefault(); } return createWeightStyle(family, weight, italic); } Loading Loading @@ -944,8 +971,10 @@ public class Typeface { * @return the default typeface that corresponds to the style */ public static Typeface defaultFromStyle(@Style int style) { synchronized (SYSTEM_FONT_MAP_LOCK) { return sDefaults[style]; } } /** * Create a new typeface from the specified font data. Loading Loading @@ -1137,11 +1166,104 @@ public class Typeface { } } static { final HashMap<String, Typeface> systemFontMap = new HashMap<>(); initSystemDefaultTypefaces(systemFontMap, SystemFonts.getRawSystemFallbackMap(), SystemFonts.getAliases()); sSystemFontMap = Collections.unmodifiableMap(systemFontMap); /** @hide */ public static SharedMemory serializeFontMap(Map<String, Typeface> fontMap) throws IOException, ErrnoException { long[] nativePtrs = new long[fontMap.size()]; // The name table will not be large, so let's create a byte array in memory. ByteArrayOutputStream namesBytes = new ByteArrayOutputStream(); int i = 0; for (Map.Entry<String, Typeface> entry : fontMap.entrySet()) { nativePtrs[i++] = entry.getValue().native_instance; writeString(namesBytes, entry.getKey()); } int typefacesBytesCount = nativeWriteTypefaces(null, nativePtrs); // int (typefacesBytesCount), typefaces, namesBytes SharedMemory sharedMemory = SharedMemory.create( "fontMap", Integer.BYTES + typefacesBytesCount + namesBytes.size()); ByteBuffer writableBuffer = sharedMemory.mapReadWrite().order(ByteOrder.BIG_ENDIAN); try { writableBuffer.putInt(typefacesBytesCount); int writtenBytesCount = nativeWriteTypefaces(writableBuffer.slice(), nativePtrs); if (writtenBytesCount != typefacesBytesCount) { throw new IOException(String.format("Unexpected bytes written: %d, expected: %d", writtenBytesCount, typefacesBytesCount)); } writableBuffer.position(writableBuffer.position() + writtenBytesCount); writableBuffer.put(namesBytes.toByteArray()); } finally { SharedMemory.unmap(writableBuffer); } sharedMemory.setProtect(OsConstants.PROT_READ); return sharedMemory; } // buffer's byte order should be BIG_ENDIAN. /** @hide */ @VisibleForTesting public static Map<String, Typeface> deserializeFontMap(ByteBuffer buffer) throws IOException { Map<String, Typeface> fontMap = new HashMap<>(); int typefacesBytesCount = buffer.getInt(); long[] nativePtrs = nativeReadTypefaces(buffer.slice()); if (nativePtrs == null) { throw new IOException("Could not read typefaces"); } buffer.position(buffer.position() + typefacesBytesCount); for (long nativePtr : nativePtrs) { String name = readString(buffer); fontMap.put(name, new Typeface(nativePtr)); } return fontMap; } private static String readString(ByteBuffer buffer) { int length = buffer.getInt(); byte[] bytes = new byte[length]; buffer.get(bytes); return new String(bytes); } private static void writeString(ByteArrayOutputStream bos, String value) throws IOException { byte[] bytes = value.getBytes(); writeInt(bos, bytes.length); bos.write(bytes); } private static void writeInt(ByteArrayOutputStream bos, int value) { // Write in the big endian order. bos.write((value >> 24) & 0xFF); bos.write((value >> 16) & 0xFF); bos.write((value >> 8) & 0xFF); bos.write(value & 0xFF); } /** * Deserialize font map and set it as system font map. This method should be called at most once * per process. */ /** @hide */ public static void setSystemFontMap(SharedMemory sharedMemory) throws IOException, ErrnoException { if (sSystemFontMapBuffer != null) { throw new UnsupportedOperationException( "Once set, buffer-based system font map cannot be updated"); } Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "setSystemFontMap"); try { sSystemFontMapBuffer = sharedMemory.mapReadOnly().order(ByteOrder.BIG_ENDIAN); Map<String, Typeface> systemFontMap = deserializeFontMap(sSystemFontMapBuffer); setSystemFontMap(systemFontMap); } finally { Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS); } } /** @hide */ @VisibleForTesting public static void setSystemFontMap(Map<String, Typeface> systemFontMap) { synchronized (SYSTEM_FONT_MAP_LOCK) { sSystemFontMap.clear(); sSystemFontMap.putAll(systemFontMap); // We can't assume DEFAULT_FAMILY available on Roboletric. if (sSystemFontMap.containsKey(DEFAULT_FAMILY)) { Loading @@ -1149,11 +1271,12 @@ public class Typeface { } // Set up defaults and typefaces exposed in public API DEFAULT = create((String) null, 0); DEFAULT_BOLD = create((String) null, Typeface.BOLD); SANS_SERIF = create("sans-serif", 0); SERIF = create("serif", 0); MONOSPACE = create("monospace", 0); // Use sDefaultTypeface here, because create(String, int) uses DEFAULT as fallback. nativeForceSetStaticFinalField("DEFAULT", create(sDefaultTypeface, 0)); nativeForceSetStaticFinalField("DEFAULT_BOLD", create(sDefaultTypeface, Typeface.BOLD)); nativeForceSetStaticFinalField("SANS_SERIF", create("sans-serif", 0)); nativeForceSetStaticFinalField("SERIF", create("serif", 0)); nativeForceSetStaticFinalField("MONOSPACE", create("monospace", 0)); sDefaults = new Typeface[]{ DEFAULT, Loading @@ -1172,6 +1295,14 @@ public class Typeface { registerGenericFamilyNative(genericFamily, systemFontMap.get(genericFamily)); } } } static { final HashMap<String, Typeface> systemFontMap = new HashMap<>(); initSystemDefaultTypefaces(systemFontMap, SystemFonts.getRawSystemFallbackMap(), SystemFonts.getAliases()); setSystemFontMap(systemFontMap); } @Override public boolean equals(Object o) { Loading Loading @@ -1210,36 +1341,6 @@ public class Typeface { 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); Loading Loading @@ -1270,4 +1371,6 @@ public class Typeface { @Nullable ByteBuffer buffer, @NonNull long[] nativePtrs); private static native @Nullable long[] nativeReadTypefaces(@NonNull ByteBuffer buffer); private static native void nativeForceSetStaticFinalField(String fieldName, Typeface typeface); } libs/hwui/jni/Typeface.cpp +16 −0 Original line number Diff line number Diff line Loading @@ -217,6 +217,20 @@ static jlongArray Typeface_readTypefaces(JNIEnv *env, jobject, jobject buffer) { return result; } static void Typeface_forceSetStaticFinalField(JNIEnv *env, jclass cls, jstring fieldName, jobject typeface) { ScopedUtfChars fieldNameChars(env, fieldName); jfieldID fid = env->GetStaticFieldID(cls, fieldNameChars.c_str(), "Landroid/graphics/Typeface;"); if (fid == 0) { jniThrowRuntimeException(env, "Unable to find field"); return; } env->SetStaticObjectField(cls, fid, typeface); } /////////////////////////////////////////////////////////////////////////////// static const JNINativeMethod gTypefaceMethods[] = { Loading @@ -237,6 +251,8 @@ static const JNINativeMethod gTypefaceMethods[] = { (void*)Typeface_registerGenericFamily }, { "nativeWriteTypefaces", "(Ljava/nio/ByteBuffer;[J)I", (void*)Typeface_writeTypefaces}, { "nativeReadTypefaces", "(Ljava/nio/ByteBuffer;)[J", (void*)Typeface_readTypefaces}, { "nativeForceSetStaticFinalField", "(Ljava/lang/String;Landroid/graphics/Typeface;)V", (void*)Typeface_forceSetStaticFinalField }, }; int register_android_graphics_Typeface(JNIEnv* env) Loading Loading
core/tests/coretests/src/android/graphics/TypefaceTest.java +64 −12 Original line number Diff line number Diff line Loading @@ -23,6 +23,8 @@ import static org.junit.Assert.assertTrue; import android.content.Context; import android.content.res.AssetManager; import android.content.res.Resources; import android.graphics.fonts.SystemFonts; import android.os.SharedMemory; import androidx.test.InstrumentationRegistry; import androidx.test.filters.LargeTest; Loading @@ -35,9 +37,9 @@ import com.android.frameworks.coretests.R; import org.junit.Test; import org.junit.runner.RunWith; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.List; import java.nio.ByteOrder; import java.util.HashMap; import java.util.Map; import java.util.Random; @RunWith(AndroidJUnit4.class) Loading @@ -54,6 +56,10 @@ public class TypefaceTest { Typeface.create(Typeface.MONOSPACE, 0) }; private static final int[] STYLES = { Typeface.NORMAL, Typeface.BOLD, Typeface.ITALIC, Typeface.BOLD_ITALIC, }; @SmallTest @Test public void testBasic() throws Exception { Loading @@ -64,6 +70,16 @@ public class TypefaceTest { assertTrue("basic", Typeface.MONOSPACE != null); } @SmallTest @Test public void testDefaults() { for (int style : STYLES) { String msg = "style = " + style; assertNotNull(msg, Typeface.defaultFromStyle(style)); assertEquals(msg, style, Typeface.defaultFromStyle(style).getStyle()); } } @SmallTest @Test public void testUnique() throws Exception { Loading Loading @@ -178,21 +194,57 @@ public class TypefaceTest { @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); HashMap<String, Typeface> systemFontMap = new HashMap<>(); Typeface.initSystemDefaultTypefaces(systemFontMap, SystemFonts.getRawSystemFallbackMap(), SystemFonts.getAliases()); SharedMemory sharedMemory = Typeface.serializeFontMap(systemFontMap); Map<String, Typeface> copiedFontMap = Typeface.deserializeFontMap(sharedMemory.mapReadOnly().order(ByteOrder.BIG_ENDIAN)); assertEquals(systemFontMap.size(), copiedFontMap.size()); for (String key : systemFontMap.keySet()) { assertTrue(copiedFontMap.containsKey(key)); Typeface original = systemFontMap.get(key); Typeface copied = copiedFontMap.get(key); assertEquals(original.getStyle(), copied.getStyle()); assertEquals(original.getWeight(), copied.getWeight()); assertEquals(measureText(original, "hello"), measureText(copied, "hello"), 1e-6); } } @SmallTest @Test public void testSetSystemFontMap() throws Exception { Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); Resources res = context.getResources(); Map<String, Typeface> fontMap = Map.of( "sans-serif", Typeface.create(res.getFont(R.font.samplefont), Typeface.NORMAL), "serif", Typeface.create(res.getFont(R.font.samplefont2), Typeface.NORMAL), "monospace", Typeface.create(res.getFont(R.font.samplefont3), Typeface.NORMAL), "sample", Typeface.create(res.getFont(R.font.samplefont4), Typeface.NORMAL), "sample-italic", Typeface.create(res.getFont(R.font.samplefont4), Typeface.ITALIC)); Typeface.setSystemFontMap(fontMap); // Test public static final fields assertEquals(fontMap.get("sans-serif"), Typeface.DEFAULT); assertEquals(Typeface.BOLD, Typeface.DEFAULT_BOLD.getStyle()); assertEquals(fontMap.get("sans-serif"), Typeface.SANS_SERIF); assertEquals(fontMap.get("serif"), Typeface.SERIF); assertEquals(fontMap.get("monospace"), Typeface.MONOSPACE); // Test defaults assertEquals(fontMap.get("sans-serif"), Typeface.defaultFromStyle(Typeface.NORMAL)); for (int style : STYLES) { String msg = "style = " + style; assertNotNull(msg, Typeface.defaultFromStyle(style)); assertEquals(msg, style, Typeface.defaultFromStyle(style).getStyle()); } // Test create() assertEquals(fontMap.get("sample"), Typeface.create("sample", Typeface.NORMAL)); assertEquals( fontMap.get("sample-italic"), Typeface.create("sample-italic", Typeface.ITALIC)); } private static float measureText(Typeface typeface, String text) { Paint paint = new Paint(); paint.setTypeface(typeface); Loading
graphics/java/android/graphics/Typeface.java +175 −72 Original line number Diff line number Diff line Loading @@ -34,8 +34,12 @@ import android.graphics.fonts.FontVariationAxis; import android.graphics.fonts.SystemFonts; import android.os.Build; import android.os.ParcelFileDescriptor; import android.os.SharedMemory; import android.os.Trace; import android.provider.FontRequest; import android.provider.FontsContract; import android.system.ErrnoException; import android.system.OsConstants; import android.text.FontConfig; import android.util.Base64; import android.util.LongSparseArray; Loading @@ -50,6 +54,7 @@ import dalvik.annotation.optimization.CriticalNative; import libcore.util.NativeAllocationRegistry; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileDescriptor; import java.io.IOException; Loading @@ -57,6 +62,7 @@ import java.io.InputStream; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; Loading @@ -79,19 +85,19 @@ public class Typeface { Typeface.class.getClassLoader(), nativeGetReleaseFunc()); /** The default NORMAL typeface object */ public static final Typeface DEFAULT; public static final Typeface DEFAULT = null; /** * The default BOLD typeface object. Note: this may be not actually be * bold, depending on what fonts are installed. Call getStyle() to know * for sure. */ public static final Typeface DEFAULT_BOLD; public static final Typeface DEFAULT_BOLD = null; /** The NORMAL style of the default sans serif typeface. */ public static final Typeface SANS_SERIF; public static final Typeface SANS_SERIF = null; /** The NORMAL style of the default serif typeface. */ public static final Typeface SERIF; public static final Typeface SERIF = null; /** The NORMAL style of the default monospace typeface. */ public static final Typeface MONOSPACE; public static final Typeface MONOSPACE = null; /** * The default {@link Typeface}s for different text styles. Loading @@ -99,6 +105,7 @@ public class Typeface { * It shouldn't be changed for app wide typeface settings. Please use theme and font XML for * the same purpose. */ @GuardedBy("SYSTEM_FONT_MAP_LOCK") @UnsupportedAppUsage(trackingBug = 123769446) static Typeface[] sDefaults; Loading @@ -125,16 +132,28 @@ public class Typeface { private static final LruCache<String, Typeface> sDynamicTypefaceCache = new LruCache<>(16); private static final Object sDynamicCacheLock = new Object(); @GuardedBy("SYSTEM_FONT_MAP_LOCK") static Typeface sDefaultTypeface; // Following two fields are not used but left for hiddenapi private list /** * sSystemFontMap is read only and unmodifiable. * Use public API {@link #create(String, int)} to get the typeface for given familyName. */ @GuardedBy("SYSTEM_FONT_MAP_LOCK") @UnsupportedAppUsage(trackingBug = 123769347) static final Map<String, Typeface> sSystemFontMap; static final Map<String, Typeface> sSystemFontMap = new HashMap<>(); // DirectByteBuffer object to hold sSystemFontMap's backing memory mapping. @GuardedBy("SYSTEM_FONT_MAP_LOCK") static ByteBuffer sSystemFontMapBuffer = null; // Lock to guard sSystemFontMap and derived default or public typefaces. // sStyledCacheLock may be held while this lock is held. Holding them in the reverse order may // introduce deadlock. private static final Object SYSTEM_FONT_MAP_LOCK = new Object(); // This field is used but left for hiddenapi private list // We cannot support sSystemFallbackMap since we will migrate to public FontFamily API. /** * @deprecated Use {@link android.graphics.fonts.FontFamily} instead. Loading Loading @@ -187,9 +206,17 @@ public class Typeface { */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) private static void setDefault(Typeface t) { synchronized (SYSTEM_FONT_MAP_LOCK) { sDefaultTypeface = t; nativeSetDefault(t.native_instance); } } private static Typeface getDefault() { synchronized (SYSTEM_FONT_MAP_LOCK) { return sDefaultTypeface; } } /** Returns the typeface's weight value */ public @IntRange(from = 0, to = 1000) int getWeight() { Loading Loading @@ -833,7 +860,7 @@ public class Typeface { style = NORMAL; } if (family == null) { family = sDefaultTypeface; family = getDefault(); } // Return early if we're asked for the same face/style Loading Loading @@ -902,7 +929,7 @@ public class Typeface { @IntRange(from = 1, to = 1000) int weight, boolean italic) { Preconditions.checkArgumentInRange(weight, 0, 1000, "weight"); if (family == null) { family = sDefaultTypeface; family = getDefault(); } return createWeightStyle(family, weight, italic); } Loading Loading @@ -944,8 +971,10 @@ public class Typeface { * @return the default typeface that corresponds to the style */ public static Typeface defaultFromStyle(@Style int style) { synchronized (SYSTEM_FONT_MAP_LOCK) { return sDefaults[style]; } } /** * Create a new typeface from the specified font data. Loading Loading @@ -1137,11 +1166,104 @@ public class Typeface { } } static { final HashMap<String, Typeface> systemFontMap = new HashMap<>(); initSystemDefaultTypefaces(systemFontMap, SystemFonts.getRawSystemFallbackMap(), SystemFonts.getAliases()); sSystemFontMap = Collections.unmodifiableMap(systemFontMap); /** @hide */ public static SharedMemory serializeFontMap(Map<String, Typeface> fontMap) throws IOException, ErrnoException { long[] nativePtrs = new long[fontMap.size()]; // The name table will not be large, so let's create a byte array in memory. ByteArrayOutputStream namesBytes = new ByteArrayOutputStream(); int i = 0; for (Map.Entry<String, Typeface> entry : fontMap.entrySet()) { nativePtrs[i++] = entry.getValue().native_instance; writeString(namesBytes, entry.getKey()); } int typefacesBytesCount = nativeWriteTypefaces(null, nativePtrs); // int (typefacesBytesCount), typefaces, namesBytes SharedMemory sharedMemory = SharedMemory.create( "fontMap", Integer.BYTES + typefacesBytesCount + namesBytes.size()); ByteBuffer writableBuffer = sharedMemory.mapReadWrite().order(ByteOrder.BIG_ENDIAN); try { writableBuffer.putInt(typefacesBytesCount); int writtenBytesCount = nativeWriteTypefaces(writableBuffer.slice(), nativePtrs); if (writtenBytesCount != typefacesBytesCount) { throw new IOException(String.format("Unexpected bytes written: %d, expected: %d", writtenBytesCount, typefacesBytesCount)); } writableBuffer.position(writableBuffer.position() + writtenBytesCount); writableBuffer.put(namesBytes.toByteArray()); } finally { SharedMemory.unmap(writableBuffer); } sharedMemory.setProtect(OsConstants.PROT_READ); return sharedMemory; } // buffer's byte order should be BIG_ENDIAN. /** @hide */ @VisibleForTesting public static Map<String, Typeface> deserializeFontMap(ByteBuffer buffer) throws IOException { Map<String, Typeface> fontMap = new HashMap<>(); int typefacesBytesCount = buffer.getInt(); long[] nativePtrs = nativeReadTypefaces(buffer.slice()); if (nativePtrs == null) { throw new IOException("Could not read typefaces"); } buffer.position(buffer.position() + typefacesBytesCount); for (long nativePtr : nativePtrs) { String name = readString(buffer); fontMap.put(name, new Typeface(nativePtr)); } return fontMap; } private static String readString(ByteBuffer buffer) { int length = buffer.getInt(); byte[] bytes = new byte[length]; buffer.get(bytes); return new String(bytes); } private static void writeString(ByteArrayOutputStream bos, String value) throws IOException { byte[] bytes = value.getBytes(); writeInt(bos, bytes.length); bos.write(bytes); } private static void writeInt(ByteArrayOutputStream bos, int value) { // Write in the big endian order. bos.write((value >> 24) & 0xFF); bos.write((value >> 16) & 0xFF); bos.write((value >> 8) & 0xFF); bos.write(value & 0xFF); } /** * Deserialize font map and set it as system font map. This method should be called at most once * per process. */ /** @hide */ public static void setSystemFontMap(SharedMemory sharedMemory) throws IOException, ErrnoException { if (sSystemFontMapBuffer != null) { throw new UnsupportedOperationException( "Once set, buffer-based system font map cannot be updated"); } Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "setSystemFontMap"); try { sSystemFontMapBuffer = sharedMemory.mapReadOnly().order(ByteOrder.BIG_ENDIAN); Map<String, Typeface> systemFontMap = deserializeFontMap(sSystemFontMapBuffer); setSystemFontMap(systemFontMap); } finally { Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS); } } /** @hide */ @VisibleForTesting public static void setSystemFontMap(Map<String, Typeface> systemFontMap) { synchronized (SYSTEM_FONT_MAP_LOCK) { sSystemFontMap.clear(); sSystemFontMap.putAll(systemFontMap); // We can't assume DEFAULT_FAMILY available on Roboletric. if (sSystemFontMap.containsKey(DEFAULT_FAMILY)) { Loading @@ -1149,11 +1271,12 @@ public class Typeface { } // Set up defaults and typefaces exposed in public API DEFAULT = create((String) null, 0); DEFAULT_BOLD = create((String) null, Typeface.BOLD); SANS_SERIF = create("sans-serif", 0); SERIF = create("serif", 0); MONOSPACE = create("monospace", 0); // Use sDefaultTypeface here, because create(String, int) uses DEFAULT as fallback. nativeForceSetStaticFinalField("DEFAULT", create(sDefaultTypeface, 0)); nativeForceSetStaticFinalField("DEFAULT_BOLD", create(sDefaultTypeface, Typeface.BOLD)); nativeForceSetStaticFinalField("SANS_SERIF", create("sans-serif", 0)); nativeForceSetStaticFinalField("SERIF", create("serif", 0)); nativeForceSetStaticFinalField("MONOSPACE", create("monospace", 0)); sDefaults = new Typeface[]{ DEFAULT, Loading @@ -1172,6 +1295,14 @@ public class Typeface { registerGenericFamilyNative(genericFamily, systemFontMap.get(genericFamily)); } } } static { final HashMap<String, Typeface> systemFontMap = new HashMap<>(); initSystemDefaultTypefaces(systemFontMap, SystemFonts.getRawSystemFallbackMap(), SystemFonts.getAliases()); setSystemFontMap(systemFontMap); } @Override public boolean equals(Object o) { Loading Loading @@ -1210,36 +1341,6 @@ public class Typeface { 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); Loading Loading @@ -1270,4 +1371,6 @@ public class Typeface { @Nullable ByteBuffer buffer, @NonNull long[] nativePtrs); private static native @Nullable long[] nativeReadTypefaces(@NonNull ByteBuffer buffer); private static native void nativeForceSetStaticFinalField(String fieldName, Typeface typeface); }
libs/hwui/jni/Typeface.cpp +16 −0 Original line number Diff line number Diff line Loading @@ -217,6 +217,20 @@ static jlongArray Typeface_readTypefaces(JNIEnv *env, jobject, jobject buffer) { return result; } static void Typeface_forceSetStaticFinalField(JNIEnv *env, jclass cls, jstring fieldName, jobject typeface) { ScopedUtfChars fieldNameChars(env, fieldName); jfieldID fid = env->GetStaticFieldID(cls, fieldNameChars.c_str(), "Landroid/graphics/Typeface;"); if (fid == 0) { jniThrowRuntimeException(env, "Unable to find field"); return; } env->SetStaticObjectField(cls, fid, typeface); } /////////////////////////////////////////////////////////////////////////////// static const JNINativeMethod gTypefaceMethods[] = { Loading @@ -237,6 +251,8 @@ static const JNINativeMethod gTypefaceMethods[] = { (void*)Typeface_registerGenericFamily }, { "nativeWriteTypefaces", "(Ljava/nio/ByteBuffer;[J)I", (void*)Typeface_writeTypefaces}, { "nativeReadTypefaces", "(Ljava/nio/ByteBuffer;)[J", (void*)Typeface_readTypefaces}, { "nativeForceSetStaticFinalField", "(Ljava/lang/String;Landroid/graphics/Typeface;)V", (void*)Typeface_forceSetStaticFinalField }, }; int register_android_graphics_Typeface(JNIEnv* env) Loading