1 /*
2  * Copyright (C) 2006 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.graphics;
18 
19 import static android.content.res.FontResourcesParser.FamilyResourceEntry;
20 import static android.content.res.FontResourcesParser.FontFamilyFilesResourceEntry;
21 import static android.content.res.FontResourcesParser.FontFileResourceEntry;
22 import static android.content.res.FontResourcesParser.ProviderResourceEntry;
23 
24 import android.annotation.IntDef;
25 import android.annotation.IntRange;
26 import android.annotation.NonNull;
27 import android.annotation.Nullable;
28 import android.annotation.TestApi;
29 import android.annotation.UiThread;
30 import android.compat.annotation.UnsupportedAppUsage;
31 import android.content.res.AssetManager;
32 import android.graphics.fonts.Font;
33 import android.graphics.fonts.FontFamily;
34 import android.graphics.fonts.FontStyle;
35 import android.graphics.fonts.FontVariationAxis;
36 import android.graphics.fonts.SystemFonts;
37 import android.icu.util.ULocale;
38 import android.os.Build;
39 import android.os.ParcelFileDescriptor;
40 import android.os.SharedMemory;
41 import android.os.SystemProperties;
42 import android.os.Trace;
43 import android.provider.FontRequest;
44 import android.provider.FontsContract;
45 import android.system.ErrnoException;
46 import android.system.OsConstants;
47 import android.text.FontConfig;
48 import android.util.ArrayMap;
49 import android.util.Base64;
50 import android.util.Log;
51 import android.util.LongSparseArray;
52 import android.util.LruCache;
53 import android.util.Pair;
54 import android.util.SparseArray;
55 
56 import com.android.internal.annotations.GuardedBy;
57 import com.android.internal.annotations.VisibleForTesting;
58 import com.android.internal.util.Preconditions;
59 
60 import dalvik.annotation.optimization.CriticalNative;
61 import dalvik.annotation.optimization.FastNative;
62 
63 import libcore.util.NativeAllocationRegistry;
64 
65 import java.io.ByteArrayOutputStream;
66 import java.io.File;
67 import java.io.FileDescriptor;
68 import java.io.IOException;
69 import java.io.InputStream;
70 import java.lang.annotation.Retention;
71 import java.lang.annotation.RetentionPolicy;
72 import java.nio.ByteBuffer;
73 import java.nio.ByteOrder;
74 import java.util.ArrayList;
75 import java.util.Arrays;
76 import java.util.Collections;
77 import java.util.List;
78 import java.util.Map;
79 import java.util.Objects;
80 
81 /**
82  * The Typeface class specifies the typeface and intrinsic style of a font.
83  * This is used in the paint, along with optionally Paint settings like
84  * textSize, textSkewX, textScaleX to specify
85  * how text appears when drawn (and measured).
86  */
87 public class Typeface {
88 
89     private static String TAG = "Typeface";
90 
91     /** @hide */
92     public static final boolean ENABLE_LAZY_TYPEFACE_INITIALIZATION = true;
93 
94     private static final NativeAllocationRegistry sRegistry =
95             NativeAllocationRegistry.createMalloced(
96             Typeface.class.getClassLoader(), nativeGetReleaseFunc());
97 
98     /** The default NORMAL typeface object */
99     public static final Typeface DEFAULT = null;
100     /**
101      * The default BOLD typeface object. Note: this may be not actually be
102      * bold, depending on what fonts are installed. Call getStyle() to know
103      * for sure.
104      */
105     public static final Typeface DEFAULT_BOLD = null;
106     /** The NORMAL style of the default sans serif typeface. */
107     public static final Typeface SANS_SERIF = null;
108     /** The NORMAL style of the default serif typeface. */
109     public static final Typeface SERIF = null;
110     /** The NORMAL style of the default monospace typeface. */
111     public static final Typeface MONOSPACE = null;
112 
113     /**
114      * The default {@link Typeface}s for different text styles.
115      * Call {@link #defaultFromStyle(int)} to get the default typeface for the given text style.
116      * It shouldn't be changed for app wide typeface settings. Please use theme and font XML for
117      * the same purpose.
118      */
119     @GuardedBy("SYSTEM_FONT_MAP_LOCK")
120     @UnsupportedAppUsage(trackingBug = 123769446)
121     static Typeface[] sDefaults;
122 
123     /**
124      * Cache for Typeface objects for style variant. Currently max size is 3.
125      */
126     @GuardedBy("sStyledCacheLock")
127     private static final LongSparseArray<SparseArray<Typeface>> sStyledTypefaceCache =
128             new LongSparseArray<>(3);
129     private static final Object sStyledCacheLock = new Object();
130 
131     /**
132      * Cache for Typeface objects for weight variant. Currently max size is 3.
133      */
134     @GuardedBy("sWeightCacheLock")
135     private static final LongSparseArray<SparseArray<Typeface>> sWeightTypefaceCache =
136             new LongSparseArray<>(3);
137     private static final Object sWeightCacheLock = new Object();
138 
139     /**
140      * Cache for Typeface objects dynamically loaded from assets. Currently max size is 16.
141      */
142     @GuardedBy("sDynamicCacheLock")
143     private static final LruCache<String, Typeface> sDynamicTypefaceCache = new LruCache<>(16);
144     private static final Object sDynamicCacheLock = new Object();
145 
146 
147     @GuardedBy("SYSTEM_FONT_MAP_LOCK")
148     static Typeface sDefaultTypeface;
149 
150     /**
151      * sSystemFontMap is read only and unmodifiable.
152      * Use public API {@link #create(String, int)} to get the typeface for given familyName.
153      */
154     @GuardedBy("SYSTEM_FONT_MAP_LOCK")
155     @UnsupportedAppUsage(trackingBug = 123769347)
156     static final Map<String, Typeface> sSystemFontMap = new ArrayMap<>();
157 
158     // DirectByteBuffer object to hold sSystemFontMap's backing memory mapping.
159     static ByteBuffer sSystemFontMapBuffer = null;
160     static SharedMemory sSystemFontMapSharedMemory = null;
161 
162     // Lock to guard sSystemFontMap and derived default or public typefaces.
163     // sStyledCacheLock may be held while this lock is held. Holding them in the reverse order may
164     // introduce deadlock.
165     private static final Object SYSTEM_FONT_MAP_LOCK = new Object();
166 
167     // This field is used but left for hiddenapi private list
168     // We cannot support sSystemFallbackMap since we will migrate to public FontFamily API.
169     /**
170      * @deprecated Use {@link android.graphics.fonts.FontFamily} instead.
171      */
172     @UnsupportedAppUsage(trackingBug = 123768928)
173     @Deprecated
174     static final Map<String, android.graphics.FontFamily[]> sSystemFallbackMap =
175             Collections.emptyMap();
176 
177     /**
178      * Returns the shared memory that used for creating Typefaces.
179      *
180      * @return A SharedMemory used for creating Typeface. Maybe null if the lazy initialization is
181      *         disabled or inside SystemServer or Zygote.
182      * @hide
183      */
184     @TestApi
getSystemFontMapSharedMemory()185     public static @Nullable SharedMemory getSystemFontMapSharedMemory() {
186         if (ENABLE_LAZY_TYPEFACE_INITIALIZATION) {
187             Objects.requireNonNull(sSystemFontMapSharedMemory);
188         }
189         return sSystemFontMapSharedMemory;
190     }
191 
192     /**
193      * @hide
194      */
195     @UnsupportedAppUsage
196     public final long native_instance;
197 
198     private final String mSystemFontFamilyName;
199 
200     private final Runnable mCleaner;
201 
202     /** @hide */
203     @IntDef(value = {NORMAL, BOLD, ITALIC, BOLD_ITALIC})
204     @Retention(RetentionPolicy.SOURCE)
205     public @interface Style {}
206 
207     // Style
208     public static final int NORMAL = 0;
209     public static final int BOLD = 1;
210     public static final int ITALIC = 2;
211     public static final int BOLD_ITALIC = 3;
212     /** @hide */ public static final int STYLE_MASK = 0x03;
213 
214     @UnsupportedAppUsage
215     private @Style final int mStyle;
216 
217     private @IntRange(from = 0, to = FontStyle.FONT_WEIGHT_MAX) final int mWeight;
218 
219     // Value for weight and italic. Indicates the value is resolved by font metadata.
220     // Must be the same as the C++ constant in core/jni/android/graphics/FontFamily.cpp
221     /** @hide */
222     public static final int RESOLVE_BY_FONT_TABLE = -1;
223     /**
224      * The key of the default font family.
225      * @hide
226      */
227     public static final String DEFAULT_FAMILY = "sans-serif";
228 
229     // Style value for building typeface.
230     private static final int STYLE_NORMAL = 0;
231     private static final int STYLE_ITALIC = 1;
232 
233     @GuardedBy("this")
234     private int[] mSupportedAxes;
235     private static final int[] EMPTY_AXES = {};
236 
237     /**
238      * Please use font in xml and also your application global theme to change the default Typeface.
239      * android:textViewStyle and its attribute android:textAppearance can be used in order to change
240      * typeface and other text related properties.
241      */
242     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
setDefault(Typeface t)243     private static void setDefault(Typeface t) {
244         synchronized (SYSTEM_FONT_MAP_LOCK) {
245             sDefaultTypeface = t;
246             nativeSetDefault(t.native_instance);
247         }
248     }
249 
getDefault()250     private static Typeface getDefault() {
251         synchronized (SYSTEM_FONT_MAP_LOCK) {
252             return sDefaultTypeface;
253         }
254     }
255 
256     /** Returns the typeface's weight value */
getWeight()257     public @IntRange(from = 0, to = 1000) int getWeight() {
258         return mWeight;
259     }
260 
261     /** Returns the typeface's intrinsic style attributes */
getStyle()262     public @Style int getStyle() {
263         return mStyle;
264     }
265 
266     /** Returns true if getStyle() has the BOLD bit set. */
isBold()267     public final boolean isBold() {
268         return (mStyle & BOLD) != 0;
269     }
270 
271     /** Returns true if getStyle() has the ITALIC bit set. */
isItalic()272     public final boolean isItalic() {
273         return (mStyle & ITALIC) != 0;
274     }
275 
276     /**
277      * Returns the system font family name if the typeface was created from a system font family,
278      * otherwise returns null.
279      */
getSystemFontFamilyName()280     public final @Nullable String getSystemFontFamilyName() {
281         return mSystemFontFamilyName;
282     }
283 
284     /**
285      * Returns true if the system has the font family with the name [familyName]. For example
286      * querying with "sans-serif" would check if the "sans-serif" family is defined in the system
287      * and return true if does.
288      *
289      * @param familyName The name of the font family, cannot be null. If null, exception will be
290      *                   thrown.
291      */
hasFontFamily(@onNull String familyName)292     private static boolean hasFontFamily(@NonNull String familyName) {
293         Objects.requireNonNull(familyName, "familyName cannot be null");
294         synchronized (SYSTEM_FONT_MAP_LOCK) {
295             return sSystemFontMap.containsKey(familyName);
296         }
297     }
298 
299     /**
300      * @hide
301      * Used by Resources to load a font resource of type xml.
302      */
303     @Nullable
createFromResources( FamilyResourceEntry entry, AssetManager mgr, String path)304     public static Typeface createFromResources(
305             FamilyResourceEntry entry, AssetManager mgr, String path) {
306         if (entry instanceof ProviderResourceEntry) {
307             final ProviderResourceEntry providerEntry = (ProviderResourceEntry) entry;
308 
309             String systemFontFamilyName = providerEntry.getSystemFontFamilyName();
310             if (systemFontFamilyName != null && hasFontFamily(systemFontFamilyName)) {
311                 return Typeface.create(systemFontFamilyName, NORMAL);
312             }
313             // Downloadable font
314             List<List<String>> givenCerts = providerEntry.getCerts();
315             List<List<byte[]>> certs = new ArrayList<>();
316             if (givenCerts != null) {
317                 for (int i = 0; i < givenCerts.size(); i++) {
318                     List<String> certSet = givenCerts.get(i);
319                     List<byte[]> byteArraySet = new ArrayList<>();
320                     for (int j = 0; j < certSet.size(); j++) {
321                         byteArraySet.add(Base64.decode(certSet.get(j), Base64.DEFAULT));
322                     }
323                     certs.add(byteArraySet);
324                 }
325             }
326             // Downloaded font and it wasn't cached, request it again and return a
327             // default font instead (nothing we can do now).
328             FontRequest request = new FontRequest(providerEntry.getAuthority(),
329                     providerEntry.getPackage(), providerEntry.getQuery(), certs);
330             Typeface typeface = FontsContract.getFontSync(request);
331             return typeface == null ? DEFAULT : typeface;
332         }
333 
334         Typeface typeface = findFromCache(mgr, path);
335         if (typeface != null) return typeface;
336 
337         // family is FontFamilyFilesResourceEntry
338         final FontFamilyFilesResourceEntry filesEntry = (FontFamilyFilesResourceEntry) entry;
339 
340         try {
341             FontFamily.Builder familyBuilder = null;
342             for (final FontFileResourceEntry fontFile : filesEntry.getEntries()) {
343                 final Font.Builder fontBuilder = new Font.Builder(mgr, fontFile.getFileName(),
344                         false /* isAsset */, AssetManager.COOKIE_UNKNOWN)
345                         .setTtcIndex(fontFile.getTtcIndex())
346                         .setFontVariationSettings(fontFile.getVariationSettings());
347                 if (fontFile.getWeight() != Typeface.RESOLVE_BY_FONT_TABLE) {
348                     fontBuilder.setWeight(fontFile.getWeight());
349                 }
350                 if (fontFile.getItalic() != Typeface.RESOLVE_BY_FONT_TABLE) {
351                     fontBuilder.setSlant(fontFile.getItalic() == FontFileResourceEntry.ITALIC
352                             ?  FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT);
353                 }
354 
355                 if (familyBuilder == null) {
356                     familyBuilder = new FontFamily.Builder(fontBuilder.build());
357                 } else {
358                     familyBuilder.addFont(fontBuilder.build());
359                 }
360             }
361             if (familyBuilder == null) {
362                 return Typeface.DEFAULT;
363             }
364             final FontFamily family = familyBuilder.build();
365             final FontStyle normal = new FontStyle(FontStyle.FONT_WEIGHT_NORMAL,
366                     FontStyle.FONT_SLANT_UPRIGHT);
367             Font bestFont = family.getFont(0);
368             int bestScore = normal.getMatchScore(bestFont.getStyle());
369             for (int i = 1; i < family.getSize(); ++i) {
370                 final Font candidate = family.getFont(i);
371                 final int score = normal.getMatchScore(candidate.getStyle());
372                 if (score < bestScore) {
373                     bestFont = candidate;
374                     bestScore = score;
375                 }
376             }
377             typeface = new Typeface.CustomFallbackBuilder(family)
378                     .setStyle(bestFont.getStyle())
379                     .build();
380         } catch (IllegalArgumentException e) {
381             // To be a compatible behavior with API28 or before, catch IllegalArgumentExcetpion
382             // thrown by native code and returns null.
383             return null;
384         } catch (IOException e) {
385             typeface = Typeface.DEFAULT;
386         }
387         synchronized (sDynamicCacheLock) {
388             final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */,
389                     null /* axes */, RESOLVE_BY_FONT_TABLE /* weight */,
390                     RESOLVE_BY_FONT_TABLE /* italic */, DEFAULT_FAMILY);
391             sDynamicTypefaceCache.put(key, typeface);
392         }
393         return typeface;
394     }
395 
396     /**
397      * Used by resources for cached loading if the font is available.
398      * @hide
399      */
findFromCache(AssetManager mgr, String path)400     public static Typeface findFromCache(AssetManager mgr, String path) {
401         synchronized (sDynamicCacheLock) {
402             final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */, null /* axes */,
403                     RESOLVE_BY_FONT_TABLE /* weight */, RESOLVE_BY_FONT_TABLE /* italic */,
404                     DEFAULT_FAMILY);
405             Typeface typeface = sDynamicTypefaceCache.get(key);
406             if (typeface != null) {
407                 return typeface;
408             }
409         }
410         return null;
411     }
412 
413     /**
414      * A builder class for creating new Typeface instance.
415      *
416      * <p>
417      * Examples,
418      * 1) Create Typeface from ttf file.
419      * <pre>
420      * <code>
421      * Typeface.Builder builder = new Typeface.Builder("your_font_file.ttf");
422      * Typeface typeface = builder.build();
423      * </code>
424      * </pre>
425      *
426      * 2) Create Typeface from ttc file in assets directory.
427      * <pre>
428      * <code>
429      * Typeface.Builder builder = new Typeface.Builder(getAssets(), "your_font_file.ttc");
430      * builder.setTtcIndex(2);  // Set index of font collection.
431      * Typeface typeface = builder.build();
432      * </code>
433      * </pre>
434      *
435      * 3) Create Typeface with variation settings.
436      * <pre>
437      * <code>
438      * Typeface.Builder builder = new Typeface.Builder("your_font_file.ttf");
439      * builder.setFontVariationSettings("'wght' 700, 'slnt' 20, 'ital' 1");
440      * builder.setWeight(700);  // Tell the system that this is a bold font.
441      * builder.setItalic(true);  // Tell the system that this is an italic style font.
442      * Typeface typeface = builder.build();
443      * </code>
444      * </pre>
445      * </p>
446      */
447     public static final class Builder {
448         /** @hide */
449         public static final int NORMAL_WEIGHT = 400;
450         /** @hide */
451         public static final int BOLD_WEIGHT = 700;
452 
453         // Kept for generating asset cache key.
454         private final AssetManager mAssetManager;
455         private final String mPath;
456 
457         private final @Nullable Font.Builder mFontBuilder;
458 
459         private String mFallbackFamilyName;
460 
461         private int mWeight = RESOLVE_BY_FONT_TABLE;
462         private int mItalic = RESOLVE_BY_FONT_TABLE;
463 
464         /**
465          * Constructs a builder with a file path.
466          *
467          * @param path The file object refers to the font file.
468          */
Builder(@onNull File path)469         public Builder(@NonNull File path) {
470             mFontBuilder = new Font.Builder(path);
471             mAssetManager = null;
472             mPath = null;
473         }
474 
475         /**
476          * Constructs a builder with a file descriptor.
477          *
478          * Caller is responsible for closing the passed file descriptor after {@link #build} is
479          * called.
480          *
481          * @param fd The file descriptor. The passed fd must be mmap-able.
482          */
Builder(@onNull FileDescriptor fd)483         public Builder(@NonNull FileDescriptor fd) {
484             Font.Builder builder;
485             try {
486                 builder = new Font.Builder(ParcelFileDescriptor.dup(fd));
487             } catch (IOException e) {
488                 // We cannot tell the error to developer at this moment since we cannot change the
489                 // public API signature. Instead, silently fallbacks to system fallback in the build
490                 // method as the same as other error cases.
491                 builder = null;
492             }
493             mFontBuilder = builder;
494             mAssetManager = null;
495             mPath = null;
496         }
497 
498         /**
499          * Constructs a builder with a file path.
500          *
501          * @param path The full path to the font file.
502          */
Builder(@onNull String path)503         public Builder(@NonNull String path) {
504             mFontBuilder = new Font.Builder(new File(path));
505             mAssetManager = null;
506             mPath = null;
507         }
508 
509         /**
510          * Constructs a builder from an asset manager and a file path in an asset directory.
511          *
512          * @param assetManager The application's asset manager
513          * @param path The file name of the font data in the asset directory
514          */
Builder(@onNull AssetManager assetManager, @NonNull String path)515         public Builder(@NonNull AssetManager assetManager, @NonNull String path) {
516             this(assetManager, path, true /* is asset */, 0 /* cookie */);
517         }
518 
519         /**
520          * Constructs a builder from an asset manager and a file path in an asset directory.
521          *
522          * @param assetManager The application's asset manager
523          * @param path The file name of the font data in the asset directory
524          * @param cookie a cookie for the asset
525          * @hide
526          */
Builder(@onNull AssetManager assetManager, @NonNull String path, boolean isAsset, int cookie)527         public Builder(@NonNull AssetManager assetManager, @NonNull String path, boolean isAsset,
528                 int cookie) {
529             mFontBuilder = new Font.Builder(assetManager, path, isAsset, cookie);
530             mAssetManager = assetManager;
531             mPath = path;
532         }
533 
534         /**
535          * Sets weight of the font.
536          *
537          * Tells the system the weight of the given font. If not provided, the system will resolve
538          * the weight value by reading font tables.
539          * @param weight a weight value.
540          */
setWeight(@ntRangefrom = 1, to = 1000) int weight)541         public Builder setWeight(@IntRange(from = 1, to = 1000) int weight) {
542             mWeight = weight;
543             mFontBuilder.setWeight(weight);
544             return this;
545         }
546 
547         /**
548          * Sets italic information of the font.
549          *
550          * Tells the system the style of the given font. If not provided, the system will resolve
551          * the style by reading font tables.
552          * @param italic {@code true} if the font is italic. Otherwise {@code false}.
553          */
setItalic(boolean italic)554         public Builder setItalic(boolean italic) {
555             mItalic = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
556             mFontBuilder.setSlant(mItalic);
557             return this;
558         }
559 
560         /**
561          * Sets an index of the font collection. See {@link android.R.attr#ttcIndex}.
562          *
563          * Can not be used for Typeface source. build() method will return null for invalid index.
564          * @param ttcIndex An index of the font collection. If the font source is not font
565          *                 collection, do not call this method or specify 0.
566          */
setTtcIndex(@ntRangefrom = 0) int ttcIndex)567         public Builder setTtcIndex(@IntRange(from = 0) int ttcIndex) {
568             mFontBuilder.setTtcIndex(ttcIndex);
569             return this;
570         }
571 
572         /**
573          * Sets a font variation settings.
574          *
575          * @param variationSettings See {@link android.widget.TextView#setFontVariationSettings}.
576          * @throws IllegalArgumentException If given string is not a valid font variation settings
577          *                                  format.
578          */
setFontVariationSettings(@ullable String variationSettings)579         public Builder setFontVariationSettings(@Nullable String variationSettings) {
580             mFontBuilder.setFontVariationSettings(variationSettings);
581             return this;
582         }
583 
584         /**
585          * Sets a font variation settings.
586          *
587          * @param axes An array of font variation axis tag-value pairs.
588          */
setFontVariationSettings(@ullable FontVariationAxis[] axes)589         public Builder setFontVariationSettings(@Nullable FontVariationAxis[] axes) {
590             mFontBuilder.setFontVariationSettings(axes);
591             return this;
592         }
593 
594         /**
595          * Sets a fallback family name.
596          *
597          * By specifying a fallback family name, a fallback Typeface will be returned if the
598          * {@link #build} method fails to create a Typeface from the provided font. The fallback
599          * family will be resolved with the provided weight and italic information specified by
600          * {@link #setWeight} and {@link #setItalic}.
601          *
602          * If {@link #setWeight} is not called, the fallback family keeps the default weight.
603          * Similarly, if {@link #setItalic} is not called, the fallback family keeps the default
604          * italic information. For example, calling {@code builder.setFallback("sans-serif-light")}
605          * is equivalent to calling {@code builder.setFallback("sans-serif").setWeight(300)} in
606          * terms of fallback. The default weight and italic information are overridden by calling
607          * {@link #setWeight} and {@link #setItalic}. For example, if a Typeface is constructed
608          * using {@code builder.setFallback("sans-serif-light").setWeight(700)}, the fallback text
609          * will render as sans serif bold.
610          *
611          * @param familyName A family name to be used for fallback if the provided font can not be
612          *                   used. By passing {@code null}, build() returns {@code null}.
613          *                   If {@link #setFallback} is not called on the builder, {@code null}
614          *                   is assumed.
615          */
setFallback(@ullable String familyName)616         public Builder setFallback(@Nullable String familyName) {
617             mFallbackFamilyName = familyName;
618             return this;
619         }
620 
621         /**
622          * Creates a unique id for a given AssetManager and asset path.
623          *
624          * @param mgr  AssetManager instance
625          * @param path The path for the asset.
626          * @param ttcIndex The TTC index for the font.
627          * @param axes The font variation settings.
628          * @return Unique id for a given AssetManager and asset path.
629          */
createAssetUid(final AssetManager mgr, String path, int ttcIndex, @Nullable FontVariationAxis[] axes, int weight, int italic, String fallback)630         private static String createAssetUid(final AssetManager mgr, String path, int ttcIndex,
631                 @Nullable FontVariationAxis[] axes, int weight, int italic, String fallback) {
632             final SparseArray<String> pkgs = mgr.getAssignedPackageIdentifiers();
633             final StringBuilder builder = new StringBuilder();
634             final int size = pkgs.size();
635             for (int i = 0; i < size; i++) {
636                 builder.append(pkgs.valueAt(i));
637                 builder.append("-");
638             }
639             builder.append(path);
640             builder.append("-");
641             builder.append(Integer.toString(ttcIndex));
642             builder.append("-");
643             builder.append(Integer.toString(weight));
644             builder.append("-");
645             builder.append(Integer.toString(italic));
646             // Family name may contain hyphen. Use double hyphen for avoiding key conflicts before
647             // and after appending falblack name.
648             builder.append("--");
649             builder.append(fallback);
650             builder.append("--");
651             if (axes != null) {
652                 for (FontVariationAxis axis : axes) {
653                     builder.append(axis.getTag());
654                     builder.append("-");
655                     builder.append(Float.toString(axis.getStyleValue()));
656                 }
657             }
658             return builder.toString();
659         }
660 
resolveFallbackTypeface()661         private Typeface resolveFallbackTypeface() {
662             if (mFallbackFamilyName == null) {
663                 return null;
664             }
665 
666             final Typeface base =  getSystemDefaultTypeface(mFallbackFamilyName);
667             if (mWeight == RESOLVE_BY_FONT_TABLE && mItalic == RESOLVE_BY_FONT_TABLE) {
668                 return base;
669             }
670 
671             final int weight = (mWeight == RESOLVE_BY_FONT_TABLE) ? base.mWeight : mWeight;
672             final boolean italic =
673                     (mItalic == RESOLVE_BY_FONT_TABLE) ? (base.mStyle & ITALIC) != 0 : mItalic == 1;
674             return createWeightStyle(base, weight, italic);
675         }
676 
677         /**
678          * Generates new Typeface from specified configuration.
679          *
680          * @return Newly created Typeface. May return null if some parameters are invalid.
681          */
build()682         public Typeface build() {
683             if (mFontBuilder == null) {
684                 return resolveFallbackTypeface();
685             }
686             try {
687                 final Font font = mFontBuilder.build();
688                 final String key = mAssetManager == null ? null : createAssetUid(
689                         mAssetManager, mPath, font.getTtcIndex(), font.getAxes(),
690                         mWeight, mItalic,
691                         mFallbackFamilyName == null ? DEFAULT_FAMILY : mFallbackFamilyName);
692                 if (key != null) {
693                     // Dynamic cache lookup is only for assets.
694                     synchronized (sDynamicCacheLock) {
695                         final Typeface typeface = sDynamicTypefaceCache.get(key);
696                         if (typeface != null) {
697                             return typeface;
698                         }
699                     }
700                 }
701                 final FontFamily family = new FontFamily.Builder(font).build();
702                 final int weight = mWeight == RESOLVE_BY_FONT_TABLE
703                         ? font.getStyle().getWeight() : mWeight;
704                 final int slant = mItalic == RESOLVE_BY_FONT_TABLE
705                         ? font.getStyle().getSlant() : mItalic;
706                 final CustomFallbackBuilder builder = new CustomFallbackBuilder(family)
707                         .setStyle(new FontStyle(weight, slant));
708                 if (mFallbackFamilyName != null) {
709                     builder.setSystemFallback(mFallbackFamilyName);
710                 }
711                 final Typeface typeface = builder.build();
712                 if (key != null) {
713                     synchronized (sDynamicCacheLock) {
714                         sDynamicTypefaceCache.put(key, typeface);
715                     }
716                 }
717                 return typeface;
718             } catch (IOException | IllegalArgumentException e) {
719                 return resolveFallbackTypeface();
720             }
721         }
722     }
723 
724     /**
725      * A builder class for creating new Typeface instance.
726      *
727      * There are two font fallback mechanisms, custom font fallback and system font fallback.
728      * The custom font fallback is a simple ordered list. The text renderer tries to see if it can
729      * render a character with the first font and if that font does not support the character, try
730      * next one and so on. It will keep trying until end of the custom fallback chain. The maximum
731      * length of the custom fallback chain is 64.
732      * The system font fallback is a system pre-defined fallback chain. The system fallback is
733      * processed only when no matching font is found in the custom font fallback.
734      *
735      * <p>
736      * Examples,
737      * 1) Create Typeface from single ttf file.
738      * <pre>
739      * <code>
740      * Font font = new Font.Builder("your_font_file.ttf").build();
741      * FontFamily family = new FontFamily.Builder(font).build();
742      * Typeface typeface = new Typeface.CustomFallbackBuilder(family).build();
743      * </code>
744      * </pre>
745      *
746      * 2) Create Typeface from multiple font files and select bold style by default.
747      * <pre>
748      * <code>
749      * Font regularFont = new Font.Builder("regular.ttf").build();
750      * Font boldFont = new Font.Builder("bold.ttf").build();
751      * FontFamily family = new FontFamily.Builder(regularFont)
752      *     .addFont(boldFont).build();
753      * Typeface typeface = new Typeface.CustomFallbackBuilder(family)
754      *     .setWeight(Font.FONT_WEIGHT_BOLD)  // Set bold style as the default style.
755      *                                        // If the font family doesn't have bold style font,
756      *                                        // system will select the closest font.
757      *     .build();
758      * </code>
759      * </pre>
760      *
761      * 3) Create Typeface from single ttf file and if that font does not have glyph for the
762      * characters, use "serif" font family instead.
763      * <pre>
764      * <code>
765      * Font font = new Font.Builder("your_font_file.ttf").build();
766      * FontFamily family = new FontFamily.Builder(font).build();
767      * Typeface typeface = new Typeface.CustomFallbackBuilder(family)
768      *     .setSystemFallback("serif")  // Set serif font family as the fallback.
769      *     .build();
770      * </code>
771      * </pre>
772      * 4) Create Typeface from single ttf file and set another ttf file for the fallback.
773      * <pre>
774      * <code>
775      * Font font = new Font.Builder("English.ttf").build();
776      * FontFamily family = new FontFamily.Builder(font).build();
777      *
778      * Font fallbackFont = new Font.Builder("Arabic.ttf").build();
779      * FontFamily fallbackFamily = new FontFamily.Builder(fallbackFont).build();
780      * Typeface typeface = new Typeface.CustomFallbackBuilder(family)
781      *     .addCustomFallback(fallbackFamily)  // Specify fallback family.
782      *     .setSystemFallback("serif")  // Set serif font family as the fallback.
783      *     .build();
784      * </code>
785      * </pre>
786      * </p>
787      */
788     public static final class CustomFallbackBuilder {
789         private static final int MAX_CUSTOM_FALLBACK = 64;
790         private final ArrayList<FontFamily> mFamilies = new ArrayList<>();
791         private String mFallbackName = null;
792         private @Nullable FontStyle mStyle;
793 
794         /**
795          * Returns the maximum capacity of custom fallback families.
796          *
797          * This includes the first font family passed to the constructor.
798          * It is guaranteed that the value will be greater than or equal to 64.
799          *
800          * @return the maximum number of font families for the custom fallback
801          */
getMaxCustomFallbackCount()802         public static @IntRange(from = 64) int getMaxCustomFallbackCount() {
803             return MAX_CUSTOM_FALLBACK;
804         }
805 
806         /**
807          * Constructs a builder with a font family.
808          *
809          * @param family a family object
810          */
CustomFallbackBuilder(@onNull FontFamily family)811         public CustomFallbackBuilder(@NonNull FontFamily family) {
812             Preconditions.checkNotNull(family);
813             mFamilies.add(family);
814         }
815 
816         /**
817          * Sets a system fallback by name.
818          *
819          * You can specify generic font family names or OEM specific family names. If the system
820          * don't have a specified fallback, the default fallback is used instead.
821          * For more information about generic font families, see <a
822          * href="https://www.w3.org/TR/css-fonts-4/#generic-font-families">CSS specification</a>
823          *
824          * For more information about fallback, see class description.
825          *
826          * @param familyName a family name to be used for fallback if the provided fonts can not be
827          *                   used
828          */
setSystemFallback(@onNull String familyName)829         public @NonNull CustomFallbackBuilder setSystemFallback(@NonNull String familyName) {
830             Preconditions.checkNotNull(familyName);
831             mFallbackName = familyName;
832             return this;
833         }
834 
835         /**
836          * Sets a font style of the Typeface.
837          *
838          * If the font family doesn't have a font of given style, system will select the closest
839          * font from font family. For example, if a font family has fonts of 300 weight and 700
840          * weight then setWeight(400) is called, system will select the font of 300 weight.
841          *
842          * @param style a font style
843          */
setStyle(@onNull FontStyle style)844         public @NonNull CustomFallbackBuilder setStyle(@NonNull FontStyle style) {
845             mStyle = style;
846             return this;
847         }
848 
849         /**
850          * Append a font family to the end of the custom font fallback.
851          *
852          * You can set up to 64 custom fallback families including the first font family you passed
853          * to the constructor.
854          * For more information about fallback, see class description.
855          *
856          * @param family a fallback family
857          * @throws IllegalArgumentException if you give more than 64 custom fallback families
858          */
addCustomFallback(@onNull FontFamily family)859         public @NonNull CustomFallbackBuilder addCustomFallback(@NonNull FontFamily family) {
860             Preconditions.checkNotNull(family);
861             Preconditions.checkArgument(mFamilies.size() < getMaxCustomFallbackCount(),
862                     "Custom fallback limit exceeded(%d)", getMaxCustomFallbackCount());
863             mFamilies.add(family);
864             return this;
865         }
866 
867         /**
868          * Create the Typeface based on the configured values.
869          *
870          * @return the Typeface object
871          */
872         public @NonNull Typeface build() {
873             final int userFallbackSize = mFamilies.size();
874             final Typeface fallbackTypeface = getSystemDefaultTypeface(mFallbackName);
875             final long[] ptrArray = new long[userFallbackSize];
876             for (int i = 0; i < userFallbackSize; ++i) {
877                 ptrArray[i] = mFamilies.get(i).getNativePtr();
878             }
879             final int weight = mStyle == null ? 400 : mStyle.getWeight();
880             final int italic =
881                     (mStyle == null || mStyle.getSlant() == FontStyle.FONT_SLANT_UPRIGHT) ?  0 : 1;
882             return new Typeface(nativeCreateFromArray(
883                     ptrArray, fallbackTypeface.native_instance, weight, italic), null);
884         }
885     }
886 
887     /**
888      * Create a typeface object given a family name, and option style information.
889      * If null is passed for the name, then the "default" font will be chosen.
890      * The resulting typeface object can be queried (getStyle()) to discover what
891      * its "real" style characteristics are.
892      *
893      * @param familyName May be null. The name of the font family.
894      * @param style  The style (normal, bold, italic) of the typeface.
895      *               e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC
896      * @return The best matching typeface.
897      */
898     public static Typeface create(String familyName, @Style int style) {
899         return create(getSystemDefaultTypeface(familyName), style);
900     }
901 
902     /**
903      * Create a typeface object that best matches the specified existing
904      * typeface and the specified Style. Use this call if you want to pick a new
905      * style from the same family of an existing typeface object. If family is
906      * null, this selects from the default font's family.
907      *
908      * <p>
909      * This method is not thread safe on API 27 or before.
910      * This method is thread safe on API 28 or after.
911      * </p>
912      *
913      * @param family An existing {@link Typeface} object. In case of {@code null}, the default
914      *               typeface is used instead.
915      * @param style  The style (normal, bold, italic) of the typeface.
916      *               e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC
917      * @return The best matching typeface.
918      */
919     public static Typeface create(Typeface family, @Style int style) {
920         if ((style & ~STYLE_MASK) != 0) {
921             style = NORMAL;
922         }
923         if (family == null) {
924             family = getDefault();
925         }
926 
927         // Return early if we're asked for the same face/style
928         if (family.mStyle == style) {
929             return family;
930         }
931 
932         final long ni = family.native_instance;
933 
934         Typeface typeface;
935         synchronized (sStyledCacheLock) {
936             SparseArray<Typeface> styles = sStyledTypefaceCache.get(ni);
937 
938             if (styles == null) {
939                 styles = new SparseArray<Typeface>(4);
940                 sStyledTypefaceCache.put(ni, styles);
941             } else {
942                 typeface = styles.get(style);
943                 if (typeface != null) {
944                     return typeface;
945                 }
946             }
947 
948             typeface = new Typeface(nativeCreateFromTypeface(ni, style),
949                     family.getSystemFontFamilyName());
950             styles.put(style, typeface);
951         }
952         return typeface;
953     }
954 
955     /**
956      * Creates a typeface object that best matches the specified existing typeface and the specified
957      * weight and italic style
958      * <p>Below are numerical values and corresponding common weight names.</p>
959      * <table>
960      * <thead>
961      * <tr><th>Value</th><th>Common weight name</th></tr>
962      * </thead>
963      * <tbody>
964      * <tr><td>100</td><td>Thin</td></tr>
965      * <tr><td>200</td><td>Extra Light</td></tr>
966      * <tr><td>300</td><td>Light</td></tr>
967      * <tr><td>400</td><td>Normal</td></tr>
968      * <tr><td>500</td><td>Medium</td></tr>
969      * <tr><td>600</td><td>Semi Bold</td></tr>
970      * <tr><td>700</td><td>Bold</td></tr>
971      * <tr><td>800</td><td>Extra Bold</td></tr>
972      * <tr><td>900</td><td>Black</td></tr>
973      * </tbody>
974      * </table>
975      *
976      * <p>
977      * This method is thread safe.
978      * </p>
979      *
980      * @param family An existing {@link Typeface} object. In case of {@code null}, the default
981      *               typeface is used instead.
982      * @param weight The desired weight to be drawn.
983      * @param italic {@code true} if italic style is desired to be drawn. Otherwise, {@code false}
984      * @return A {@link Typeface} object for drawing specified weight and italic style. Never
985      *         returns {@code null}
986      *
987      * @see #getWeight()
988      * @see #isItalic()
989      */
990     public static @NonNull Typeface create(@Nullable Typeface family,
991             @IntRange(from = 1, to = 1000) int weight, boolean italic) {
992         Preconditions.checkArgumentInRange(weight, 0, 1000, "weight");
993         if (family == null) {
994             family = getDefault();
995         }
996         return createWeightStyle(family, weight, italic);
997     }
998 
999     private static @NonNull Typeface createWeightStyle(@NonNull Typeface base,
1000             @IntRange(from = 1, to = 1000) int weight, boolean italic) {
1001         final int key = (weight << 1) | (italic ? 1 : 0);
1002 
1003         Typeface typeface;
1004         synchronized(sWeightCacheLock) {
1005             SparseArray<Typeface> innerCache = sWeightTypefaceCache.get(base.native_instance);
1006             if (innerCache == null) {
1007                 innerCache = new SparseArray<>(4);
1008                 sWeightTypefaceCache.put(base.native_instance, innerCache);
1009             } else {
1010                 typeface = innerCache.get(key);
1011                 if (typeface != null) {
1012                     return typeface;
1013                 }
1014             }
1015 
1016             typeface = new Typeface(
1017                     nativeCreateFromTypefaceWithExactStyle(base.native_instance, weight, italic),
1018                     base.getSystemFontFamilyName());
1019             innerCache.put(key, typeface);
1020         }
1021         return typeface;
1022     }
1023 
1024     /** @hide */
1025     public static Typeface createFromTypefaceWithVariation(@Nullable Typeface family,
1026             @NonNull List<FontVariationAxis> axes) {
1027         final Typeface base = family == null ? Typeface.DEFAULT : family;
1028         Typeface typeface = new Typeface(
1029                 nativeCreateFromTypefaceWithVariation(base.native_instance, axes),
1030                 base.getSystemFontFamilyName());
1031         return typeface;
1032     }
1033 
1034     /**
1035      * Returns one of the default typeface objects, based on the specified style
1036      *
1037      * @return the default typeface that corresponds to the style
1038      */
1039     public static Typeface defaultFromStyle(@Style int style) {
1040         synchronized (SYSTEM_FONT_MAP_LOCK) {
1041             return sDefaults[style];
1042         }
1043     }
1044 
1045     /**
1046      * Create a new typeface from the specified font data.
1047      *
1048      * @param mgr  The application's asset manager
1049      * @param path The file name of the font data in the assets directory
1050      * @return The new typeface.
1051      */
1052     public static Typeface createFromAsset(AssetManager mgr, String path) {
1053         Preconditions.checkNotNull(path); // for backward compatibility
1054         Preconditions.checkNotNull(mgr);
1055 
1056         Typeface typeface = new Builder(mgr, path).build();
1057         if (typeface != null) return typeface;
1058         // check if the file exists, and throw an exception for backward compatibility
1059         try (InputStream inputStream = mgr.open(path)) {
1060         } catch (IOException e) {
1061             throw new RuntimeException("Font asset not found " + path);
1062         }
1063 
1064         return Typeface.DEFAULT;
1065     }
1066 
1067     /**
1068      * Creates a unique id for a given font provider and query.
1069      */
1070     private static String createProviderUid(String authority, String query) {
1071         final StringBuilder builder = new StringBuilder();
1072         builder.append("provider:");
1073         builder.append(authority);
1074         builder.append("-");
1075         builder.append(query);
1076         return builder.toString();
1077     }
1078 
1079     /**
1080      * Create a new typeface from the specified font file.
1081      *
1082      * @param file The path to the font data.
1083      * @return The new typeface.
1084      */
1085     public static Typeface createFromFile(@Nullable File file) {
1086         // For the compatibility reasons, leaving possible NPE here.
1087         // See android.graphics.cts.TypefaceTest#testCreateFromFileByFileReferenceNull
1088 
1089         Typeface typeface = new Builder(file).build();
1090         if (typeface != null) return typeface;
1091 
1092         // check if the file exists, and throw an exception for backward compatibility
1093         if (!file.exists()) {
1094             throw new RuntimeException("Font asset not found " + file.getAbsolutePath());
1095         }
1096 
1097         return Typeface.DEFAULT;
1098     }
1099 
1100     /**
1101      * Create a new typeface from the specified font file.
1102      *
1103      * @param path The full path to the font data.
1104      * @return The new typeface.
1105      */
1106     public static Typeface createFromFile(@Nullable String path) {
1107         Preconditions.checkNotNull(path); // for backward compatibility
1108         return createFromFile(new File(path));
1109     }
1110 
1111     /**
1112      * Create a new typeface from an array of font families.
1113      *
1114      * @param families array of font families
1115      * @deprecated
1116      */
1117     @Deprecated
1118     @UnsupportedAppUsage(trackingBug = 123768928)
1119     private static Typeface createFromFamilies(android.graphics.FontFamily[] families) {
1120         long[] ptrArray = new long[families.length];
1121         for (int i = 0; i < families.length; i++) {
1122             ptrArray[i] = families[i].mNativePtr;
1123         }
1124         return new Typeface(nativeCreateFromArray(
1125                 ptrArray, 0, RESOLVE_BY_FONT_TABLE,
1126                 RESOLVE_BY_FONT_TABLE), null);
1127     }
1128 
1129     /**
1130      * Create a new typeface from an array of android.graphics.fonts.FontFamily.
1131      *
1132      * @param families array of font families
1133      */
1134     private static Typeface createFromFamilies(@NonNull String familyName,
1135             @Nullable FontFamily[] families) {
1136         final long[] ptrArray = new long[families.length];
1137         for (int i = 0; i < families.length; ++i) {
1138             ptrArray[i] = families[i].getNativePtr();
1139         }
1140         return new Typeface(nativeCreateFromArray(ptrArray, 0,
1141                   RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE), familyName);
1142     }
1143 
1144     /**
1145      * This method is used by supportlib-v27.
1146      *
1147      * @deprecated Use {@link android.graphics.fonts.FontFamily} instead.
1148      */
1149     @UnsupportedAppUsage(trackingBug = 123768395)
1150     @Deprecated
1151     private static Typeface createFromFamiliesWithDefault(
1152             android.graphics.FontFamily[] families, int weight, int italic) {
1153         return createFromFamiliesWithDefault(families, DEFAULT_FAMILY, weight, italic);
1154     }
1155 
1156     /**
1157      * Create a new typeface from an array of font families, including
1158      * also the font families in the fallback list.
1159      * @param fallbackName the family name. If given families don't support characters, the
1160      *               characters will be rendered with this family.
1161      * @param weight the weight for this family. In that case, the table information in the first
1162      *               family's font is used. If the first family has multiple fonts, the closest to
1163      *               the regular weight and upright font is used.
1164      * @param italic the italic information for this family. In that case, the table information in
1165      *               the first family's font is used. If the first family has multiple fonts, the
1166      *               closest to the regular weight and upright font is used.
1167      * @param families array of font families
1168      *
1169      * @deprecated Use {@link android.graphics.fonts.FontFamily} instead.
1170      */
1171     @UnsupportedAppUsage(trackingBug = 123768928)
1172     @Deprecated
1173     private static Typeface createFromFamiliesWithDefault(android.graphics.FontFamily[] families,
1174                 String fallbackName, int weight, int italic) {
1175         final Typeface fallbackTypeface = getSystemDefaultTypeface(fallbackName);
1176         long[] ptrArray = new long[families.length];
1177         for (int i = 0; i < families.length; i++) {
1178             ptrArray[i] = families[i].mNativePtr;
1179         }
1180         return new Typeface(nativeCreateFromArray(
1181                 ptrArray, fallbackTypeface.native_instance, weight, italic), null);
1182     }
1183 
1184     // don't allow clients to call this directly
1185     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
1186     private Typeface(long ni) {
1187         this(ni, null);
1188     }
1189 
1190     // don't allow clients to call this directly
1191     private Typeface(long ni, @Nullable String systemFontFamilyName) {
1192         if (ni == 0) {
1193             throw new RuntimeException("native typeface cannot be made");
1194         }
1195 
1196         native_instance = ni;
1197         mCleaner = sRegistry.registerNativeAllocation(this, native_instance);
1198         mStyle = nativeGetStyle(ni);
1199         mWeight = nativeGetWeight(ni);
1200         mSystemFontFamilyName = systemFontFamilyName;
1201     }
1202 
1203     /**
1204      * Releases the underlying native object.
1205      *
1206      * <p>For testing only. Do not use the instance after this method is called.
1207      * It is safe to call this method twice or more on the same instance.
1208      * @hide
1209      */
1210     @TestApi
1211     public void releaseNativeObjectForTest() {
1212         mCleaner.run();
1213     }
1214 
1215     private static Typeface getSystemDefaultTypeface(@NonNull String familyName) {
1216         Typeface tf = sSystemFontMap.get(familyName);
1217         return tf == null ? Typeface.DEFAULT : tf;
1218     }
1219 
1220     /** @hide */
1221     @VisibleForTesting
1222     public static void initSystemDefaultTypefaces(Map<String, FontFamily[]> fallbacks,
1223             List<FontConfig.Alias> aliases,
1224             Map<String, Typeface> outSystemFontMap) {
1225         for (Map.Entry<String, FontFamily[]> entry : fallbacks.entrySet()) {
1226             outSystemFontMap.put(entry.getKey(),
1227                     createFromFamilies(entry.getKey(), entry.getValue()));
1228         }
1229 
1230         for (int i = 0; i < aliases.size(); ++i) {
1231             final FontConfig.Alias alias = aliases.get(i);
1232             if (outSystemFontMap.containsKey(alias.getName())) {
1233                 continue; // If alias and named family are conflict, use named family.
1234             }
1235             final Typeface base = outSystemFontMap.get(alias.getOriginal());
1236             if (base == null) {
1237                 // The missing target is a valid thing, some configuration don't have font files,
1238                 // e.g. wear devices. Just skip this alias.
1239                 continue;
1240             }
1241             final int weight = alias.getWeight();
1242             final Typeface newFace = weight == 400 ? base : new Typeface(
1243                     nativeCreateWeightAlias(base.native_instance, weight), alias.getName());
1244             outSystemFontMap.put(alias.getName(), newFace);
1245         }
1246     }
1247 
1248     private static void registerGenericFamilyNative(@NonNull String familyName,
1249             @Nullable Typeface typeface) {
1250         if (typeface != null) {
1251             nativeRegisterGenericFamily(familyName, typeface.native_instance);
1252         }
1253     }
1254 
1255     /**
1256      * Create a serialized system font mappings.
1257      *
1258      * @hide
1259      */
1260     @TestApi
1261     public static @NonNull SharedMemory serializeFontMap(@NonNull Map<String, Typeface> fontMap)
1262             throws IOException, ErrnoException {
1263         long[] nativePtrs = new long[fontMap.size()];
1264         // The name table will not be large, so let's create a byte array in memory.
1265         ByteArrayOutputStream namesBytes = new ByteArrayOutputStream();
1266         int i = 0;
1267         for (Map.Entry<String, Typeface> entry : fontMap.entrySet()) {
1268             nativePtrs[i++] = entry.getValue().native_instance;
1269             writeString(namesBytes, entry.getKey());
1270         }
1271         // int (typefacesBytesCount), typefaces, namesBytes
1272         final int typefaceBytesCountSize = Integer.BYTES;
1273         int typefacesBytesCount = nativeWriteTypefaces(null, typefaceBytesCountSize, nativePtrs);
1274         SharedMemory sharedMemory = SharedMemory.create(
1275                 "fontMap", typefaceBytesCountSize + typefacesBytesCount + namesBytes.size());
1276         ByteBuffer writableBuffer = sharedMemory.mapReadWrite().order(ByteOrder.BIG_ENDIAN);
1277         try {
1278             writableBuffer.putInt(typefacesBytesCount);
1279             int writtenBytesCount =
1280                     nativeWriteTypefaces(writableBuffer, writableBuffer.position(), nativePtrs);
1281             if (writtenBytesCount != typefacesBytesCount) {
1282                 throw new IOException(String.format("Unexpected bytes written: %d, expected: %d",
1283                         writtenBytesCount, typefacesBytesCount));
1284             }
1285             writableBuffer.position(writableBuffer.position() + writtenBytesCount);
1286             writableBuffer.put(namesBytes.toByteArray());
1287         } finally {
1288             SharedMemory.unmap(writableBuffer);
1289         }
1290         sharedMemory.setProtect(OsConstants.PROT_READ);
1291         return sharedMemory;
1292     }
1293 
1294     // buffer's byte order should be BIG_ENDIAN.
1295     /**
1296      * Deserialize the font mapping from the serialized byte buffer.
1297      *
1298      * <p>Warning: the given {@code buffer} must outlive generated Typeface
1299      * objects in {@code out}. In production code, this is guaranteed by
1300      * storing the buffer in {@link #sSystemFontMapBuffer}.
1301      * If you call this method in a test, please make sure to destroy the
1302      * generated Typeface objects by calling
1303      * {@link #releaseNativeObjectForTest()}.
1304      *
1305      * @hide
1306      */
1307     @TestApi
1308     public static @NonNull long[] deserializeFontMap(
1309             @NonNull ByteBuffer buffer, @NonNull Map<String, Typeface> out)
1310             throws IOException {
1311         int typefacesBytesCount = buffer.getInt();
1312         // Note: Do not call buffer.slice(), as nativeReadTypefaces() expects
1313         // that buffer.address() is page-aligned.
1314         long[] nativePtrs = nativeReadTypefaces(buffer, buffer.position());
1315         if (nativePtrs == null) {
1316             throw new IOException("Could not read typefaces");
1317         }
1318         out.clear();
1319         buffer.position(buffer.position() + typefacesBytesCount);
1320         for (long nativePtr : nativePtrs) {
1321             String name = readString(buffer);
1322             out.put(name, new Typeface(nativePtr, name));
1323         }
1324         return nativePtrs;
1325     }
1326 
1327     private static String readString(ByteBuffer buffer) {
1328         int length = buffer.getInt();
1329         byte[] bytes = new byte[length];
1330         buffer.get(bytes);
1331         return new String(bytes);
1332     }
1333 
1334     private static void writeString(ByteArrayOutputStream bos, String value) throws IOException {
1335         byte[] bytes = value.getBytes();
1336         writeInt(bos, bytes.length);
1337         bos.write(bytes);
1338     }
1339 
1340     private static void writeInt(ByteArrayOutputStream bos, int value) {
1341         // Write in the big endian order.
1342         bos.write((value >> 24) & 0xFF);
1343         bos.write((value >> 16) & 0xFF);
1344         bos.write((value >> 8) & 0xFF);
1345         bos.write(value & 0xFF);
1346     }
1347 
1348     /** @hide */
1349     public static Map<String, Typeface> getSystemFontMap() {
1350         synchronized (SYSTEM_FONT_MAP_LOCK) {
1351             return sSystemFontMap;
1352         }
1353     }
1354 
1355     /**
1356      * Deserialize font map and set it as system font map. This method should be called at most once
1357      * per process.
1358      */
1359     /** @hide */
1360     @UiThread
1361     public static void setSystemFontMap(@Nullable SharedMemory sharedMemory)
1362             throws IOException, ErrnoException {
1363         if (sSystemFontMapBuffer != null) {
1364             // Apps can re-send BIND_APPLICATION message from their code. This is a work around to
1365             // detect it and avoid crashing.
1366             if (sharedMemory == null || sharedMemory == sSystemFontMapSharedMemory) {
1367                 return;
1368             }
1369             throw new UnsupportedOperationException(
1370                     "Once set, buffer-based system font map cannot be updated");
1371         }
1372         sSystemFontMapSharedMemory = sharedMemory;
1373         Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "setSystemFontMap");
1374         try {
1375             if (sharedMemory == null) {
1376                 // FontManagerService is not started. This may happen in FACTORY_TEST_LOW_LEVEL
1377                 // mode for example.
1378                 loadPreinstalledSystemFontMap();
1379                 return;
1380             }
1381             sSystemFontMapBuffer = sharedMemory.mapReadOnly().order(ByteOrder.BIG_ENDIAN);
1382             Map<String, Typeface> systemFontMap = new ArrayMap<>();
1383             long[] nativePtrs = deserializeFontMap(sSystemFontMapBuffer, systemFontMap);
1384 
1385             // Initialize native font APIs. The native font API will read fonts.xml by itself if
1386             // Typeface is initialized with loadPreinstalledSystemFontMap.
1387             for (long ptr : nativePtrs) {
1388                 nativeAddFontCollections(ptr);
1389             }
1390             setSystemFontMap(systemFontMap);
1391         } finally {
1392             Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
1393         }
1394     }
1395 
1396     /** @hide */
1397     @VisibleForTesting
1398     public static void setSystemFontMap(Map<String, Typeface> systemFontMap) {
1399         synchronized (SYSTEM_FONT_MAP_LOCK) {
1400             sSystemFontMap.clear();
1401             sSystemFontMap.putAll(systemFontMap);
1402 
1403             // We can't assume DEFAULT_FAMILY available on Roboletric.
1404             if (sSystemFontMap.containsKey(DEFAULT_FAMILY)) {
1405                 setDefault(sSystemFontMap.get(DEFAULT_FAMILY));
1406             }
1407 
1408             // Set up defaults and typefaces exposed in public API
1409             // Use sDefaultTypeface here, because create(String, int) uses DEFAULT as fallback.
1410             nativeForceSetStaticFinalField("DEFAULT", create(sDefaultTypeface, 0));
1411             nativeForceSetStaticFinalField("DEFAULT_BOLD", create(sDefaultTypeface, Typeface.BOLD));
1412             nativeForceSetStaticFinalField("SANS_SERIF", create("sans-serif", 0));
1413             nativeForceSetStaticFinalField("SERIF", create("serif", 0));
1414             nativeForceSetStaticFinalField("MONOSPACE", create("monospace", 0));
1415 
1416             sDefaults = new Typeface[]{
1417                 DEFAULT,
1418                 DEFAULT_BOLD,
1419                 create((String) null, Typeface.ITALIC),
1420                 create((String) null, Typeface.BOLD_ITALIC),
1421             };
1422 
1423             // A list of generic families to be registered in native.
1424             // https://www.w3.org/TR/css-fonts-4/#generic-font-families
1425             String[] genericFamilies = {
1426                 "serif", "sans-serif", "cursive", "fantasy", "monospace", "system-ui"
1427             };
1428 
1429             for (String genericFamily : genericFamilies) {
1430                 registerGenericFamilyNative(genericFamily, systemFontMap.get(genericFamily));
1431             }
1432         }
1433     }
1434 
1435     /**
1436      * Change default typefaces for testing purpose.
1437      *
1438      * Note: The existing TextView or Paint instance still holds the old Typeface.
1439      *
1440      * @param defaults array of [default, default_bold, default_italic, default_bolditalic].
1441      * @param genericFamilies array of [sans-serif, serif, monospace]
1442      * @return return the old defaults and genericFamilies
1443      * @hide
1444      */
1445     @TestApi
1446     @NonNull
1447     public static Pair<List<Typeface>, List<Typeface>> changeDefaultFontForTest(
1448             @NonNull List<Typeface> defaults,
1449             @NonNull List<Typeface> genericFamilies
1450     ) {
1451         synchronized (SYSTEM_FONT_MAP_LOCK) {
1452             List<Typeface> oldDefaults = Arrays.asList(sDefaults);
1453             sDefaults = defaults.toArray(new Typeface[4]);
1454             setDefault(defaults.get(0));
1455 
1456             ArrayList<Typeface> oldGenerics = new ArrayList<>();
1457             oldGenerics.add(sSystemFontMap.get("sans-serif"));
1458             sSystemFontMap.put("sans-serif", genericFamilies.get(0));
1459 
1460             oldGenerics.add(sSystemFontMap.get("serif"));
1461             sSystemFontMap.put("serif", genericFamilies.get(1));
1462 
1463             oldGenerics.add(sSystemFontMap.get("monospace"));
1464             sSystemFontMap.put("monospace", genericFamilies.get(2));
1465 
1466             return new Pair<>(oldDefaults, oldGenerics);
1467         }
1468     }
1469 
1470     static {
1471         // Preload Roboto-Regular.ttf in Zygote for improving app launch performance.
1472         preloadFontFile("/system/fonts/Roboto-Regular.ttf");
1473         preloadFontFile("/system/fonts/RobotoStatic-Regular.ttf");
1474 
1475         String locale = SystemProperties.get("persist.sys.locale", "en-US");
1476         String script = ULocale.addLikelySubtags(ULocale.forLanguageTag(locale)).getScript();
1477 
1478         // The feature flag cannot be referred from Zygote. Use legacy fonts.xml for preloading font
1479         // files.
1480         // TODO(nona): Use new XML file once the feature is fully launched.
1481         FontConfig config = SystemFonts.getSystemPreinstalledFontConfigFromLegacyXml();
1482         for (int i = 0; i < config.getFontFamilies().size(); ++i) {
1483             FontConfig.FontFamily family = config.getFontFamilies().get(i);
1484             if (!family.getLocaleList().isEmpty()) {
1485                 nativeRegisterLocaleList(family.getLocaleList().toLanguageTags());
1486             }
1487             boolean loadFamily = false;
1488             for (int j = 0; j < family.getLocaleList().size(); ++j) {
1489                 String fontScript = ULocale.addLikelySubtags(
1490                         ULocale.forLocale(family.getLocaleList().get(j))).getScript();
1491                 loadFamily = fontScript.equals(script);
1492                 if (loadFamily) {
1493                     break;
1494                 }
1495             }
1496             if (loadFamily) {
1497                 for (int j = 0; j < family.getFontList().size(); ++j) {
1498                     preloadFontFile(family.getFontList().get(j).getFile().getAbsolutePath());
1499                 }
1500             }
1501         }
1502     }
1503 
1504     private static void preloadFontFile(String filePath) {
1505         File file = new File(filePath);
1506         if (file.exists()) {
1507             Log.i(TAG, "Preloading " + file.getAbsolutePath());
1508             nativeWarmUpCache(filePath);
1509         }
1510     }
1511 
1512     /** @hide */
1513     @VisibleForTesting
1514     public static void destroySystemFontMap() {
1515         synchronized (SYSTEM_FONT_MAP_LOCK) {
1516             for (Typeface typeface : sSystemFontMap.values()) {
1517                 typeface.releaseNativeObjectForTest();
1518             }
1519             sSystemFontMap.clear();
1520             if (sSystemFontMapBuffer != null) {
1521                 SharedMemory.unmap(sSystemFontMapBuffer);
1522             }
1523             sSystemFontMapBuffer = null;
1524             sSystemFontMapSharedMemory = null;
1525             synchronized (sStyledCacheLock) {
1526                 destroyTypefaceCacheLocked(sStyledTypefaceCache);
1527             }
1528             synchronized (sWeightCacheLock) {
1529                 destroyTypefaceCacheLocked(sWeightTypefaceCache);
1530             }
1531         }
1532     }
1533 
1534     private static void destroyTypefaceCacheLocked(LongSparseArray<SparseArray<Typeface>> cache) {
1535         for (int i = 0; i < cache.size(); i++) {
1536             SparseArray<Typeface> array = cache.valueAt(i);
1537             for (int j = 0; j < array.size(); j++) {
1538                 array.valueAt(j).releaseNativeObjectForTest();
1539             }
1540         }
1541         cache.clear();
1542     }
1543 
1544     /** @hide */
1545     public static void loadPreinstalledSystemFontMap() {
1546         final FontConfig fontConfig = SystemFonts.getSystemPreinstalledFontConfig();
1547         final Map<String, FontFamily[]> fallback = SystemFonts.buildSystemFallback(fontConfig);
1548         final Map<String, Typeface> typefaceMap =
1549                 SystemFonts.buildSystemTypefaces(fontConfig, fallback);
1550         setSystemFontMap(typefaceMap);
1551     }
1552 
1553     static {
1554         if (!ENABLE_LAZY_TYPEFACE_INITIALIZATION) {
1555             loadPreinstalledSystemFontMap();
1556         }
1557     }
1558 
1559     @Override
1560     public boolean equals(Object o) {
1561         if (this == o) return true;
1562         if (o == null || getClass() != o.getClass()) return false;
1563 
1564         Typeface typeface = (Typeface) o;
1565 
1566         return mStyle == typeface.mStyle && native_instance == typeface.native_instance;
1567     }
1568 
1569     @Override
1570     public int hashCode() {
1571         /*
1572          * Modified method for hashCode with long native_instance derived from
1573          * http://developer.android.com/reference/java/lang/Object.html
1574          */
1575         int result = 17;
1576         result = 31 * result + (int) (native_instance ^ (native_instance >>> 32));
1577         result = 31 * result + mStyle;
1578         return result;
1579     }
1580 
1581     /** @hide */
1582     public boolean isSupportedAxes(int axis) {
1583         synchronized (this) {
1584             if (mSupportedAxes == null) {
1585                 mSupportedAxes = nativeGetSupportedAxes(native_instance);
1586                 if (mSupportedAxes == null) {
1587                     mSupportedAxes = EMPTY_AXES;
1588                 }
1589             }
1590         }
1591         return Arrays.binarySearch(mSupportedAxes, axis) >= 0;
1592     }
1593 
1594     private static native long nativeCreateFromTypeface(long native_instance, int style);
1595     private static native long nativeCreateFromTypefaceWithExactStyle(
1596             long native_instance, int weight, boolean italic);
1597     // TODO: clean up: change List<FontVariationAxis> to FontVariationAxis[]
1598     private static native long nativeCreateFromTypefaceWithVariation(
1599             long native_instance, List<FontVariationAxis> axes);
1600     @UnsupportedAppUsage
1601     private static native long nativeCreateWeightAlias(long native_instance, int weight);
1602     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
1603     private static native long nativeCreateFromArray(
1604             long[] familyArray, long fallbackTypeface, int weight, int italic);
1605     private static native int[] nativeGetSupportedAxes(long native_instance);
1606 
1607     @CriticalNative
1608     private static native void nativeSetDefault(long nativePtr);
1609 
1610     @CriticalNative
1611     private static native int  nativeGetStyle(long nativePtr);
1612 
1613     @CriticalNative
1614     private static native int  nativeGetWeight(long nativePtr);
1615 
1616     @CriticalNative
1617     private static native long nativeGetReleaseFunc();
1618 
1619     private static native void nativeRegisterGenericFamily(String str, long nativePtr);
1620 
1621     private static native int nativeWriteTypefaces(
1622             @Nullable ByteBuffer buffer, int position, @NonNull long[] nativePtrs);
1623 
1624     private static native
1625             @Nullable long[] nativeReadTypefaces(@NonNull ByteBuffer buffer, int position);
1626 
1627     private static native void nativeForceSetStaticFinalField(String fieldName, Typeface typeface);
1628 
1629     @CriticalNative
1630     private static native void nativeAddFontCollections(long nativePtr);
1631 
1632     private static native void nativeWarmUpCache(String fileName);
1633 
1634     @FastNative
1635     private static native void nativeRegisterLocaleList(String locales);
1636 }
1637