• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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