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