From 46d6a3f9cee723c5ac3a872f1fa80f2c7bec2a1d Mon Sep 17 00:00:00 2001 From: Seigo Nonaka Date: Mon, 18 Jan 2021 20:25:16 -0800 Subject: [PATCH] Add FontManager API and font manager shell commands FontManager is an interface of communicating with FontManaserService. With this CL, system apps can know the primitive information about what font files are installed and how it is configured. This CL also adds shell command of dumping font configuration. Example usages are: # dump primitive font configuration. adb shell dumpsys font # dump fallback font list adb shell cmd font dump serif Bug: 173619554 Test: atest FontManagerTest Test: Manually confirmed above commands works. Change-Id: I5cb855308e199ac38bd7b56b60821346247bdce3 --- core/api/system-current.txt | 54 +++ core/api/test-current.txt | 50 +++ .../android/app/SystemServiceRegistry.java | 10 + core/java/android/content/Context.java | 12 + .../android/graphics/fonts/FontManager.java | 79 ++++ .../graphics/fonts/SystemFontState.aidl | 20 + core/java/android/text/FontConfig.java | 346 ++++++++++++------ .../internal/graphics/fonts/IFontManager.aidl | 29 ++ .../java/android/graphics/FontListParser.java | 18 +- graphics/java/android/graphics/Typeface.java | 6 +- .../fonts/FontCustomizationParser.java | 20 +- .../android/graphics/fonts/FontFamily.java | 2 +- .../android/graphics/fonts/SystemFonts.java | 34 +- .../graphics/fonts/FontManagerService.java | 49 ++- .../fonts/FontManagerShellCommand.java | 269 ++++++++++++++ 15 files changed, 838 insertions(+), 160 deletions(-) create mode 100644 core/java/android/graphics/fonts/FontManager.java create mode 100644 core/java/android/graphics/fonts/SystemFontState.aidl create mode 100644 core/java/com/android/internal/graphics/fonts/IFontManager.aidl create mode 100644 services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 3495f9520abc3..cc943472bc22e 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -1927,6 +1927,7 @@ package android.content { field public static final String CONTEXTHUB_SERVICE = "contexthub"; field public static final String ETHERNET_SERVICE = "ethernet"; field public static final String EUICC_CARD_SERVICE = "euicc_card"; + field public static final String FONT_SERVICE = "font"; field public static final String HDMI_CONTROL_SERVICE = "hdmi_control"; field public static final String MEDIA_TRANSCODING_SERVICE = "media_transcoding"; field public static final String MUSIC_RECOGNITION_SERVICE = "music_recognition"; @@ -2588,6 +2589,14 @@ package android.graphics.drawable { } +package android.graphics.fonts { + + public class FontManager { + method @Nullable public android.text.FontConfig getFontConfig(); + } + +} + package android.hardware { public final class Sensor { @@ -13451,6 +13460,51 @@ package android.telephony.mbms.vendor { } +package android.text { + + public final class FontConfig implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.List getAliases(); + method @NonNull public java.util.List getFontFamilies(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + + public static final class FontConfig.Alias implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public String getName(); + method @NonNull public String getOriginal(); + method @IntRange(from=0, to=1000) public int getWeight(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + + public static final class FontConfig.Font implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.io.File getFile(); + method @Nullable public String getFontFamilyName(); + method @NonNull public String getFontVariationSettings(); + method @NonNull public android.graphics.fonts.FontStyle getStyle(); + method public int getTtcIndex(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + + public static final class FontConfig.FontFamily implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.List getFontList(); + method @NonNull public android.os.LocaleList getLocaleList(); + method @Nullable public String getName(); + method public int getVariant(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + field public static final int VARIANT_COMPACT = 1; // 0x1 + field public static final int VARIANT_DEFAULT = 0; // 0x0 + field public static final int VARIANT_ELEGANT = 2; // 0x2 + } + +} + package android.util { public class EventLog { diff --git a/core/api/test-current.txt b/core/api/test-current.txt index d6abbeca44808..cc8210b705d7f 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -627,6 +627,7 @@ package android.content { field public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "content_capture"; field public static final String DEVICE_IDLE_CONTROLLER = "deviceidle"; field public static final String DREAM_SERVICE = "dream"; + field public static final String FONT_SERVICE = "font"; field public static final String POWER_WHITELIST_MANAGER = "power_whitelist"; field public static final String TEST_NETWORK_SERVICE = "test_network"; } @@ -819,6 +820,14 @@ package android.graphics.drawable { } +package android.graphics.fonts { + + public class FontManager { + method @Nullable public android.text.FontConfig getFontConfig(); + } + +} + package android.hardware { public final class SensorPrivacyManager { @@ -2014,6 +2023,47 @@ package android.telephony.ims.feature { package android.text { + public final class FontConfig implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.List getAliases(); + method @NonNull public java.util.List getFontFamilies(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + + public static final class FontConfig.Alias implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public String getName(); + method @NonNull public String getOriginal(); + method @IntRange(from=0, to=1000) public int getWeight(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + + public static final class FontConfig.Font implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.io.File getFile(); + method @Nullable public String getFontFamilyName(); + method @NonNull public String getFontVariationSettings(); + method @NonNull public android.graphics.fonts.FontStyle getStyle(); + method public int getTtcIndex(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + + public static final class FontConfig.FontFamily implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.List getFontList(); + method @NonNull public android.os.LocaleList getLocaleList(); + method @Nullable public String getName(); + method public int getVariant(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + field public static final int VARIANT_COMPACT = 1; // 0x1 + field public static final int VARIANT_DEFAULT = 0; // 0x0 + field public static final int VARIANT_ELEGANT = 2; // 0x2 + } + public static final class Selection.MemoryTextWatcher implements android.text.TextWatcher { ctor public Selection.MemoryTextWatcher(); method public void afterTextChanged(android.text.Editable); diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 5b0ad228dc436..e084c01f7e1dd 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -72,6 +72,7 @@ import android.content.res.Resources; import android.content.rollback.RollbackManagerFrameworkInitializer; import android.debug.AdbManager; import android.debug.IAdbManager; +import android.graphics.fonts.FontManager; import android.hardware.ConsumerIrManager; import android.hardware.ISerialManager; import android.hardware.SensorManager; @@ -216,6 +217,7 @@ import com.android.internal.app.IAppOpsService; import com.android.internal.app.IBatteryStats; import com.android.internal.app.ISoundTriggerService; import com.android.internal.appwidget.IAppWidgetService; +import com.android.internal.graphics.fonts.IFontManager; import com.android.internal.net.INetworkWatchlistManager; import com.android.internal.os.IDropBoxManagerService; import com.android.internal.policy.PhoneLayoutInflater; @@ -344,6 +346,14 @@ public final class SystemServiceRegistry { return new TextClassificationManager(ctx); }}); + registerService(Context.FONT_SERVICE, FontManager.class, + new CachedServiceFetcher() { + @Override + public FontManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.FONT_SERVICE); + return FontManager.create(IFontManager.Stub.asInterface(b)); + }}); + registerService(Context.CLIPBOARD_SERVICE, ClipboardManager.class, new CachedServiceFetcher() { @Override diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 3f7479b864943..6df4304143e7b 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -4476,6 +4476,18 @@ public abstract class Context { */ public static final String TEXT_CLASSIFICATION_SERVICE = "textclassification"; + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.graphics.fonts.FontManager} for font services. + * + * @see #getSystemService(String) + * @see android.graphics.fonts.FontManager + * @hide + */ + @SystemApi + @TestApi + public static final String FONT_SERVICE = "font"; + /** * Use with {@link #getSystemService(String)} to retrieve a * {@link com.android.server.attention.AttentionManagerService} for attention services. diff --git a/core/java/android/graphics/fonts/FontManager.java b/core/java/android/graphics/fonts/FontManager.java new file mode 100644 index 0000000000000..ea6cf2f44be91 --- /dev/null +++ b/core/java/android/graphics/fonts/FontManager.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics.fonts; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.annotation.SystemService; +import android.annotation.TestApi; +import android.content.Context; +import android.os.RemoteException; +import android.text.FontConfig; +import android.util.Log; + +import com.android.internal.graphics.fonts.IFontManager; + +import java.util.Objects; + +/** + * This class gives you control of system installed font files. + * @hide + */ +@SystemApi +@TestApi +@SystemService(Context.FONT_SERVICE) +public class FontManager { + private static final String TAG = "FontManager"; + private final @NonNull IFontManager mIFontManager; + + private FontManager(@NonNull IFontManager iFontManager) { + mIFontManager = iFontManager; + } + + /** + * Returns the system font configuration. + * + * This information is expected to be used by system font updater. If you are looking for APIs + * about drawing text and/or high-level system font information, use + * {@link android.graphics.Typeface} or {@link SystemFonts} instead. + * + * @return The current font configuration. null if failed to fetch information from the system + * service. + */ + public @Nullable FontConfig getFontConfig() { + try { + return mIFontManager.getFontConfig(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to call getFontConfig", e); + return null; + } + } + + /** + * Factory method of the FontManager. + * + * Do not use this method directly. Use getSystemService(Context.FONT_SERVICE) instead. + * + * @return A new instance of FontManager + * @hide + */ + public static FontManager create(@NonNull IFontManager iFontManager) { + Objects.requireNonNull(iFontManager); + return new FontManager(iFontManager); + } +} diff --git a/core/java/android/graphics/fonts/SystemFontState.aidl b/core/java/android/graphics/fonts/SystemFontState.aidl new file mode 100644 index 0000000000000..19b20f268486d --- /dev/null +++ b/core/java/android/graphics/fonts/SystemFontState.aidl @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics.fonts; + +/** @hide */ +parcelable SystemFontState; \ No newline at end of file diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java index 8492363eb5031..82d7399c86e09 100644 --- a/core/java/android/text/FontConfig.java +++ b/core/java/android/text/FontConfig.java @@ -22,51 +22,66 @@ import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.graphics.fonts.FontStyle; import android.graphics.fonts.FontVariationAxis; import android.os.Build; import android.os.LocaleList; +import android.os.Parcel; +import android.os.Parcelable; import java.io.File; import java.lang.annotation.Retention; +import java.util.ArrayList; import java.util.List; /** * Font configuration descriptions for System fonts. - * @hide // TODO Make this SystemApi. + * + * FontConfig represents the configuration for the fonts installed on the system. It is made of list + * of font families and aliases. + * + * @see FontFamily + * @see Alias + * @hide */ -public final class FontConfig { - private final @NonNull List mFamilies; +@SystemApi +@TestApi +public final class FontConfig implements Parcelable { + private final @NonNull List mFamilies; private final @NonNull List mAliases; /** - * Construct a SystemFontConfig instance. + * Construct a FontConfig instance. * * @param families a list of font families. * @param aliases a list of aliases. * * @hide Only system server can create this instance and passed via IPC. */ - public FontConfig(@NonNull List families, @NonNull List aliases) { + public FontConfig(@NonNull List families, @NonNull List aliases) { mFamilies = families; mAliases = aliases; } /** - * Returns the ordered list of families included in the system fonts. + * Returns the ordered list of font families available in the system. * * @return a list of font families. + * @see FontFamily */ - public @NonNull List getFontFamilies() { + public @NonNull List getFontFamilies() { return mFamilies; } /** - * Returns the list of aliases defined for the font families in the system fonts. + * Returns the list of aliases for mapping font families with other names. * * @return a list of font families. + * @see Alias */ public @NonNull List getAliases() { return mAliases; @@ -75,106 +90,167 @@ public final class FontConfig { /** * Returns the ordered list of families included in the system fonts. * @deprecated Use getFontFamilies instead. + * @hide */ @Deprecated @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public @NonNull Family[] getFamilies() { - return mFamilies.toArray(new Family[0]); + public @NonNull FontFamily[] getFamilies() { + return mFamilies.toArray(new FontFamily[0]); } + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeParcelableList(mFamilies, flags); + dest.writeParcelableList(mAliases, flags); + } + + public static final @NonNull Creator CREATOR = new Creator() { + @Override + public FontConfig createFromParcel(Parcel source) { + List families = source.readParcelableList(new ArrayList<>(), + FontFamily.class.getClassLoader()); + List aliases = source.readParcelableList(new ArrayList<>(), + Alias.class.getClassLoader()); + return new FontConfig(families, aliases); + } + + @Override + public FontConfig[] newArray(int size) { + return new FontConfig[size]; + } + }; + /** - * A class represents single font entry in system font configuration. + * Represents single font entry in system font configuration. + * + * A font is the most primitive unit of drawing character shapes. A font in system configuration + * is always referring a single OpenType compliant regular file in the file system. + * + * @see android.graphics.fonts.Font */ - public static final class Font { - private final @NonNull File mFilePath; - private final @Nullable File mOriginalPath; + public static final class Font implements Parcelable { + private final @NonNull File mFile; + private final @Nullable File mOriginalFile; private final @NonNull FontStyle mStyle; private final @IntRange(from = 0) int mIndex; private final @NonNull String mFontVariationSettings; - private final @Nullable String mFallback; + private final @Nullable String mFontFamilyName; /** * Construct a Font instance. * * @hide Only system server can create this instance and passed via IPC. */ - public Font(@NonNull File filePath, @Nullable File originalPath, @NonNull FontStyle style, + public Font(@NonNull File file, @Nullable File originalFile, @NonNull FontStyle style, @IntRange(from = 0) int index, @NonNull String fontVariationSettings, - @Nullable String fallback) { - mFilePath = filePath; - mOriginalPath = originalPath; + @Nullable String fontFamilyName) { + mFile = file; + mOriginalFile = originalFile; mStyle = style; mIndex = index; mFontVariationSettings = fontVariationSettings; - mFallback = fallback; + mFontFamilyName = fontFamilyName; } + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString8(mFile.getAbsolutePath()); + dest.writeString8(mOriginalFile == null ? null : mOriginalFile.getAbsolutePath()); + dest.writeInt(mStyle.getWeight()); + dest.writeInt(mStyle.getSlant()); + dest.writeInt(mIndex); + dest.writeString8(mFontVariationSettings); + dest.writeString8(mFontFamilyName); + } + + public static final @NonNull Creator CREATOR = new Creator() { + + @Override + public Font createFromParcel(Parcel source) { + File path = new File(source.readString8()); + String originalPathStr = source.readString8(); + File originalPath = originalPathStr == null ? null : new File(originalPathStr); + int weight = source.readInt(); + int slant = source.readInt(); + int index = source.readInt(); + String varSettings = source.readString8(); + String fallback = source.readString8(); + + return new Font(path, originalPath, new FontStyle(weight, slant), index, + varSettings, fallback); + } + + @Override + public Font[] newArray(int size) { + return new Font[size]; + } + }; + /** - * Returns a file to the font file. - * - * @return a font file. + * Returns the font file. */ - public @NonNull File getFilePath() { - return mFilePath; + public @NonNull File getFile() { + return mFile; } /** - * Returns an original font file in the system directory. + * Returns the original font file in the system directory. * * If the font file is not updated, returns null. * * @return returns the original font file in the system if the font file is updated. Returns * null if the font file is not updated. + * @hide */ - public @Nullable File getOriginalPath() { - return mOriginalPath; + public @Nullable File getOriginalFile() { + return mOriginalFile; } /** - * Returns a font style. - * - * @return a font style. + * Returns the font style. */ public @NonNull FontStyle getStyle() { return mStyle; } - /** - * Returns a font index. - * - * @return a font index. - */ - public @IntRange(from = 0) int getIndex() { - return mIndex; - } /** * Return a font variation settings. - * - * @return a font variation settings. */ public @NonNull String getFontVariationSettings() { return mFontVariationSettings; } /** - * Returns font family name that uses this font as a fallback. + * A {@link Font} can be configured to be in the {@code Fallback List} for a + * {@link FontFamily}. * - * If this font is a fallback for the default font family, this is null. + * For example a serif Hebrew [Font] can be defined in the {@code Fallback List} for + * {@code "serif"} {@link FontFamily}. * - * @return a font family name. + * If the return value is not {@code null}, then the font will be used in the + * {@code Fallback List} of that {@link FontFamily}. + * + * If the return value is {@code null}, then the font will be used in {@code Fallback List} + * of all {@link FontFamily}s. */ - public @Nullable String getFallback() { - return mFallback; + public @Nullable String getFontFamilyName() { + return mFontFamilyName; } /** * Returns the index to be used to access this font when accessing a TTC file. - * @deprecated Use getIndex instead. - * @hide */ - @Deprecated - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int getTtcIndex() { return mIndex; } @@ -214,48 +290,46 @@ public final class FontConfig { } /** - * A class represents alias between named font families. + * Alias provides an alternative name for an existing font family. * - * In the system font configuration, an font family can be an alias of another font family with + * In the system font configuration, a font family can be an alias of another font family with * different font weight. For example, "sans-serif-medium" can be a medium weight of - * sans-serif font family. + * "sans-serif" font family. In this example, {@link #getName()} returns "sans-serif-medium" and + * {@link #getOriginal()} return "sans-serif". The font family that doesn't have name can not be + * an original of the alias. */ - public static final class Alias { - private final @NonNull String mAliasName; - private final @NonNull String mReferName; + public static final class Alias implements Parcelable { + private final @NonNull String mName; + private final @NonNull String mOriginal; private final @IntRange(from = 0, to = 1000) int mWeight; /** * Construct an alias instance. * - * @param aliasName an alias of the named font family. - * @param referName a referring font family name. - * @param weight a font weight of the referring font family. + * @param name alias for the font family. + * @param original original font family name. + * @param weight font weight of the original font family. * @hide Only system server can create this instance and passed via IPC. */ - public Alias(@NonNull String aliasName, @NonNull String referName, + public Alias(@NonNull String name, @NonNull String original, @IntRange(from = 0, to = 1000) int weight) { - mAliasName = aliasName; - mReferName = referName; + mName = name; + mOriginal = original; mWeight = weight; } /** - * An alias of the named font family. - * - * @return an alias of the named font family. + * Alias for the font family */ - public @NonNull String getAliasName() { - return mAliasName; + public @NonNull String getName() { + return mName; } /** - * A name of font family referring from {@link #getAliasName()} - * - * @return a referring font family name. + * The name of the original font family. */ - public @NonNull String getReferName() { - return mReferName; + public @NonNull String getOriginal() { + return mOriginal; } /** @@ -266,17 +340,47 @@ public final class FontConfig { public @IntRange(from = 0, to = 1000) int getWeight() { return mWeight; } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString8(mName); + dest.writeString8(mOriginal); + dest.writeInt(mWeight); + } + + public static final @NonNull Creator CREATOR = new Creator() { + + @Override + public Alias createFromParcel(Parcel source) { + String alias = source.readString8(); + String referName = source.readString8(); + int weight = source.readInt(); + return new Alias(alias, referName, weight); + } + + @Override + public Alias[] newArray(int size) { + return new Alias[size]; + } + }; } /** - * A class represents single font family entry in system font configuration. + * Represents a font family in the system font configuration. * - *

