Loading core/java/android/graphics/fonts/FontManager.java +9 −0 Original line number Diff line number Diff line Loading @@ -198,6 +198,15 @@ public class FontManager { */ public static final int RESULT_ERROR_INVALID_XML = -10007; /** * Indicates a failure due to invalid debug certificate file. * * This error code is only used with the shell command interaction. * * @hide */ public static final int RESULT_ERROR_INVALID_DEBUG_CERTIFICATE = -10008; private FontManager(@NonNull IFontManager iFontManager) { mIFontManager = iFontManager; } Loading core/res/res/values/config.xml +4 −0 Original line number Diff line number Diff line Loading @@ -5775,4 +5775,8 @@ <string-array name="config_serviceStateLocationAllowedPackages"> <item>"com.android.phone"</item> </string-array> <!-- List of certificate to be used for font fs-verity integrity verification --> <string-array translatable="false" name="config_fontManagerServiceCerts"> </string-array> </resources> core/res/res/values/symbols.xml +1 −0 Original line number Diff line number Diff line Loading @@ -2273,6 +2273,7 @@ <java-symbol type="id" name="media_actions" /> <java-symbol type="dimen" name="config_mediaMetadataBitmapMaxSize" /> <java-symbol type="array" name="config_fontManagerServiceCerts" /> <!-- From SystemUI --> <java-symbol type="anim" name="push_down_in" /> Loading services/core/java/com/android/server/graphics/fonts/FontManagerService.java +69 −18 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.graphics.fonts.FontFamily; import android.graphics.fonts.FontManager; import android.graphics.fonts.FontUpdateRequest; import android.graphics.fonts.SystemFonts; import android.os.Build; import android.os.ParcelFileDescriptor; import android.os.ResultReceiver; import android.os.SharedMemory; Loading @@ -35,8 +36,10 @@ import android.text.FontConfig; import android.util.AndroidException; import android.util.ArrayMap; import android.util.IndentingPrintWriter; import android.util.Log; import android.util.Slog; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.graphics.fonts.IFontManager; import com.android.internal.security.VerityUtils; Loading @@ -47,7 +50,9 @@ import com.android.server.SystemService; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.nio.ByteBuffer; import java.nio.DirectByteBuffer; Loading Loading @@ -154,9 +159,30 @@ public final class FontManagerService extends IFontManager.Stub { } private static class FsverityUtilImpl implements UpdatableFontDir.FsverityUtil { private final String[] mDerCertPaths; FsverityUtilImpl(String[] derCertPaths) { mDerCertPaths = derCertPaths; } @Override public boolean hasFsverity(String filePath) { return VerityUtils.hasFsverity(filePath); public boolean isFromTrustedProvider(String fontPath, byte[] pkcs7Signature) { final byte[] digest = VerityUtils.getFsverityDigest(fontPath); if (digest == null) { Log.w(TAG, "Failed to get fs-verity digest for " + fontPath); return false; } for (String certPath : mDerCertPaths) { try (InputStream is = new FileInputStream(certPath)) { if (VerityUtils.verifyPkcs7DetachedSignature(pkcs7Signature, digest, is)) { return true; } } catch (IOException e) { Log.w(TAG, "Failed to read certificate file: " + certPath); } } return false; } @Override Loading @@ -174,11 +200,15 @@ public final class FontManagerService extends IFontManager.Stub { @NonNull private final Context mContext; private final boolean mIsSafeMode; private final Object mUpdatableFontDirLock = new Object(); private String mDebugCertFilePath = null; @GuardedBy("mUpdatableFontDirLock") @Nullable private final UpdatableFontDir mUpdatableFontDir; private UpdatableFontDir mUpdatableFontDir; // mSerializedFontMapLock can be acquired while holding mUpdatableFontDirLock. // mUpdatableFontDirLock should not be newly acquired while holding mSerializedFontMapLock. Loading @@ -194,22 +224,43 @@ public final class FontManagerService extends IFontManager.Stub { UpdatableFontDir.deleteAllFiles(new File(FONT_FILES_DIR), new File(CONFIG_XML_FILE)); } mContext = context; mUpdatableFontDir = createUpdatableFontDir(safeMode); mIsSafeMode = safeMode; initialize(); } @Nullable private static UpdatableFontDir createUpdatableFontDir(boolean safeMode) { private UpdatableFontDir createUpdatableFontDir() { // Never read updatable font files in safe mode. if (safeMode) return null; if (mIsSafeMode) return null; // If apk verity is supported, fs-verity should be available. if (!VerityUtils.isFsVeritySupported()) return null; String[] certs = mContext.getResources().getStringArray( R.array.config_fontManagerServiceCerts); if (mDebugCertFilePath != null && (Build.IS_USERDEBUG || Build.IS_ENG)) { String[] tmp = new String[certs.length + 1]; System.arraycopy(certs, 0, tmp, 0, certs.length); tmp[certs.length] = mDebugCertFilePath; certs = tmp; } return new UpdatableFontDir(new File(FONT_FILES_DIR), new OtfFontFileParser(), new FsverityUtilImpl(), new File(CONFIG_XML_FILE)); new FsverityUtilImpl(certs), new File(CONFIG_XML_FILE)); } /** * Add debug certificate to the cert list. This must be called only on userdebug/eng * build. * @param debugCertPath a debug certificate file path */ public void addDebugCertificate(@Nullable String debugCertPath) { mDebugCertFilePath = debugCertPath; } private void initialize() { synchronized (mUpdatableFontDirLock) { mUpdatableFontDir = createUpdatableFontDir(); if (mUpdatableFontDir == null) { setSerializedFontMap(serializeSystemServerFontMap()); return; Loading @@ -232,12 +283,12 @@ public final class FontManagerService extends IFontManager.Stub { /* package */ void update(int baseVersion, List<FontUpdateRequest> requests) throws SystemFontException { synchronized (mUpdatableFontDirLock) { if (mUpdatableFontDir == null) { throw new SystemFontException( FontManager.RESULT_ERROR_FONT_UPDATER_DISABLED, "The font updater is disabled."); } synchronized (mUpdatableFontDirLock) { // baseVersion == -1 only happens from shell command. This is filtered and treated as // error from SystemApi call. if (baseVersion != -1 && mUpdatableFontDir.getConfigVersion() != baseVersion) { Loading Loading @@ -272,10 +323,10 @@ public final class FontManagerService extends IFontManager.Stub { } /* package */ Map<String, File> getFontFileMap() { synchronized (mUpdatableFontDirLock) { if (mUpdatableFontDir == null) { return Collections.emptyMap(); } synchronized (mUpdatableFontDirLock) { return mUpdatableFontDir.getPostScriptMap(); } } Loading @@ -301,10 +352,10 @@ public final class FontManagerService extends IFontManager.Stub { * Returns an active system font configuration. */ public @NonNull FontConfig getSystemFontConfig() { synchronized (mUpdatableFontDirLock) { if (mUpdatableFontDir == null) { return SystemFonts.getSystemPreinstalledFontConfig(); } synchronized (mUpdatableFontDirLock) { return mUpdatableFontDir.getSystemFontConfig(); } } Loading services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java +34 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.graphics.fonts.FontUpdateRequest; import android.graphics.fonts.FontVariationAxis; import android.graphics.fonts.SystemFonts; import android.os.Binder; import android.os.Build; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.ShellCommand; Loading Loading @@ -103,6 +104,10 @@ public class FontManagerShellCommand extends ShellCommand { w.println("update-family [family definition XML path]"); w.println(" Update font families with the new definitions."); w.println(); w.println("install-debug-cert [cert file path]"); w.println(" Install debug certificate file. This command can be used only on userdebug"); w.println(" or eng device with root user."); w.println(); w.println("clear"); w.println(" Remove all installed font files and reset to the initial state."); w.println(); Loading Loading @@ -322,6 +327,33 @@ public class FontManagerShellCommand extends ShellCommand { return 0; } private int installCert(ShellCommand shell) throws SystemFontException { if (!(Build.IS_USERDEBUG || Build.IS_ENG)) { throw new SecurityException("Only userdebug/eng device can add debug certificate"); } if (Binder.getCallingUid() != Process.ROOT_UID) { throw new SecurityException("Only root can add debug certificate"); } String certPath = shell.getNextArg(); if (certPath == null) { throw new SystemFontException( FontManager.RESULT_ERROR_INVALID_DEBUG_CERTIFICATE, "Cert file path argument is required."); } File file = new File(certPath); if (!file.isFile()) { throw new SystemFontException( FontManager.RESULT_ERROR_INVALID_DEBUG_CERTIFICATE, "Cert file (" + file + ") is not found"); } mService.addDebugCertificate(certPath); mService.restart(); shell.getOutPrintWriter().println("Success"); return 0; } private int update(ShellCommand shell) throws SystemFontException { String fontPath = shell.getNextArg(); if (fontPath == null) { Loading Loading @@ -494,6 +526,8 @@ public class FontManagerShellCommand extends ShellCommand { return restart(shell); case "status": return status(shell); case "install-debug-cert": return installCert(shell); default: return shell.handleDefaultCommands(cmd); } Loading Loading
core/java/android/graphics/fonts/FontManager.java +9 −0 Original line number Diff line number Diff line Loading @@ -198,6 +198,15 @@ public class FontManager { */ public static final int RESULT_ERROR_INVALID_XML = -10007; /** * Indicates a failure due to invalid debug certificate file. * * This error code is only used with the shell command interaction. * * @hide */ public static final int RESULT_ERROR_INVALID_DEBUG_CERTIFICATE = -10008; private FontManager(@NonNull IFontManager iFontManager) { mIFontManager = iFontManager; } Loading
core/res/res/values/config.xml +4 −0 Original line number Diff line number Diff line Loading @@ -5775,4 +5775,8 @@ <string-array name="config_serviceStateLocationAllowedPackages"> <item>"com.android.phone"</item> </string-array> <!-- List of certificate to be used for font fs-verity integrity verification --> <string-array translatable="false" name="config_fontManagerServiceCerts"> </string-array> </resources>
core/res/res/values/symbols.xml +1 −0 Original line number Diff line number Diff line Loading @@ -2273,6 +2273,7 @@ <java-symbol type="id" name="media_actions" /> <java-symbol type="dimen" name="config_mediaMetadataBitmapMaxSize" /> <java-symbol type="array" name="config_fontManagerServiceCerts" /> <!-- From SystemUI --> <java-symbol type="anim" name="push_down_in" /> Loading
services/core/java/com/android/server/graphics/fonts/FontManagerService.java +69 −18 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.graphics.fonts.FontFamily; import android.graphics.fonts.FontManager; import android.graphics.fonts.FontUpdateRequest; import android.graphics.fonts.SystemFonts; import android.os.Build; import android.os.ParcelFileDescriptor; import android.os.ResultReceiver; import android.os.SharedMemory; Loading @@ -35,8 +36,10 @@ import android.text.FontConfig; import android.util.AndroidException; import android.util.ArrayMap; import android.util.IndentingPrintWriter; import android.util.Log; import android.util.Slog; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.graphics.fonts.IFontManager; import com.android.internal.security.VerityUtils; Loading @@ -47,7 +50,9 @@ import com.android.server.SystemService; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.nio.ByteBuffer; import java.nio.DirectByteBuffer; Loading Loading @@ -154,9 +159,30 @@ public final class FontManagerService extends IFontManager.Stub { } private static class FsverityUtilImpl implements UpdatableFontDir.FsverityUtil { private final String[] mDerCertPaths; FsverityUtilImpl(String[] derCertPaths) { mDerCertPaths = derCertPaths; } @Override public boolean hasFsverity(String filePath) { return VerityUtils.hasFsverity(filePath); public boolean isFromTrustedProvider(String fontPath, byte[] pkcs7Signature) { final byte[] digest = VerityUtils.getFsverityDigest(fontPath); if (digest == null) { Log.w(TAG, "Failed to get fs-verity digest for " + fontPath); return false; } for (String certPath : mDerCertPaths) { try (InputStream is = new FileInputStream(certPath)) { if (VerityUtils.verifyPkcs7DetachedSignature(pkcs7Signature, digest, is)) { return true; } } catch (IOException e) { Log.w(TAG, "Failed to read certificate file: " + certPath); } } return false; } @Override Loading @@ -174,11 +200,15 @@ public final class FontManagerService extends IFontManager.Stub { @NonNull private final Context mContext; private final boolean mIsSafeMode; private final Object mUpdatableFontDirLock = new Object(); private String mDebugCertFilePath = null; @GuardedBy("mUpdatableFontDirLock") @Nullable private final UpdatableFontDir mUpdatableFontDir; private UpdatableFontDir mUpdatableFontDir; // mSerializedFontMapLock can be acquired while holding mUpdatableFontDirLock. // mUpdatableFontDirLock should not be newly acquired while holding mSerializedFontMapLock. Loading @@ -194,22 +224,43 @@ public final class FontManagerService extends IFontManager.Stub { UpdatableFontDir.deleteAllFiles(new File(FONT_FILES_DIR), new File(CONFIG_XML_FILE)); } mContext = context; mUpdatableFontDir = createUpdatableFontDir(safeMode); mIsSafeMode = safeMode; initialize(); } @Nullable private static UpdatableFontDir createUpdatableFontDir(boolean safeMode) { private UpdatableFontDir createUpdatableFontDir() { // Never read updatable font files in safe mode. if (safeMode) return null; if (mIsSafeMode) return null; // If apk verity is supported, fs-verity should be available. if (!VerityUtils.isFsVeritySupported()) return null; String[] certs = mContext.getResources().getStringArray( R.array.config_fontManagerServiceCerts); if (mDebugCertFilePath != null && (Build.IS_USERDEBUG || Build.IS_ENG)) { String[] tmp = new String[certs.length + 1]; System.arraycopy(certs, 0, tmp, 0, certs.length); tmp[certs.length] = mDebugCertFilePath; certs = tmp; } return new UpdatableFontDir(new File(FONT_FILES_DIR), new OtfFontFileParser(), new FsverityUtilImpl(), new File(CONFIG_XML_FILE)); new FsverityUtilImpl(certs), new File(CONFIG_XML_FILE)); } /** * Add debug certificate to the cert list. This must be called only on userdebug/eng * build. * @param debugCertPath a debug certificate file path */ public void addDebugCertificate(@Nullable String debugCertPath) { mDebugCertFilePath = debugCertPath; } private void initialize() { synchronized (mUpdatableFontDirLock) { mUpdatableFontDir = createUpdatableFontDir(); if (mUpdatableFontDir == null) { setSerializedFontMap(serializeSystemServerFontMap()); return; Loading @@ -232,12 +283,12 @@ public final class FontManagerService extends IFontManager.Stub { /* package */ void update(int baseVersion, List<FontUpdateRequest> requests) throws SystemFontException { synchronized (mUpdatableFontDirLock) { if (mUpdatableFontDir == null) { throw new SystemFontException( FontManager.RESULT_ERROR_FONT_UPDATER_DISABLED, "The font updater is disabled."); } synchronized (mUpdatableFontDirLock) { // baseVersion == -1 only happens from shell command. This is filtered and treated as // error from SystemApi call. if (baseVersion != -1 && mUpdatableFontDir.getConfigVersion() != baseVersion) { Loading Loading @@ -272,10 +323,10 @@ public final class FontManagerService extends IFontManager.Stub { } /* package */ Map<String, File> getFontFileMap() { synchronized (mUpdatableFontDirLock) { if (mUpdatableFontDir == null) { return Collections.emptyMap(); } synchronized (mUpdatableFontDirLock) { return mUpdatableFontDir.getPostScriptMap(); } } Loading @@ -301,10 +352,10 @@ public final class FontManagerService extends IFontManager.Stub { * Returns an active system font configuration. */ public @NonNull FontConfig getSystemFontConfig() { synchronized (mUpdatableFontDirLock) { if (mUpdatableFontDir == null) { return SystemFonts.getSystemPreinstalledFontConfig(); } synchronized (mUpdatableFontDirLock) { return mUpdatableFontDir.getSystemFontConfig(); } } Loading
services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java +34 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.graphics.fonts.FontUpdateRequest; import android.graphics.fonts.FontVariationAxis; import android.graphics.fonts.SystemFonts; import android.os.Binder; import android.os.Build; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.ShellCommand; Loading Loading @@ -103,6 +104,10 @@ public class FontManagerShellCommand extends ShellCommand { w.println("update-family [family definition XML path]"); w.println(" Update font families with the new definitions."); w.println(); w.println("install-debug-cert [cert file path]"); w.println(" Install debug certificate file. This command can be used only on userdebug"); w.println(" or eng device with root user."); w.println(); w.println("clear"); w.println(" Remove all installed font files and reset to the initial state."); w.println(); Loading Loading @@ -322,6 +327,33 @@ public class FontManagerShellCommand extends ShellCommand { return 0; } private int installCert(ShellCommand shell) throws SystemFontException { if (!(Build.IS_USERDEBUG || Build.IS_ENG)) { throw new SecurityException("Only userdebug/eng device can add debug certificate"); } if (Binder.getCallingUid() != Process.ROOT_UID) { throw new SecurityException("Only root can add debug certificate"); } String certPath = shell.getNextArg(); if (certPath == null) { throw new SystemFontException( FontManager.RESULT_ERROR_INVALID_DEBUG_CERTIFICATE, "Cert file path argument is required."); } File file = new File(certPath); if (!file.isFile()) { throw new SystemFontException( FontManager.RESULT_ERROR_INVALID_DEBUG_CERTIFICATE, "Cert file (" + file + ") is not found"); } mService.addDebugCertificate(certPath); mService.restart(); shell.getOutPrintWriter().println("Success"); return 0; } private int update(ShellCommand shell) throws SystemFontException { String fontPath = shell.getNextArg(); if (fontPath == null) { Loading Loading @@ -494,6 +526,8 @@ public class FontManagerShellCommand extends ShellCommand { return restart(shell); case "status": return status(shell); case "install-debug-cert": return installCert(shell); default: return shell.handleDefaultCommands(cmd); } Loading