1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.graphics.fonts; 18 19 import static com.google.common.truth.Truth.assertThat; 20 import static com.google.common.truth.Truth.assertWithMessage; 21 22 import static org.junit.Assert.fail; 23 24 import android.content.Context; 25 import android.graphics.FontListParser; 26 import android.graphics.fonts.FontManager; 27 import android.graphics.fonts.FontStyle; 28 import android.graphics.fonts.FontUpdateRequest; 29 import android.graphics.fonts.SystemFonts; 30 import android.os.FileUtils; 31 import android.os.ParcelFileDescriptor; 32 import android.platform.test.annotations.Presubmit; 33 import android.platform.test.flag.junit.CheckFlagsRule; 34 import android.platform.test.flag.junit.DeviceFlagsValueProvider; 35 import android.system.Os; 36 import android.text.FontConfig; 37 import android.util.Xml; 38 39 import androidx.test.InstrumentationRegistry; 40 import androidx.test.filters.SmallTest; 41 import androidx.test.runner.AndroidJUnit4; 42 43 import org.junit.After; 44 import org.junit.Before; 45 import org.junit.Rule; 46 import org.junit.Test; 47 import org.junit.runner.RunWith; 48 import org.xmlpull.v1.XmlPullParser; 49 50 import java.io.ByteArrayInputStream; 51 import java.io.File; 52 import java.io.FileInputStream; 53 import java.io.FileOutputStream; 54 import java.io.IOException; 55 import java.io.InputStream; 56 import java.nio.charset.StandardCharsets; 57 import java.util.ArrayList; 58 import java.util.Arrays; 59 import java.util.Collections; 60 import java.util.HashSet; 61 import java.util.List; 62 import java.util.Map; 63 import java.util.Set; 64 import java.util.function.Function; 65 import java.util.function.Supplier; 66 import java.util.stream.Collectors; 67 68 @Presubmit 69 @SmallTest 70 @RunWith(AndroidJUnit4.class) 71 public final class UpdatableFontDirTest { 72 73 private static final String LEGACY_FONTS_XML = "/system/etc/fonts.xml"; 74 75 @Rule 76 public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); 77 78 /** 79 * A {@link UpdatableFontDir.FontFileParser} for testing. Instead of using real font files, 80 * this test uses fake font files. A fake font file has its PostScript naem and revision as the 81 * file content. 82 */ 83 private static class FakeFontFileParser implements UpdatableFontDir.FontFileParser { 84 @Override getPostScriptName(File file)85 public String getPostScriptName(File file) throws IOException { 86 String content = FileUtils.readTextFile(file, 100, ""); 87 return content.split(",")[2]; 88 } 89 90 @Override buildFontFileName(File file)91 public String buildFontFileName(File file) throws IOException { 92 String content = FileUtils.readTextFile(file, 100, ""); 93 return content.split(",")[0]; 94 } 95 96 @Override getRevision(File file)97 public long getRevision(File file) throws IOException { 98 String content = FileUtils.readTextFile(file, 100, ""); 99 return Long.parseLong(content.split(",")[1]); 100 } 101 102 @Override tryToCreateTypeface(File file)103 public void tryToCreateTypeface(File file) throws Throwable { 104 } 105 } 106 107 // FakeFsverityUtil will successfully set up fake fs-verity if the signature is GOOD_SIGNATURE. 108 private static final String GOOD_SIGNATURE = "Good signature"; 109 110 /** A fake FsverityUtil to keep fake verity bit in memory. */ 111 private static class FakeFsverityUtil implements UpdatableFontDir.FsverityUtil { 112 private final Set<String> mHasFsverityPaths = new HashSet<>(); 113 remove(String name)114 public void remove(String name) { 115 mHasFsverityPaths.remove(name); 116 } 117 118 @Override isFromTrustedProvider(String path, byte[] signature)119 public boolean isFromTrustedProvider(String path, byte[] signature) { 120 if (!mHasFsverityPaths.contains(path)) { 121 return false; 122 } 123 String fakeSignature = new String(signature, StandardCharsets.UTF_8); 124 return GOOD_SIGNATURE.equals(fakeSignature); 125 } 126 127 @Override setUpFsverity(String path)128 public void setUpFsverity(String path) throws IOException { 129 mHasFsverityPaths.add(path); 130 } 131 132 @Override rename(File src, File dest)133 public boolean rename(File src, File dest) { 134 if (src.renameTo(dest)) { 135 mHasFsverityPaths.remove(src.getAbsolutePath()); 136 mHasFsverityPaths.add(dest.getAbsolutePath()); 137 return true; 138 } 139 return false; 140 } 141 } 142 143 private static final long CURRENT_TIME = 1234567890L; 144 145 private File mCacheDir; 146 private File mUpdatableFontFilesDir; 147 private File mConfigFile; 148 private List<File> mPreinstalledFontDirs; 149 private final Supplier<Long> mCurrentTimeSupplier = () -> CURRENT_TIME; 150 private final Function<Map<String, File>, FontConfig> mConfigSupplier = 151 (map) -> SystemFonts.getSystemFontConfigForTesting(LEGACY_FONTS_XML, map, 0, 0); 152 private FakeFontFileParser mParser; 153 private FakeFsverityUtil mFakeFsverityUtil; 154 155 @SuppressWarnings("ResultOfMethodCallIgnored") 156 @Before setUp()157 public void setUp() { 158 Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); 159 mCacheDir = new File(context.getCacheDir(), "UpdatableFontDirTest"); 160 FileUtils.deleteContentsAndDir(mCacheDir); 161 mCacheDir.mkdirs(); 162 mUpdatableFontFilesDir = new File(mCacheDir, "updatable_fonts"); 163 mUpdatableFontFilesDir.mkdir(); 164 mPreinstalledFontDirs = new ArrayList<>(); 165 mPreinstalledFontDirs.add(new File(mCacheDir, "system_fonts")); 166 mPreinstalledFontDirs.add(new File(mCacheDir, "product_fonts")); 167 for (File dir : mPreinstalledFontDirs) { 168 dir.mkdir(); 169 } 170 mConfigFile = new File(mCacheDir, "config.xml"); 171 mParser = new FakeFontFileParser(); 172 mFakeFsverityUtil = new FakeFsverityUtil(); 173 } 174 175 @After tearDown()176 public void tearDown() { 177 FileUtils.deleteContentsAndDir(mCacheDir); 178 } 179 180 @Test construct()181 public void construct() throws Exception { 182 long expectedModifiedDate = CURRENT_TIME / 2; 183 PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config(); 184 config.lastModifiedMillis = expectedModifiedDate; 185 writeConfig(config, mConfigFile); 186 UpdatableFontDir dirForPreparation = new UpdatableFontDir( 187 mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, 188 mConfigFile, mCurrentTimeSupplier, mConfigSupplier); 189 dirForPreparation.loadFontFileMap(); 190 assertThat(dirForPreparation.getSystemFontConfig().getLastModifiedTimeMillis()) 191 .isEqualTo(expectedModifiedDate); 192 dirForPreparation.update(Arrays.asList( 193 newFontUpdateRequest("foo.ttf,1,foo", GOOD_SIGNATURE), 194 newFontUpdateRequest("bar.ttf,2,bar", GOOD_SIGNATURE), 195 newFontUpdateRequest("foo.ttf,3,foo", GOOD_SIGNATURE), 196 newFontUpdateRequest("bar.ttf,4,bar", GOOD_SIGNATURE), 197 newAddFontFamilyRequest("<family name='foobar'>" 198 + " <font>foo.ttf</font>" 199 + " <font>bar.ttf</font>" 200 + "</family>"))); 201 // Verifies that getLastModifiedTimeMillis() returns the value of currentTimeMillis. 202 assertThat(dirForPreparation.getSystemFontConfig().getLastModifiedTimeMillis()) 203 .isEqualTo(CURRENT_TIME); 204 // Four font dirs are created. 205 assertThat(mUpdatableFontFilesDir.list()).hasLength(4); 206 assertThat(dirForPreparation.getSystemFontConfig().getLastModifiedTimeMillis()) 207 .isNotEqualTo(expectedModifiedDate); 208 209 UpdatableFontDir dir = new UpdatableFontDir( 210 mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, 211 mConfigFile, mCurrentTimeSupplier, mConfigSupplier); 212 dir.loadFontFileMap(); 213 assertThat(dir.getPostScriptMap()).containsKey("foo"); 214 assertThat(mParser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(3); 215 assertThat(dir.getPostScriptMap()).containsKey("bar"); 216 assertThat(mParser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(4); 217 // Outdated font dir should be deleted. 218 assertThat(mUpdatableFontFilesDir.list()).hasLength(2); 219 assertNamedFamilyExists(dir.getSystemFontConfig(), "foobar"); 220 assertThat(dir.getFontFamilyMap()).containsKey("foobar"); 221 assertThat(dir.getFontFamilyMap().get("foobar").getFamilies().size()).isEqualTo(1); 222 FontConfig.FontFamily foobar = dir.getFontFamilyMap().get("foobar").getFamilies().get(0); 223 assertThat(foobar.getFontList()).hasSize(2); 224 assertThat(foobar.getFontList().get(0).getFile()) 225 .isEqualTo(dir.getPostScriptMap().get("foo")); 226 assertThat(foobar.getFontList().get(1).getFile()) 227 .isEqualTo(dir.getPostScriptMap().get("bar")); 228 } 229 230 @Test construct_empty()231 public void construct_empty() { 232 UpdatableFontDir dir = new UpdatableFontDir( 233 mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, 234 mConfigFile, mCurrentTimeSupplier, mConfigSupplier); 235 dir.loadFontFileMap(); 236 assertThat(dir.getPostScriptMap()).isEmpty(); 237 assertThat(dir.getFontFamilyMap()).isEmpty(); 238 } 239 240 @Test construct_missingFsverity()241 public void construct_missingFsverity() throws Exception { 242 UpdatableFontDir dirForPreparation = new UpdatableFontDir( 243 mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, 244 mConfigFile, mCurrentTimeSupplier, mConfigSupplier); 245 dirForPreparation.loadFontFileMap(); 246 dirForPreparation.update(Arrays.asList( 247 newFontUpdateRequest("foo.ttf,1,foo", GOOD_SIGNATURE), 248 newFontUpdateRequest("bar.ttf,2,bar", GOOD_SIGNATURE), 249 newFontUpdateRequest("foo.ttf,3,foo", GOOD_SIGNATURE), 250 newFontUpdateRequest("bar.ttf,4,bar", GOOD_SIGNATURE), 251 newAddFontFamilyRequest("<family name='foobar'>" 252 + " <font>foo.ttf</font>" 253 + " <font>bar.ttf</font>" 254 + "</family>"))); 255 // Four font dirs are created. 256 assertThat(mUpdatableFontFilesDir.list()).hasLength(4); 257 258 mFakeFsverityUtil.remove( 259 dirForPreparation.getPostScriptMap().get("foo").getAbsolutePath()); 260 UpdatableFontDir dir = new UpdatableFontDir( 261 mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, 262 mConfigFile, mCurrentTimeSupplier, mConfigSupplier); 263 dir.loadFontFileMap(); 264 assertThat(dir.getPostScriptMap()).isEmpty(); 265 // All font dirs (including dir for "bar.ttf") should be deleted. 266 assertThat(mUpdatableFontFilesDir.list()).hasLength(0); 267 assertThat(dir.getFontFamilyMap()).isEmpty(); 268 } 269 270 @Test construct_fontNameMismatch()271 public void construct_fontNameMismatch() throws Exception { 272 UpdatableFontDir dirForPreparation = new UpdatableFontDir( 273 mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, 274 mConfigFile, mCurrentTimeSupplier, mConfigSupplier); 275 dirForPreparation.loadFontFileMap(); 276 dirForPreparation.update(Arrays.asList( 277 newFontUpdateRequest("foo.ttf,1,foo", GOOD_SIGNATURE), 278 newFontUpdateRequest("bar.ttf,2,bar", GOOD_SIGNATURE), 279 newFontUpdateRequest("foo.ttf,3,foo", GOOD_SIGNATURE), 280 newFontUpdateRequest("bar.ttf,4,bar", GOOD_SIGNATURE), 281 newAddFontFamilyRequest("<family name='foobar'>" 282 + " <font>foo.ttf</font>" 283 + " <font>bar.ttf</font>" 284 + "</family>"))); 285 // Four font dirs are created. 286 assertThat(mUpdatableFontFilesDir.list()).hasLength(4); 287 288 // Overwrite "foo.ttf" with wrong contents. 289 FileUtils.stringToFile(dirForPreparation.getPostScriptMap().get("foo"), "bar,4"); 290 291 UpdatableFontDir dir = new UpdatableFontDir( 292 mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, 293 mConfigFile, mCurrentTimeSupplier, mConfigSupplier); 294 dir.loadFontFileMap(); 295 assertThat(dir.getPostScriptMap()).isEmpty(); 296 // All font dirs (including dir for "bar.ttf") should be deleted. 297 assertThat(mUpdatableFontFilesDir.list()).hasLength(0); 298 assertThat(dir.getFontFamilyMap()).isEmpty(); 299 } 300 301 @Test construct_missingSignatureFile()302 public void construct_missingSignatureFile() throws Exception { 303 UpdatableFontDir dirForPreparation = new UpdatableFontDir( 304 mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, 305 mConfigFile, mCurrentTimeSupplier, mConfigSupplier); 306 dirForPreparation.loadFontFileMap(); 307 dirForPreparation.update(Arrays.asList( 308 newFontUpdateRequest("foo.ttf,1,foo", GOOD_SIGNATURE))); 309 assertThat(mUpdatableFontFilesDir.list()).hasLength(1); 310 311 // Remove signature file next to the font file. 312 File fontDir = dirForPreparation.getPostScriptMap().get("foo"); 313 File sigFile = new File(fontDir.getParentFile(), "font.fsv_sig"); 314 assertThat(sigFile.exists()).isTrue(); 315 sigFile.delete(); 316 317 UpdatableFontDir dir = new UpdatableFontDir( 318 mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, 319 mConfigFile, mCurrentTimeSupplier, mConfigSupplier); 320 dir.loadFontFileMap(); 321 // The font file should be removed and should not be loaded. 322 assertThat(dir.getPostScriptMap()).isEmpty(); 323 assertThat(mUpdatableFontFilesDir.list()).hasLength(0); 324 assertThat(dir.getFontFamilyMap()).isEmpty(); 325 } 326 327 @Test construct_olderThanPreinstalledFont()328 public void construct_olderThanPreinstalledFont() throws Exception { 329 Function<Map<String, File>, FontConfig> configSupplier = (map) -> { 330 FontConfig.Font fooFont = new FontConfig.Font( 331 new File(mPreinstalledFontDirs.get(0), "foo.ttf"), null, "foo", 332 new FontStyle(400, FontStyle.FONT_SLANT_UPRIGHT), 0, null, null, 333 FontConfig.Font.VAR_TYPE_AXES_NONE); 334 FontConfig.Font barFont = new FontConfig.Font( 335 new File(mPreinstalledFontDirs.get(1), "bar.ttf"), null, "bar", 336 new FontStyle(400, FontStyle.FONT_SLANT_UPRIGHT), 0, null, null, 337 FontConfig.Font.VAR_TYPE_AXES_NONE); 338 339 FontConfig.FontFamily family = new FontConfig.FontFamily( 340 Arrays.asList(fooFont, barFont), null, 341 FontConfig.FontFamily.VARIANT_DEFAULT); 342 return new FontConfig(Collections.emptyList(), 343 Collections.emptyList(), 344 Collections.singletonList(new FontConfig.NamedFamilyList( 345 Collections.singletonList(family), "sans-serif")), 346 Collections.emptyList(), 0, 1); 347 }; 348 349 UpdatableFontDir dirForPreparation = new UpdatableFontDir( 350 mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, 351 mConfigFile, mCurrentTimeSupplier, configSupplier); 352 dirForPreparation.loadFontFileMap(); 353 dirForPreparation.update(Arrays.asList( 354 newFontUpdateRequest("foo.ttf,1,foo", GOOD_SIGNATURE), 355 newFontUpdateRequest("bar.ttf,2,bar", GOOD_SIGNATURE), 356 newFontUpdateRequest("foo.ttf,3,foo", GOOD_SIGNATURE), 357 newFontUpdateRequest("bar.ttf,4,bar", GOOD_SIGNATURE), 358 newAddFontFamilyRequest("<family name='foobar'>" 359 + " <font>foo.ttf</font>" 360 + " <font>bar.ttf</font>" 361 + "</family>"))); 362 // Four font dirs are created. 363 assertThat(mUpdatableFontFilesDir.list()).hasLength(4); 364 365 // Add preinstalled fonts. 366 FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "foo.ttf"), "foo,5,foo"); 367 FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(1), "bar.ttf"), "bar,1,bar"); 368 FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(1), "bar.ttf"), "bar,2,bar"); 369 UpdatableFontDir dir = new UpdatableFontDir( 370 mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, 371 mConfigFile, mCurrentTimeSupplier, configSupplier); 372 dir.loadFontFileMap(); 373 // For foo.ttf, preinstalled font (revision 5) should be used. 374 assertThat(dir.getPostScriptMap()).doesNotContainKey("foo"); 375 // For bar.ttf, updated font (revision 4) should be used. 376 assertThat(dir.getPostScriptMap()).containsKey("bar"); 377 assertThat(mParser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(4); 378 // Outdated font dir should be deleted. 379 // We don't delete bar.ttf in this case, because it's normal that OTA updates preinstalled 380 // fonts. 381 assertThat(mUpdatableFontFilesDir.list()).hasLength(1); 382 // Font family depending on obsoleted font should be removed. 383 assertThat(dir.getFontFamilyMap()).isEmpty(); 384 } 385 386 @Test construct_failedToLoadConfig()387 public void construct_failedToLoadConfig() throws Exception { 388 UpdatableFontDir dir = new UpdatableFontDir( 389 mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, 390 new File("/dev/null"), mCurrentTimeSupplier, mConfigSupplier); 391 dir.loadFontFileMap(); 392 assertThat(dir.getPostScriptMap()).isEmpty(); 393 assertThat(dir.getFontFamilyMap()).isEmpty(); 394 } 395 396 @Test construct_afterBatchFailure()397 public void construct_afterBatchFailure() throws Exception { 398 UpdatableFontDir dirForPreparation = new UpdatableFontDir( 399 mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, 400 mConfigFile, mCurrentTimeSupplier, mConfigSupplier); 401 dirForPreparation.loadFontFileMap(); 402 dirForPreparation.update(Arrays.asList( 403 newFontUpdateRequest("foo.ttf,1,foo", GOOD_SIGNATURE), 404 newAddFontFamilyRequest("<family name='foobar'>" 405 + " <font>foo.ttf</font>" 406 + "</family>"))); 407 try { 408 dirForPreparation.update(Arrays.asList( 409 newFontUpdateRequest("foo.ttf,2,foo", GOOD_SIGNATURE), 410 newFontUpdateRequest("bar.ttf,2,bar", "Invalid signature"), 411 newAddFontFamilyRequest("<family name='foobar'>" 412 + " <font>foo.ttf</font>" 413 + " <font>bar.ttf</font>" 414 + "</family>"))); 415 fail("Batch update with invalid signature should fail"); 416 } catch (FontManagerService.SystemFontException e) { 417 // Expected 418 } 419 420 UpdatableFontDir dir = new UpdatableFontDir( 421 mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, 422 mConfigFile, mCurrentTimeSupplier, mConfigSupplier); 423 dir.loadFontFileMap(); 424 // The state should be rolled back as a whole if one of the update requests fail. 425 assertThat(dir.getPostScriptMap()).containsKey("foo"); 426 assertThat(mParser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(1); 427 assertThat(dir.getFontFamilyMap()).containsKey("foobar"); 428 assertThat(dir.getFontFamilyMap().get("foobar").getFamilies().size()).isEqualTo(1); 429 FontConfig.FontFamily foobar = dir.getFontFamilyMap().get("foobar").getFamilies().get(0); 430 assertThat(foobar.getFontList()).hasSize(1); 431 assertThat(foobar.getFontList().get(0).getFile()) 432 .isEqualTo(dir.getPostScriptMap().get("foo")); 433 } 434 435 @Test loadFontFileMap_twice()436 public void loadFontFileMap_twice() throws Exception { 437 UpdatableFontDir dir = new UpdatableFontDir( 438 mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, 439 mConfigFile, mCurrentTimeSupplier, mConfigSupplier); 440 dir.loadFontFileMap(); 441 dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1,test", 442 GOOD_SIGNATURE))); 443 assertThat(dir.getPostScriptMap()).containsKey("test"); 444 File fontFile = dir.getPostScriptMap().get("test"); 445 dir.loadFontFileMap(); 446 assertThat(dir.getPostScriptMap().get("test")).isEqualTo(fontFile); 447 } 448 449 @Test installFontFile()450 public void installFontFile() throws Exception { 451 UpdatableFontDir dir = new UpdatableFontDir( 452 mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, 453 mConfigFile, mCurrentTimeSupplier, mConfigSupplier); 454 dir.loadFontFileMap(); 455 456 dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1,test", 457 GOOD_SIGNATURE))); 458 assertThat(dir.getPostScriptMap()).containsKey("test"); 459 assertThat(mParser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(1); 460 File fontFile = dir.getPostScriptMap().get("test"); 461 assertThat(Os.stat(fontFile.getAbsolutePath()).st_mode & 0777).isEqualTo(0644); 462 File fontDir = fontFile.getParentFile(); 463 assertThat(Os.stat(fontDir.getAbsolutePath()).st_mode & 0777).isEqualTo(0711); 464 } 465 466 @Test installFontFile_upgrade()467 public void installFontFile_upgrade() throws Exception { 468 UpdatableFontDir dir = new UpdatableFontDir( 469 mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, 470 mConfigFile, mCurrentTimeSupplier, mConfigSupplier); 471 dir.loadFontFileMap(); 472 473 dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1,test", 474 GOOD_SIGNATURE))); 475 Map<String, File> mapBeforeUpgrade = dir.getPostScriptMap(); 476 dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,2,test", 477 GOOD_SIGNATURE))); 478 assertThat(dir.getPostScriptMap()).containsKey("test"); 479 assertThat(mParser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(2); 480 assertThat(mapBeforeUpgrade).containsKey("test"); 481 assertWithMessage("Older fonts should not be deleted until next loadFontFileMap") 482 .that(mParser.getRevision(mapBeforeUpgrade.get("test"))).isEqualTo(1); 483 // Check that updatedFontDirs is pruned. 484 assertWithMessage("config.updatedFontDirs should only list latest active dirs") 485 .that(readConfig(mConfigFile).updatedFontDirs) 486 .containsExactly(dir.getPostScriptMap().get("test").getParentFile().getName()); 487 } 488 489 @Test installFontFile_systemFontHasPSNameDifferentFromFileName()490 public void installFontFile_systemFontHasPSNameDifferentFromFileName() throws Exception { 491 492 // Setup the environment that the system installed font file named "foo.ttf" has PostScript 493 // name "bar". 494 File file = new File(mPreinstalledFontDirs.get(0), "foo.ttf"); 495 FileUtils.stringToFile(file, "foo.ttf,1,bar"); 496 UpdatableFontDir dir = new UpdatableFontDir( 497 mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, 498 mConfigFile, mCurrentTimeSupplier, (map) -> { 499 FontConfig.Font font = new FontConfig.Font( 500 file, null, "bar", new FontStyle(400, FontStyle.FONT_SLANT_UPRIGHT), 501 0, null, null, FontConfig.Font.VAR_TYPE_AXES_NONE); 502 FontConfig.FontFamily family = new FontConfig.FontFamily( 503 Collections.singletonList(font), null, FontConfig.FontFamily.VARIANT_DEFAULT); 504 return new FontConfig( 505 Collections.emptyList(), 506 Collections.emptyList(), 507 Collections.singletonList(new FontConfig.NamedFamilyList( 508 Collections.singletonList(family), "sans-serif")), 509 Collections.emptyList(), 0, 1); 510 }); 511 dir.loadFontFileMap(); 512 513 dir.update(Collections.singletonList(newFontUpdateRequest("bar.ttf,2,bar", 514 GOOD_SIGNATURE))); 515 assertThat(dir.getPostScriptMap()).containsKey("bar"); 516 assertThat(dir.getPostScriptMap().size()).isEqualTo(1); 517 assertThat(mParser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(2); 518 File fontFile = dir.getPostScriptMap().get("bar"); 519 assertThat(Os.stat(fontFile.getAbsolutePath()).st_mode & 0777).isEqualTo(0644); 520 File fontDir = fontFile.getParentFile(); 521 assertThat(Os.stat(fontDir.getAbsolutePath()).st_mode & 0777).isEqualTo(0711); 522 } 523 524 @Test installFontFile_sameVersion()525 public void installFontFile_sameVersion() throws Exception { 526 UpdatableFontDir dir = new UpdatableFontDir( 527 mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, 528 mConfigFile, mCurrentTimeSupplier, mConfigSupplier); 529 dir.loadFontFileMap(); 530 531 dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1,test", 532 GOOD_SIGNATURE))); 533 dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1,test", 534 GOOD_SIGNATURE))); 535 assertThat(dir.getPostScriptMap()).containsKey("test"); 536 assertThat(mParser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(1); 537 } 538 539 @Test installFontFile_downgrade()540 public void installFontFile_downgrade() throws Exception { 541 UpdatableFontDir dir = new UpdatableFontDir( 542 mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, 543 mConfigFile, mCurrentTimeSupplier, mConfigSupplier); 544 dir.loadFontFileMap(); 545 546 dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,2,test", 547 GOOD_SIGNATURE))); 548 try { 549 dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1,test", 550 GOOD_SIGNATURE))); 551 fail("Expect SystemFontException"); 552 } catch (FontManagerService.SystemFontException e) { 553 assertThat(e.getErrorCode()).isEqualTo(FontManager.RESULT_ERROR_DOWNGRADING); 554 } 555 assertThat(dir.getPostScriptMap()).containsKey("test"); 556 assertWithMessage("Font should not be downgraded to an older revision") 557 .that(mParser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(2); 558 // Check that updatedFontDirs is not updated. 559 assertWithMessage("config.updatedFontDirs should only list latest active dirs") 560 .that(readConfig(mConfigFile).updatedFontDirs) 561 .containsExactly(dir.getPostScriptMap().get("test").getParentFile().getName()); 562 } 563 564 @Test installFontFile_multiple()565 public void installFontFile_multiple() throws Exception { 566 UpdatableFontDir dir = new UpdatableFontDir( 567 mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, 568 mConfigFile, mCurrentTimeSupplier, mConfigSupplier); 569 dir.loadFontFileMap(); 570 571 dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1,foo", 572 GOOD_SIGNATURE))); 573 dir.update(Collections.singletonList(newFontUpdateRequest("bar.ttf,2,bar", 574 GOOD_SIGNATURE))); 575 assertThat(dir.getPostScriptMap()).containsKey("foo"); 576 assertThat(mParser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(1); 577 assertThat(dir.getPostScriptMap()).containsKey("bar"); 578 assertThat(mParser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(2); 579 } 580 581 @Test installFontFile_batch()582 public void installFontFile_batch() throws Exception { 583 UpdatableFontDir dir = new UpdatableFontDir( 584 mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, 585 mConfigFile, mCurrentTimeSupplier, mConfigSupplier); 586 dir.loadFontFileMap(); 587 588 dir.update(Arrays.asList( 589 newFontUpdateRequest("foo.ttf,1,foo", GOOD_SIGNATURE), 590 newFontUpdateRequest("bar.ttf,2,bar", GOOD_SIGNATURE))); 591 assertThat(dir.getPostScriptMap()).containsKey("foo"); 592 assertThat(mParser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(1); 593 assertThat(dir.getPostScriptMap()).containsKey("bar"); 594 assertThat(mParser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(2); 595 } 596 597 @Test installFontFile_invalidSignature()598 public void installFontFile_invalidSignature() throws Exception { 599 UpdatableFontDir dir = new UpdatableFontDir( 600 mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, 601 mConfigFile, mCurrentTimeSupplier, mConfigSupplier); 602 dir.loadFontFileMap(); 603 604 try { 605 dir.update( 606 Collections.singletonList(newFontUpdateRequest("test.ttf,1,test", 607 "Invalid signature"))); 608 fail("Expect SystemFontException"); 609 } catch (FontManagerService.SystemFontException e) { 610 assertThat(e.getErrorCode()) 611 .isEqualTo(FontManager.RESULT_ERROR_VERIFICATION_FAILURE); 612 } 613 assertThat(dir.getPostScriptMap()).isEmpty(); 614 } 615 616 @Test installFontFile_preinstalled_upgrade()617 public void installFontFile_preinstalled_upgrade() throws Exception { 618 FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), 619 "test.ttf,1,test"); 620 UpdatableFontDir dir = new UpdatableFontDir( 621 mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, 622 mConfigFile, mCurrentTimeSupplier, mConfigSupplier); 623 dir.loadFontFileMap(); 624 625 dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,2,test", 626 GOOD_SIGNATURE))); 627 assertThat(dir.getPostScriptMap()).containsKey("test"); 628 assertThat(mParser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(2); 629 } 630 631 @Test installFontFile_preinstalled_sameVersion()632 public void installFontFile_preinstalled_sameVersion() throws Exception { 633 FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), 634 "test.ttf,1,test"); 635 UpdatableFontDir dir = new UpdatableFontDir( 636 mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, 637 mConfigFile, mCurrentTimeSupplier, mConfigSupplier); 638 dir.loadFontFileMap(); 639 640 dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1,test", 641 GOOD_SIGNATURE))); 642 assertThat(dir.getPostScriptMap()).containsKey("test"); 643 assertThat(mParser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(1); 644 } 645 646 @Test installFontFile_preinstalled_downgrade()647 public void installFontFile_preinstalled_downgrade() throws Exception { 648 File file = new File(mPreinstalledFontDirs.get(0), "test.ttf"); 649 FileUtils.stringToFile(file, "test.ttf,2,test"); 650 UpdatableFontDir dir = new UpdatableFontDir( 651 mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, 652 mConfigFile, mCurrentTimeSupplier, (map) -> { 653 FontConfig.Font font = new FontConfig.Font( 654 file, null, "test", new FontStyle(400, FontStyle.FONT_SLANT_UPRIGHT), 0, null, 655 null, FontConfig.Font.VAR_TYPE_AXES_NONE); 656 FontConfig.FontFamily family = new FontConfig.FontFamily( 657 Collections.singletonList(font), null, FontConfig.FontFamily.VARIANT_DEFAULT); 658 return new FontConfig(Collections.emptyList(), Collections.emptyList(), 659 Collections.singletonList(new FontConfig.NamedFamilyList( 660 Collections.singletonList(family), "sans-serif")), 661 Collections.emptyList(), 0, 1); 662 }); 663 dir.loadFontFileMap(); 664 665 try { 666 dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1,test", 667 GOOD_SIGNATURE))); 668 fail("Expect SystemFontException"); 669 } catch (FontManagerService.SystemFontException e) { 670 assertThat(e.getErrorCode()).isEqualTo(FontManager.RESULT_ERROR_DOWNGRADING); 671 } 672 assertThat(dir.getPostScriptMap()).isEmpty(); 673 } 674 675 @Test installFontFile_failedToWriteConfigXml()676 public void installFontFile_failedToWriteConfigXml() throws Exception { 677 long expectedModifiedDate = 1234567890; 678 FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), 679 "test.ttf,1,test"); 680 681 File readonlyDir = new File(mCacheDir, "readonly"); 682 assertThat(readonlyDir.mkdir()).isTrue(); 683 File readonlyFile = new File(readonlyDir, "readonly_config.xml"); 684 685 PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config(); 686 config.lastModifiedMillis = expectedModifiedDate; 687 writeConfig(config, readonlyFile); 688 689 assertThat(readonlyDir.setWritable(false, false)).isTrue(); 690 try { 691 UpdatableFontDir dir = new UpdatableFontDir( 692 mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, 693 readonlyFile, mCurrentTimeSupplier, mConfigSupplier); 694 dir.loadFontFileMap(); 695 696 try { 697 dir.update( 698 Collections.singletonList(newFontUpdateRequest("test.ttf,2,test", 699 GOOD_SIGNATURE))); 700 } catch (FontManagerService.SystemFontException e) { 701 assertThat(e.getErrorCode()) 702 .isEqualTo(FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG); 703 } 704 assertThat(dir.getSystemFontConfig().getLastModifiedTimeMillis()) 705 .isEqualTo(expectedModifiedDate); 706 assertThat(dir.getPostScriptMap()).isEmpty(); 707 } finally { 708 assertThat(readonlyDir.setWritable(true, true)).isTrue(); 709 } 710 } 711 712 @Test installFontFile_failedToParsePostScript()713 public void installFontFile_failedToParsePostScript() throws Exception { 714 UpdatableFontDir dir = new UpdatableFontDir( 715 mUpdatableFontFilesDir, 716 new UpdatableFontDir.FontFileParser() { 717 718 @Override 719 public String getPostScriptName(File file) throws IOException { 720 return null; 721 } 722 723 @Override 724 public String buildFontFileName(File file) throws IOException { 725 return null; 726 } 727 728 @Override 729 public long getRevision(File file) throws IOException { 730 return 0; 731 } 732 733 @Override 734 public void tryToCreateTypeface(File file) throws IOException { 735 } 736 }, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); 737 dir.loadFontFileMap(); 738 739 try { 740 dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1,foo", 741 GOOD_SIGNATURE))); 742 fail("Expect SystemFontException"); 743 } catch (FontManagerService.SystemFontException e) { 744 assertThat(e.getErrorCode()) 745 .isEqualTo(FontManager.RESULT_ERROR_INVALID_FONT_NAME); 746 } 747 assertThat(dir.getPostScriptMap()).isEmpty(); 748 } 749 750 @Test installFontFile_failedToParsePostScriptName_invalidFont()751 public void installFontFile_failedToParsePostScriptName_invalidFont() throws Exception { 752 UpdatableFontDir dir = new UpdatableFontDir( 753 mUpdatableFontFilesDir, 754 new UpdatableFontDir.FontFileParser() { 755 @Override 756 public String getPostScriptName(File file) throws IOException { 757 throw new IOException(); 758 } 759 760 @Override 761 public String buildFontFileName(File file) throws IOException { 762 throw new IOException(); 763 } 764 765 @Override 766 public long getRevision(File file) throws IOException { 767 return 0; 768 } 769 770 @Override 771 public void tryToCreateTypeface(File file) throws IOException { 772 } 773 }, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); 774 dir.loadFontFileMap(); 775 776 try { 777 dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1,foo", 778 GOOD_SIGNATURE))); 779 fail("Expect SystemFontException"); 780 } catch (FontManagerService.SystemFontException e) { 781 assertThat(e.getErrorCode()) 782 .isEqualTo(FontManager.RESULT_ERROR_INVALID_FONT_FILE); 783 } 784 assertThat(dir.getPostScriptMap()).isEmpty(); 785 } 786 787 @Test installFontFile_failedToCreateTypeface()788 public void installFontFile_failedToCreateTypeface() throws Exception { 789 UpdatableFontDir dir = new UpdatableFontDir( 790 mUpdatableFontFilesDir, 791 new UpdatableFontDir.FontFileParser() { 792 @Override 793 public String getPostScriptName(File file) throws IOException { 794 return mParser.getPostScriptName(file); 795 } 796 797 @Override 798 public String buildFontFileName(File file) throws IOException { 799 return mParser.buildFontFileName(file); 800 } 801 802 @Override 803 public long getRevision(File file) throws IOException { 804 return mParser.getRevision(file); 805 } 806 807 @Override 808 public void tryToCreateTypeface(File file) throws IOException { 809 throw new IOException(); 810 } 811 }, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); 812 dir.loadFontFileMap(); 813 814 try { 815 dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1,foo", 816 GOOD_SIGNATURE))); 817 fail("Expect SystemFontException"); 818 } catch (FontManagerService.SystemFontException e) { 819 assertThat(e.getErrorCode()) 820 .isEqualTo(FontManager.RESULT_ERROR_INVALID_FONT_FILE); 821 } 822 assertThat(dir.getPostScriptMap()).isEmpty(); 823 } 824 825 @Test installFontFile_renameToPsNameFailure()826 public void installFontFile_renameToPsNameFailure() throws Exception { 827 UpdatableFontDir.FsverityUtil fakeFsverityUtil = new UpdatableFontDir.FsverityUtil() { 828 829 @Override 830 public boolean isFromTrustedProvider(String path, byte[] signature) { 831 return mFakeFsverityUtil.isFromTrustedProvider(path, signature); 832 } 833 834 @Override 835 public void setUpFsverity(String path) throws IOException { 836 mFakeFsverityUtil.setUpFsverity(path); 837 } 838 839 @Override 840 public boolean rename(File src, File dest) { 841 return false; 842 } 843 }; 844 UpdatableFontDir dir = new UpdatableFontDir( 845 mUpdatableFontFilesDir, mParser, fakeFsverityUtil, 846 mConfigFile, mCurrentTimeSupplier, mConfigSupplier); 847 dir.loadFontFileMap(); 848 849 try { 850 dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1,foo", 851 GOOD_SIGNATURE))); 852 fail("Expect SystemFontException"); 853 } catch (FontManagerService.SystemFontException e) { 854 assertThat(e.getErrorCode()) 855 .isEqualTo(FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE); 856 } 857 assertThat(dir.getPostScriptMap()).isEmpty(); 858 } 859 860 @Test installFontFile_batchFailure()861 public void installFontFile_batchFailure() throws Exception { 862 UpdatableFontDir dir = new UpdatableFontDir( 863 mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, 864 mConfigFile, mCurrentTimeSupplier, mConfigSupplier); 865 dir.loadFontFileMap(); 866 867 dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1,foo", 868 GOOD_SIGNATURE))); 869 try { 870 dir.update(Arrays.asList( 871 newFontUpdateRequest("foo.ttf,2,foo", GOOD_SIGNATURE), 872 newFontUpdateRequest("bar.ttf,2,bar", "Invalid signature"))); 873 fail("Batch update with invalid signature should fail"); 874 } catch (FontManagerService.SystemFontException e) { 875 // Expected 876 } 877 // The state should be rolled back as a whole if one of the update requests fail. 878 assertThat(dir.getPostScriptMap()).containsKey("foo"); 879 assertThat(mParser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(1); 880 } 881 882 @Test addFontFamily()883 public void addFontFamily() throws Exception { 884 UpdatableFontDir dir = new UpdatableFontDir( 885 mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, 886 mConfigFile, mCurrentTimeSupplier, mConfigSupplier); 887 dir.loadFontFileMap(); 888 889 dir.update(Arrays.asList( 890 newFontUpdateRequest("test.ttf,1,test", GOOD_SIGNATURE), 891 newAddFontFamilyRequest("<family name='test'>" 892 + " <font>test.ttf</font>" 893 + "</family>"))); 894 assertThat(dir.getPostScriptMap()).containsKey("test"); 895 assertThat(dir.getFontFamilyMap()).containsKey("test"); 896 assertThat(dir.getFontFamilyMap().get("test").getFamilies().size()).isEqualTo(1); 897 FontConfig.FontFamily test = dir.getFontFamilyMap().get("test").getFamilies().get(0); 898 assertThat(test.getFontList()).hasSize(1); 899 assertThat(test.getFontList().get(0).getFile()) 900 .isEqualTo(dir.getPostScriptMap().get("test")); 901 } 902 903 @Test(expected = IllegalArgumentException.class) addFontFamily_noName()904 public void addFontFamily_noName() throws Exception { 905 UpdatableFontDir dir = new UpdatableFontDir( 906 mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, 907 mConfigFile, mCurrentTimeSupplier, mConfigSupplier); 908 dir.loadFontFileMap(); 909 910 List<FontUpdateRequest> requests = Arrays.asList( 911 newFontUpdateRequest("test.ttf,1,test", GOOD_SIGNATURE), 912 newAddFontFamilyRequest("<family lang='en'>" 913 + " <font>test.ttf</font>" 914 + "</family>")); 915 dir.update(requests); 916 } 917 918 @Test addFontFamily_fontNotAvailable()919 public void addFontFamily_fontNotAvailable() throws Exception { 920 UpdatableFontDir dir = new UpdatableFontDir( 921 mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, 922 mConfigFile, mCurrentTimeSupplier, mConfigSupplier); 923 dir.loadFontFileMap(); 924 925 try { 926 dir.update(Arrays.asList(newAddFontFamilyRequest("<family name='test'>" 927 + " <font>test.ttf</font>" 928 + "</family>"))); 929 fail("Expect SystemFontException"); 930 } catch (FontManagerService.SystemFontException e) { 931 assertThat(e.getErrorCode()) 932 .isEqualTo(FontManager.RESULT_ERROR_FONT_NOT_FOUND); 933 } 934 } 935 936 @Test getSystemFontConfig()937 public void getSystemFontConfig() throws Exception { 938 UpdatableFontDir dir = new UpdatableFontDir( 939 mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, 940 mConfigFile, mCurrentTimeSupplier, mConfigSupplier); 941 dir.loadFontFileMap(); 942 // We assume we have monospace. 943 assertNamedFamilyExists(dir.getSystemFontConfig(), "monospace"); 944 945 dir.update(Arrays.asList( 946 newFontUpdateRequest("test.ttf,1,test", GOOD_SIGNATURE), 947 // Updating an existing font family. 948 newAddFontFamilyRequest("<family name='monospace'>" 949 + " <font>test.ttf</font>" 950 + "</family>"), 951 // Adding a new font family. 952 newAddFontFamilyRequest("<family name='test'>" 953 + " <font>test.ttf</font>" 954 + "</family>"))); 955 FontConfig fontConfig = dir.getSystemFontConfig(); 956 assertNamedFamilyExists(fontConfig, "monospace"); 957 FontConfig.FontFamily monospace = getLastFamily(fontConfig, "monospace"); 958 assertThat(monospace.getFontList()).hasSize(1); 959 assertThat(monospace.getFontList().get(0).getFile()) 960 .isEqualTo(dir.getPostScriptMap().get("test")); 961 assertNamedFamilyExists(fontConfig, "test"); 962 assertThat(getLastFamily(fontConfig, "test").getFontList()) 963 .isEqualTo(monospace.getFontList()); 964 } 965 966 @Test getSystemFontConfig_preserveFirstFontFamily()967 public void getSystemFontConfig_preserveFirstFontFamily() throws Exception { 968 UpdatableFontDir dir = new UpdatableFontDir( 969 mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, 970 mConfigFile, mCurrentTimeSupplier, mConfigSupplier); 971 dir.loadFontFileMap(); 972 assertThat(dir.getSystemFontConfig().getFontFamilies()).isNotEmpty(); 973 FontConfig.FontFamily firstFontFamily = dir.getSystemFontConfig().getFontFamilies().get(0); 974 975 dir.update(Arrays.asList( 976 newFontUpdateRequest("test.ttf,1,test", GOOD_SIGNATURE), 977 newAddFontFamilyRequest("<family name='sans-serif'>" 978 + " <font>test.ttf</font>" 979 + "</family>"))); 980 FontConfig fontConfig = dir.getSystemFontConfig(); 981 assertThat(dir.getSystemFontConfig().getFontFamilies()).isNotEmpty(); 982 assertThat(fontConfig.getFontFamilies().get(0)).isEqualTo(firstFontFamily); 983 FontConfig.FontFamily updated = getLastFamily(fontConfig, "sans-serif"); 984 assertThat(updated.getFontList()).hasSize(1); 985 assertThat(updated.getFontList().get(0).getFile()) 986 .isEqualTo(dir.getPostScriptMap().get("test")); 987 assertThat(updated).isNotEqualTo(firstFontFamily); 988 } 989 990 @Test deleteAllFiles()991 public void deleteAllFiles() throws Exception { 992 FakeFontFileParser parser = new FakeFontFileParser(); 993 FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); 994 UpdatableFontDir dirForPreparation = new UpdatableFontDir( 995 mUpdatableFontFilesDir, parser, fakeFsverityUtil, 996 mConfigFile, mCurrentTimeSupplier, mConfigSupplier); 997 dirForPreparation.loadFontFileMap(); 998 dirForPreparation.update(Collections.singletonList( 999 newFontUpdateRequest("foo.ttf,1,foo", GOOD_SIGNATURE))); 1000 assertThat(mConfigFile.exists()).isTrue(); 1001 assertThat(mUpdatableFontFilesDir.list()).hasLength(1); 1002 1003 UpdatableFontDir.deleteAllFiles(mUpdatableFontFilesDir, mConfigFile); 1004 assertThat(mConfigFile.exists()).isFalse(); 1005 assertThat(mUpdatableFontFilesDir.list()).hasLength(0); 1006 } 1007 createNewUpdateDir()1008 private UpdatableFontDir createNewUpdateDir() { 1009 UpdatableFontDir dir = new UpdatableFontDir( 1010 mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, 1011 mConfigFile, mCurrentTimeSupplier, mConfigSupplier); 1012 dir.loadFontFileMap(); 1013 return dir; 1014 } 1015 installTestFontFamilies(int version)1016 private UpdatableFontDir installTestFontFamilies(int version) { 1017 UpdatableFontDir dir = createNewUpdateDir(); 1018 try { 1019 dir.update(Arrays.asList( 1020 newFontUpdateRequest("foo.ttf," + version + ",foo", GOOD_SIGNATURE), 1021 newFontUpdateRequest("bar.ttf," + version + ",bar", GOOD_SIGNATURE), 1022 newAddFontFamilyRequest("<family name='foobar'>" 1023 + " <font>foo.ttf</font>" 1024 + " <font>bar.ttf</font>" 1025 + "</family>"))); 1026 } catch (Exception e) { 1027 throw new RuntimeException(e); 1028 } 1029 return dir; 1030 } 1031 installTestFontFile(int numFonts, int version)1032 private UpdatableFontDir installTestFontFile(int numFonts, int version) { 1033 UpdatableFontDir dir = createNewUpdateDir(); 1034 List<FontUpdateRequest> requests = new ArrayList<>(); 1035 if (numFonts <= 0 || numFonts > 3) { 1036 throw new IllegalArgumentException("numFont must be 1, 2 or 3"); 1037 } 1038 try { 1039 requests.add(newFontUpdateRequest("foo.ttf," + version + ",foo", GOOD_SIGNATURE)); 1040 if (numFonts >= 2) { 1041 requests.add(newFontUpdateRequest("bar.ttf," + version + ",bar", GOOD_SIGNATURE)); 1042 } 1043 if (numFonts == 3) { 1044 requests.add(newFontUpdateRequest("baz.ttf," + version + ",baz", GOOD_SIGNATURE)); 1045 } 1046 dir.update(requests); 1047 } catch (Exception e) { 1048 throw new RuntimeException(e); 1049 } 1050 return dir; 1051 } 1052 collectSignatureFiles()1053 private List<File> collectSignatureFiles() { 1054 return Arrays.stream(mUpdatableFontFilesDir.listFiles()) 1055 .map((file) -> file.listFiles((unused, s) -> s.endsWith(".fsv_sig"))) 1056 .flatMap(Arrays::stream) 1057 .toList(); 1058 } 1059 collectFontFiles()1060 private List<File> collectFontFiles() { 1061 return Arrays.stream(mUpdatableFontFilesDir.listFiles()) 1062 .map((file) -> file.listFiles((unused, s) -> s.endsWith(".ttf"))) 1063 .flatMap(Arrays::stream) 1064 .toList(); 1065 } 1066 removeAll(List<File> files)1067 private void removeAll(List<File> files) { 1068 files.forEach((File file) -> { 1069 if (file.isDirectory()) { 1070 removeAll(List.of(file.listFiles())); 1071 } else { 1072 assertThat(file.delete()).isTrue(); 1073 } 1074 }); 1075 } 1076 assertTestFontFamilyInstalled(UpdatableFontDir dir, int version)1077 private void assertTestFontFamilyInstalled(UpdatableFontDir dir, int version) { 1078 try { 1079 assertNamedFamilyExists(dir.getSystemFontConfig(), "foobar"); 1080 assertThat(dir.getFontFamilyMap()).containsKey("foobar"); 1081 assertThat(dir.getFontFamilyMap().get("foobar").getFamilies().size()).isEqualTo(1); 1082 FontConfig.FontFamily foobar = dir.getFontFamilyMap().get("foobar").getFamilies() 1083 .get(0); 1084 assertThat(foobar.getFontList()).hasSize(2); 1085 assertThat(foobar.getFontList().get(0).getFile()) 1086 .isEqualTo(dir.getPostScriptMap().get("foo")); 1087 assertThat(mParser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(version); 1088 assertThat(foobar.getFontList().get(1).getFile()) 1089 .isEqualTo(dir.getPostScriptMap().get("bar")); 1090 assertThat(mParser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(version); 1091 } catch (Exception e) { 1092 throw new RuntimeException(e); 1093 } 1094 } 1095 assertTestFontInstalled(UpdatableFontDir dir, int version)1096 private void assertTestFontInstalled(UpdatableFontDir dir, int version) { 1097 try { 1098 assertThat(dir.getPostScriptMap().containsKey("foo")).isTrue(); 1099 assertThat(mParser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(version); 1100 } catch (Exception e) { 1101 throw new RuntimeException(e); 1102 } 1103 } 1104 1105 @Test signatureMissingCase_fontFamilyInstalled_fontFamilyInstallLater()1106 public void signatureMissingCase_fontFamilyInstalled_fontFamilyInstallLater() { 1107 // Install font families, foo.ttf, bar.ttf. 1108 installTestFontFamilies(1 /* version */); 1109 1110 // Delete one signature file 1111 assertThat(collectSignatureFiles().get(0).delete()).isTrue(); 1112 1113 // New instance of UpdatableFontDir, this emulate a device reboot. 1114 UpdatableFontDir dir = installTestFontFamilies(2 /* version */); 1115 1116 // Make sure the font installation succeeds. 1117 assertTestFontFamilyInstalled(dir, 2 /* version */); 1118 1119 // Make sure after the reboot, the configuration remains. 1120 UpdatableFontDir nextDir = createNewUpdateDir(); 1121 assertTestFontFamilyInstalled(nextDir, 2 /* version */); 1122 } 1123 1124 @Test signatureMissingCase_fontFamilyInstalled_fontInstallLater()1125 public void signatureMissingCase_fontFamilyInstalled_fontInstallLater() { 1126 // Install font families, foo.ttf, bar.ttf. 1127 installTestFontFamilies(1); 1128 1129 // Delete one signature file 1130 assertThat(collectSignatureFiles().get(0).delete()).isTrue(); 1131 1132 // New instance of UpdatableFontDir, this emulate a device reboot. 1133 UpdatableFontDir dir = installTestFontFile(1 /* numFonts */, 2 /* version */); 1134 1135 // Make sure the font installation succeeds. 1136 assertTestFontInstalled(dir, 2 /* version */); 1137 1138 // Make sure after the reboot, the configuration remains. 1139 UpdatableFontDir nextDir = createNewUpdateDir(); 1140 assertTestFontInstalled(nextDir, 2 /* version */); 1141 } 1142 1143 @Test signatureMissingCase_fontFileInstalled_fontFamilyInstallLater()1144 public void signatureMissingCase_fontFileInstalled_fontFamilyInstallLater() { 1145 // Install font file, foo.ttf and bar.ttf 1146 installTestFontFile(2 /* numFonts */, 1 /* version */); 1147 1148 // Delete one signature file 1149 assertThat(collectSignatureFiles().get(0).delete()).isTrue(); 1150 1151 // New instance of UpdatableFontDir, this emulate a device reboot. 1152 UpdatableFontDir dir = installTestFontFamilies(2 /* version */); 1153 1154 // Make sure the font installation succeeds. 1155 assertTestFontFamilyInstalled(dir, 2 /* version */); 1156 1157 // Make sure after the reboot, the configuration remains. 1158 UpdatableFontDir nextDir = createNewUpdateDir(); 1159 assertTestFontFamilyInstalled(nextDir, 2 /* version */); 1160 } 1161 1162 @Test signatureMissingCase_fontFileInstalled_fontFileInstallLater()1163 public void signatureMissingCase_fontFileInstalled_fontFileInstallLater() { 1164 // Install font file, foo.ttf and bar.ttf 1165 installTestFontFile(2 /* numFonts */, 1 /* version */); 1166 1167 // Delete one signature file 1168 assertThat(collectSignatureFiles().get(0).delete()).isTrue(); 1169 1170 // New instance of UpdatableFontDir, this emulate a device reboot. 1171 UpdatableFontDir dir = installTestFontFile(2 /* numFonts */, 2 /* version */); 1172 1173 // Make sure the font installation succeeds. 1174 assertTestFontInstalled(dir, 2 /* version */); 1175 1176 // Make sure after the reboot, the configuration remains. 1177 UpdatableFontDir nextDir = createNewUpdateDir(); 1178 assertTestFontInstalled(nextDir, 2 /* version */); 1179 } 1180 1181 @Test signatureAllMissingCase_fontFamilyInstalled_fontFamilyInstallLater()1182 public void signatureAllMissingCase_fontFamilyInstalled_fontFamilyInstallLater() { 1183 // Install font families, foo.ttf, bar.ttf. 1184 installTestFontFamilies(1 /* version */); 1185 1186 // Delete all signature files 1187 removeAll(collectSignatureFiles()); 1188 1189 // New instance of UpdatableFontDir, this emulate a device reboot. 1190 UpdatableFontDir dir = installTestFontFamilies(2 /* version */); 1191 1192 // Make sure the font installation succeeds. 1193 assertTestFontFamilyInstalled(dir, 2 /* version */); 1194 1195 // Make sure after the reboot, the configuration remains. 1196 UpdatableFontDir nextDir = createNewUpdateDir(); 1197 assertTestFontFamilyInstalled(nextDir, 2 /* version */); 1198 } 1199 1200 @Test signatureAllMissingCase_fontFamilyInstalled_fontInstallLater()1201 public void signatureAllMissingCase_fontFamilyInstalled_fontInstallLater() { 1202 // Install font families, foo.ttf, bar.ttf. 1203 installTestFontFamilies(1 /* version */); 1204 1205 // Delete all signature files 1206 removeAll(collectSignatureFiles()); 1207 1208 // New instance of UpdatableFontDir, this emulate a device reboot. 1209 UpdatableFontDir dir = installTestFontFile(1 /* numFonts */, 2 /* version */); 1210 1211 // Make sure the font installation succeeds. 1212 assertTestFontInstalled(dir, 2 /* version */); 1213 1214 // Make sure after the reboot, the configuration remains. 1215 UpdatableFontDir nextDir = createNewUpdateDir(); 1216 assertTestFontInstalled(nextDir, 2 /* version */); 1217 } 1218 1219 @Test signatureAllMissingCase_fontFileInstalled_fontFamilyInstallLater()1220 public void signatureAllMissingCase_fontFileInstalled_fontFamilyInstallLater() { 1221 // Install font file, foo.ttf 1222 installTestFontFile(1 /* numFonts */, 1 /* version */); 1223 1224 // Delete all signature files 1225 removeAll(collectSignatureFiles()); 1226 1227 // New instance of UpdatableFontDir, this emulate a device reboot. 1228 UpdatableFontDir dir = installTestFontFamilies(2 /* version */); 1229 1230 // Make sure the font installation succeeds. 1231 assertTestFontFamilyInstalled(dir, 2 /* version */); 1232 1233 // Make sure after the reboot, the configuration remains. 1234 UpdatableFontDir nextDir = createNewUpdateDir(); 1235 assertTestFontFamilyInstalled(nextDir, 2 /* version */); 1236 } 1237 1238 @Test signatureAllMissingCase_fontFileInstalled_fontFileInstallLater()1239 public void signatureAllMissingCase_fontFileInstalled_fontFileInstallLater() { 1240 // Install font file, foo.ttf 1241 installTestFontFile(1 /* numFonts */, 1 /* version */); 1242 1243 // Delete all signature files 1244 removeAll(collectSignatureFiles()); 1245 1246 // New instance of UpdatableFontDir, this emulate a device reboot. 1247 UpdatableFontDir dir = installTestFontFile(1 /* numFonts */, 2 /* version */); 1248 1249 // Make sure the font installation succeeds. 1250 assertTestFontInstalled(dir, 2 /* version */); 1251 1252 // Make sure after the reboot, the configuration remains. 1253 UpdatableFontDir nextDir = createNewUpdateDir(); 1254 assertTestFontInstalled(nextDir, 2 /* version */); 1255 } 1256 1257 @Test fontMissingCase_fontFamilyInstalled_fontFamilyInstallLater()1258 public void fontMissingCase_fontFamilyInstalled_fontFamilyInstallLater() { 1259 // Install font families, foo.ttf, bar.ttf. 1260 installTestFontFamilies(1 /* version */); 1261 1262 // Delete one font file 1263 assertThat(collectFontFiles().get(0).delete()).isTrue(); 1264 1265 // New instance of UpdatableFontDir, this emulate a device reboot. 1266 UpdatableFontDir dir = installTestFontFamilies(2 /* version */); 1267 1268 // Make sure the font installation succeeds. 1269 assertTestFontFamilyInstalled(dir, 2 /* version */); 1270 1271 // Make sure after the reboot, the configuration remains. 1272 UpdatableFontDir nextDir = createNewUpdateDir(); 1273 assertTestFontFamilyInstalled(nextDir, 2 /* version */); 1274 } 1275 1276 @Test fontMissingCase_fontFamilyInstalled_fontInstallLater()1277 public void fontMissingCase_fontFamilyInstalled_fontInstallLater() { 1278 // Install font families, foo.ttf, bar.ttf. 1279 installTestFontFamilies(1); 1280 1281 // Delete one font file 1282 assertThat(collectFontFiles().get(0).delete()).isTrue(); 1283 1284 // New instance of UpdatableFontDir, this emulate a device reboot. 1285 UpdatableFontDir dir = installTestFontFile(1 /* numFonts */, 2 /* version */); 1286 1287 // Make sure the font installation succeeds. 1288 assertTestFontInstalled(dir, 2 /* version */); 1289 1290 // Make sure after the reboot, the configuration remains. 1291 UpdatableFontDir nextDir = createNewUpdateDir(); 1292 assertTestFontInstalled(nextDir, 2 /* version */); 1293 } 1294 1295 @Test fontMissingCase_fontFileInstalled_fontFamilyInstallLater()1296 public void fontMissingCase_fontFileInstalled_fontFamilyInstallLater() { 1297 // Install font file, foo.ttf and bar.ttf 1298 installTestFontFile(2 /* numFonts */, 1 /* version */); 1299 1300 // Delete one font file 1301 assertThat(collectFontFiles().get(0).delete()).isTrue(); 1302 1303 // New instance of UpdatableFontDir, this emulate a device reboot. 1304 UpdatableFontDir dir = installTestFontFamilies(2 /* version */); 1305 1306 // Make sure the font installation succeeds. 1307 assertTestFontFamilyInstalled(dir, 2 /* version */); 1308 1309 // Make sure after the reboot, the configuration remains. 1310 UpdatableFontDir nextDir = createNewUpdateDir(); 1311 assertTestFontFamilyInstalled(nextDir, 2 /* version */); 1312 } 1313 1314 @Test fontMissingCase_fontFileInstalled_fontFileInstallLater()1315 public void fontMissingCase_fontFileInstalled_fontFileInstallLater() { 1316 // Install font file, foo.ttf and bar.ttf 1317 installTestFontFile(2 /* numFonts */, 1 /* version */); 1318 1319 // Delete one font file 1320 assertThat(collectFontFiles().get(0).delete()).isTrue(); 1321 1322 // New instance of UpdatableFontDir, this emulate a device reboot. 1323 UpdatableFontDir dir = installTestFontFile(2 /* numFonts */, 2 /* version */); 1324 1325 // Make sure the font installation succeeds. 1326 assertTestFontInstalled(dir, 2 /* version */); 1327 1328 // Make sure after the reboot, the configuration remains. 1329 UpdatableFontDir nextDir = createNewUpdateDir(); 1330 assertTestFontInstalled(nextDir, 2 /* version */); 1331 } 1332 1333 @Test fontAllMissingCase_fontFamilyInstalled_fontFamilyInstallLater()1334 public void fontAllMissingCase_fontFamilyInstalled_fontFamilyInstallLater() { 1335 // Install font families, foo.ttf, bar.ttf. 1336 installTestFontFamilies(1 /* version */); 1337 1338 // Delete all font files 1339 removeAll(collectFontFiles()); 1340 1341 // New instance of UpdatableFontDir, this emulate a device reboot. 1342 UpdatableFontDir dir = installTestFontFamilies(2 /* version */); 1343 1344 // Make sure the font installation succeeds. 1345 assertTestFontFamilyInstalled(dir, 2 /* version */); 1346 1347 // Make sure after the reboot, the configuration remains. 1348 UpdatableFontDir nextDir = createNewUpdateDir(); 1349 assertTestFontFamilyInstalled(nextDir, 2 /* version */); 1350 } 1351 1352 @Test fontAllMissingCase_fontFamilyInstalled_fontInstallLater()1353 public void fontAllMissingCase_fontFamilyInstalled_fontInstallLater() { 1354 // Install font families, foo.ttf, bar.ttf. 1355 installTestFontFamilies(1 /* version */); 1356 1357 // Delete all font files 1358 removeAll(collectFontFiles()); 1359 1360 // New instance of UpdatableFontDir, this emulate a device reboot. 1361 UpdatableFontDir dir = installTestFontFile(1 /* numFonts */, 2 /* version */); 1362 1363 // Make sure the font installation succeeds. 1364 assertTestFontInstalled(dir, 2 /* version */); 1365 1366 // Make sure after the reboot, the configuration remains. 1367 UpdatableFontDir nextDir = createNewUpdateDir(); 1368 assertTestFontInstalled(nextDir, 2 /* version */); 1369 } 1370 1371 @Test fontAllMissingCase_fontFileInstalled_fontFamilyInstallLater()1372 public void fontAllMissingCase_fontFileInstalled_fontFamilyInstallLater() { 1373 // Install font file, foo.ttf 1374 installTestFontFile(1 /* numFonts */, 1 /* version */); 1375 1376 // Delete all font files 1377 removeAll(collectFontFiles()); 1378 1379 // New instance of UpdatableFontDir, this emulate a device reboot. 1380 UpdatableFontDir dir = installTestFontFamilies(2 /* version */); 1381 1382 // Make sure the font installation succeeds. 1383 assertTestFontFamilyInstalled(dir, 2 /* version */); 1384 1385 // Make sure after the reboot, the configuration remains. 1386 UpdatableFontDir nextDir = createNewUpdateDir(); 1387 assertTestFontFamilyInstalled(nextDir, 2 /* version */); 1388 } 1389 1390 @Test fontAllMissingCase_fontFileInstalled_fontFileInstallLater()1391 public void fontAllMissingCase_fontFileInstalled_fontFileInstallLater() { 1392 // Install font file, foo.ttf 1393 installTestFontFile(1 /* numFonts */, 1 /* version */); 1394 1395 // Delete all font files 1396 removeAll(collectFontFiles()); 1397 1398 // New instance of UpdatableFontDir, this emulate a device reboot. 1399 UpdatableFontDir dir = installTestFontFile(1 /* numFonts */, 2 /* version */); 1400 1401 // Make sure the font installation succeeds. 1402 assertTestFontInstalled(dir, 2 /* version */); 1403 1404 // Make sure after the reboot, the configuration remains. 1405 UpdatableFontDir nextDir = createNewUpdateDir(); 1406 assertTestFontInstalled(nextDir, 2 /* version */); 1407 } 1408 1409 @Test fontDirAllMissingCase_fontFamilyInstalled_fontFamilyInstallLater()1410 public void fontDirAllMissingCase_fontFamilyInstalled_fontFamilyInstallLater() { 1411 // Install font families, foo.ttf, bar.ttf. 1412 installTestFontFamilies(1 /* version */); 1413 1414 // Delete all font files 1415 removeAll(List.of(mUpdatableFontFilesDir.listFiles())); 1416 1417 // New instance of UpdatableFontDir, this emulate a device reboot. 1418 UpdatableFontDir dir = installTestFontFamilies(2 /* version */); 1419 1420 // Make sure the font installation succeeds. 1421 assertTestFontFamilyInstalled(dir, 2 /* version */); 1422 1423 // Make sure after the reboot, the configuration remains. 1424 UpdatableFontDir nextDir = createNewUpdateDir(); 1425 assertTestFontFamilyInstalled(nextDir, 2 /* version */); 1426 } 1427 1428 @Test fontDirAllMissingCase_fontFamilyInstalled_fontInstallLater()1429 public void fontDirAllMissingCase_fontFamilyInstalled_fontInstallLater() { 1430 // Install font families, foo.ttf, bar.ttf. 1431 installTestFontFamilies(1 /* version */); 1432 1433 // Delete all font files 1434 removeAll(List.of(mUpdatableFontFilesDir.listFiles())); 1435 1436 // New instance of UpdatableFontDir, this emulate a device reboot. 1437 UpdatableFontDir dir = installTestFontFile(1 /* numFonts */, 2 /* version */); 1438 1439 // Make sure the font installation succeeds. 1440 assertTestFontInstalled(dir, 2 /* version */); 1441 1442 // Make sure after the reboot, the configuration remains. 1443 UpdatableFontDir nextDir = createNewUpdateDir(); 1444 assertTestFontInstalled(nextDir, 2 /* version */); 1445 } 1446 1447 @Test fontDirAllMissingCase_fontFileInstalled_fontFamilyInstallLater()1448 public void fontDirAllMissingCase_fontFileInstalled_fontFamilyInstallLater() { 1449 // Install font file, foo.ttf 1450 installTestFontFile(1 /* numFonts */, 1 /* version */); 1451 1452 // Delete all font files 1453 removeAll(List.of(mUpdatableFontFilesDir.listFiles())); 1454 1455 // New instance of UpdatableFontDir, this emulate a device reboot. 1456 UpdatableFontDir dir = installTestFontFamilies(2 /* version */); 1457 1458 // Make sure the font installation succeeds. 1459 assertTestFontFamilyInstalled(dir, 2 /* version */); 1460 1461 // Make sure after the reboot, the configuration remains. 1462 UpdatableFontDir nextDir = createNewUpdateDir(); 1463 assertTestFontFamilyInstalled(nextDir, 2 /* version */); 1464 } 1465 1466 @Test fontDirAllMissingCase_fontFileInstalled_fontFileInstallLater()1467 public void fontDirAllMissingCase_fontFileInstalled_fontFileInstallLater() { 1468 // Install font file, foo.ttf 1469 installTestFontFile(1 /* numFonts */, 1 /* version */); 1470 1471 // Delete all font files 1472 removeAll(List.of(mUpdatableFontFilesDir.listFiles())); 1473 1474 // New instance of UpdatableFontDir, this emulate a device reboot. 1475 UpdatableFontDir dir = installTestFontFile(1 /* numFonts */, 2 /* version */); 1476 1477 // Make sure the font installation succeeds. 1478 assertTestFontInstalled(dir, 2 /* version */); 1479 1480 // Make sure after the reboot, the configuration remains. 1481 UpdatableFontDir nextDir = createNewUpdateDir(); 1482 assertTestFontInstalled(nextDir, 2 /* version */); 1483 } 1484 1485 @Test dirContentAllMissingCase_fontFamilyInstalled_fontFamilyInstallLater()1486 public void dirContentAllMissingCase_fontFamilyInstalled_fontFamilyInstallLater() { 1487 // Install font families, foo.ttf, bar.ttf. 1488 installTestFontFamilies(1 /* version */); 1489 1490 // Delete all font files 1491 removeAll(collectFontFiles()); 1492 removeAll(collectSignatureFiles()); 1493 1494 // New instance of UpdatableFontDir, this emulate a device reboot. 1495 UpdatableFontDir dir = installTestFontFamilies(2 /* version */); 1496 1497 // Make sure the font installation succeeds. 1498 assertTestFontFamilyInstalled(dir, 2 /* version */); 1499 1500 // Make sure after the reboot, the configuration remains. 1501 UpdatableFontDir nextDir = createNewUpdateDir(); 1502 assertTestFontFamilyInstalled(nextDir, 2 /* version */); 1503 } 1504 1505 @Test dirContentAllMissingCase_fontFamilyInstalled_fontInstallLater()1506 public void dirContentAllMissingCase_fontFamilyInstalled_fontInstallLater() { 1507 // Install font families, foo.ttf, bar.ttf. 1508 installTestFontFamilies(1 /* version */); 1509 1510 // Delete all font files 1511 removeAll(collectFontFiles()); 1512 removeAll(collectSignatureFiles()); 1513 1514 // New instance of UpdatableFontDir, this emulate a device reboot. 1515 UpdatableFontDir dir = installTestFontFile(1 /* numFonts */, 2 /* version */); 1516 1517 // Make sure the font installation succeeds. 1518 assertTestFontInstalled(dir, 2 /* version */); 1519 1520 // Make sure after the reboot, the configuration remains. 1521 UpdatableFontDir nextDir = createNewUpdateDir(); 1522 assertTestFontInstalled(nextDir, 2 /* version */); 1523 } 1524 1525 @Test dirContentAllMissingCase_fontFileInstalled_fontFamilyInstallLater()1526 public void dirContentAllMissingCase_fontFileInstalled_fontFamilyInstallLater() { 1527 // Install font file, foo.ttf 1528 installTestFontFile(1 /* numFonts */, 1 /* version */); 1529 1530 // Delete all font files 1531 removeAll(collectFontFiles()); 1532 removeAll(collectSignatureFiles()); 1533 1534 // New instance of UpdatableFontDir, this emulate a device reboot. 1535 UpdatableFontDir dir = installTestFontFamilies(2 /* version */); 1536 1537 // Make sure the font installation succeeds. 1538 assertTestFontFamilyInstalled(dir, 2 /* version */); 1539 1540 // Make sure after the reboot, the configuration remains. 1541 UpdatableFontDir nextDir = createNewUpdateDir(); 1542 assertTestFontFamilyInstalled(nextDir, 2 /* version */); 1543 } 1544 1545 @Test dirContentAllMissingCase_fontFileInstalled_fontFileInstallLater()1546 public void dirContentAllMissingCase_fontFileInstalled_fontFileInstallLater() { 1547 // Install font file, foo.ttf 1548 installTestFontFile(1 /* numFonts */, 1 /* version */); 1549 1550 // Delete all font files 1551 removeAll(collectFontFiles()); 1552 removeAll(collectSignatureFiles()); 1553 1554 // New instance of UpdatableFontDir, this emulate a device reboot. 1555 UpdatableFontDir dir = installTestFontFile(1 /* numFonts */, 2 /* version */); 1556 1557 // Make sure the font installation succeeds. 1558 assertTestFontInstalled(dir, 2 /* version */); 1559 1560 // Make sure after the reboot, the configuration remains. 1561 UpdatableFontDir nextDir = createNewUpdateDir(); 1562 assertTestFontInstalled(nextDir, 2 /* version */); 1563 } 1564 newFontUpdateRequest(String content, String signature)1565 private FontUpdateRequest newFontUpdateRequest(String content, String signature) 1566 throws Exception { 1567 File file = File.createTempFile("font", "ttf", mCacheDir); 1568 FileUtils.stringToFile(file, content); 1569 return new FontUpdateRequest( 1570 ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY), 1571 signature.getBytes()); 1572 } 1573 newAddFontFamilyRequest(String xml)1574 private static FontUpdateRequest newAddFontFamilyRequest(String xml) throws Exception { 1575 XmlPullParser mParser = Xml.newPullParser(); 1576 ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8)); 1577 mParser.setInput(is, "UTF-8"); 1578 mParser.nextTag(); 1579 1580 FontConfig.NamedFamilyList namedFamilyList = FontListParser.readNamedFamily( 1581 mParser, "", null, true); 1582 FontConfig.FontFamily fontFamily = namedFamilyList.getFamilies().get(0); 1583 List<FontUpdateRequest.Font> fonts = new ArrayList<>(); 1584 for (FontConfig.Font font : fontFamily.getFontList()) { 1585 String name = font.getFile().getName(); 1586 String psName = name.substring(0, name.length() - 4); // drop suffix 1587 FontUpdateRequest.Font updateFont = new FontUpdateRequest.Font( 1588 psName, font.getStyle(), font.getTtcIndex(), font.getFontVariationSettings()); 1589 fonts.add(updateFont); 1590 } 1591 FontUpdateRequest.Family family = new FontUpdateRequest.Family( 1592 namedFamilyList.getName(), fonts); 1593 return new FontUpdateRequest(family); 1594 } 1595 readConfig(File file)1596 private static PersistentSystemFontConfig.Config readConfig(File file) throws Exception { 1597 PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config(); 1598 try (InputStream is = new FileInputStream(file)) { 1599 PersistentSystemFontConfig.loadFromXml(is, config); 1600 } 1601 return config; 1602 } 1603 writeConfig(PersistentSystemFontConfig.Config config, File file)1604 private static void writeConfig(PersistentSystemFontConfig.Config config, 1605 File file) throws IOException { 1606 try (FileOutputStream fos = new FileOutputStream(file)) { 1607 PersistentSystemFontConfig.writeToXml(fos, config); 1608 } 1609 } 1610 1611 // Returns the last family with the given name, which will be used for creating Typeface. getLastFamily(FontConfig fontConfig, String familyName)1612 private static FontConfig.FontFamily getLastFamily(FontConfig fontConfig, String familyName) { 1613 List<FontConfig.NamedFamilyList> namedFamilyLists = fontConfig.getNamedFamilyLists(); 1614 for (int i = namedFamilyLists.size() - 1; i >= 0; i--) { 1615 if (familyName.equals(namedFamilyLists.get(i).getName())) { 1616 return namedFamilyLists.get(i).getFamilies().get(0); 1617 } 1618 } 1619 return null; 1620 } 1621 assertNamedFamilyExists(FontConfig fontConfig, String familyName)1622 private static void assertNamedFamilyExists(FontConfig fontConfig, String familyName) { 1623 assertThat(fontConfig.getNamedFamilyLists().stream() 1624 .map(FontConfig.NamedFamilyList::getName) 1625 .collect(Collectors.toSet())).contains(familyName); 1626 } 1627 } 1628