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; + } +}