- * A font family is a bundle of fonts for drawing text in various styles. - * For example, regular style font and bold style font can be bundled into a single font family, - * then system will select the correct style font from family for drawing. + * A {@link FontFamily} is a list of {@link Font}s for drawing text in various styles such as + * weight, slant. + * + * For example, a {@link FontFamily} can include the regular and bold styles of a {@link Font}. + * + * @see android.graphics.fonts.FontFamily */ - public static final class Family { + public static final class FontFamily implements Parcelable { private final @NonNull List mFonts; private final @Nullable String mName; private final @Nullable LocaleList mLocaleList; @@ -319,7 +423,7 @@ public final class FontConfig { * * @hide Only system server can create this instance and passed via IPC. */ - public Family(@NonNull List fonts, @Nullable String name, + public FontFamily(@NonNull List fonts, @Nullable String name, @Nullable LocaleList localeList, @Variant int variant) { mFonts = fonts; mName = name; @@ -328,7 +432,7 @@ public final class FontConfig { } /** - * Returns a list of font files in this family. + * Returns the list of {@link Font}s in this {@link FontFamily}. * * @return a list of font files. */ @@ -337,61 +441,77 @@ public final class FontConfig { } /** - * Returns a family name if this family defines a new fallback. + * Returns the name of the {@link FontFamily}. * - * @return non-null if a family name is associated. Otherwise null. + * When the name of a {@link FontFamily} is not null, this name is used to create a new + * {@code Fallback List}, and that {@code Fallback List}. Fallback List is the + * main building block for a {@link android.graphics.Typeface}. + * + * For example, if the {@link FontFamily} has the name "serif", then the system will create + * a “serif” {@code Fallback List} and it can be used by creating a Typeface via + * {@code Typeface.create("serif", Typeface.NORMAL);} + * + * When the name of a {@link FontFamily} is null, it will be appended to all of the + * {@code Fallback List}s. */ - public @Nullable String getFallbackName() { + public @Nullable String getName() { return mName; } /** - * Returns a locale list if associated. + * Returns the locale list if available. * - * @return non-null if a locale list is associated. Otherwise null. + * The locale list will be used for deciding which font family should be used in fallback + * list. + * + * @return non-null if a locale list is available. Otherwise null. */ public @NonNull LocaleList getLocaleList() { return mLocaleList; } /** - * Returns a text height variant. - * - * @return text height variant. + * Returns the text height variant. */ - public @Variant int getTextHeightVariant() { - return mVariant; - } - - /** - * Returns a family variant associated. - * - * @return a family variant. - * @deprecated Use getTextHeightVariant instead. - * @hide - */ - @Deprecated - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public @Variant int getVariant() { return mVariant; } - /** - * Returns a family name if associated. - * - * @return non-null if a family name is associated. Otherwise null. - * @deprecated Use getFallbackName instead. - * @hide - */ - @Deprecated - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public @Nullable String getName() { - return mName; + @Override + public int describeContents() { + return 0; } + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeParcelableList(mFonts, flags); + dest.writeString8(mName); + dest.writeString8(mLocaleList.toLanguageTags()); + dest.writeInt(mVariant); + } + + public static final @NonNull Creator CREATOR = new Creator() { + + @Override + public FontFamily createFromParcel(Parcel source) { + List fonts = source.readParcelableList( + new ArrayList<>(), Font.class.getClassLoader()); + String name = source.readString8(); + String langTags = source.readString8(); + int variant = source.readInt(); + + return new FontFamily(fonts, name, LocaleList.forLanguageTags(langTags), variant); + } + + @Override + public FontFamily[] newArray(int size) { + return new FontFamily[size]; + } + }; + /** * Returns the list of fonts included in this family. - * @deprecated Use getFontFiles instead + * @deprecated Use getFontList instead * @hide */ @Deprecated diff --git a/core/java/com/android/internal/graphics/fonts/IFontManager.aidl b/core/java/com/android/internal/graphics/fonts/IFontManager.aidl new file mode 100644 index 0000000000000..a11c7effc688a --- /dev/null +++ b/core/java/com/android/internal/graphics/fonts/IFontManager.aidl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.graphics.fonts; + +import android.text.FontConfig; +import android.graphics.fonts.SystemFontState; + +/** + * System private interface for talking with + * {@link com.android.server.graphics.fonts.FontManagerService}. + * @hide + */ +interface IFontManager { + FontConfig getFontConfig(); +} diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java index ff381176e38d3..c8ff95d156265 100644 --- a/graphics/java/android/graphics/FontListParser.java +++ b/graphics/java/android/graphics/FontListParser.java @@ -100,10 +100,10 @@ public class FontListParser { @NonNull FontCustomizationParser.Result customization, @Nullable Map updatableFontMap) throws XmlPullParserException, IOException { - List families = new ArrayList<>(); + List families = new ArrayList<>(); List aliases = new ArrayList<>(customization.getAdditionalAliases()); - Map oemNamedFamilies = + Map oemNamedFamilies = customization.getAdditionalNamedFamilies(); parser.require(XmlPullParser.START_TAG, null, "familyset"); @@ -111,8 +111,8 @@ public class FontListParser { if (parser.getEventType() != XmlPullParser.START_TAG) continue; String tag = parser.getName(); if (tag.equals("family")) { - FontConfig.Family family = readFamily(parser, fontDir, updatableFontMap); - String name = family.getFallbackName(); + FontConfig.FontFamily family = readFamily(parser, fontDir, updatableFontMap); + String name = family.getName(); if (name == null || !oemNamedFamilies.containsKey(name)) { // The OEM customization overrides system named family. Skip if OEM // customization XML defines the same named family. @@ -132,7 +132,7 @@ public class FontListParser { /** * Read family tag in fonts.xml or oem_customization.xml */ - public static FontConfig.Family readFamily(XmlPullParser parser, String fontDir, + public static FontConfig.FontFamily readFamily(XmlPullParser parser, String fontDir, @Nullable Map updatableFontMap) throws XmlPullParserException, IOException { final String name = parser.getAttributeValue(null, "name"); @@ -148,15 +148,15 @@ public class FontListParser { skip(parser); } } - int intVariant = FontConfig.Family.VARIANT_DEFAULT; + int intVariant = FontConfig.FontFamily.VARIANT_DEFAULT; if (variant != null) { if (variant.equals("compact")) { - intVariant = FontConfig.Family.VARIANT_COMPACT; + intVariant = FontConfig.FontFamily.VARIANT_COMPACT; } else if (variant.equals("elegant")) { - intVariant = FontConfig.Family.VARIANT_ELEGANT; + intVariant = FontConfig.FontFamily.VARIANT_ELEGANT; } } - return new FontConfig.Family(fonts, name, LocaleList.forLanguageTags(lang), intVariant); + return new FontConfig.FontFamily(fonts, name, LocaleList.forLanguageTags(lang), intVariant); } /** Matches leading and trailing XML whitespace. */ diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index f1866cdceae7c..a0568bf3bf5c0 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -1151,10 +1151,10 @@ public class Typeface { for (int i = 0; i < aliases.size(); ++i) { final FontConfig.Alias alias = aliases.get(i); - if (outSystemFontMap.containsKey(alias.getAliasName())) { + if (outSystemFontMap.containsKey(alias.getName())) { continue; // If alias and named family are conflict, use named family. } - final Typeface base = outSystemFontMap.get(alias.getReferName()); + final Typeface base = outSystemFontMap.get(alias.getOriginal()); if (base == null) { // The missing target is a valid thing, some configuration don't have font files, // e.g. wear devices. Just skip this alias. @@ -1163,7 +1163,7 @@ public class Typeface { final int weight = alias.getWeight(); final Typeface newFace = weight == 400 ? base : new Typeface(nativeCreateWeightAlias(base.native_instance, weight)); - outSystemFontMap.put(alias.getAliasName(), newFace); + outSystemFontMap.put(alias.getName(), newFace); } } diff --git a/graphics/java/android/graphics/fonts/FontCustomizationParser.java b/graphics/java/android/graphics/fonts/FontCustomizationParser.java index 1ad6fbe264cbd..42033ba017bfc 100644 --- a/graphics/java/android/graphics/fonts/FontCustomizationParser.java +++ b/graphics/java/android/graphics/fonts/FontCustomizationParser.java @@ -17,7 +17,7 @@ package android.graphics.fonts; import static android.text.FontConfig.Alias; -import static android.text.FontConfig.Family; +import static android.text.FontConfig.FontFamily; import android.annotation.NonNull; import android.annotation.Nullable; @@ -46,7 +46,7 @@ public class FontCustomizationParser { * Represents a customization XML */ public static class Result { - private final Map mAdditionalNamedFamilies; + private final Map mAdditionalNamedFamilies; private final List mAdditionalAliases; public Result() { @@ -54,13 +54,13 @@ public class FontCustomizationParser { mAdditionalAliases = Collections.emptyList(); } - public Result(Map additionalNamedFamilies, + public Result(Map additionalNamedFamilies, List additionalAliases) { mAdditionalNamedFamilies = additionalNamedFamilies; mAdditionalAliases = additionalAliases; } - public Map getAdditionalNamedFamilies() { + public Map getAdditionalNamedFamilies() { return mAdditionalNamedFamilies; } @@ -85,11 +85,11 @@ public class FontCustomizationParser { return readFamilies(parser, fontDir, updatableFontMap); } - private static Map validateAndTransformToMap(List families) { - HashMap namedFamily = new HashMap<>(); + private static Map validateAndTransformToMap(List families) { + HashMap namedFamily = new HashMap<>(); for (int i = 0; i < families.size(); ++i) { - final Family family = families.get(i); - final String name = family.getFallbackName(); + final FontFamily family = families.get(i); + final String name = family.getName(); if (name == null) { throw new IllegalArgumentException("new-named-family requires name attribute"); } @@ -106,7 +106,7 @@ public class FontCustomizationParser { @NonNull String fontDir, @Nullable Map updatableFontMap ) throws XmlPullParserException, IOException { - List families = new ArrayList<>(); + List families = new ArrayList<>(); List aliases = new ArrayList<>(); parser.require(XmlPullParser.START_TAG, null, "fonts-modification"); while (parser.next() != XmlPullParser.END_TAG) { @@ -126,7 +126,7 @@ public class FontCustomizationParser { private static void readFamily( @NonNull XmlPullParser parser, @NonNull String fontDir, - @NonNull List out, + @NonNull List out, @Nullable Map updatableFontMap) throws XmlPullParserException, IOException { final String customizationType = parser.getAttributeValue(null, "customizationType"); diff --git a/graphics/java/android/graphics/fonts/FontFamily.java b/graphics/java/android/graphics/fonts/FontFamily.java index 2f0c26f06df79..c29c194861f12 100644 --- a/graphics/java/android/graphics/fonts/FontFamily.java +++ b/graphics/java/android/graphics/fonts/FontFamily.java @@ -109,7 +109,7 @@ public final class FontFamily { * @return a font family */ public @NonNull FontFamily build() { - return build("", FontConfig.Family.VARIANT_DEFAULT, true /* isCustomFallback */); + return build("", FontConfig.FontFamily.VARIANT_DEFAULT, true /* isCustomFallback */); } /** @hide */ diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java index 6aa50dbd078eb..a41215f9cae16 100644 --- a/graphics/java/android/graphics/fonts/SystemFonts.java +++ b/graphics/java/android/graphics/fonts/SystemFonts.java @@ -117,12 +117,12 @@ public final class SystemFonts { } } - private static void pushFamilyToFallback(@NonNull FontConfig.Family xmlFamily, + private static void pushFamilyToFallback(@NonNull FontConfig.FontFamily xmlFamily, @NonNull ArrayMap> fallbackMap, @NonNull Map cache) { final String languageTags = xmlFamily.getLocaleList().toLanguageTags(); - final int variant = xmlFamily.getTextHeightVariant(); + final int variant = xmlFamily.getVariant(); final ArrayList defaultFonts = new ArrayList<>(); final ArrayMap> specificFallbackFonts = @@ -130,7 +130,7 @@ public final class SystemFonts { // Collect default fallback and specific fallback fonts. for (final FontConfig.Font font : xmlFamily.getFonts()) { - final String fallbackName = font.getFallback(); + final String fallbackName = font.getFontFamilyName(); if (fallbackName == null) { defaultFonts.add(font); } else { @@ -144,14 +144,14 @@ public final class SystemFonts { } final FontFamily defaultFamily = defaultFonts.isEmpty() ? null : createFontFamily( - xmlFamily.getFallbackName(), defaultFonts, languageTags, variant, cache); + xmlFamily.getName(), defaultFonts, languageTags, variant, cache); // Insert family into fallback map. for (int i = 0; i < fallbackMap.size(); i++) { String name = fallbackMap.keyAt(i); final ArrayList fallback = specificFallbackFonts.get(name); if (fallback == null) { - String familyName = xmlFamily.getFallbackName(); + String familyName = xmlFamily.getName(); if (defaultFamily != null // do not add myself to the fallback chain. && (familyName == null || !familyName.equals(name))) { @@ -159,7 +159,7 @@ public final class SystemFonts { } } else { final FontFamily family = createFontFamily( - xmlFamily.getFallbackName(), fallback, languageTags, variant, cache); + xmlFamily.getName(), fallback, languageTags, variant, cache); if (family != null) { fallbackMap.valueAt(i).add(family); } else if (defaultFamily != null) { @@ -174,7 +174,7 @@ public final class SystemFonts { private static @Nullable FontFamily createFontFamily(@NonNull String familyName, @NonNull List fonts, @NonNull String languageTags, - @FontConfig.Family.Variant int variant, + @FontConfig.FontFamily.Variant int variant, @NonNull Map cache) { if (fonts.size() == 0) { return null; @@ -183,7 +183,7 @@ public final class SystemFonts { FontFamily.Builder b = null; for (int i = 0; i < fonts.size(); i++) { final FontConfig.Font fontConfig = fonts.get(i); - final String fullPath = fontConfig.getFilePath().getAbsolutePath(); + final String fullPath = fontConfig.getFile().getAbsolutePath(); ByteBuffer buffer = cache.get(fullPath); if (buffer == null) { if (cache.containsKey(fullPath)) { @@ -201,7 +201,7 @@ public final class SystemFonts { font = new Font.Builder(buffer, new File(fullPath), languageTags) .setWeight(fontConfig.getStyle().getWeight()) .setSlant(fontConfig.getStyle().getSlant()) - .setTtcIndex(fontConfig.getIndex()) + .setTtcIndex(fontConfig.getTtcIndex()) .setFontVariationSettings(fontConfig.getFontVariationSettings()) .build(); } catch (IOException e) { @@ -217,13 +217,13 @@ public final class SystemFonts { return b == null ? null : b.build(languageTags, variant, false /* isCustomFallback */); } - private static void appendNamedFamily(@NonNull FontConfig.Family xmlFamily, + private static void appendNamedFamily(@NonNull FontConfig.FontFamily xmlFamily, @NonNull HashMap bufferCache, @NonNull ArrayMap> fallbackListMap) { - final String familyName = xmlFamily.getFallbackName(); + final String familyName = xmlFamily.getName(); final FontFamily family = createFontFamily( familyName, xmlFamily.getFontList(), - xmlFamily.getLocaleList().toLanguageTags(), xmlFamily.getTextHeightVariant(), + xmlFamily.getLocaleList().toLanguageTags(), xmlFamily.getVariant(), bufferCache); if (family == null) { return; @@ -281,12 +281,12 @@ public final class SystemFonts { public static Map buildSystemFallback(FontConfig fontConfig) { final Map fallbackMap = new HashMap<>(); final HashMap bufferCache = new HashMap<>(); - final List xmlFamilies = fontConfig.getFontFamilies(); + final List xmlFamilies = fontConfig.getFontFamilies(); final ArrayMap> fallbackListMap = new ArrayMap<>(); // First traverse families which have a 'name' attribute to create fallback map. - for (final FontConfig.Family xmlFamily : xmlFamilies) { - final String familyName = xmlFamily.getFallbackName(); + for (final FontConfig.FontFamily xmlFamily : xmlFamilies) { + final String familyName = xmlFamily.getName(); if (familyName == null) { continue; } @@ -295,10 +295,10 @@ public final class SystemFonts { // Then, add fallback fonts to the each fallback map. for (int i = 0; i < xmlFamilies.size(); i++) { - final FontConfig.Family xmlFamily = xmlFamilies.get(i); + final FontConfig.FontFamily xmlFamily = xmlFamilies.get(i); // The first family (usually the sans-serif family) is always placed immediately // after the primary family in the fallback. - if (i == 0 || xmlFamily.getFallbackName() == null) { + if (i == 0 || xmlFamily.getName() == null) { pushFamilyToFallback(xmlFamily, fallbackListMap, bufferCache); } } diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java index 425e3ab0d4711..bda4240a58582 100644 --- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java +++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java @@ -23,12 +23,17 @@ import android.graphics.Typeface; import android.graphics.fonts.FontFamily; import android.graphics.fonts.FontFileUtil; import android.graphics.fonts.SystemFonts; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; import android.os.SharedMemory; import android.system.ErrnoException; import android.text.FontConfig; +import android.util.IndentingPrintWriter; import android.util.Slog; import com.android.internal.annotations.GuardedBy; +import com.android.internal.graphics.fonts.IFontManager; +import com.android.internal.util.DumpUtils; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.security.FileIntegrityService; @@ -38,6 +43,7 @@ import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.IOException; +import java.io.PrintWriter; import java.nio.ByteBuffer; import java.nio.NioUtils; import java.nio.channels.FileChannel; @@ -46,21 +52,25 @@ import java.util.Map; /** A service for managing system fonts. */ // TODO(b/173619554): Add API to update fonts. -public final class FontManagerService { - +public final class FontManagerService extends IFontManager.Stub { private static final String TAG = "FontManagerService"; // TODO: make this a DeviceConfig flag. private static final boolean ENABLE_FONT_UPDATES = false; private static final String FONT_FILES_DIR = "/data/fonts/files"; + @Override + public FontConfig getFontConfig() throws RemoteException { + return getCurrentFontSettings().getSystemFontConfig(); + } + /** Class to manage FontManagerService's lifecycle. */ public static final class Lifecycle extends SystemService { private final FontManagerService mService; public Lifecycle(@NonNull Context context) { super(context); - mService = new FontManagerService(); + mService = new FontManagerService(context); } @Override @@ -76,10 +86,11 @@ public final class FontManagerService { return mService.getCurrentFontSettings().getSerializedSystemFontMap(); } }); + publishBinderService(Context.FONT_SERVICE, mService); } } - private static class OtfFontFileParser implements UpdatableFontDir.FontFileParser { + /* package */ static class OtfFontFileParser implements UpdatableFontDir.FontFileParser { @Override public String getPostScriptName(File file) throws IOException { ByteBuffer buffer = mmap(file); @@ -133,7 +144,8 @@ public final class FontManagerService { @Nullable private SystemFontSettings mCurrentFontSettings = null; - private FontManagerService() { + private FontManagerService(Context context) { + mContext = context; mUpdatableFontDir = createUpdatableFontDir(); } @@ -148,8 +160,16 @@ public final class FontManagerService { new OtfFontFileParser(), new FsverityUtilImpl()); } + @NonNull - private SystemFontSettings getCurrentFontSettings() { + private final Context mContext; + + @NonNull + public Context getContext() { + return mContext; + } + + @NonNull /* package */ SystemFontSettings getCurrentFontSettings() { synchronized (FontManagerService.this) { if (mCurrentFontSettings == null) { mCurrentFontSettings = SystemFontSettings.create(mUpdatableFontDir); @@ -174,7 +194,22 @@ public final class FontManagerService { } } - private static class SystemFontSettings { + @Override + public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer, + @Nullable String[] args) { + if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return; + new FontManagerShellCommand(this).dumpAll(new IndentingPrintWriter(writer, " ")); + } + + @Override + public int handleShellCommand(@NonNull ParcelFileDescriptor in, + @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err, + @NonNull String[] args) { + return new FontManagerShellCommand(this).exec(this, + in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(), args); + } + + /* package */ static class SystemFontSettings { private final @NonNull SharedMemory mSerializedSystemFontMap; private final @NonNull FontConfig mSystemFontConfig; private final @NonNull Map mSystemFallbackMap; diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java b/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java new file mode 100644 index 0000000000000..acb582637e556 --- /dev/null +++ b/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.graphics.fonts; + +import android.annotation.NonNull; +import android.content.Context; +import android.graphics.fonts.Font; +import android.graphics.fonts.FontFamily; +import android.graphics.fonts.FontVariationAxis; +import android.os.ShellCommand; +import android.text.FontConfig; +import android.util.IndentingPrintWriter; + +import com.android.internal.util.DumpUtils; + +import java.io.File; +import java.io.PrintWriter; +import java.util.List; +import java.util.Map; + +/** + * A shell command implementation of font service. + */ +public class FontManagerShellCommand extends ShellCommand { + private static final String TAG = "FontManagerShellCommand"; + + @NonNull private final FontManagerService mService; + + FontManagerShellCommand(@NonNull FontManagerService service) { + mService = service; + } + + @Override + public int onCommand(String cmd) { + return execCommand(this, cmd); + } + + @Override + public void onHelp() { + dumpHelp(getOutPrintWriter()); + } + + /* package */ void dumpAll(@NonNull IndentingPrintWriter w) { + final FontManagerService.SystemFontSettings settings = mService.getCurrentFontSettings(); + dumpFontConfig(w, settings.getSystemFontConfig()); + } + + private void dumpSingleFontConfig( + @NonNull IndentingPrintWriter w, + @NonNull FontConfig.Font font + ) { + StringBuilder sb = new StringBuilder(); + sb.append("style = "); + sb.append(font.getStyle()); + sb.append(", path = "); + sb.append(font.getFile().getAbsolutePath()); + if (font.getTtcIndex() != 0) { + sb.append(", index = "); + sb.append(font.getTtcIndex()); + } + if (!font.getFontVariationSettings().isEmpty()) { + sb.append(", axes = "); + sb.append(font.getFontVariationSettings()); + } + if (font.getFontFamilyName() != null) { + sb.append(", fallback = "); + sb.append(font.getFontFamilyName()); + } + w.println(sb.toString()); + + if (font.getOriginalFile() != null) { + w.increaseIndent(); + w.println("Font is updated from " + font.getOriginalFile()); + w.decreaseIndent(); + } + } + + private void dumpFontConfig( + @NonNull IndentingPrintWriter w, + @NonNull FontConfig fontConfig + ) { + // Dump named font family first. + List families = fontConfig.getFontFamilies(); + + w.println("Named Font Families"); + w.increaseIndent(); + for (int i = 0; i < families.size(); ++i) { + final FontConfig.FontFamily family = families.get(i); + + // Here, only dump the named family only. + if (family.getName() == null) continue; + + w.println("Named Family (" + family.getName() + ")"); + final List fonts = family.getFontList(); + w.increaseIndent(); + for (int j = 0; j < fonts.size(); ++j) { + dumpSingleFontConfig(w, fonts.get(j)); + } + w.decreaseIndent(); + } + w.decreaseIndent(); + + // Dump Fallback fonts. + w.println("Dump Fallback Families"); + w.increaseIndent(); + int c = 0; + for (int i = 0; i < families.size(); ++i) { + final FontConfig.FontFamily family = families.get(i); + + // Skip named font family since they are already dumped. + if (family.getName() != null) continue; + + StringBuilder sb = new StringBuilder("Fallback Family ["); + sb.append(c++); + sb.append("]: lang=\""); + sb.append(family.getLocaleList().toLanguageTags()); + if (family.getVariant() != FontConfig.FontFamily.VARIANT_DEFAULT) { + sb.append("\", variant="); + switch (family.getVariant()) { + case FontConfig.FontFamily.VARIANT_COMPACT: + sb.append("Compact"); + break; + case FontConfig.FontFamily.VARIANT_ELEGANT: + sb.append("Elegant"); + break; + default: + sb.append("Unknown"); + break; + } + } + w.println(sb.toString()); + + final List fonts = family.getFontList(); + w.increaseIndent(); + for (int j = 0; j < fonts.size(); ++j) { + dumpSingleFontConfig(w, fonts.get(j)); + } + w.decreaseIndent(); + } + w.decreaseIndent(); + + // Dump aliases + w.println("Dump Family Aliases"); + w.increaseIndent(); + List aliases = fontConfig.getAliases(); + for (int i = 0; i < aliases.size(); ++i) { + final FontConfig.Alias alias = aliases.get(i); + w.println("alias = " + alias.getName() + ", reference = " + alias.getOriginal() + + ", width = " + alias.getWeight()); + } + w.decreaseIndent(); + } + + private static void dumpHelp(@NonNull PrintWriter w) { + w.println("Font service (font) commands"); + w.println("help"); + w.println(" Print this help text."); + w.println(); + w.println("dump [family name]"); + w.println(" Dump all font files in the specified family name."); + w.println(" Dump current system font configuration if no family name was specified."); + } + + private void dumpFallback(@NonNull IndentingPrintWriter writer, + @NonNull FontFamily[] families) { + for (FontFamily family : families) { + dumpFamily(writer, family); + } + } + + private void dumpFamily(@NonNull IndentingPrintWriter writer, @NonNull FontFamily family) { + StringBuilder sb = new StringBuilder("Family:"); + if (family.getLangTags() != null) { + sb.append(" langTag = "); + sb.append(family.getLangTags()); + } + if (family.getVariant() != FontConfig.FontFamily.VARIANT_DEFAULT) { + sb.append(" variant = "); + switch (family.getVariant()) { + case FontConfig.FontFamily.VARIANT_COMPACT: + sb.append("Compact"); + break; + case FontConfig.FontFamily.VARIANT_ELEGANT: + sb.append("Elegant"); + break; + default: + sb.append("UNKNOWN"); + break; + } + + } + writer.println(sb.toString()); + for (int i = 0; i < family.getSize(); ++i) { + writer.increaseIndent(); + try { + dumpFont(writer, family.getFont(i)); + } finally { + writer.decreaseIndent(); + } + } + } + + private void dumpFont(@NonNull IndentingPrintWriter writer, @NonNull Font font) { + File file = font.getFile(); + StringBuilder sb = new StringBuilder(); + sb.append(font.getStyle()); + sb.append(", path = "); + sb.append(file == null ? "[Not a file]" : file.getAbsolutePath()); + if (font.getTtcIndex() != 0) { + sb.append(", index = "); + sb.append(font.getTtcIndex()); + } + FontVariationAxis[] axes = font.getAxes(); + if (axes != null && axes.length != 0) { + sb.append(", axes = \""); + sb.append(FontVariationAxis.toFontVariationSettings(axes)); + sb.append("\""); + } + writer.println(sb.toString()); + } + + private int execCommand(@NonNull ShellCommand shell, @NonNull String cmd) { + final Context ctx = mService.getContext(); + if (cmd == null) { + return shell.handleDefaultCommands(null); + } + + final FontManagerService.SystemFontSettings settings = mService.getCurrentFontSettings(); + + switch (cmd) { + case "dump": + if (!DumpUtils.checkDumpPermission(ctx, TAG, shell.getErrPrintWriter())) { + return 1; + } + final IndentingPrintWriter writer = + new IndentingPrintWriter(shell.getOutPrintWriter(), " "); + String nextArg = shell.getNextArg(); + if (nextArg == null) { + dumpFontConfig(writer, settings.getSystemFontConfig()); + } else { + final Map fallbackMap = settings.getSystemFallbackMap(); + FontFamily[] families = fallbackMap.get(nextArg); + if (families == null) { + writer.println("Font Family \"" + nextArg + "\" not found"); + } else { + dumpFallback(writer, families); + } + } + return 0; + default: + shell.handleDefaultCommands(cmd); + } + return 0; + } +}