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.internal.content.res; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertNotNull; 22 import static org.junit.Assert.assertNull; 23 import static org.junit.Assert.assertTrue; 24 25 import android.content.pm.PackagePartitions; 26 import android.os.FileUtils; 27 import android.os.SystemProperties; 28 import android.platform.test.annotations.Presubmit; 29 30 import androidx.test.InstrumentationRegistry; 31 import androidx.test.runner.AndroidJUnit4; 32 33 import com.android.frameworks.coretests.R; 34 import com.android.internal.content.om.OverlayConfig; 35 import com.android.internal.content.om.OverlayConfig.IdmapInvocation; 36 import com.android.internal.content.om.OverlayConfigParser.OverlayPartition; 37 import com.android.internal.content.om.OverlayScanner; 38 39 import org.junit.Rule; 40 import org.junit.Test; 41 import org.junit.rules.ExpectedException; 42 import org.junit.rules.RuleChain; 43 import org.junit.rules.TemporaryFolder; 44 import org.junit.runner.RunWith; 45 46 import java.io.File; 47 import java.io.FileOutputStream; 48 import java.io.IOException; 49 import java.io.InputStream; 50 import java.util.ArrayList; 51 import java.util.HashMap; 52 import java.util.List; 53 import java.util.Map; 54 55 @Presubmit 56 @RunWith(AndroidJUnit4.class) 57 public class OverlayConfigTest { 58 private static final String TEST_APK_PACKAGE_NAME = 59 "com.android.frameworks.coretests.overlay_config"; 60 61 private ExpectedException mExpectedException = ExpectedException.none(); 62 private OverlayConfigIterationRule mScannerRule = new OverlayConfigIterationRule(); 63 private TemporaryFolder mTestFolder = new TemporaryFolder(); 64 65 @Rule 66 public RuleChain chain = RuleChain.outerRule(mExpectedException) 67 .around(mTestFolder).around(mScannerRule); 68 createConfigImpl()69 private OverlayConfig createConfigImpl() throws IOException { 70 return new OverlayConfig(mTestFolder.getRoot().getCanonicalFile(), 71 mScannerRule.getScannerFactory(), mScannerRule.getPackageProvider()); 72 } 73 createFile(String fileName)74 private File createFile(String fileName) throws IOException { 75 return createFile(fileName, ""); 76 } 77 createFile(String fileName, String content)78 private File createFile(String fileName, String content) throws IOException { 79 final File f = new File(String.format("%s/%s", mTestFolder.getRoot(), fileName)); 80 if (!f.getParentFile().equals(mTestFolder.getRoot())) { 81 f.getParentFile().mkdirs(); 82 } 83 FileUtils.stringToFile(f.getPath(), content); 84 return f; 85 } 86 assertConfig(OverlayConfig overlayConfig, String packageName, boolean mutable, boolean enabled, int configIndex)87 private static void assertConfig(OverlayConfig overlayConfig, String packageName, 88 boolean mutable, boolean enabled, int configIndex) { 89 final OverlayConfig.Configuration config = overlayConfig.getConfiguration(packageName); 90 assertNotNull(config); 91 assertEquals(mutable, config.parsedConfig.mutable); 92 assertEquals(enabled, config.parsedConfig.enabled); 93 assertEquals(configIndex, config.configIndex); 94 } 95 generatePartitionOrderString(List<OverlayPartition> partitions)96 private String generatePartitionOrderString(List<OverlayPartition> partitions) { 97 StringBuilder partitionOrder = new StringBuilder(); 98 for (int i = 0; i < partitions.size(); i++) { 99 partitionOrder.append(partitions.get(i).getName()); 100 if (i < partitions.size() - 1) { 101 partitionOrder.append(", "); 102 } 103 } 104 return partitionOrder.toString(); 105 } 106 107 // configIndex should come from real time partition order cause partitions could get 108 // reordered by /product/overlay/partition_order.xml createConfigIndexes(OverlayConfig overlayConfig, String... configPartitions)109 private Map<String, Integer> createConfigIndexes(OverlayConfig overlayConfig, 110 String... configPartitions) { 111 Map<String, Integer> configIndexes = new HashMap<>(); 112 for (int i = 0; i < configPartitions.length; i++) { 113 configIndexes.put(configPartitions[i], -1); 114 } 115 116 String[] partitions = overlayConfig.getPartitionOrder().split(", "); 117 int index = 0; 118 for (int i = 0; i < partitions.length; i++) { 119 if (configIndexes.containsKey(partitions[i])) { 120 configIndexes.put(partitions[i], index++); 121 } 122 } 123 return configIndexes; 124 } 125 126 @Test testImmutableAfterNonImmutableFails()127 public void testImmutableAfterNonImmutableFails() throws IOException { 128 mExpectedException.expect(IllegalStateException.class); 129 mExpectedException.expectMessage("immutable overlays must precede mutable overlays"); 130 131 createFile("/product/overlay/config/config.xml", 132 "<config>" 133 + " <overlay package=\"one\" enabled=\"true\" />" 134 + " <overlay package=\"two\" mutable=\"false\" enabled=\"true\" />" 135 + "</config>"); 136 137 mScannerRule.addOverlay(createFile("/product/overlay/one.apk"), "one"); 138 mScannerRule.addOverlay(createFile("/product/overlay/two.apk"), "two"); 139 createConfigImpl(); 140 } 141 142 @Test testConfigureAbsentPackageFails()143 public void testConfigureAbsentPackageFails() throws IOException { 144 mExpectedException.expect(IllegalStateException.class); 145 mExpectedException.expectMessage("not present in partition"); 146 147 createFile("/product/overlay/config/config.xml", 148 "<config>" 149 + " <overlay package=\"one\" enabled=\"true\" />" 150 + "</config>"); 151 152 createConfigImpl(); 153 } 154 155 @Test testConfigurePackageTwiceFails()156 public void testConfigurePackageTwiceFails() throws IOException { 157 mExpectedException.expect(IllegalStateException.class); 158 mExpectedException.expectMessage("configured multiple times in a single partition"); 159 160 createFile("/product/overlay/config/config.xml", 161 "<config>" 162 + " <overlay package=\"one\" enabled=\"true\" />" 163 + " <overlay package=\"one\" mutable=\"false\" />" 164 + "</config>"); 165 166 mScannerRule.addOverlay(createFile("/product/overlay/one.apk"), "one"); 167 createConfigImpl(); 168 } 169 170 @Test testConfigureOverlayAcrossPartitionsFails()171 public void testConfigureOverlayAcrossPartitionsFails() throws IOException { 172 mExpectedException.expect(IllegalStateException.class); 173 mExpectedException.expectMessage("not present in partition"); 174 175 createFile("/vendor/overlay/config/config.xml", 176 "<config>" 177 + " <overlay package=\"one\" enabled=\"true\" />" 178 + "</config>"); 179 180 mScannerRule.addOverlay(createFile("/product/overlay/one.apk"), "one"); 181 createConfigImpl(); 182 } 183 184 @Test testConfigureOverlayOutsideOverlayDirFails()185 public void testConfigureOverlayOutsideOverlayDirFails() throws IOException { 186 mExpectedException.expect(IllegalStateException.class); 187 mExpectedException.expectMessage("not present in partition"); 188 189 createFile("/vendor/overlay/config/config.xml", 190 "<config>" 191 + " <overlay package=\"one\" enabled=\"true\" />" 192 + "</config>"); 193 194 mScannerRule.addOverlay(createFile("/product/app/one.apk"), "one"); 195 createConfigImpl(); 196 } 197 198 @Test testMergeOAbsolutePathFails()199 public void testMergeOAbsolutePathFails() throws IOException { 200 mExpectedException.expect(IllegalStateException.class); 201 mExpectedException.expectMessage("must be relative to the directory"); 202 203 createFile("/product/overlay/config/config.xml", 204 "<config>" 205 + " <merge path=\"/product/overlay/config/auto-generated-config.xml\" />" 206 + "</config>"); 207 208 createConfigImpl(); 209 } 210 211 @Test testMergeOutsideDirFails()212 public void testMergeOutsideDirFails() throws IOException { 213 mExpectedException.expect(IllegalStateException.class); 214 mExpectedException.expectMessage("outside of configuration directory"); 215 216 createFile("/product/overlay/auto-generated-config.xml"); 217 createFile("/product/overlay/config/config.xml", 218 "<config>" 219 + " <merge path=\"../auto-generated-config.xml\" />" 220 + "</config>"); 221 222 createConfigImpl(); 223 } 224 225 @Test testMergeOutsidePartitionFails()226 public void testMergeOutsidePartitionFails() throws IOException { 227 mExpectedException.expect(IllegalStateException.class); 228 mExpectedException.expectMessage("outside of configuration directory"); 229 230 createFile("/vendor/overlay/config/config2.xml"); 231 createFile("/product/overlay/config/config.xml", 232 "<config>" 233 + " <merge path=\"../../../vendor/overlay/config/config2.xml\" />" 234 + "</config>"); 235 236 createConfigImpl(); 237 } 238 239 @Test testMergeCircularFails()240 public void testMergeCircularFails() throws IOException { 241 mExpectedException.expect(IllegalStateException.class); 242 mExpectedException.expectMessage("Maximum <merge> depth exceeded"); 243 244 createFile("/product/overlay/config/config.xml", 245 "<config>" 246 + " <merge path=\"config2.xml\" />" 247 + "</config>"); 248 createFile("/product/overlay/config/config2.xml", 249 "<config>" 250 + " <merge path=\"config.xml\" />" 251 + "</config>"); 252 253 createConfigImpl(); 254 } 255 256 @Test testMergeMissingFileFails()257 public void testMergeMissingFileFails() throws IOException { 258 mExpectedException.expect(IllegalStateException.class); 259 mExpectedException.expectMessage("does not exist"); 260 261 createFile("/product/overlay/config/config.xml", 262 "<config>" 263 + " <merge path=\"config2.xml\" />" 264 + "</config>"); 265 createConfigImpl(); 266 } 267 268 @Test testProductOverridesVendor()269 public void testProductOverridesVendor() throws IOException { 270 createFile("/vendor/overlay/config/config.xml", 271 "<config>" 272 + " <overlay package=\"one\" enabled=\"false\" />" 273 + "</config>"); 274 createFile("/product/overlay/config/config.xml", 275 "<config>" 276 + " <overlay package=\"one\" enabled=\"true\" />" 277 + "</config>"); 278 279 mScannerRule.addOverlay(createFile("/vendor/overlay/one.apk"), "one"); 280 mScannerRule.addOverlay(createFile("/product/overlay/one.apk"), "one"); 281 282 final OverlayConfig overlayConfig = createConfigImpl(); 283 assertConfig(overlayConfig, "one", true, true, 1); 284 } 285 286 @Test testPartitionPrecedence()287 public void testPartitionPrecedence() throws IOException { 288 createFile("/vendor/overlay/config/config.xml", 289 "<config>" 290 + " <overlay package=\"one\" enabled=\"true\" />" 291 + "</config>"); 292 createFile("/odm/overlay/config/config.xml", 293 "<config>" 294 + " <overlay package=\"two\" enabled=\"true\" />" 295 + "</config>"); 296 createFile("/oem/overlay/config/config.xml", 297 "<config>" 298 + " <overlay package=\"three\" enabled=\"true\" />" 299 + "</config>"); 300 createFile("/product/overlay/config/config.xml", 301 "<config>" 302 + " <overlay package=\"four\" enabled=\"true\" />" 303 + "</config>"); 304 createFile("/system_ext/overlay/config/config.xml", 305 "<config>" 306 + " <overlay package=\"five\" enabled=\"true\" />" 307 + "</config>"); 308 309 mScannerRule.addOverlay(createFile("/vendor/overlay/one.apk"), "one"); 310 mScannerRule.addOverlay(createFile("/odm/overlay/two.apk"), "two"); 311 mScannerRule.addOverlay(createFile("/oem/overlay/three.apk"), "three"); 312 mScannerRule.addOverlay(createFile("/product/overlay/four.apk"), "four"); 313 mScannerRule.addOverlay(createFile("/system_ext/overlay/five.apk"), "five"); 314 315 final OverlayConfig overlayConfig = createConfigImpl(); 316 Map<String, Integer> configIndexes = createConfigIndexes(overlayConfig, 317 "vendor", "odm", "oem", "product", "system_ext"); 318 assertConfig(overlayConfig, "one", true, true, configIndexes.get("vendor")); 319 assertConfig(overlayConfig, "two", true, true, configIndexes.get("odm")); 320 assertConfig(overlayConfig, "three", true, true, configIndexes.get("oem")); 321 assertConfig(overlayConfig, "four", true, true, configIndexes.get("product")); 322 assertConfig(overlayConfig, "five", true, true, configIndexes.get("system_ext")); 323 } 324 325 @Test testPartialConfigPartitionPrecedence()326 public void testPartialConfigPartitionPrecedence() throws IOException { 327 createFile("/odm/overlay/config/config.xml", 328 "<config>" 329 + " <overlay package=\"two\" enabled=\"true\" />" 330 + "</config>"); 331 332 mScannerRule.addOverlay(createFile("/vendor/overlay/one.apk"), "one", "android", 0, true, 333 1); 334 mScannerRule.addOverlay(createFile("/odm/overlay/two.apk"), "two"); 335 mScannerRule.addOverlay(createFile("/product/overlay/three.apk"), "three", "android", 0, 336 true, 0); 337 338 final OverlayConfig overlayConfig = createConfigImpl(); 339 Map<String, Integer> configIndexes = createConfigIndexes(overlayConfig, 340 "vendor", "odm", "product"); 341 assertConfig(overlayConfig, "one", false, true, configIndexes.get("vendor")); 342 assertConfig(overlayConfig, "two", true, true, configIndexes.get("odm")); 343 assertConfig(overlayConfig, "three", false, true, configIndexes.get("product")); 344 } 345 346 @Test testNoConfigPartitionPrecedence()347 public void testNoConfigPartitionPrecedence() throws IOException { 348 mScannerRule.addOverlay(createFile("/vendor/overlay/one.apk"), "one", "android", 0, true, 349 1); 350 mScannerRule.addOverlay(createFile("/odm/overlay/two.apk"), "two", "android", 0, true, 2); 351 mScannerRule.addOverlay(createFile("/product/overlay/three.apk"), "three", "android", 0, 352 true, 0); 353 354 final OverlayConfig overlayConfig = createConfigImpl(); 355 Map<String, Integer> configIndexes = createConfigIndexes(overlayConfig, 356 "vendor", "odm", "product"); 357 assertConfig(overlayConfig, "one", false, true, configIndexes.get("vendor")); 358 assertConfig(overlayConfig, "two", false, true, configIndexes.get("odm")); 359 assertConfig(overlayConfig, "three", false, true, configIndexes.get("product")); 360 } 361 362 @Test testImmutable()363 public void testImmutable() throws IOException { 364 createFile("/product/overlay/config/config.xml", 365 "<config>" 366 + " <overlay package=\"one\" mutable=\"false\" />" 367 + " <overlay package=\"two\" />" 368 + " <overlay package=\"three\" mutable=\"true\" />" 369 + "</config>"); 370 371 372 mScannerRule.addOverlay(createFile("/product/overlay/one.apk"), "one"); 373 mScannerRule.addOverlay(createFile("/product/overlay/two.apk"), "two"); 374 mScannerRule.addOverlay(createFile("/product/overlay/three.apk"), "three"); 375 376 final OverlayConfig overlayConfig = createConfigImpl(); 377 assertConfig(overlayConfig, "one", false, false, 0); 378 assertConfig(overlayConfig, "two", true, false, 1); 379 assertConfig(overlayConfig, "three", true, false, 2); 380 } 381 382 @Test testEnabled()383 public void testEnabled() throws IOException { 384 createFile("/product/overlay/config/config.xml", 385 "<config>" 386 + " <overlay package=\"one\" />" 387 + " <overlay package=\"two\" enabled=\"true\" />" 388 + " <overlay package=\"three\" enabled=\"false\" />" 389 + "</config>"); 390 391 392 mScannerRule.addOverlay(createFile("/product/overlay/one.apk"), "one"); 393 mScannerRule.addOverlay(createFile("/product/overlay/two.apk"), "two"); 394 mScannerRule.addOverlay(createFile("/product/overlay/three.apk"), "three"); 395 396 final OverlayConfig overlayConfig = createConfigImpl(); 397 assertConfig(overlayConfig, "one", true, false, 0); 398 assertConfig(overlayConfig, "two", true, true, 1); 399 assertConfig(overlayConfig, "three", true, false, 2); 400 } 401 402 @Test testMerge()403 public void testMerge() throws IOException { 404 createFile("/product/overlay/config/auto-generated-config.xml", 405 "<config>" 406 + " <overlay package=\"two\" mutable=\"false\" enabled=\"true\" />" 407 + " <overlay package=\"three\" mutable=\"false\" enabled=\"true\" />" 408 + "</config>"); 409 410 createFile("/product/overlay/config/config.xml", 411 "<config>" 412 + " <overlay package=\"one\" mutable=\"false\" enabled=\"true\" />" 413 + " <merge path=\"auto-generated-config.xml\" />" 414 + " <overlay package=\"four\" enabled=\"true\" />" 415 + "</config>"); 416 417 mScannerRule.addOverlay(createFile("/product/overlay/one.apk"), "one"); 418 mScannerRule.addOverlay(createFile("/product/overlay/two.apk"), "two"); 419 mScannerRule.addOverlay(createFile("/product/overlay/three.apk"), "three"); 420 mScannerRule.addOverlay(createFile("/product/overlay/four.apk"), "four"); 421 422 final OverlayConfig overlayConfig = createConfigImpl(); 423 OverlayConfig.Configuration o1 = overlayConfig.getConfiguration("one"); 424 assertNotNull(o1); 425 assertFalse(o1.parsedConfig.mutable); 426 assertTrue(o1.parsedConfig.enabled); 427 assertEquals(0, o1.configIndex); 428 429 OverlayConfig.Configuration o2 = overlayConfig.getConfiguration("two"); 430 assertNotNull(o2); 431 assertFalse(o2.parsedConfig.mutable); 432 assertTrue(o2.parsedConfig.enabled); 433 assertEquals(1, o2.configIndex); 434 435 OverlayConfig.Configuration o3 = overlayConfig.getConfiguration("three"); 436 assertNotNull(o3); 437 assertFalse(o3.parsedConfig.mutable); 438 assertTrue(o3.parsedConfig.enabled); 439 assertEquals(2, o3.configIndex); 440 441 OverlayConfig.Configuration o4 = overlayConfig.getConfiguration("four"); 442 assertNotNull(o4); 443 assertTrue(o4.parsedConfig.mutable); 444 assertTrue(o4.parsedConfig.enabled); 445 assertEquals(3, o4.configIndex); 446 } 447 448 @Test testIdmapInvocationsFrameworkImmutable()449 public void testIdmapInvocationsFrameworkImmutable() throws IOException { 450 createFile("/vendor/overlay/config/config.xml", 451 "<config>" 452 + " <overlay package=\"one\" mutable=\"false\" enabled=\"true\" />" 453 + " <overlay package=\"two\" mutable=\"false\" enabled=\"true\" />" 454 + " <overlay package=\"three\" enabled=\"true\" />" 455 + "</config>"); 456 457 createFile("/product/overlay/config/config.xml", 458 "<config>" 459 + " <overlay package=\"four\" mutable=\"false\" enabled=\"true\" />" 460 + " <overlay package=\"five\" mutable=\"false\" enabled=\"true\" />" 461 + " <overlay package=\"six\" mutable=\"false\" enabled=\"false\" />" 462 + "</config>"); 463 464 mScannerRule.addOverlay(createFile("/vendor/overlay/one.apk"), "one", "android"); 465 mScannerRule.addOverlay(createFile("/vendor/overlay/two.apk"), "two", "android"); 466 mScannerRule.addOverlay(createFile("/vendor/overlay/three.apk"), "three", "android"); 467 mScannerRule.addOverlay(createFile("/product/overlay/four.apk"), "four", "android"); 468 mScannerRule.addOverlay(createFile("/product/overlay/five.apk"), "five"); 469 mScannerRule.addOverlay(createFile("/product/overlay/six.apk"), "six", "android"); 470 471 final OverlayConfig overlayConfig = createConfigImpl(); 472 if (mScannerRule.getIteration() == OverlayConfigIterationRule.Iteration.ZYGOTE) { 473 final ArrayList<IdmapInvocation> idmapInvocations = 474 overlayConfig.getImmutableFrameworkOverlayIdmapInvocations(); 475 assertEquals(2, idmapInvocations.size()); 476 477 final IdmapInvocation i0 = idmapInvocations.get(0); 478 assertTrue(i0.enforceOverlayable); 479 assertEquals("vendor", i0.policy); 480 assertEquals(2, i0.overlayPaths.size()); 481 assertTrue(i0.overlayPaths.get(0).endsWith("/vendor/overlay/one.apk")); 482 assertTrue(i0.overlayPaths.get(1).endsWith("/vendor/overlay/two.apk")); 483 484 final IdmapInvocation i1 = idmapInvocations.get(1); 485 assertTrue(i1.enforceOverlayable); 486 assertEquals("product", i1.policy); 487 assertEquals(1, i1.overlayPaths.size()); 488 assertTrue(i1.overlayPaths.get(0).endsWith("/product/overlay/four.apk")); 489 } 490 } 491 492 @Test testIdmapInvocationsDifferentTargetSdk()493 public void testIdmapInvocationsDifferentTargetSdk() throws IOException { 494 createFile("/product/overlay/config/config.xml", 495 "<config>" 496 + " <overlay package=\"one\" mutable=\"false\" enabled=\"true\" />" 497 + " <overlay package=\"two\" mutable=\"false\" enabled=\"true\" />" 498 + " <overlay package=\"three\" mutable=\"false\" enabled=\"true\" />" 499 + " <overlay package=\"four\" mutable=\"false\" enabled=\"true\" />" 500 + "</config>"); 501 502 mScannerRule.addOverlay(createFile("/product/overlay/one.apk"), "one", "android"); 503 mScannerRule.addOverlay(createFile("/product/overlay/two.apk"), "two", "android"); 504 mScannerRule.addOverlay(createFile("/product/overlay/three.apk"), "three", "android", 28); 505 mScannerRule.addOverlay(createFile("/product/overlay/four.apk"), "four", "android"); 506 507 final OverlayConfig overlayConfig = createConfigImpl(); 508 509 if (mScannerRule.getIteration() == OverlayConfigIterationRule.Iteration.ZYGOTE) { 510 final ArrayList<IdmapInvocation> idmapInvocations = 511 overlayConfig.getImmutableFrameworkOverlayIdmapInvocations(); 512 assertEquals(3, idmapInvocations.size()); 513 514 final IdmapInvocation i0 = idmapInvocations.get(0); 515 assertTrue(i0.enforceOverlayable); 516 assertEquals(2, i0.overlayPaths.size()); 517 assertTrue(i0.overlayPaths.get(0).endsWith("/product/overlay/one.apk")); 518 assertTrue(i0.overlayPaths.get(1).endsWith("/product/overlay/two.apk")); 519 520 final IdmapInvocation i1 = idmapInvocations.get(1); 521 assertFalse(i1.enforceOverlayable); 522 assertEquals(1, i1.overlayPaths.size()); 523 assertTrue(i1.overlayPaths.get(0).endsWith("/product/overlay/three.apk")); 524 525 final IdmapInvocation i2 = idmapInvocations.get(2); 526 assertTrue(i2.enforceOverlayable); 527 assertEquals(1, i2.overlayPaths.size()); 528 assertTrue(i2.overlayPaths.get(0).endsWith("/product/overlay/four.apk")); 529 } 530 } 531 532 @Test testNoConfigIsStatic()533 public void testNoConfigIsStatic() throws IOException { 534 mScannerRule.addOverlay(createFile("/product/overlay/one.apk"), "one", "android", 28, true, 535 1); 536 mScannerRule.addOverlay(createFile("/product/overlay/two.apk"), "two", "android", 28, false, 537 0); 538 mScannerRule.addOverlay(createFile("/product/overlay/three.apk"), "three", "android", 28, 539 true, 0); 540 mScannerRule.addOverlay(createFile("/product/overlay/four.apk"), "four", "android", 28, 541 false, 2); 542 543 final OverlayConfig overlayConfig = createConfigImpl(); 544 assertConfig(overlayConfig, "one", false, true, 1); 545 assertConfig(overlayConfig, "three", false, true, 0); 546 547 } 548 549 @Test testVendorStaticPrecedesProductImmutable()550 public void testVendorStaticPrecedesProductImmutable() throws IOException { 551 createFile("/product/overlay/config/config.xml", 552 "<config>" 553 + " <overlay package=\"two\" mutable=\"false\" enabled=\"true\" />" 554 + "</config>"); 555 556 mScannerRule.addOverlay(createFile("/vendor/overlay/one.apk"), "one", "android", 0, true, 557 1); 558 mScannerRule.addOverlay(createFile("/product/overlay/two.apk"), "two", "android", 0, true, 559 0); 560 561 final OverlayConfig overlayConfig = createConfigImpl(); 562 assertConfig(overlayConfig, "one", false, true, 0); 563 assertConfig(overlayConfig, "two", false, true, 1); 564 } 565 566 @Test testVendorImmutablePrecededProductStatic()567 public void testVendorImmutablePrecededProductStatic() throws IOException { 568 createFile("/vendor/overlay/config/config.xml", 569 "<config>" 570 + " <overlay package=\"one\" mutable=\"false\" enabled=\"true\" />" 571 + "</config>"); 572 573 mScannerRule.addOverlay(createFile("/vendor/overlay/one.apk"), "one", "android", 0, true, 574 1); 575 mScannerRule.addOverlay(createFile("/product/overlay/two.apk"), "two", "android", 0, true, 576 0); 577 578 final OverlayConfig overlayConfig = createConfigImpl(); 579 assertConfig(overlayConfig, "one", false, true, 0); 580 assertConfig(overlayConfig, "two", false, true, 1); 581 } 582 583 @Test testStaticOverlayOutsideOverlayDir()584 public void testStaticOverlayOutsideOverlayDir() throws IOException { 585 mScannerRule.addOverlay(createFile("/product/app/one.apk"), "one", "android", 0, true, 0); 586 587 final OverlayConfig overlayConfig = createConfigImpl(); 588 if (mScannerRule.getIteration() == OverlayConfigIterationRule.Iteration.SYSTEM_SERVER) { 589 assertConfig(overlayConfig, "one", false, true, 0); 590 } 591 } 592 593 @Test testSortStaticOverlaysDifferentTargets()594 public void testSortStaticOverlaysDifferentTargets() throws IOException { 595 mScannerRule.addOverlay(createFile("/vendor/overlay/one.apk"), "one", "other", 0, true, 0); 596 mScannerRule.addOverlay(createFile("/vendor/overlay/two.apk"), "two", "android", 0, true, 597 0); 598 599 final OverlayConfig overlayConfig = createConfigImpl(); 600 assertConfig(overlayConfig, "one", false, true, 1); 601 assertConfig(overlayConfig, "two", false, true, 0); 602 } 603 604 @Test testSortStaticOverlaysDifferentPartitions()605 public void testSortStaticOverlaysDifferentPartitions() throws IOException { 606 mScannerRule.addOverlay(createFile("/vendor/overlay/one.apk"), "one", "android", 0, true, 607 2); 608 mScannerRule.addOverlay(createFile("/vendor/overlay/two.apk"), "two", "android", 0, true, 609 3); 610 mScannerRule.addOverlay(createFile("/product/overlay/three.apk"), "three", "android", 0, 611 true, 0); 612 mScannerRule.addOverlay(createFile("/product/overlay/four.apk"), "four", "android", 0, 613 true, 1); 614 615 final OverlayConfig overlayConfig = createConfigImpl(); 616 assertConfig(overlayConfig, "one", false, true, 0); 617 assertConfig(overlayConfig, "two", false, true, 1); 618 assertConfig(overlayConfig, "three", false, true, 2); 619 assertConfig(overlayConfig, "four", false, true, 3); 620 } 621 622 @Test testSortStaticOverlaysSamePriority()623 public void testSortStaticOverlaysSamePriority() throws IOException { 624 mScannerRule.addOverlay(createFile("/vendor/overlay/one.apk"), "one", "android", 0, true, 625 0); 626 mScannerRule.addOverlay(createFile("/vendor/overlay/two.apk"), "two", "android", 0, true, 627 0); 628 629 final OverlayConfig overlayConfig = createConfigImpl(); 630 assertConfig(overlayConfig, "one", false, true, 0); 631 assertConfig(overlayConfig, "two", false, true, 1); 632 } 633 634 @Test testNonSystemOverlayCannotBeStatic()635 public void testNonSystemOverlayCannotBeStatic() throws IOException { 636 mScannerRule.addOverlay(createFile("/data/overlay/one.apk"), "one", "android", 0, true, 637 0); 638 639 final OverlayConfig overlayConfig = createConfigImpl(); 640 assertTrue(overlayConfig.isMutable("one")); 641 assertFalse(overlayConfig.isEnabled("one")); 642 assertEquals(Integer.MAX_VALUE, overlayConfig.getPriority("one")); 643 } 644 645 @Test testGetOverlayInfo()646 public void testGetOverlayInfo() throws IOException { 647 if (mScannerRule.getIteration() != OverlayConfigIterationRule.Iteration.ZYGOTE) { 648 // Run only one iteration of the test. 649 return; 650 } 651 652 final InputStream is = InstrumentationRegistry.getContext().getResources() 653 .openRawResource(R.raw.overlay_config); 654 final File partitionDir = mTestFolder.newFolder("product", "overlay"); 655 final File testApk = new File(partitionDir, "test.apk"); 656 FileUtils.copy(is, new FileOutputStream(testApk)); 657 658 final OverlayScanner scanner = new OverlayScanner(); 659 scanner.scanDir(partitionDir); 660 661 final OverlayScanner.ParsedOverlayInfo info = scanner.getParsedInfo(TEST_APK_PACKAGE_NAME); 662 assertNotNull(info); 663 assertEquals(TEST_APK_PACKAGE_NAME, info.packageName); 664 assertEquals("android", info.targetPackageName); 665 assertEquals(testApk.getPath(), info.path.getPath()); 666 assertEquals(21, info.targetSdkVersion); 667 } 668 669 @Test testOverlayManifest_withRequiredSystemPropertyAndValueNotMatched()670 public void testOverlayManifest_withRequiredSystemPropertyAndValueNotMatched() 671 throws IOException { 672 final String systemPropertyName = "foo.name"; 673 final String systemPropertyValue = "foo.value"; 674 675 createFile("/product/overlay/config/config.xml", 676 "<config>" 677 + " <overlay package=\"one\" />" 678 + " <overlay package=\"two\" />" 679 + " <overlay package=\"three\" />" 680 + "</config>"); 681 682 mScannerRule.addOverlay(createFile("/product/overlay/one.apk"), "one", "android", 0, 683 true, 1, systemPropertyName, systemPropertyValue); 684 mScannerRule.addOverlay(createFile("/product/overlay/two.apk"), "two", "android", 0, 685 true, 1, systemPropertyName, systemPropertyValue); 686 mScannerRule.addOverlay(createFile("/product/overlay/three.apk"), "three"); 687 688 final OverlayConfig overlayConfig = createConfigImpl(); 689 OverlayConfig.Configuration o1 = overlayConfig.getConfiguration("one"); 690 assertNull(o1); 691 692 OverlayConfig.Configuration o2 = overlayConfig.getConfiguration("two"); 693 assertNull(o2); 694 695 OverlayConfig.Configuration o3 = overlayConfig.getConfiguration("three"); 696 assertNotNull(o3); 697 } 698 699 @Test testOverlayManifest_withRequiredSystemPropertyAndValueMatched()700 public void testOverlayManifest_withRequiredSystemPropertyAndValueMatched() 701 throws IOException { 702 final String systemPropertyName = "ro.build.version.sdk"; 703 final String systemPropertyValue = SystemProperties.get(systemPropertyName, null); 704 assertNotNull(systemPropertyValue); 705 706 createFile("/product/overlay/config/config.xml", 707 "<config>" 708 + " <overlay package=\"one\" />" 709 + " <overlay package=\"two\" />" 710 + " <overlay package=\"three\" />" 711 + "</config>"); 712 713 mScannerRule.addOverlay(createFile("/product/overlay/one.apk"), "one", "android", 0, 714 true, 1, systemPropertyName, systemPropertyValue); 715 mScannerRule.addOverlay(createFile("/product/overlay/two.apk"), "two", "android", 0, 716 true, 1, systemPropertyName, systemPropertyValue); 717 mScannerRule.addOverlay(createFile("/product/overlay/three.apk"), "three"); 718 719 final OverlayConfig overlayConfig = createConfigImpl(); 720 OverlayConfig.Configuration o1 = overlayConfig.getConfiguration("one"); 721 assertNotNull(o1); 722 723 OverlayConfig.Configuration o2 = overlayConfig.getConfiguration("two"); 724 assertNotNull(o2); 725 726 OverlayConfig.Configuration o3 = overlayConfig.getConfiguration("three"); 727 assertNotNull(o3); 728 } 729 730 @Test testSortPartitionsWithoutXml()731 public void testSortPartitionsWithoutXml() throws IOException { 732 ArrayList<OverlayPartition> partitions = new ArrayList<>( 733 PackagePartitions.getOrderedPartitions(OverlayPartition::new)); 734 735 final OverlayConfig overlayConfig = createConfigImpl(); 736 String partitionOrderFilePath = String.format("%s/%s", mTestFolder.getRoot(), 737 "/product/overlay/partition_order.xml"); 738 assertEquals(false, overlayConfig.sortPartitions(partitionOrderFilePath, partitions)); 739 assertEquals("system, vendor, odm, oem, product, system_ext", 740 generatePartitionOrderString(partitions)); 741 } 742 743 @Test testSortPartitionsWithInvalidXmlRootElement()744 public void testSortPartitionsWithInvalidXmlRootElement() throws IOException { 745 ArrayList<OverlayPartition> partitions = new ArrayList<>( 746 PackagePartitions.getOrderedPartitions(OverlayPartition::new)); 747 createFile("/product/overlay/partition_order.xml", 748 "<partition-list>\n" 749 + " <partition name=\"system_ext\"/>\n" 750 + " <partition name=\"vendor\"/>\n" 751 + " <partition name=\"oem\"/>\n" 752 + " <partition name=\"odm\"/>\n" 753 + " <partition name=\"product\"/>\n" 754 + " <partition name=\"system\"/>\n" 755 + "</partition-list>\n"); 756 final OverlayConfig overlayConfig = createConfigImpl(); 757 String partitionOrderFilePath = String.format("%s/%s", mTestFolder.getRoot(), 758 "/product/overlay/partition_order.xml"); 759 assertEquals(false, overlayConfig.sortPartitions(partitionOrderFilePath, partitions)); 760 assertEquals("system, vendor, odm, oem, product, system_ext", 761 generatePartitionOrderString(partitions)); 762 } 763 764 @Test testSortPartitionsWithInvalidPartition()765 public void testSortPartitionsWithInvalidPartition() throws IOException { 766 ArrayList<OverlayPartition> partitions = new ArrayList<>( 767 PackagePartitions.getOrderedPartitions(OverlayPartition::new)); 768 createFile("/product/overlay/partition_order.xml", 769 "<partition-order>\n" 770 + " <partition name=\"INVALID\"/>\n" 771 + " <partition name=\"vendor\"/>\n" 772 + " <partition name=\"oem\"/>\n" 773 + " <partition name=\"odm\"/>\n" 774 + " <partition name=\"product\"/>\n" 775 + " <partition name=\"system\"/>\n" 776 + "</partition-order>\n"); 777 final OverlayConfig overlayConfig = createConfigImpl(); 778 String partitionOrderFilePath = String.format("%s/%s", mTestFolder.getRoot(), 779 "/product/overlay/partition_order.xml"); 780 assertEquals(false, overlayConfig.sortPartitions(partitionOrderFilePath, partitions)); 781 assertEquals("system, vendor, odm, oem, product, system_ext", 782 generatePartitionOrderString(partitions)); 783 } 784 785 @Test testSortPartitionsWithDuplicatePartition()786 public void testSortPartitionsWithDuplicatePartition() throws IOException { 787 ArrayList<OverlayPartition> partitions = new ArrayList<>( 788 PackagePartitions.getOrderedPartitions(OverlayPartition::new)); 789 createFile("/product/overlay/partition_order.xml", 790 "<partition-order>\n" 791 + " <partition name=\"system_ext\"/>\n" 792 + " <partition name=\"system\"/>\n" 793 + " <partition name=\"vendor\"/>\n" 794 + " <partition name=\"oem\"/>\n" 795 + " <partition name=\"odm\"/>\n" 796 + " <partition name=\"product\"/>\n" 797 + " <partition name=\"system\"/>\n" 798 + "</partition-order>\n"); 799 final OverlayConfig overlayConfig = createConfigImpl(); 800 String partitionOrderFilePath = String.format("%s/%s", mTestFolder.getRoot(), 801 "/product/overlay/partition_order.xml"); 802 assertEquals(false, overlayConfig.sortPartitions(partitionOrderFilePath, partitions)); 803 assertEquals("system, vendor, odm, oem, product, system_ext", 804 generatePartitionOrderString(partitions)); 805 } 806 807 @Test testSortPartitionsWithMissingPartition()808 public void testSortPartitionsWithMissingPartition() throws IOException { 809 ArrayList<OverlayPartition> partitions = new ArrayList<>( 810 PackagePartitions.getOrderedPartitions(OverlayPartition::new)); 811 createFile("/product/overlay/partition_order.xml", 812 "<partition-order>\n" 813 + " <partition name=\"vendor\"/>\n" 814 + " <partition name=\"oem\"/>\n" 815 + " <partition name=\"odm\"/>\n" 816 + " <partition name=\"product\"/>\n" 817 + " <partition name=\"system\"/>\n" 818 + "</partition-order>\n"); 819 final OverlayConfig overlayConfig = createConfigImpl(); 820 String partitionOrderFilePath = String.format("%s/%s", mTestFolder.getRoot(), 821 "/product/overlay/partition_order.xml"); 822 assertEquals(false, overlayConfig.sortPartitions(partitionOrderFilePath, partitions)); 823 assertEquals("system, vendor, odm, oem, product, system_ext", 824 generatePartitionOrderString(partitions)); 825 } 826 827 @Test testSortPartitionsWithCorrectPartitionOrderXml()828 public void testSortPartitionsWithCorrectPartitionOrderXml() throws IOException { 829 ArrayList<OverlayPartition> partitions = new ArrayList<>( 830 PackagePartitions.getOrderedPartitions(OverlayPartition::new)); 831 createFile("/product/overlay/partition_order.xml", 832 "<partition-order>\n" 833 + " <partition name=\"system_ext\"/>\n" 834 + " <partition name=\"vendor\"/>\n" 835 + " <partition name=\"oem\"/>\n" 836 + " <partition name=\"odm\"/>\n" 837 + " <partition name=\"product\"/>\n" 838 + " <partition name=\"system\"/>\n" 839 + "</partition-order>\n"); 840 final OverlayConfig overlayConfig = createConfigImpl(); 841 String partitionOrderFilePath = String.format("%s/%s", mTestFolder.getRoot(), 842 "/product/overlay/partition_order.xml"); 843 assertEquals(true, overlayConfig.sortPartitions(partitionOrderFilePath, partitions)); 844 assertEquals("system_ext, vendor, oem, odm, product, system", 845 generatePartitionOrderString(partitions)); 846 } 847 } 848