Loading graphics/java/android/graphics/fonts/FontFileUtil.java +17 −0 Original line number Diff line number Diff line Loading @@ -183,6 +183,23 @@ public class FontFileUtil { return nIsPostScriptType1Font(buffer, index); } /** * Analyze the file content and returns 1 if the font file is an OpenType collection file, 0 if * the font file is a OpenType font file, -1 otherwise. */ public static int isCollectionFont(@NonNull ByteBuffer buffer) { ByteBuffer copied = buffer.slice(); copied.order(ByteOrder.BIG_ENDIAN); int magicNumber = copied.getInt(0); if (magicNumber == TTC_TAG) { return 1; } else if (magicNumber == SFNT_VERSION_1 || magicNumber == SFNT_VERSION_OTTO) { return 0; } else { return -1; } } @FastNative private static native long nGetFontRevision(@NonNull ByteBuffer buffer, @IntRange(from = 0) int index); Loading services/core/java/com/android/server/graphics/fonts/FontManagerService.java +17 −2 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import android.os.SharedMemory; import android.os.ShellCallback; import android.system.ErrnoException; import android.text.FontConfig; import android.text.TextUtils; import android.util.AndroidException; import android.util.IndentingPrintWriter; import android.util.Slog; Loading Loading @@ -148,10 +149,24 @@ public final class FontManagerService extends IFontManager.Stub { /* package */ static class OtfFontFileParser implements UpdatableFontDir.FontFileParser { @Override public String getPostScriptName(File file) throws IOException { public String getCanonicalFileName(File file) throws IOException { ByteBuffer buffer = mmap(file); try { return FontFileUtil.getPostScriptName(buffer, 0); String psName = FontFileUtil.getPostScriptName(buffer, 0); int isType1Font = FontFileUtil.isPostScriptType1Font(buffer, 0); int isCollection = FontFileUtil.isCollectionFont(buffer); if (TextUtils.isEmpty(psName) || isType1Font == -1 || isCollection == -1) { return null; } String extension; if (isCollection == 1) { extension = isType1Font == 1 ? ".otc" : ".ttc"; } else { extension = isType1Font == 1 ? ".otf" : ".ttf"; } return psName + extension; } finally { NioUtils.freeDirectBuffer(buffer); } Loading services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java +38 −18 Original line number Diff line number Diff line Loading @@ -56,14 +56,12 @@ final class UpdatableFontDir { private static final String TAG = "UpdatableFontDir"; private static final String RANDOM_DIR_PREFIX = "~~"; // TODO: Support .otf private static final String ALLOWED_EXTENSION = ".ttf"; private static final String CONFIG_XML_FILE = "/data/fonts/config/config.xml"; /** Interface to mock font file access in tests. */ interface FontFileParser { String getPostScriptName(File file) throws IOException; String getCanonicalFileName(File file) throws IOException; long getRevision(File file) throws IOException; } Loading Loading @@ -321,20 +319,20 @@ final class UpdatableFontDir { FontManager.RESULT_ERROR_VERIFICATION_FAILURE, "Failed to setup fs-verity.", e); } String postScriptName; String canonicalFileName; try { postScriptName = mParser.getPostScriptName(tempNewFontFile); canonicalFileName = mParser.getCanonicalFileName(tempNewFontFile); } catch (IOException e) { throw new SystemFontException( FontManager.RESULT_ERROR_INVALID_FONT_FILE, "Failed to read PostScript name from font file", e); } if (postScriptName == null) { if (canonicalFileName == null) { throw new SystemFontException( FontManager.RESULT_ERROR_INVALID_FONT_NAME, "Failed to read PostScript name from font file"); } File newFontFile = new File(newDir, postScriptName + ALLOWED_EXTENSION); File newFontFile = new File(newDir, canonicalFileName); if (!mFsverityUtil.rename(tempNewFontFile, newFontFile)) { throw new SystemFontException( FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE, Loading Loading @@ -380,20 +378,38 @@ final class UpdatableFontDir { return dir; } private FontFileInfo lookupFontFileInfo(File file) { String name = file.getName(); if (!name.endsWith(".ttf") && !name.endsWith(".otf") && !name.endsWith(".ttc") && !name.endsWith(".otc")) { return null; } String key = name.substring(0, name.length() - 4); return mFontFileInfoMap.get(key); } private void putFontFileInfo(FontFileInfo info) { String name = info.getFile().getName(); // The file name in FontFileInfo is already validated. Thus, just strip last 4 chars. String key = name.substring(0, name.length() - 4); mFontFileInfoMap.put(key, info); } /** * Add the given {@link FontFileInfo} to {@link #mFontFileInfoMap} if its font revision is * higher than the currently used font file (either in {@link #mFontFileInfoMap} or {@link * #mPreinstalledFontDirs}). */ private boolean addFileToMapIfNewer(FontFileInfo fontFileInfo, boolean deleteOldFile) { String name = fontFileInfo.getFile().getName(); FontFileInfo existingInfo = mFontFileInfoMap.get(name); FontFileInfo existingInfo = lookupFontFileInfo(fontFileInfo.getFile()); final boolean shouldAddToMap; if (existingInfo == null) { // We got a new updatable font. We need to check if it's newer than preinstalled fonts. // Note that getPreinstalledFontRevision() returns -1 if there is no preinstalled font // with 'name'. shouldAddToMap = getPreinstalledFontRevision(name) < fontFileInfo.getRevision(); long preInstalledRev = getPreinstalledFontRevision(fontFileInfo.getFile().getName()); shouldAddToMap = preInstalledRev < fontFileInfo.getRevision(); } else { shouldAddToMap = existingInfo.getRevision() < fontFileInfo.getRevision(); } Loading @@ -401,7 +417,7 @@ final class UpdatableFontDir { if (deleteOldFile && existingInfo != null) { FileUtils.deleteContentsAndDir(existingInfo.getRandomizedFontDir()); } mFontFileInfoMap.put(name, fontFileInfo); putFontFileInfo(fontFileInfo); } else { if (deleteOldFile) { FileUtils.deleteContentsAndDir(fontFileInfo.getRandomizedFontDir()); Loading Loading @@ -464,15 +480,18 @@ final class UpdatableFontDir { */ private boolean validateFontFileName(File file) { String fileName = file.getName(); String postScriptName = getPostScriptName(file); return (postScriptName + ALLOWED_EXTENSION).equals(fileName); String canonicalFileName = getCanonicalFileName(file); if (canonicalFileName == null) { return false; } return canonicalFileName.equals(fileName); } /** Returns the PostScript name of the given font file, or null. */ @Nullable private String getPostScriptName(File file) { private String getCanonicalFileName(File file) { try { return mParser.getPostScriptName(file); return mParser.getCanonicalFileName(file); } catch (IOException e) { Slog.e(TAG, "Failed to read font file", e); return null; Loading Loading @@ -514,7 +533,7 @@ final class UpdatableFontDir { List<FontConfig.Font> fontList = fontFamily.getFontList(); for (int i = 0; i < fontList.size(); i++) { FontConfig.Font font = fontList.get(i); FontFileInfo info = mFontFileInfoMap.get(font.getFile().getName()); FontFileInfo info = lookupFontFileInfo(font.getFile()); if (info == null) { return null; } Loading @@ -537,8 +556,9 @@ final class UpdatableFontDir { Map<String, File> getFontFileMap() { Map<String, File> map = new ArrayMap<>(); for (Map.Entry<String, FontFileInfo> entry : mFontFileInfoMap.entrySet()) { map.put(entry.getKey(), entry.getValue().getFile()); for (int i = 0; i < mFontFileInfoMap.size(); ++i) { File file = mFontFileInfoMap.valueAt(i).getFile(); map.put(file.getName(), file); } return map; } Loading services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java +53 −46 Original line number Diff line number Diff line Loading @@ -68,7 +68,7 @@ public final class UpdatableFontDirTest { */ private static class FakeFontFileParser implements UpdatableFontDir.FontFileParser { @Override public String getPostScriptName(File file) throws IOException { public String getCanonicalFileName(File file) throws IOException { String content = FileUtils.readTextFile(file, 100, ""); return content.split(",")[0]; } Loading Loading @@ -160,10 +160,10 @@ public final class UpdatableFontDirTest { assertThat(dirForPreparation.getSystemFontConfig().getLastModifiedTimeMillis()) .isEqualTo(expectedModifiedDate); dirForPreparation.update(Arrays.asList( newFontUpdateRequest("foo,1", GOOD_SIGNATURE), newFontUpdateRequest("bar,2", GOOD_SIGNATURE), newFontUpdateRequest("foo,3", GOOD_SIGNATURE), newFontUpdateRequest("bar,4", GOOD_SIGNATURE), newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE), newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE), newFontUpdateRequest("foo.ttf,3", GOOD_SIGNATURE), newFontUpdateRequest("bar.ttf,4", GOOD_SIGNATURE), newAddFontFamilyRequest("<family name='foobar'>" + " <font>foo.ttf</font>" + " <font>bar.ttf</font>" Loading Loading @@ -214,10 +214,10 @@ public final class UpdatableFontDirTest { mConfigFile); dirForPreparation.loadFontFileMap(); dirForPreparation.update(Arrays.asList( newFontUpdateRequest("foo,1", GOOD_SIGNATURE), newFontUpdateRequest("bar,2", GOOD_SIGNATURE), newFontUpdateRequest("foo,3", GOOD_SIGNATURE), newFontUpdateRequest("bar,4", GOOD_SIGNATURE), newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE), newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE), newFontUpdateRequest("foo.ttf,3", GOOD_SIGNATURE), newFontUpdateRequest("bar.ttf,4", GOOD_SIGNATURE), newAddFontFamilyRequest("<family name='foobar'>" + " <font>foo.ttf</font>" + " <font>bar.ttf</font>" Loading Loading @@ -246,10 +246,10 @@ public final class UpdatableFontDirTest { mConfigFile); dirForPreparation.loadFontFileMap(); dirForPreparation.update(Arrays.asList( newFontUpdateRequest("foo,1", GOOD_SIGNATURE), newFontUpdateRequest("bar,2", GOOD_SIGNATURE), newFontUpdateRequest("foo,3", GOOD_SIGNATURE), newFontUpdateRequest("bar,4", GOOD_SIGNATURE), newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE), newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE), newFontUpdateRequest("foo.ttf,3", GOOD_SIGNATURE), newFontUpdateRequest("bar.ttf,4", GOOD_SIGNATURE), newAddFontFamilyRequest("<family name='foobar'>" + " <font>foo.ttf</font>" + " <font>bar.ttf</font>" Loading Loading @@ -279,10 +279,10 @@ public final class UpdatableFontDirTest { mConfigFile); dirForPreparation.loadFontFileMap(); dirForPreparation.update(Arrays.asList( newFontUpdateRequest("foo,1", GOOD_SIGNATURE), newFontUpdateRequest("bar,2", GOOD_SIGNATURE), newFontUpdateRequest("foo,3", GOOD_SIGNATURE), newFontUpdateRequest("bar,4", GOOD_SIGNATURE), newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE), newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE), newFontUpdateRequest("foo.ttf,3", GOOD_SIGNATURE), newFontUpdateRequest("bar.ttf,4", GOOD_SIGNATURE), newAddFontFamilyRequest("<family name='foobar'>" + " <font>foo.ttf</font>" + " <font>bar.ttf</font>" Loading Loading @@ -332,14 +332,14 @@ public final class UpdatableFontDirTest { mConfigFile); dirForPreparation.loadFontFileMap(); dirForPreparation.update(Arrays.asList( newFontUpdateRequest("foo,1", GOOD_SIGNATURE), newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE), newAddFontFamilyRequest("<family name='foobar'>" + " <font>foo.ttf</font>" + "</family>"))); try { dirForPreparation.update(Arrays.asList( newFontUpdateRequest("foo,2", GOOD_SIGNATURE), newFontUpdateRequest("bar,2", "Invalid signature"), newFontUpdateRequest("foo.ttf,2", GOOD_SIGNATURE), newFontUpdateRequest("bar.ttf,2", "Invalid signature"), newAddFontFamilyRequest("<family name='foobar'>" + " <font>foo.ttf</font>" + " <font>bar.ttf</font>" Loading Loading @@ -372,7 +372,7 @@ public final class UpdatableFontDirTest { mConfigFile); dir.loadFontFileMap(); dir.update(Collections.singletonList(newFontUpdateRequest("test,1", GOOD_SIGNATURE))); dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE))); assertThat(dir.getFontFileMap()).containsKey("test.ttf"); assertThat(parser.getRevision(dir.getFontFileMap().get("test.ttf"))).isEqualTo(1); File fontFile = dir.getFontFileMap().get("test.ttf"); Loading @@ -390,9 +390,9 @@ public final class UpdatableFontDirTest { mConfigFile); dir.loadFontFileMap(); dir.update(Collections.singletonList(newFontUpdateRequest("test,1", GOOD_SIGNATURE))); dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE))); Map<String, File> mapBeforeUpgrade = dir.getFontFileMap(); dir.update(Collections.singletonList(newFontUpdateRequest("test,2", GOOD_SIGNATURE))); dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,2", GOOD_SIGNATURE))); assertThat(dir.getFontFileMap()).containsKey("test.ttf"); assertThat(parser.getRevision(dir.getFontFileMap().get("test.ttf"))).isEqualTo(2); assertThat(mapBeforeUpgrade).containsKey("test.ttf"); Loading @@ -409,9 +409,10 @@ public final class UpdatableFontDirTest { mConfigFile); dir.loadFontFileMap(); dir.update(Collections.singletonList(newFontUpdateRequest("test,2", GOOD_SIGNATURE))); dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,2", GOOD_SIGNATURE))); try { dir.update(Collections.singletonList(newFontUpdateRequest("test,1", GOOD_SIGNATURE))); dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE))); fail("Expect SystemFontException"); } catch (FontManagerService.SystemFontException e) { assertThat(e.getErrorCode()).isEqualTo(FontManager.RESULT_ERROR_DOWNGRADING); Loading @@ -430,8 +431,8 @@ public final class UpdatableFontDirTest { mConfigFile); dir.loadFontFileMap(); dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE))); dir.update(Collections.singletonList(newFontUpdateRequest("bar,2", GOOD_SIGNATURE))); dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE))); dir.update(Collections.singletonList(newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE))); assertThat(dir.getFontFileMap()).containsKey("foo.ttf"); assertThat(parser.getRevision(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(1); assertThat(dir.getFontFileMap()).containsKey("bar.ttf"); Loading @@ -448,8 +449,8 @@ public final class UpdatableFontDirTest { dir.loadFontFileMap(); dir.update(Arrays.asList( newFontUpdateRequest("foo,1", GOOD_SIGNATURE), newFontUpdateRequest("bar,2", GOOD_SIGNATURE))); newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE), newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE))); assertThat(dir.getFontFileMap()).containsKey("foo.ttf"); assertThat(parser.getRevision(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(1); assertThat(dir.getFontFileMap()).containsKey("bar.ttf"); Loading @@ -467,7 +468,8 @@ public final class UpdatableFontDirTest { try { dir.update( Collections.singletonList(newFontUpdateRequest("test,1", "Invalid signature"))); Collections.singletonList(newFontUpdateRequest("test.ttf,1", "Invalid signature"))); fail("Expect SystemFontException"); } catch (FontManagerService.SystemFontException e) { assertThat(e.getErrorCode()) Loading @@ -480,14 +482,15 @@ public final class UpdatableFontDirTest { public void installFontFile_olderThanPreinstalledFont() throws Exception { FakeFontFileParser parser = new FakeFontFileParser(); FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), "test,1"); FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), "test.ttf,1"); UpdatableFontDir dir = new UpdatableFontDir( mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, mConfigFile); dir.loadFontFileMap(); try { dir.update(Collections.singletonList(newFontUpdateRequest("test,1", GOOD_SIGNATURE))); dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE))); fail("Expect SystemFontException"); } catch (FontManagerService.SystemFontException e) { assertThat(e.getErrorCode()).isEqualTo(FontManager.RESULT_ERROR_DOWNGRADING); Loading @@ -500,7 +503,7 @@ public final class UpdatableFontDirTest { long expectedModifiedDate = 1234567890; FakeFontFileParser parser = new FakeFontFileParser(); FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), "test,1"); FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), "test.ttf,1"); File readonlyDir = new File(mCacheDir, "readonly"); assertThat(readonlyDir.mkdir()).isTrue(); Loading @@ -519,7 +522,8 @@ public final class UpdatableFontDirTest { try { dir.update( Collections.singletonList(newFontUpdateRequest("test,2", GOOD_SIGNATURE))); Collections.singletonList(newFontUpdateRequest("test.ttf,2", GOOD_SIGNATURE))); } catch (FontManagerService.SystemFontException e) { assertThat(e.getErrorCode()) .isEqualTo(FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG); Loading @@ -539,7 +543,7 @@ public final class UpdatableFontDirTest { mUpdatableFontFilesDir, mPreinstalledFontDirs, new UpdatableFontDir.FontFileParser() { @Override public String getPostScriptName(File file) throws IOException { public String getCanonicalFileName(File file) throws IOException { return null; } Loading @@ -551,7 +555,8 @@ public final class UpdatableFontDirTest { dir.loadFontFileMap(); try { dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE))); dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE))); fail("Expect SystemFontException"); } catch (FontManagerService.SystemFontException e) { assertThat(e.getErrorCode()) Loading @@ -567,7 +572,7 @@ public final class UpdatableFontDirTest { mUpdatableFontFilesDir, mPreinstalledFontDirs, new UpdatableFontDir.FontFileParser() { @Override public String getPostScriptName(File file) throws IOException { public String getCanonicalFileName(File file) throws IOException { throw new IOException(); } Loading @@ -579,7 +584,8 @@ public final class UpdatableFontDirTest { dir.loadFontFileMap(); try { dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE))); dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE))); fail("Expect SystemFontException"); } catch (FontManagerService.SystemFontException e) { assertThat(e.getErrorCode()) Loading Loading @@ -615,7 +621,8 @@ public final class UpdatableFontDirTest { dir.loadFontFileMap(); try { dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE))); dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE))); fail("Expect SystemFontException"); } catch (FontManagerService.SystemFontException e) { assertThat(e.getErrorCode()) Loading @@ -633,11 +640,11 @@ public final class UpdatableFontDirTest { mConfigFile); dir.loadFontFileMap(); dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE))); dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE))); try { dir.update(Arrays.asList( newFontUpdateRequest("foo,2", GOOD_SIGNATURE), newFontUpdateRequest("bar,2", "Invalid signature"))); newFontUpdateRequest("foo.ttf,2", GOOD_SIGNATURE), newFontUpdateRequest("bar.ttf,2", "Invalid signature"))); fail("Batch update with invalid signature should fail"); } catch (FontManagerService.SystemFontException e) { // Expected Loading @@ -657,7 +664,7 @@ public final class UpdatableFontDirTest { dir.loadFontFileMap(); dir.update(Arrays.asList( newFontUpdateRequest("test,1", GOOD_SIGNATURE), newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE), newAddFontFamilyRequest("<family name='test'>" + " <font>test.ttf</font>" + "</family>"))); Loading @@ -680,7 +687,7 @@ public final class UpdatableFontDirTest { try { dir.update(Arrays.asList( newFontUpdateRequest("test,1", GOOD_SIGNATURE), newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE), newAddFontFamilyRequest("<family lang='en'>" + " <font>test.ttf</font>" + "</family>"))); Loading Loading @@ -722,7 +729,7 @@ public final class UpdatableFontDirTest { assertNamedFamilyExists(dir.getSystemFontConfig(), "monospace"); dir.update(Arrays.asList( newFontUpdateRequest("test,1", GOOD_SIGNATURE), newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE), // Updating an existing font family. newAddFontFamilyRequest("<family name='monospace'>" + " <font>test.ttf</font>" Loading Loading @@ -755,7 +762,7 @@ public final class UpdatableFontDirTest { assertThat(firstFontFamily.getName()).isNotEmpty(); dir.update(Arrays.asList( newFontUpdateRequest("test,1", GOOD_SIGNATURE), newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE), newAddFontFamilyRequest("<family name='" + firstFontFamily.getName() + "'>" + " <font>test.ttf</font>" + "</family>"))); Loading Loading
graphics/java/android/graphics/fonts/FontFileUtil.java +17 −0 Original line number Diff line number Diff line Loading @@ -183,6 +183,23 @@ public class FontFileUtil { return nIsPostScriptType1Font(buffer, index); } /** * Analyze the file content and returns 1 if the font file is an OpenType collection file, 0 if * the font file is a OpenType font file, -1 otherwise. */ public static int isCollectionFont(@NonNull ByteBuffer buffer) { ByteBuffer copied = buffer.slice(); copied.order(ByteOrder.BIG_ENDIAN); int magicNumber = copied.getInt(0); if (magicNumber == TTC_TAG) { return 1; } else if (magicNumber == SFNT_VERSION_1 || magicNumber == SFNT_VERSION_OTTO) { return 0; } else { return -1; } } @FastNative private static native long nGetFontRevision(@NonNull ByteBuffer buffer, @IntRange(from = 0) int index); Loading
services/core/java/com/android/server/graphics/fonts/FontManagerService.java +17 −2 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import android.os.SharedMemory; import android.os.ShellCallback; import android.system.ErrnoException; import android.text.FontConfig; import android.text.TextUtils; import android.util.AndroidException; import android.util.IndentingPrintWriter; import android.util.Slog; Loading Loading @@ -148,10 +149,24 @@ public final class FontManagerService extends IFontManager.Stub { /* package */ static class OtfFontFileParser implements UpdatableFontDir.FontFileParser { @Override public String getPostScriptName(File file) throws IOException { public String getCanonicalFileName(File file) throws IOException { ByteBuffer buffer = mmap(file); try { return FontFileUtil.getPostScriptName(buffer, 0); String psName = FontFileUtil.getPostScriptName(buffer, 0); int isType1Font = FontFileUtil.isPostScriptType1Font(buffer, 0); int isCollection = FontFileUtil.isCollectionFont(buffer); if (TextUtils.isEmpty(psName) || isType1Font == -1 || isCollection == -1) { return null; } String extension; if (isCollection == 1) { extension = isType1Font == 1 ? ".otc" : ".ttc"; } else { extension = isType1Font == 1 ? ".otf" : ".ttf"; } return psName + extension; } finally { NioUtils.freeDirectBuffer(buffer); } Loading
services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java +38 −18 Original line number Diff line number Diff line Loading @@ -56,14 +56,12 @@ final class UpdatableFontDir { private static final String TAG = "UpdatableFontDir"; private static final String RANDOM_DIR_PREFIX = "~~"; // TODO: Support .otf private static final String ALLOWED_EXTENSION = ".ttf"; private static final String CONFIG_XML_FILE = "/data/fonts/config/config.xml"; /** Interface to mock font file access in tests. */ interface FontFileParser { String getPostScriptName(File file) throws IOException; String getCanonicalFileName(File file) throws IOException; long getRevision(File file) throws IOException; } Loading Loading @@ -321,20 +319,20 @@ final class UpdatableFontDir { FontManager.RESULT_ERROR_VERIFICATION_FAILURE, "Failed to setup fs-verity.", e); } String postScriptName; String canonicalFileName; try { postScriptName = mParser.getPostScriptName(tempNewFontFile); canonicalFileName = mParser.getCanonicalFileName(tempNewFontFile); } catch (IOException e) { throw new SystemFontException( FontManager.RESULT_ERROR_INVALID_FONT_FILE, "Failed to read PostScript name from font file", e); } if (postScriptName == null) { if (canonicalFileName == null) { throw new SystemFontException( FontManager.RESULT_ERROR_INVALID_FONT_NAME, "Failed to read PostScript name from font file"); } File newFontFile = new File(newDir, postScriptName + ALLOWED_EXTENSION); File newFontFile = new File(newDir, canonicalFileName); if (!mFsverityUtil.rename(tempNewFontFile, newFontFile)) { throw new SystemFontException( FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE, Loading Loading @@ -380,20 +378,38 @@ final class UpdatableFontDir { return dir; } private FontFileInfo lookupFontFileInfo(File file) { String name = file.getName(); if (!name.endsWith(".ttf") && !name.endsWith(".otf") && !name.endsWith(".ttc") && !name.endsWith(".otc")) { return null; } String key = name.substring(0, name.length() - 4); return mFontFileInfoMap.get(key); } private void putFontFileInfo(FontFileInfo info) { String name = info.getFile().getName(); // The file name in FontFileInfo is already validated. Thus, just strip last 4 chars. String key = name.substring(0, name.length() - 4); mFontFileInfoMap.put(key, info); } /** * Add the given {@link FontFileInfo} to {@link #mFontFileInfoMap} if its font revision is * higher than the currently used font file (either in {@link #mFontFileInfoMap} or {@link * #mPreinstalledFontDirs}). */ private boolean addFileToMapIfNewer(FontFileInfo fontFileInfo, boolean deleteOldFile) { String name = fontFileInfo.getFile().getName(); FontFileInfo existingInfo = mFontFileInfoMap.get(name); FontFileInfo existingInfo = lookupFontFileInfo(fontFileInfo.getFile()); final boolean shouldAddToMap; if (existingInfo == null) { // We got a new updatable font. We need to check if it's newer than preinstalled fonts. // Note that getPreinstalledFontRevision() returns -1 if there is no preinstalled font // with 'name'. shouldAddToMap = getPreinstalledFontRevision(name) < fontFileInfo.getRevision(); long preInstalledRev = getPreinstalledFontRevision(fontFileInfo.getFile().getName()); shouldAddToMap = preInstalledRev < fontFileInfo.getRevision(); } else { shouldAddToMap = existingInfo.getRevision() < fontFileInfo.getRevision(); } Loading @@ -401,7 +417,7 @@ final class UpdatableFontDir { if (deleteOldFile && existingInfo != null) { FileUtils.deleteContentsAndDir(existingInfo.getRandomizedFontDir()); } mFontFileInfoMap.put(name, fontFileInfo); putFontFileInfo(fontFileInfo); } else { if (deleteOldFile) { FileUtils.deleteContentsAndDir(fontFileInfo.getRandomizedFontDir()); Loading Loading @@ -464,15 +480,18 @@ final class UpdatableFontDir { */ private boolean validateFontFileName(File file) { String fileName = file.getName(); String postScriptName = getPostScriptName(file); return (postScriptName + ALLOWED_EXTENSION).equals(fileName); String canonicalFileName = getCanonicalFileName(file); if (canonicalFileName == null) { return false; } return canonicalFileName.equals(fileName); } /** Returns the PostScript name of the given font file, or null. */ @Nullable private String getPostScriptName(File file) { private String getCanonicalFileName(File file) { try { return mParser.getPostScriptName(file); return mParser.getCanonicalFileName(file); } catch (IOException e) { Slog.e(TAG, "Failed to read font file", e); return null; Loading Loading @@ -514,7 +533,7 @@ final class UpdatableFontDir { List<FontConfig.Font> fontList = fontFamily.getFontList(); for (int i = 0; i < fontList.size(); i++) { FontConfig.Font font = fontList.get(i); FontFileInfo info = mFontFileInfoMap.get(font.getFile().getName()); FontFileInfo info = lookupFontFileInfo(font.getFile()); if (info == null) { return null; } Loading @@ -537,8 +556,9 @@ final class UpdatableFontDir { Map<String, File> getFontFileMap() { Map<String, File> map = new ArrayMap<>(); for (Map.Entry<String, FontFileInfo> entry : mFontFileInfoMap.entrySet()) { map.put(entry.getKey(), entry.getValue().getFile()); for (int i = 0; i < mFontFileInfoMap.size(); ++i) { File file = mFontFileInfoMap.valueAt(i).getFile(); map.put(file.getName(), file); } return map; } Loading
services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java +53 −46 Original line number Diff line number Diff line Loading @@ -68,7 +68,7 @@ public final class UpdatableFontDirTest { */ private static class FakeFontFileParser implements UpdatableFontDir.FontFileParser { @Override public String getPostScriptName(File file) throws IOException { public String getCanonicalFileName(File file) throws IOException { String content = FileUtils.readTextFile(file, 100, ""); return content.split(",")[0]; } Loading Loading @@ -160,10 +160,10 @@ public final class UpdatableFontDirTest { assertThat(dirForPreparation.getSystemFontConfig().getLastModifiedTimeMillis()) .isEqualTo(expectedModifiedDate); dirForPreparation.update(Arrays.asList( newFontUpdateRequest("foo,1", GOOD_SIGNATURE), newFontUpdateRequest("bar,2", GOOD_SIGNATURE), newFontUpdateRequest("foo,3", GOOD_SIGNATURE), newFontUpdateRequest("bar,4", GOOD_SIGNATURE), newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE), newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE), newFontUpdateRequest("foo.ttf,3", GOOD_SIGNATURE), newFontUpdateRequest("bar.ttf,4", GOOD_SIGNATURE), newAddFontFamilyRequest("<family name='foobar'>" + " <font>foo.ttf</font>" + " <font>bar.ttf</font>" Loading Loading @@ -214,10 +214,10 @@ public final class UpdatableFontDirTest { mConfigFile); dirForPreparation.loadFontFileMap(); dirForPreparation.update(Arrays.asList( newFontUpdateRequest("foo,1", GOOD_SIGNATURE), newFontUpdateRequest("bar,2", GOOD_SIGNATURE), newFontUpdateRequest("foo,3", GOOD_SIGNATURE), newFontUpdateRequest("bar,4", GOOD_SIGNATURE), newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE), newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE), newFontUpdateRequest("foo.ttf,3", GOOD_SIGNATURE), newFontUpdateRequest("bar.ttf,4", GOOD_SIGNATURE), newAddFontFamilyRequest("<family name='foobar'>" + " <font>foo.ttf</font>" + " <font>bar.ttf</font>" Loading Loading @@ -246,10 +246,10 @@ public final class UpdatableFontDirTest { mConfigFile); dirForPreparation.loadFontFileMap(); dirForPreparation.update(Arrays.asList( newFontUpdateRequest("foo,1", GOOD_SIGNATURE), newFontUpdateRequest("bar,2", GOOD_SIGNATURE), newFontUpdateRequest("foo,3", GOOD_SIGNATURE), newFontUpdateRequest("bar,4", GOOD_SIGNATURE), newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE), newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE), newFontUpdateRequest("foo.ttf,3", GOOD_SIGNATURE), newFontUpdateRequest("bar.ttf,4", GOOD_SIGNATURE), newAddFontFamilyRequest("<family name='foobar'>" + " <font>foo.ttf</font>" + " <font>bar.ttf</font>" Loading Loading @@ -279,10 +279,10 @@ public final class UpdatableFontDirTest { mConfigFile); dirForPreparation.loadFontFileMap(); dirForPreparation.update(Arrays.asList( newFontUpdateRequest("foo,1", GOOD_SIGNATURE), newFontUpdateRequest("bar,2", GOOD_SIGNATURE), newFontUpdateRequest("foo,3", GOOD_SIGNATURE), newFontUpdateRequest("bar,4", GOOD_SIGNATURE), newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE), newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE), newFontUpdateRequest("foo.ttf,3", GOOD_SIGNATURE), newFontUpdateRequest("bar.ttf,4", GOOD_SIGNATURE), newAddFontFamilyRequest("<family name='foobar'>" + " <font>foo.ttf</font>" + " <font>bar.ttf</font>" Loading Loading @@ -332,14 +332,14 @@ public final class UpdatableFontDirTest { mConfigFile); dirForPreparation.loadFontFileMap(); dirForPreparation.update(Arrays.asList( newFontUpdateRequest("foo,1", GOOD_SIGNATURE), newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE), newAddFontFamilyRequest("<family name='foobar'>" + " <font>foo.ttf</font>" + "</family>"))); try { dirForPreparation.update(Arrays.asList( newFontUpdateRequest("foo,2", GOOD_SIGNATURE), newFontUpdateRequest("bar,2", "Invalid signature"), newFontUpdateRequest("foo.ttf,2", GOOD_SIGNATURE), newFontUpdateRequest("bar.ttf,2", "Invalid signature"), newAddFontFamilyRequest("<family name='foobar'>" + " <font>foo.ttf</font>" + " <font>bar.ttf</font>" Loading Loading @@ -372,7 +372,7 @@ public final class UpdatableFontDirTest { mConfigFile); dir.loadFontFileMap(); dir.update(Collections.singletonList(newFontUpdateRequest("test,1", GOOD_SIGNATURE))); dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE))); assertThat(dir.getFontFileMap()).containsKey("test.ttf"); assertThat(parser.getRevision(dir.getFontFileMap().get("test.ttf"))).isEqualTo(1); File fontFile = dir.getFontFileMap().get("test.ttf"); Loading @@ -390,9 +390,9 @@ public final class UpdatableFontDirTest { mConfigFile); dir.loadFontFileMap(); dir.update(Collections.singletonList(newFontUpdateRequest("test,1", GOOD_SIGNATURE))); dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE))); Map<String, File> mapBeforeUpgrade = dir.getFontFileMap(); dir.update(Collections.singletonList(newFontUpdateRequest("test,2", GOOD_SIGNATURE))); dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,2", GOOD_SIGNATURE))); assertThat(dir.getFontFileMap()).containsKey("test.ttf"); assertThat(parser.getRevision(dir.getFontFileMap().get("test.ttf"))).isEqualTo(2); assertThat(mapBeforeUpgrade).containsKey("test.ttf"); Loading @@ -409,9 +409,10 @@ public final class UpdatableFontDirTest { mConfigFile); dir.loadFontFileMap(); dir.update(Collections.singletonList(newFontUpdateRequest("test,2", GOOD_SIGNATURE))); dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,2", GOOD_SIGNATURE))); try { dir.update(Collections.singletonList(newFontUpdateRequest("test,1", GOOD_SIGNATURE))); dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE))); fail("Expect SystemFontException"); } catch (FontManagerService.SystemFontException e) { assertThat(e.getErrorCode()).isEqualTo(FontManager.RESULT_ERROR_DOWNGRADING); Loading @@ -430,8 +431,8 @@ public final class UpdatableFontDirTest { mConfigFile); dir.loadFontFileMap(); dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE))); dir.update(Collections.singletonList(newFontUpdateRequest("bar,2", GOOD_SIGNATURE))); dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE))); dir.update(Collections.singletonList(newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE))); assertThat(dir.getFontFileMap()).containsKey("foo.ttf"); assertThat(parser.getRevision(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(1); assertThat(dir.getFontFileMap()).containsKey("bar.ttf"); Loading @@ -448,8 +449,8 @@ public final class UpdatableFontDirTest { dir.loadFontFileMap(); dir.update(Arrays.asList( newFontUpdateRequest("foo,1", GOOD_SIGNATURE), newFontUpdateRequest("bar,2", GOOD_SIGNATURE))); newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE), newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE))); assertThat(dir.getFontFileMap()).containsKey("foo.ttf"); assertThat(parser.getRevision(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(1); assertThat(dir.getFontFileMap()).containsKey("bar.ttf"); Loading @@ -467,7 +468,8 @@ public final class UpdatableFontDirTest { try { dir.update( Collections.singletonList(newFontUpdateRequest("test,1", "Invalid signature"))); Collections.singletonList(newFontUpdateRequest("test.ttf,1", "Invalid signature"))); fail("Expect SystemFontException"); } catch (FontManagerService.SystemFontException e) { assertThat(e.getErrorCode()) Loading @@ -480,14 +482,15 @@ public final class UpdatableFontDirTest { public void installFontFile_olderThanPreinstalledFont() throws Exception { FakeFontFileParser parser = new FakeFontFileParser(); FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), "test,1"); FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), "test.ttf,1"); UpdatableFontDir dir = new UpdatableFontDir( mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, mConfigFile); dir.loadFontFileMap(); try { dir.update(Collections.singletonList(newFontUpdateRequest("test,1", GOOD_SIGNATURE))); dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE))); fail("Expect SystemFontException"); } catch (FontManagerService.SystemFontException e) { assertThat(e.getErrorCode()).isEqualTo(FontManager.RESULT_ERROR_DOWNGRADING); Loading @@ -500,7 +503,7 @@ public final class UpdatableFontDirTest { long expectedModifiedDate = 1234567890; FakeFontFileParser parser = new FakeFontFileParser(); FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), "test,1"); FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), "test.ttf,1"); File readonlyDir = new File(mCacheDir, "readonly"); assertThat(readonlyDir.mkdir()).isTrue(); Loading @@ -519,7 +522,8 @@ public final class UpdatableFontDirTest { try { dir.update( Collections.singletonList(newFontUpdateRequest("test,2", GOOD_SIGNATURE))); Collections.singletonList(newFontUpdateRequest("test.ttf,2", GOOD_SIGNATURE))); } catch (FontManagerService.SystemFontException e) { assertThat(e.getErrorCode()) .isEqualTo(FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG); Loading @@ -539,7 +543,7 @@ public final class UpdatableFontDirTest { mUpdatableFontFilesDir, mPreinstalledFontDirs, new UpdatableFontDir.FontFileParser() { @Override public String getPostScriptName(File file) throws IOException { public String getCanonicalFileName(File file) throws IOException { return null; } Loading @@ -551,7 +555,8 @@ public final class UpdatableFontDirTest { dir.loadFontFileMap(); try { dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE))); dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE))); fail("Expect SystemFontException"); } catch (FontManagerService.SystemFontException e) { assertThat(e.getErrorCode()) Loading @@ -567,7 +572,7 @@ public final class UpdatableFontDirTest { mUpdatableFontFilesDir, mPreinstalledFontDirs, new UpdatableFontDir.FontFileParser() { @Override public String getPostScriptName(File file) throws IOException { public String getCanonicalFileName(File file) throws IOException { throw new IOException(); } Loading @@ -579,7 +584,8 @@ public final class UpdatableFontDirTest { dir.loadFontFileMap(); try { dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE))); dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE))); fail("Expect SystemFontException"); } catch (FontManagerService.SystemFontException e) { assertThat(e.getErrorCode()) Loading Loading @@ -615,7 +621,8 @@ public final class UpdatableFontDirTest { dir.loadFontFileMap(); try { dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE))); dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE))); fail("Expect SystemFontException"); } catch (FontManagerService.SystemFontException e) { assertThat(e.getErrorCode()) Loading @@ -633,11 +640,11 @@ public final class UpdatableFontDirTest { mConfigFile); dir.loadFontFileMap(); dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE))); dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE))); try { dir.update(Arrays.asList( newFontUpdateRequest("foo,2", GOOD_SIGNATURE), newFontUpdateRequest("bar,2", "Invalid signature"))); newFontUpdateRequest("foo.ttf,2", GOOD_SIGNATURE), newFontUpdateRequest("bar.ttf,2", "Invalid signature"))); fail("Batch update with invalid signature should fail"); } catch (FontManagerService.SystemFontException e) { // Expected Loading @@ -657,7 +664,7 @@ public final class UpdatableFontDirTest { dir.loadFontFileMap(); dir.update(Arrays.asList( newFontUpdateRequest("test,1", GOOD_SIGNATURE), newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE), newAddFontFamilyRequest("<family name='test'>" + " <font>test.ttf</font>" + "</family>"))); Loading @@ -680,7 +687,7 @@ public final class UpdatableFontDirTest { try { dir.update(Arrays.asList( newFontUpdateRequest("test,1", GOOD_SIGNATURE), newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE), newAddFontFamilyRequest("<family lang='en'>" + " <font>test.ttf</font>" + "</family>"))); Loading Loading @@ -722,7 +729,7 @@ public final class UpdatableFontDirTest { assertNamedFamilyExists(dir.getSystemFontConfig(), "monospace"); dir.update(Arrays.asList( newFontUpdateRequest("test,1", GOOD_SIGNATURE), newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE), // Updating an existing font family. newAddFontFamilyRequest("<family name='monospace'>" + " <font>test.ttf</font>" Loading Loading @@ -755,7 +762,7 @@ public final class UpdatableFontDirTest { assertThat(firstFontFamily.getName()).isNotEmpty(); dir.update(Arrays.asList( newFontUpdateRequest("test,1", GOOD_SIGNATURE), newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE), newAddFontFamilyRequest("<family name='" + firstFontFamily.getName() + "'>" + " <font>test.ttf</font>" + "</family>"))); Loading