Merge changes I09ac3f34,Id3e36922 into sc-dev

* changes:
  Close FDs / mmap handles promptly.
  Remove unused IFontManager#updateFontFile().
This commit is contained in:
Kohsuke Yatoh
2021-05-13 20:42:10 +00:00
committed by Android (Google) Code Review
6 changed files with 332 additions and 267 deletions

View File

@@ -30,7 +30,5 @@ import java.util.List;
interface IFontManager {
FontConfig getFontConfig();
int updateFontFile(in FontUpdateRequest request, int baseVersion);
int updateFontFamily(in List<FontUpdateRequest> request, int baseVersion);
}

View File

@@ -262,8 +262,14 @@ public final class SystemFonts {
*/
@VisibleForTesting
public static Map<String, FontFamily[]> buildSystemFallback(FontConfig fontConfig) {
return buildSystemFallback(fontConfig, new ArrayMap<>());
}
/** @hide */
@VisibleForTesting
public static Map<String, FontFamily[]> buildSystemFallback(FontConfig fontConfig,
ArrayMap<String, ByteBuffer> outBufferCache) {
final Map<String, FontFamily[]> fallbackMap = new ArrayMap<>();
final ArrayMap<String, ByteBuffer> bufferCache = new ArrayMap<>();
final List<FontConfig.FontFamily> xmlFamilies = fontConfig.getFontFamilies();
final ArrayMap<String, ArrayList<FontFamily>> fallbackListMap = new ArrayMap<>();
@@ -273,7 +279,7 @@ public final class SystemFonts {
if (familyName == null) {
continue;
}
appendNamedFamily(xmlFamily, bufferCache, fallbackListMap);
appendNamedFamily(xmlFamily, outBufferCache, fallbackListMap);
}
// Then, add fallback fonts to the each fallback map.
@@ -282,7 +288,7 @@ public final class SystemFonts {
// The first family (usually the sans-serif family) is always placed immediately
// after the primary family in the fallback.
if (i == 0 || xmlFamily.getName() == null) {
pushFamilyToFallback(xmlFamily, fallbackListMap, bufferCache);
pushFamilyToFallback(xmlFamily, fallbackListMap, outBufferCache);
}
}

View File

@@ -20,25 +20,19 @@ import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Typeface;
import android.graphics.fonts.Font;
import android.graphics.fonts.FontFamily;
import android.graphics.fonts.FontFileUtil;
import android.graphics.fonts.FontManager;
import android.graphics.fonts.FontUpdateRequest;
import android.graphics.fonts.SystemFonts;
import android.os.ParcelFileDescriptor;
import android.os.ResultReceiver;
import android.os.SharedMemory;
import android.os.ShellCallback;
import android.system.ErrnoException;
import android.text.FontConfig;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.AndroidException;
import android.util.ArrayMap;
import android.util.IndentingPrintWriter;
import android.util.Slog;
@@ -52,19 +46,17 @@ import com.android.server.SystemService;
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.DirectByteBuffer;
import java.nio.NioUtils;
import java.nio.channels.FileChannel;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/** A service for managing system fonts. */
// TODO(b/173619554): Add API to update fonts.
public final class FontManagerService extends IFontManager.Stub {
private static final String TAG = "FontManagerService";
@@ -77,23 +69,6 @@ public final class FontManagerService extends IFontManager.Stub {
return getSystemFontConfig();
}
@Override
public int updateFontFile(@NonNull FontUpdateRequest request, int baseVersion) {
Preconditions.checkArgumentNonnegative(baseVersion);
Objects.requireNonNull(request);
Objects.requireNonNull(request.getFd());
Objects.requireNonNull(request.getSignature());
getContext().enforceCallingPermission(Manifest.permission.UPDATE_FONTS,
"UPDATE_FONTS permission required.");
try {
update(baseVersion, Collections.singletonList(request));
return FontManager.RESULT_SUCCESS;
} catch (SystemFontException e) {
Slog.e(TAG, "Failed to update font file", e);
return e.getErrorCode();
}
}
@Override
public int updateFontFamily(@NonNull List<FontUpdateRequest> requests, int baseVersion) {
Preconditions.checkArgumentNonnegative(baseVersion);
@@ -106,6 +81,17 @@ public final class FontManagerService extends IFontManager.Stub {
} catch (SystemFontException e) {
Slog.e(TAG, "Failed to update font family", e);
return e.getErrorCode();
} finally {
for (FontUpdateRequest request : requests) {
ParcelFileDescriptor fd = request.getFd();
if (fd != null) {
try {
fd.close();
} catch (IOException e) {
Slog.w(TAG, "Failed to close fd", e);
}
}
}
}
}
@@ -154,89 +140,6 @@ public final class FontManagerService extends IFontManager.Stub {
}
}
/* package */ static class OtfFontFileParser implements UpdatableFontDir.FontFileParser {
@Override
public String getPostScriptName(File file) throws IOException {
ByteBuffer buffer = mmap(file);
try {
return FontFileUtil.getPostScriptName(buffer, 0);
} finally {
NioUtils.freeDirectBuffer(buffer);
}
}
@Override
public String buildFontFileName(File file) throws IOException {
ByteBuffer buffer = mmap(file);
try {
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);
}
}
@Override
public long getRevision(File file) throws IOException {
ByteBuffer buffer = mmap(file);
try {
return FontFileUtil.getRevision(buffer, 0);
} finally {
NioUtils.freeDirectBuffer(buffer);
}
}
@Override
public void tryToCreateTypeface(File file) throws Throwable {
Font font = new Font.Builder(file).build();
FontFamily family = new FontFamily.Builder(font).build();
Typeface typeface = new Typeface.CustomFallbackBuilder(family).build();
TextPaint p = new TextPaint();
p.setTextSize(24f);
p.setTypeface(typeface);
// Test string to try with the passed font.
// TODO: Good to extract from font file.
String testTextToDraw = "abcXYZ@- "
+ "\uD83E\uDED6" // Emoji E13.0
+ "\uD83C\uDDFA\uD83C\uDDF8" // Emoji Flags
+ "\uD83D\uDC8F\uD83C\uDFFB" // Emoji Skin tone Sequence
// ZWJ Sequence
+ "\uD83D\uDC68\uD83C\uDFFC\u200D\u2764\uFE0F\u200D\uD83D\uDC8B\u200D"
+ "\uD83D\uDC68\uD83C\uDFFF";
int width = (int) Math.ceil(Layout.getDesiredWidth(testTextToDraw, p));
StaticLayout layout = StaticLayout.Builder.obtain(
testTextToDraw, 0, testTextToDraw.length(), p, width).build();
Bitmap bmp = Bitmap.createBitmap(
layout.getWidth(), layout.getHeight(), Bitmap.Config.ALPHA_8);
Canvas canvas = new Canvas(bmp);
layout.draw(canvas);
}
private static ByteBuffer mmap(File file) throws IOException {
try (FileInputStream in = new FileInputStream(file)) {
FileChannel fileChannel = in.getChannel();
return fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size());
}
}
}
private static class FsverityUtilImpl implements UpdatableFontDir.FsverityUtil {
@Override
public boolean hasFsverity(String filePath) {
@@ -289,13 +192,7 @@ public final class FontManagerService extends IFontManager.Stub {
private void initialize() {
synchronized (mUpdatableFontDirLock) {
if (mUpdatableFontDir == null) {
synchronized (mSerializedFontMapLock) {
try {
mSerializedFontMap = Typeface.serializeFontMap(Typeface.getSystemFontMap());
} catch (IOException | ErrnoException e) {
mSerializedFontMap = null;
}
}
setSerializedFontMap(serializeSystemServerFontMap());
return;
}
mUpdatableFontDir.loadFontFileMap();
@@ -391,36 +288,57 @@ public final class FontManagerService extends IFontManager.Stub {
/**
* Makes new serialized font map data and updates mSerializedFontMap.
*/
public void updateSerializedFontMap() {
private void updateSerializedFontMap() {
SharedMemory serializedFontMap = serializeFontMap(getSystemFontConfig());
if (serializedFontMap == null) {
// Fallback to the preloaded config.
serializedFontMap = serializeSystemServerFontMap();
}
setSerializedFontMap(serializedFontMap);
}
@Nullable
private static SharedMemory serializeFontMap(FontConfig fontConfig) {
final ArrayMap<String, ByteBuffer> bufferCache = new ArrayMap<>();
try {
final FontConfig fontConfig = getSystemFontConfig();
final Map<String, FontFamily[]> fallback = SystemFonts.buildSystemFallback(fontConfig);
final Map<String, FontFamily[]> fallback =
SystemFonts.buildSystemFallback(fontConfig, bufferCache);
final Map<String, Typeface> typefaceMap =
SystemFonts.buildSystemTypefaces(fontConfig, fallback);
SharedMemory serializeFontMap = Typeface.serializeFontMap(typefaceMap);
synchronized (mSerializedFontMapLock) {
mSerializedFontMap = serializeFontMap;
}
return;
return Typeface.serializeFontMap(typefaceMap);
} catch (IOException | ErrnoException e) {
Slog.w(TAG, "Failed to serialize updatable font map. "
+ "Retrying with system image fonts.", e);
}
try {
final FontConfig fontConfig = SystemFonts.getSystemPreinstalledFontConfig();
final Map<String, FontFamily[]> fallback = SystemFonts.buildSystemFallback(fontConfig);
final Map<String, Typeface> typefaceMap =
SystemFonts.buildSystemTypefaces(fontConfig, fallback);
SharedMemory serializeFontMap = Typeface.serializeFontMap(typefaceMap);
synchronized (mSerializedFontMapLock) {
mSerializedFontMap = serializeFontMap;
return null;
} finally {
// Unmap buffers promptly, as we map a lot of files and may hit mmap limit before
// GC collects ByteBuffers and unmaps them.
for (ByteBuffer buffer : bufferCache.values()) {
if (buffer instanceof DirectByteBuffer) {
NioUtils.freeDirectBuffer(buffer);
}
}
} catch (IOException | ErrnoException e) {
Slog.e(TAG, "Failed to serialize SystemServer system font map", e);
}
}
@Nullable
private static SharedMemory serializeSystemServerFontMap() {
try {
return Typeface.serializeFontMap(Typeface.getSystemFontMap());
} catch (IOException | ErrnoException e) {
Slog.e(TAG, "Failed to serialize SystemServer system font map", e);
return null;
}
}
private void setSerializedFontMap(SharedMemory serializedFontMap) {
SharedMemory oldFontMap = null;
synchronized (mSerializedFontMapLock) {
oldFontMap = mSerializedFontMap;
mSerializedFontMap = serializedFontMap;
}
if (oldFontMap != null) {
oldFontMap.close();
}
}
}

View File

@@ -0,0 +1,130 @@
/*
* 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.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Typeface;
import android.graphics.fonts.Font;
import android.graphics.fonts.FontFamily;
import android.graphics.fonts.FontFileUtil;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.text.TextUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.DirectByteBuffer;
import java.nio.NioUtils;
import java.nio.channels.FileChannel;
/* package */ class OtfFontFileParser implements UpdatableFontDir.FontFileParser {
@Override
public String getPostScriptName(File file) throws IOException {
ByteBuffer buffer = mmap(file);
try {
return FontFileUtil.getPostScriptName(buffer, 0);
} finally {
unmap(buffer);
}
}
@Override
public String buildFontFileName(File file) throws IOException {
ByteBuffer buffer = mmap(file);
try {
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 {
unmap(buffer);
}
}
@Override
public long getRevision(File file) throws IOException {
ByteBuffer buffer = mmap(file);
try {
return FontFileUtil.getRevision(buffer, 0);
} finally {
unmap(buffer);
}
}
@Override
public void tryToCreateTypeface(File file) throws Throwable {
ByteBuffer buffer = mmap(file);
try {
Font font = new Font.Builder(buffer).build();
FontFamily family = new FontFamily.Builder(font).build();
Typeface typeface = new Typeface.CustomFallbackBuilder(family).build();
TextPaint p = new TextPaint();
p.setTextSize(24f);
p.setTypeface(typeface);
// Test string to try with the passed font.
// TODO: Good to extract from font file.
String testTextToDraw = "abcXYZ@- "
+ "\uD83E\uDED6" // Emoji E13.0
+ "\uD83C\uDDFA\uD83C\uDDF8" // Emoji Flags
+ "\uD83D\uDC8F\uD83C\uDFFB" // Emoji Skin tone Sequence
// ZWJ Sequence
+ "\uD83D\uDC68\uD83C\uDFFC\u200D\u2764\uFE0F\u200D\uD83D\uDC8B\u200D"
+ "\uD83D\uDC68\uD83C\uDFFF";
int width = (int) Math.ceil(Layout.getDesiredWidth(testTextToDraw, p));
StaticLayout layout = StaticLayout.Builder.obtain(
testTextToDraw, 0, testTextToDraw.length(), p, width).build();
Bitmap bmp = Bitmap.createBitmap(
layout.getWidth(), layout.getHeight(), Bitmap.Config.ALPHA_8);
Canvas canvas = new Canvas(bmp);
layout.draw(canvas);
} finally {
unmap(buffer);
}
}
private static ByteBuffer mmap(File file) throws IOException {
try (FileInputStream in = new FileInputStream(file)) {
FileChannel fileChannel = in.getChannel();
return fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size());
}
}
private static void unmap(ByteBuffer buffer) {
if (buffer instanceof DirectByteBuffer) {
NioUtils.freeDirectBuffer(buffer);
}
}
}

View File

@@ -139,9 +139,11 @@ public final class UpdatableFontDirTest {
private File mUpdatableFontFilesDir;
private File mConfigFile;
private List<File> mPreinstalledFontDirs;
private Supplier<Long> mCurrentTimeSupplier = () -> CURRENT_TIME;
private Function<Map<String, File>, FontConfig> mConfigSupplier =
private final Supplier<Long> mCurrentTimeSupplier = () -> CURRENT_TIME;
private final Function<Map<String, File>, FontConfig> mConfigSupplier =
(map) -> SystemFonts.getSystemFontConfig(map, 0, 0);
private FakeFontFileParser mParser;
private FakeFsverityUtil mFakeFsverityUtil;
@SuppressWarnings("ResultOfMethodCallIgnored")
@Before
@@ -159,6 +161,8 @@ public final class UpdatableFontDirTest {
dir.mkdir();
}
mConfigFile = new File(mCacheDir, "config.xml");
mParser = new FakeFontFileParser();
mFakeFsverityUtil = new FakeFsverityUtil();
}
@After
@@ -169,13 +173,11 @@ public final class UpdatableFontDirTest {
@Test
public void construct() throws Exception {
long expectedModifiedDate = CURRENT_TIME / 2;
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config();
config.lastModifiedMillis = expectedModifiedDate;
writeConfig(config, mConfigFile);
UpdatableFontDir dirForPreparation = new UpdatableFontDir(
mUpdatableFontFilesDir, parser, fakeFsverityUtil,
mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dirForPreparation.loadFontFileMap();
assertThat(dirForPreparation.getSystemFontConfig().getLastModifiedTimeMillis())
@@ -198,13 +200,13 @@ public final class UpdatableFontDirTest {
.isNotEqualTo(expectedModifiedDate);
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, parser, fakeFsverityUtil,
mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
assertThat(dir.getPostScriptMap()).containsKey("foo");
assertThat(parser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(3);
assertThat(mParser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(3);
assertThat(dir.getPostScriptMap()).containsKey("bar");
assertThat(parser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(4);
assertThat(mParser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(4);
// Outdated font dir should be deleted.
assertThat(mUpdatableFontFilesDir.list()).hasLength(2);
assertNamedFamilyExists(dir.getSystemFontConfig(), "foobar");
@@ -219,10 +221,8 @@ public final class UpdatableFontDirTest {
@Test
public void construct_empty() {
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, parser, fakeFsverityUtil,
mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
assertThat(dir.getPostScriptMap()).isEmpty();
@@ -231,10 +231,8 @@ public final class UpdatableFontDirTest {
@Test
public void construct_missingFsverity() throws Exception {
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dirForPreparation = new UpdatableFontDir(
mUpdatableFontFilesDir, parser, fakeFsverityUtil,
mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dirForPreparation.loadFontFileMap();
dirForPreparation.update(Arrays.asList(
@@ -249,10 +247,10 @@ public final class UpdatableFontDirTest {
// Four font dirs are created.
assertThat(mUpdatableFontFilesDir.list()).hasLength(4);
fakeFsverityUtil.remove(
mFakeFsverityUtil.remove(
dirForPreparation.getPostScriptMap().get("foo").getAbsolutePath());
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, parser, fakeFsverityUtil,
mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
assertThat(dir.getPostScriptMap()).isEmpty();
@@ -263,10 +261,8 @@ public final class UpdatableFontDirTest {
@Test
public void construct_fontNameMismatch() throws Exception {
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dirForPreparation = new UpdatableFontDir(
mUpdatableFontFilesDir, parser, fakeFsverityUtil,
mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dirForPreparation.loadFontFileMap();
dirForPreparation.update(Arrays.asList(
@@ -285,7 +281,7 @@ public final class UpdatableFontDirTest {
FileUtils.stringToFile(dirForPreparation.getPostScriptMap().get("foo"), "bar,4");
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, parser, fakeFsverityUtil,
mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
assertThat(dir.getPostScriptMap()).isEmpty();
@@ -296,8 +292,6 @@ public final class UpdatableFontDirTest {
@Test
public void construct_olderThanPreinstalledFont() throws Exception {
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
Function<Map<String, File>, FontConfig> configSupplier = (map) -> {
FontConfig.Font fooFont = new FontConfig.Font(
new File(mPreinstalledFontDirs.get(0), "foo.ttf"), null, "foo",
@@ -314,7 +308,7 @@ public final class UpdatableFontDirTest {
};
UpdatableFontDir dirForPreparation = new UpdatableFontDir(
mUpdatableFontFilesDir, parser, fakeFsverityUtil,
mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, configSupplier);
dirForPreparation.loadFontFileMap();
dirForPreparation.update(Arrays.asList(
@@ -334,14 +328,14 @@ public final class UpdatableFontDirTest {
FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(1), "bar.ttf"), "bar,1,bar");
FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(1), "bar.ttf"), "bar,2,bar");
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, parser, fakeFsverityUtil,
mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, configSupplier);
dir.loadFontFileMap();
// For foo.ttf, preinstalled font (revision 5) should be used.
assertThat(dir.getPostScriptMap()).doesNotContainKey("foo");
// For bar.ttf, updated font (revision 4) should be used.
assertThat(dir.getPostScriptMap()).containsKey("bar");
assertThat(parser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(4);
assertThat(mParser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(4);
// Outdated font dir should be deleted.
// We don't delete bar.ttf in this case, because it's normal that OTA updates preinstalled
// fonts.
@@ -352,10 +346,8 @@ public final class UpdatableFontDirTest {
@Test
public void construct_failedToLoadConfig() throws Exception {
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, parser, fakeFsverityUtil,
mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
new File("/dev/null"), mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
assertThat(dir.getPostScriptMap()).isEmpty();
@@ -364,10 +356,8 @@ public final class UpdatableFontDirTest {
@Test
public void construct_afterBatchFailure() throws Exception {
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dirForPreparation = new UpdatableFontDir(
mUpdatableFontFilesDir, parser, fakeFsverityUtil,
mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dirForPreparation.loadFontFileMap();
dirForPreparation.update(Arrays.asList(
@@ -389,12 +379,12 @@ public final class UpdatableFontDirTest {
}
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, parser, fakeFsverityUtil,
mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
// The state should be rolled back as a whole if one of the update requests fail.
assertThat(dir.getPostScriptMap()).containsKey("foo");
assertThat(parser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(1);
assertThat(mParser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(1);
assertThat(dir.getFontFamilyMap()).containsKey("foobar");
FontConfig.FontFamily foobar = dir.getFontFamilyMap().get("foobar");
assertThat(foobar.getFontList()).hasSize(1);
@@ -404,10 +394,8 @@ public final class UpdatableFontDirTest {
@Test
public void loadFontFileMap_twice() throws Exception {
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, parser, fakeFsverityUtil,
mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1,test",
@@ -420,17 +408,15 @@ public final class UpdatableFontDirTest {
@Test
public void installFontFile() throws Exception {
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, parser, fakeFsverityUtil,
mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1,test",
GOOD_SIGNATURE)));
assertThat(dir.getPostScriptMap()).containsKey("test");
assertThat(parser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(1);
assertThat(mParser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(1);
File fontFile = dir.getPostScriptMap().get("test");
assertThat(Os.stat(fontFile.getAbsolutePath()).st_mode & 0777).isEqualTo(0644);
File fontDir = fontFile.getParentFile();
@@ -439,10 +425,8 @@ public final class UpdatableFontDirTest {
@Test
public void installFontFile_upgrade() throws Exception {
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, parser, fakeFsverityUtil,
mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
@@ -452,10 +436,10 @@ public final class UpdatableFontDirTest {
dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,2,test",
GOOD_SIGNATURE)));
assertThat(dir.getPostScriptMap()).containsKey("test");
assertThat(parser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(2);
assertThat(mParser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(2);
assertThat(mapBeforeUpgrade).containsKey("test");
assertWithMessage("Older fonts should not be deleted until next loadFontFileMap")
.that(parser.getRevision(mapBeforeUpgrade.get("test"))).isEqualTo(1);
.that(mParser.getRevision(mapBeforeUpgrade.get("test"))).isEqualTo(1);
// Check that updatedFontDirs is pruned.
assertWithMessage("config.updatedFontDirs should only list latest active dirs")
.that(readConfig(mConfigFile).updatedFontDirs)
@@ -464,15 +448,13 @@ public final class UpdatableFontDirTest {
@Test
public void installFontFile_systemFontHasPSNameDifferentFromFileName() throws Exception {
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
// Setup the environment that the system installed font file named "foo.ttf" has PostScript
// name "bar".
File file = new File(mPreinstalledFontDirs.get(0), "foo.ttf");
FileUtils.stringToFile(file, "foo.ttf,1,bar");
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, parser, fakeFsverityUtil,
mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, (map) -> {
FontConfig.Font font = new FontConfig.Font(
file, null, "bar", new FontStyle(400, FontStyle.FONT_SLANT_UPRIGHT),
@@ -489,7 +471,7 @@ public final class UpdatableFontDirTest {
GOOD_SIGNATURE)));
assertThat(dir.getPostScriptMap()).containsKey("bar");
assertThat(dir.getPostScriptMap().size()).isEqualTo(1);
assertThat(parser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(2);
assertThat(mParser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(2);
File fontFile = dir.getPostScriptMap().get("bar");
assertThat(Os.stat(fontFile.getAbsolutePath()).st_mode & 0777).isEqualTo(0644);
File fontDir = fontFile.getParentFile();
@@ -498,10 +480,8 @@ public final class UpdatableFontDirTest {
@Test
public void installFontFile_sameVersion() throws Exception {
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, parser, fakeFsverityUtil,
mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
@@ -510,15 +490,13 @@ public final class UpdatableFontDirTest {
dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1,test",
GOOD_SIGNATURE)));
assertThat(dir.getPostScriptMap()).containsKey("test");
assertThat(parser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(1);
assertThat(mParser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(1);
}
@Test
public void installFontFile_downgrade() throws Exception {
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, parser, fakeFsverityUtil,
mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
@@ -533,7 +511,7 @@ public final class UpdatableFontDirTest {
}
assertThat(dir.getPostScriptMap()).containsKey("test");
assertWithMessage("Font should not be downgraded to an older revision")
.that(parser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(2);
.that(mParser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(2);
// Check that updatedFontDirs is not updated.
assertWithMessage("config.updatedFontDirs should only list latest active dirs")
.that(readConfig(mConfigFile).updatedFontDirs)
@@ -542,10 +520,8 @@ public final class UpdatableFontDirTest {
@Test
public void installFontFile_multiple() throws Exception {
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, parser, fakeFsverityUtil,
mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
@@ -554,17 +530,15 @@ public final class UpdatableFontDirTest {
dir.update(Collections.singletonList(newFontUpdateRequest("bar.ttf,2,bar",
GOOD_SIGNATURE)));
assertThat(dir.getPostScriptMap()).containsKey("foo");
assertThat(parser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(1);
assertThat(mParser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(1);
assertThat(dir.getPostScriptMap()).containsKey("bar");
assertThat(parser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(2);
assertThat(mParser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(2);
}
@Test
public void installFontFile_batch() throws Exception {
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, parser, fakeFsverityUtil,
mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
@@ -572,17 +546,15 @@ public final class UpdatableFontDirTest {
newFontUpdateRequest("foo.ttf,1,foo", GOOD_SIGNATURE),
newFontUpdateRequest("bar.ttf,2,bar", GOOD_SIGNATURE)));
assertThat(dir.getPostScriptMap()).containsKey("foo");
assertThat(parser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(1);
assertThat(mParser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(1);
assertThat(dir.getPostScriptMap()).containsKey("bar");
assertThat(parser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(2);
assertThat(mParser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(2);
}
@Test
public void installFontFile_invalidSignature() throws Exception {
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, parser, fakeFsverityUtil,
mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
@@ -600,46 +572,40 @@ public final class UpdatableFontDirTest {
@Test
public void installFontFile_preinstalled_upgrade() throws Exception {
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"),
"test.ttf,1,test");
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, parser, fakeFsverityUtil,
mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,2,test",
GOOD_SIGNATURE)));
assertThat(dir.getPostScriptMap()).containsKey("test");
assertThat(parser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(2);
assertThat(mParser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(2);
}
@Test
public void installFontFile_preinstalled_sameVersion() throws Exception {
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"),
"test.ttf,1,test");
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, parser, fakeFsverityUtil,
mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1,test",
GOOD_SIGNATURE)));
assertThat(dir.getPostScriptMap()).containsKey("test");
assertThat(parser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(1);
assertThat(mParser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(1);
}
@Test
public void installFontFile_preinstalled_downgrade() throws Exception {
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
File file = new File(mPreinstalledFontDirs.get(0), "test.ttf");
FileUtils.stringToFile(file, "test.ttf,2,test");
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, parser, fakeFsverityUtil,
mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, (map) -> {
FontConfig.Font font = new FontConfig.Font(
file, null, "test", new FontStyle(400, FontStyle.FONT_SLANT_UPRIGHT), 0, null,
@@ -664,8 +630,6 @@ public final class UpdatableFontDirTest {
@Test
public void installFontFile_failedToWriteConfigXml() throws Exception {
long expectedModifiedDate = 1234567890;
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"),
"test.ttf,1,test");
@@ -680,7 +644,7 @@ public final class UpdatableFontDirTest {
assertThat(readonlyDir.setWritable(false, false)).isTrue();
try {
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, parser, fakeFsverityUtil,
mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
readonlyFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
@@ -702,7 +666,6 @@ public final class UpdatableFontDirTest {
@Test
public void installFontFile_failedToParsePostScript() throws Exception {
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir,
new UpdatableFontDir.FontFileParser() {
@@ -725,7 +688,7 @@ public final class UpdatableFontDirTest {
@Override
public void tryToCreateTypeface(File file) throws IOException {
}
}, fakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
}, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
try {
@@ -741,7 +704,6 @@ public final class UpdatableFontDirTest {
@Test
public void installFontFile_failedToParsePostScriptName_invalidFont() throws Exception {
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir,
new UpdatableFontDir.FontFileParser() {
@@ -763,7 +725,7 @@ public final class UpdatableFontDirTest {
@Override
public void tryToCreateTypeface(File file) throws IOException {
}
}, fakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
}, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
try {
@@ -779,31 +741,29 @@ public final class UpdatableFontDirTest {
@Test
public void installFontFile_failedToCreateTypeface() throws Exception {
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
FakeFontFileParser parser = new FakeFontFileParser();
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir,
new UpdatableFontDir.FontFileParser() {
@Override
public String getPostScriptName(File file) throws IOException {
return parser.getPostScriptName(file);
return mParser.getPostScriptName(file);
}
@Override
public String buildFontFileName(File file) throws IOException {
return parser.buildFontFileName(file);
return mParser.buildFontFileName(file);
}
@Override
public long getRevision(File file) throws IOException {
return parser.getRevision(file);
return mParser.getRevision(file);
}
@Override
public void tryToCreateTypeface(File file) throws IOException {
throw new IOException();
}
}, fakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
}, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
try {
@@ -820,16 +780,15 @@ public final class UpdatableFontDirTest {
@Test
public void installFontFile_renameToPsNameFailure() throws Exception {
UpdatableFontDir.FsverityUtil fakeFsverityUtil = new UpdatableFontDir.FsverityUtil() {
private final FakeFsverityUtil mFake = new FakeFsverityUtil();
@Override
public boolean hasFsverity(String path) {
return mFake.hasFsverity(path);
return mFakeFsverityUtil.hasFsverity(path);
}
@Override
public void setUpFsverity(String path, byte[] pkcs7Signature) throws IOException {
mFake.setUpFsverity(path, pkcs7Signature);
mFakeFsverityUtil.setUpFsverity(path, pkcs7Signature);
}
@Override
@@ -837,9 +796,8 @@ public final class UpdatableFontDirTest {
return false;
}
};
FakeFontFileParser parser = new FakeFontFileParser();
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, parser, fakeFsverityUtil,
mUpdatableFontFilesDir, mParser, fakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
@@ -856,10 +814,8 @@ public final class UpdatableFontDirTest {
@Test
public void installFontFile_batchFailure() throws Exception {
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, parser, fakeFsverityUtil,
mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
@@ -875,15 +831,13 @@ public final class UpdatableFontDirTest {
}
// The state should be rolled back as a whole if one of the update requests fail.
assertThat(dir.getPostScriptMap()).containsKey("foo");
assertThat(parser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(1);
assertThat(mParser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(1);
}
@Test
public void addFontFamily() throws Exception {
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, parser, fakeFsverityUtil,
mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
@@ -902,10 +856,8 @@ public final class UpdatableFontDirTest {
@Test
public void addFontFamily_noName() throws Exception {
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, parser, fakeFsverityUtil,
mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
@@ -924,10 +876,8 @@ public final class UpdatableFontDirTest {
@Test
public void addFontFamily_fontNotAvailable() throws Exception {
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, parser, fakeFsverityUtil,
mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
@@ -944,10 +894,8 @@ public final class UpdatableFontDirTest {
@Test
public void getSystemFontConfig() throws Exception {
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, parser, fakeFsverityUtil,
mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
// We assume we have monospace.
@@ -976,10 +924,8 @@ public final class UpdatableFontDirTest {
@Test
public void getSystemFontConfig_preserveFirstFontFamily() throws Exception {
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
mUpdatableFontFilesDir, parser, fakeFsverityUtil,
mUpdatableFontFilesDir, mParser, mFakeFsverityUtil,
mConfigFile, mCurrentTimeSupplier, mConfigSupplier);
dir.loadFontFileMap();
assertThat(dir.getSystemFontConfig().getFontFamilies()).isNotEmpty();
@@ -1011,12 +957,12 @@ public final class UpdatableFontDirTest {
}
private static FontUpdateRequest newAddFontFamilyRequest(String xml) throws Exception {
XmlPullParser parser = Xml.newPullParser();
XmlPullParser mParser = Xml.newPullParser();
ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8));
parser.setInput(is, "UTF-8");
parser.nextTag();
mParser.setInput(is, "UTF-8");
mParser.nextTag();
FontConfig.FontFamily fontFamily = FontListParser.readFamily(parser, "", null, true);
FontConfig.FontFamily fontFamily = FontListParser.readFamily(mParser, "", null, true);
List<FontUpdateRequest.Font> fonts = new ArrayList<>();
for (FontConfig.Font font : fontFamily.getFontList()) {
String name = font.getFile().getName();

View File

@@ -56,6 +56,11 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
/**
* Tests if fonts can be updated by {@link FontManager} API.
@@ -95,6 +100,12 @@ public class UpdatableSystemFontTest {
EMOJI_RENDERING_TEST_APP_ID + "/.EmojiRenderingTestActivity";
private static final long ACTIVITY_TIMEOUT_MILLIS = SECONDS.toMillis(10);
private static final Pattern PATTERN_FONT_FILES = Pattern.compile("\\.(ttf|otf|ttc|otc)$");
private static final Pattern PATTERN_TMP_FILES = Pattern.compile("^/data/local/tmp/");
private static final Pattern PATTERN_DATA_FONT_FILES = Pattern.compile("^/data/fonts/files/");
private static final Pattern PATTERN_SYSTEM_FONT_FILES =
Pattern.compile("^/(system|product)/fonts/");
private String mKeyId;
private FontManager mFontManager;
@@ -236,6 +247,32 @@ public class UpdatableSystemFontTest {
assertThat(fontPathAfterReboot).isEqualTo(fontPath);
}
@Test
public void fdLeakTest() throws Exception {
long originalOpenFontCount =
countMatch(getOpenFiles("system_server"), PATTERN_FONT_FILES);
Pattern patternEmojiVPlus1 =
Pattern.compile(Pattern.quote(TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF));
for (int i = 0; i < 10; i++) {
assertThat(updateFontFile(
TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
List<String> openFiles = getOpenFiles("system_server");
for (Pattern p : Arrays.asList(PATTERN_FONT_FILES, PATTERN_SYSTEM_FONT_FILES,
PATTERN_DATA_FONT_FILES, PATTERN_TMP_FILES)) {
Log.i(TAG, String.format("num of %s: %d", p, countMatch(openFiles, p)));
}
// system_server should not keep /data/fonts files open.
assertThat(countMatch(openFiles, PATTERN_DATA_FONT_FILES)).isEqualTo(0);
// system_server should not keep passed FD open.
assertThat(countMatch(openFiles, patternEmojiVPlus1)).isEqualTo(0);
// The number of open font FD should not increase.
assertThat(countMatch(openFiles, PATTERN_FONT_FILES))
.isAtMost(originalOpenFontCount);
}
}
private static String insertCert(String certPath) throws Exception {
Pair<String, String> result;
try (InputStream is = new FileInputStream(certPath)) {
@@ -338,7 +375,37 @@ public class UpdatableSystemFontTest {
return !expectCommandToSucceed(cmd).trim().isEmpty();
}
private static List<String> getOpenFiles(String appId) throws Exception {
String pid = pidOf(appId);
if (pid.isEmpty()) {
return Collections.emptyList();
}
String cmd = String.format("lsof -p %s", pid);
String out = expectCommandToSucceed(cmd);
List<String> paths = new ArrayList<>();
boolean first = true;
for (String line : out.split("\n")) {
// Skip the header.
if (first) {
first = false;
continue;
}
String[] records = line.split(" ");
if (records.length > 0) {
paths.add(records[records.length - 1]);
}
}
return paths;
}
private static String pidOf(String appId) throws Exception {
return expectCommandToSucceed("pidof " + appId).trim();
}
private static long countMatch(List<String> paths, Pattern pattern) {
// Note: asPredicate() returns true for partial matching.
return paths.stream()
.filter(pattern.asPredicate())
.count();
}
}