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