1 /*
2  * Copyright (C) 2019 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.systemconfig;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.junit.Assert.assertEquals;
22 import static org.testng.Assert.expectThrows;
23 
24 import android.os.Build;
25 import android.platform.test.annotations.Presubmit;
26 import android.util.ArrayMap;
27 import android.util.ArraySet;
28 import android.util.Log;
29 import android.util.Xml;
30 
31 import androidx.test.filters.SmallTest;
32 import androidx.test.runner.AndroidJUnit4;
33 
34 import com.android.server.SystemConfig;
35 
36 import org.junit.Before;
37 import org.junit.Rule;
38 import org.junit.Test;
39 import org.junit.rules.TemporaryFolder;
40 import org.junit.runner.RunWith;
41 import org.xmlpull.v1.XmlPullParser;
42 import org.xmlpull.v1.XmlPullParserException;
43 
44 import java.io.BufferedWriter;
45 import java.io.File;
46 import java.io.FileReader;
47 import java.io.FileWriter;
48 import java.io.IOException;
49 import java.util.Arrays;
50 import java.util.Map;
51 import java.util.Scanner;
52 import java.util.Set;
53 
54 /**
55  * Tests for {@link SystemConfig}.
56  *
57  * Build/Install/Run:
58  *  atest FrameworksServicesTests:SystemConfigTest
59  */
60 @SmallTest
61 @Presubmit
62 @RunWith(AndroidJUnit4.class)
63 public class SystemConfigTest {
64     private static final String LOG_TAG = "SystemConfigTest";
65 
66     private SystemConfig mSysConfig;
67     private File mFooJar;
68 
69     @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
70 
71     @Before
setUp()72     public void setUp() throws Exception {
73         mSysConfig = new SystemConfigTestClass();
74         mFooJar = createTempFile(
75                 mTemporaryFolder.getRoot().getCanonicalFile(), "foo.jar", "JAR");
76     }
77 
78     /**
79      * Subclass of SystemConfig without running the constructor.
80      */
81     private class SystemConfigTestClass extends SystemConfig {
SystemConfigTestClass()82         SystemConfigTestClass() {
83             super(false);
84         }
85     }
86 
readPermissions(File libraryDir, int permissionFlag)87     private void readPermissions(File libraryDir, int permissionFlag) {
88         final XmlPullParser parser = Xml.newPullParser();
89         mSysConfig.readPermissions(parser, libraryDir, permissionFlag);
90     }
91 
92     /**
93      * Tests that readPermissions works correctly for the tag: install-in-user-type
94      */
95     @Test
testInstallInUserType()96     public void testInstallInUserType() throws Exception {
97         final String contents1 =
98                   "<permissions>\n"
99                 + "    <install-in-user-type package=\"com.android.package1\">\n"
100                 + "        <install-in user-type=\"FULL\" />\n"
101                 + "        <install-in user-type=\"PROFILE\" />\n"
102                 + "    </install-in-user-type>\n"
103                 + "    <install-in-user-type package=\"com.android.package2\">\n"
104                 + "        <install-in user-type=\"FULL\" />\n"
105                 + "        <install-in user-type=\"PROFILE\" />\n"
106                 + "        <do-not-install-in user-type=\"GUEST\" />\n"
107                 + "    </install-in-user-type>\n"
108                 + "</permissions>";
109 
110         final String contents2 =
111                   "<permissions>\n"
112                 + "    <install-in-user-type package=\"com.android.package2\">\n"
113                 + "        <install-in user-type=\"SYSTEM\" />\n"
114                 + "        <do-not-install-in user-type=\"PROFILE\" />\n"
115                 + "    </install-in-user-type>\n"
116                 + "</permissions>";
117 
118         final String contents3 =
119                   "<permissions>\n"
120                 + "    <install-in-user-type package=\"com.android.package2\">\n"
121                 + "        <install-in invalid-attribute=\"ADMIN\" />\n" // Ignore invalid attribute
122                 + "    </install-in-user-type>\n"
123                 + "    <install-in-user-type package=\"com.android.package2\">\n"
124                 + "        <install-in user-type=\"RESTRICTED\" />\n"  // Valid
125                 + "    </install-in-user-type>\n"
126                 + "    <install-in-user-type>\n" // Ignored since missing package name
127                 + "        <install-in user-type=\"ADMIN\" />\n"
128                 + "    </install-in-user-type>\n"
129                 + "</permissions>";
130 
131         Map<String, Set<String>> expectedWhite = new ArrayMap<>();
132         expectedWhite.put("com.android.package1",
133                 new ArraySet<>(Arrays.asList("FULL", "PROFILE")));
134         expectedWhite.put("com.android.package2",
135                 new ArraySet<>(Arrays.asList("FULL", "PROFILE", "RESTRICTED", "SYSTEM")));
136 
137         Map<String, Set<String>> expectedBlack = new ArrayMap<>();
138         expectedBlack.put("com.android.package2",
139                 new ArraySet<>(Arrays.asList("GUEST", "PROFILE")));
140 
141         final File folder1 = createTempSubfolder("folder1");
142         createTempFile(folder1, "permissionFile1.xml", contents1);
143 
144         final File folder2 = createTempSubfolder("folder2");
145         createTempFile(folder2, "permissionFile2.xml", contents2);
146 
147         // Also, make a third file, but with the name folder1/permissionFile2.xml, to prove no
148         // conflicts.
149         createTempFile(folder1, "permissionFile2.xml", contents3);
150 
151         readPermissions(folder1, /* No permission needed anyway */ 0);
152         readPermissions(folder2, /* No permission needed anyway */ 0);
153 
154         Map<String, Set<String>> actualWhite = mSysConfig.getAndClearPackageToUserTypeWhitelist();
155         Map<String, Set<String>> actualBlack = mSysConfig.getAndClearPackageToUserTypeBlacklist();
156 
157         assertEquals("Whitelist was not cleared", 0,
158                 mSysConfig.getAndClearPackageToUserTypeWhitelist().size());
159         assertEquals("Blacklist was not cleared", 0,
160                 mSysConfig.getAndClearPackageToUserTypeBlacklist().size());
161 
162         assertEquals("Incorrect whitelist.", expectedWhite, actualWhite);
163         assertEquals("Incorrect blacklist", expectedBlack, actualBlack);
164     }
165 
166     @Test
testComponentOverride()167     public void testComponentOverride() throws Exception {
168         final String contents =
169                 "<permissions>"
170                 + "    <component-override package=\"com.android.package1\">\n"
171                 + "        <component class=\"com.android.package1.Full\" enabled=\"true\"/>"
172                 + "        <component class=\".Relative\" enabled=\"false\" />\n"
173                 + "    </component-override>"
174                 + "    <component-override package=\"com.android.package2\" >\n"
175                 + "        <component class=\"com.android.package3.Relative2\" enabled=\"yes\" />\n"
176                 + "    </component-override>\n"
177                 + "</permissions>";
178 
179         final File folder = createTempSubfolder("folder");
180         createTempFile(folder, "component-override.xml", contents);
181 
182         readPermissions(folder, /* No permission needed anyway */ 0);
183 
184         final ArrayMap<String, Boolean> packageOneExpected = new ArrayMap<>();
185         packageOneExpected.put("com.android.package1.Full", true);
186         packageOneExpected.put("com.android.package1.Relative", false);
187 
188         final ArrayMap<String, Boolean> packageTwoExpected = new ArrayMap<>();
189         packageTwoExpected.put("com.android.package3.Relative2", true);
190 
191         final Map<String, Boolean> packageOne = mSysConfig.getComponentsEnabledStates(
192                 "com.android.package1");
193         assertEquals(packageOneExpected, packageOne);
194 
195         final Map<String, Boolean> packageTwo = mSysConfig.getComponentsEnabledStates(
196                 "com.android.package2");
197         assertEquals(packageTwoExpected, packageTwo);
198     }
199 
200     /**
201      * Tests that readPermissions works correctly with {@link SystemConfig#ALLOW_APP_CONFIGS}
202      * permission flag for the tag: allowlisted-staged-installer.
203      */
204     @Test
readPermissions_allowAppConfigs_parsesStagedInstallerWhitelist()205     public void readPermissions_allowAppConfigs_parsesStagedInstallerWhitelist()
206             throws IOException {
207         final String contents =
208                 "<config>\n"
209                 + "    <whitelisted-staged-installer package=\"com.android.package1\" />\n"
210                 + "</config>";
211         final File folder = createTempSubfolder("folder");
212         createTempFile(folder, "staged-installer-whitelist.xml", contents);
213 
214         readPermissions(folder, /* Grant all permission flags */ ~0);
215 
216         assertThat(mSysConfig.getWhitelistedStagedInstallers())
217                 .containsExactly("com.android.package1");
218         assertThat(mSysConfig.getModulesInstallerPackageName()).isNull();
219     }
220 
221     @Test
readPermissions_parsesStagedInstallerWhitelist_modulesInstaller()222     public void readPermissions_parsesStagedInstallerWhitelist_modulesInstaller()
223             throws IOException {
224         final String contents =
225                 "<config>\n"
226                 + "    <whitelisted-staged-installer package=\"com.android.package1\" "
227                 + "         isModulesInstaller=\"true\" />\n"
228                 + "</config>";
229         final File folder = createTempSubfolder("folder");
230         createTempFile(folder, "staged-installer-whitelist.xml", contents);
231 
232         readPermissions(folder, /* Grant all permission flags */ ~0);
233 
234         assertThat(mSysConfig.getWhitelistedStagedInstallers())
235                 .containsExactly("com.android.package1");
236         assertThat(mSysConfig.getModulesInstallerPackageName())
237                 .isEqualTo("com.android.package1");
238     }
239 
240     @Test
readPermissions_parsesStagedInstallerWhitelist_multipleModulesInstallers()241     public void readPermissions_parsesStagedInstallerWhitelist_multipleModulesInstallers()
242             throws IOException {
243         final String contents =
244                 "<config>\n"
245                 + "    <whitelisted-staged-installer package=\"com.android.package1\" "
246                 + "         isModulesInstaller=\"true\" />\n"
247                 + "    <whitelisted-staged-installer package=\"com.android.package2\" "
248                 + "         isModulesInstaller=\"true\" />\n"
249                 + "</config>";
250         final File folder = createTempSubfolder("folder");
251         createTempFile(folder, "staged-installer-whitelist.xml", contents);
252 
253         IllegalStateException e = expectThrows(
254                 IllegalStateException.class,
255                 () -> readPermissions(folder, /* Grant all permission flags */ ~0));
256 
257         assertThat(e).hasMessageThat().contains("Multiple modules installers");
258     }
259 
260     /**
261      * Tests that readPermissions works correctly without {@link SystemConfig#ALLOW_APP_CONFIGS}
262      * permission flag for the tag: allowlisted-staged-installer.
263      */
264     @Test
readPermissions_notAllowAppConfigs_wontParseStagedInstallerWhitelist()265     public void readPermissions_notAllowAppConfigs_wontParseStagedInstallerWhitelist()
266             throws IOException {
267         final String contents =
268                 "<config>\n"
269                 + "    <whitelisted-staged-installer package=\"com.android.package1\" />\n"
270                 + "</config>";
271         final File folder = createTempSubfolder("folder");
272         createTempFile(folder, "staged-installer-whitelist.xml", contents);
273 
274         readPermissions(folder, /* Grant all but ALLOW_APP_CONFIGS flag */ ~0x08);
275 
276         assertThat(mSysConfig.getWhitelistedStagedInstallers()).isEmpty();
277     }
278 
279     /**
280      * Tests that readPermissions works correctly with {@link SystemConfig#ALLOW_VENDOR_APEX}
281      * permission flag for the tag: {@code allowed-vendor-apex}.
282      */
283     @Test
readPermissions_allowVendorApex_parsesVendorApexAllowList()284     public void readPermissions_allowVendorApex_parsesVendorApexAllowList()
285             throws IOException {
286         final String contents =
287                 "<config>\n"
288                         + "    <allowed-vendor-apex package=\"com.android.apex1\" "
289                         + "installerPackage=\"com.installer\" />\n"
290                         + "</config>";
291         final File folder = createTempSubfolder("folder");
292         createTempFile(folder, "vendor-apex-allowlist.xml", contents);
293 
294         readPermissions(folder, /* Grant all permission flags */ ~0);
295 
296         assertThat(mSysConfig.getAllowedVendorApexes())
297                 .containsExactly("com.android.apex1", "com.installer");
298     }
299 
300     /**
301      * Tests that readPermissions works correctly with {@link SystemConfig#ALLOW_VENDOR_APEX}
302      * permission flag for the tag: {@code allowed-vendor-apex}.
303      */
304     @Test
readPermissions_allowVendorApex_parsesVendorApexAllowList_noPackage()305     public void readPermissions_allowVendorApex_parsesVendorApexAllowList_noPackage()
306             throws IOException {
307         final String contents =
308                 "<config>\n"
309                         + "    <allowed-vendor-apex/>\n"
310                         + "</config>";
311         final File folder = createTempSubfolder("folder");
312         createTempFile(folder, "vendor-apex-allowlist.xml", contents);
313 
314         readPermissions(folder, /* Grant all permission flags */ ~0);
315 
316         assertThat(mSysConfig.getAllowedVendorApexes()).isEmpty();
317     }
318 
319 
320     /**
321      * Tests that readPermissions works correctly without {@link SystemConfig#ALLOW_VENDOR_APEX}
322      * permission flag for the tag: {@code allowed-oem-apex}.
323      */
324     @Test
readPermissions_notAllowVendorApex_doesNotParseVendorApexAllowList()325     public void readPermissions_notAllowVendorApex_doesNotParseVendorApexAllowList()
326             throws IOException {
327         final String contents =
328                 "<config>\n"
329                         + "    <allowed-vendor-apex package=\"com.android.apex1\" />\n"
330                         + "</config>";
331         final File folder = createTempSubfolder("folder");
332         createTempFile(folder, "vendor-apex-allowlist.xml", contents);
333 
334         readPermissions(folder, /* Grant all but ALLOW_VENDOR_APEX flag */ ~0x400);
335 
336         assertThat(mSysConfig.getAllowedVendorApexes()).isEmpty();
337     }
338 
339     /**
340      * Tests that readPermissions works correctly for the tag: {@code install-constraints-allowed}.
341      */
342     @Test
readPermissions_installConstraints_successful()343     public void readPermissions_installConstraints_successful() throws IOException {
344         final String contents =
345                 "<config>\n"
346                         + "    <install-constraints-allowed package=\"com.android.apex1\" />\n"
347                         + "</config>";
348         final File folder = createTempSubfolder("folder");
349         createTempFile(folder, "install-constraints-allowlist.xml", contents);
350 
351         readPermissions(folder, /* Grant all permission flags */ ~0);
352 
353         assertThat(mSysConfig.getInstallConstraintsAllowlist())
354                 .containsExactly("com.android.apex1");
355     }
356 
357     /**
358      * Tests that readPermissions works correctly for the tag: {@code install-constraints-allowed}.
359      */
360     @Test
readPermissions_installConstraints_noPackage()361     public void readPermissions_installConstraints_noPackage() throws IOException {
362         final String contents =
363                 "<config>\n"
364                         + "    <install-constraints-allowed/>\n"
365                         + "</config>";
366         final File folder = createTempSubfolder("folder");
367         createTempFile(folder, "install-constraints-allowlist.xml", contents);
368 
369         readPermissions(folder, /* Grant all permission flags */ ~0);
370 
371         assertThat(mSysConfig.getInstallConstraintsAllowlist()).isEmpty();
372     }
373 
374     /**
375      * Tests that readPermissions works correctly for the tag {@code install-constraints-allowed}
376      * without {@link SystemConfig#ALLOW_VENDOR_APEX}.
377      */
378     @Test
readPermissions_installConstraints_noAppConfigs()379     public void readPermissions_installConstraints_noAppConfigs() throws IOException {
380         final String contents =
381                 "<config>\n"
382                         + "    <install-constraints-allowed package=\"com.android.apex1\" />\n"
383                         + "</config>";
384         final File folder = createTempSubfolder("folder");
385         createTempFile(folder, "install-constraints-allowlist.xml", contents);
386 
387         readPermissions(folder,  /* Grant all but ALLOW_APP_CONFIGS flag */ ~0x08);
388 
389         assertThat(mSysConfig.getInstallConstraintsAllowlist()).isEmpty();
390     }
391 
392     @Test
readApexPrivAppPermissions_addAllPermissions()393     public void readApexPrivAppPermissions_addAllPermissions()
394             throws Exception {
395         final String contents =
396                 "<privapp-permissions package=\"com.android.apk_in_apex\">"
397                         + "<permission name=\"android.permission.FOO\"/>"
398                         + "<deny-permission name=\"android.permission.BAR\"/>"
399                         + "</privapp-permissions>";
400         File apexDir = createTempSubfolder("apex");
401         File permissionFile = createTempFile(
402                 createTempSubfolder("apex/com.android.my_module/etc/permissions"),
403                     "permissions.xml", contents);
404         XmlPullParser parser = readXmlUntilStartTag(permissionFile);
405 
406         mSysConfig.readApexPrivAppPermissions(parser, permissionFile, apexDir.toPath());
407 
408         ArrayMap<String, Boolean> permissions = mSysConfig.getPermissionAllowlist()
409                 .getApexPrivilegedAppAllowlists().get("com.android.my_module")
410                 .get("com.android.apk_in_apex");
411         assertThat(permissions)
412             .containsExactly("android.permission.FOO", true, "android.permission.BAR", false);
413     }
414 
415     /**
416      * Tests that readPermissions works correctly for a library with on-bootclasspath-before
417      * and on-bootclasspath-since.
418      */
419     @Test
readPermissions_allowLibs_parsesSimpleLibrary()420     public void readPermissions_allowLibs_parsesSimpleLibrary() throws IOException {
421         String contents =
422                 "<permissions>\n"
423                         + "    <library \n"
424                         + "        name=\"foo\"\n"
425                         + "        file=\"" + mFooJar + "\"\n"
426                         + "        on-bootclasspath-before=\"10\"\n"
427                         + "        on-bootclasspath-since=\"20\"\n"
428                         + "     />\n\n"
429                         + " </permissions>";
430         parseSharedLibraries(contents);
431         assertFooIsOnlySharedLibrary();
432         SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get("foo");
433         assertThat(entry.onBootclasspathBefore).isEqualTo("10");
434         assertThat(entry.onBootclasspathSince).isEqualTo("20");
435     }
436 
437     /**
438      * Tests that readPermissions works correctly for a library with on-bootclasspath-before
439      * and on-bootclasspath-since that uses codenames.
440      */
441     @Test
readPermissions_allowLibs_parsesSimpleLibraryWithCodenames()442     public void readPermissions_allowLibs_parsesSimpleLibraryWithCodenames() throws IOException {
443         String contents =
444                 "<permissions>\n"
445                         + "    <library \n"
446                         + "        name=\"foo\"\n"
447                         + "        file=\"" + mFooJar + "\"\n"
448                         + "        on-bootclasspath-before=\"Q\"\n"
449                         + "        on-bootclasspath-since=\"W\"\n"
450                         + "     />\n\n"
451                         + " </permissions>";
452         parseSharedLibraries(contents);
453         assertFooIsOnlySharedLibrary();
454         SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get("foo");
455         assertThat(entry.onBootclasspathBefore).isEqualTo("Q");
456         assertThat(entry.onBootclasspathSince).isEqualTo("W");
457     }
458 
459     /**
460      * Tests that readPermissions works correctly for a library using the new
461      * {@code apex-library} tag.
462      */
463     @Test
readPermissions_allowLibs_parsesUpdatableLibrary()464     public void readPermissions_allowLibs_parsesUpdatableLibrary() throws IOException {
465         String contents =
466                 "<permissions>\n"
467                         + "    <apex-library \n"
468                         + "        name=\"foo\"\n"
469                         + "        file=\"" + mFooJar + "\"\n"
470                         + "        on-bootclasspath-before=\"10\"\n"
471                         + "        on-bootclasspath-since=\"20\"\n"
472                         + "     />\n\n"
473                         + " </permissions>";
474         parseSharedLibraries(contents);
475         assertFooIsOnlySharedLibrary();
476         SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get("foo");
477         assertThat(entry.onBootclasspathBefore).isEqualTo("10");
478         assertThat(entry.onBootclasspathSince).isEqualTo("20");
479     }
480 
481     /**
482      * Tests that readPermissions for a library with {@code min-device-sdk} lower than the current
483      * SDK results in the library being added to the shared libraries.
484      */
485     @Test
readPermissions_allowLibs_allowsOldMinSdk()486     public void readPermissions_allowLibs_allowsOldMinSdk() throws IOException {
487         String contents =
488                 "<permissions>\n"
489                 + "    <library \n"
490                 + "        name=\"foo\"\n"
491                 + "        file=\"" + mFooJar + "\"\n"
492                 + "        min-device-sdk=\"30\"\n"
493                 + "     />\n\n"
494                 + " </permissions>";
495         parseSharedLibraries(contents);
496         assertFooIsOnlySharedLibrary();
497     }
498 
499     /**
500      * Tests that readPermissions for a library with {@code min-device-sdk} equal to the current
501      * SDK results in the library being added to the shared libraries.
502      */
503     @Test
readPermissions_allowLibs_allowsCurrentMinSdk()504     public void readPermissions_allowLibs_allowsCurrentMinSdk() throws IOException {
505         String contents =
506                 "<permissions>\n"
507                 + "    <library \n"
508                 + "        name=\"foo\"\n"
509                 + "        file=\"" + mFooJar + "\"\n"
510                 + "        min-device-sdk=\"" + Build.VERSION.SDK_INT + "\"\n"
511                 + "     />\n\n"
512                 + " </permissions>";
513         parseSharedLibraries(contents);
514         assertFooIsOnlySharedLibrary();
515     }
516 
517     /**
518      * Tests that readPermissions for a library with {@code min-device-sdk} greater than the current
519      * SDK results in the library being ignored.
520      */
521     @Test
readPermissions_allowLibs_ignoresMinSdkInFuture()522     public void readPermissions_allowLibs_ignoresMinSdkInFuture() throws IOException {
523         String contents =
524                 "<permissions>\n"
525                 + "    <library \n"
526                 + "        name=\"foo\"\n"
527                 + "        file=\"" + mFooJar + "\"\n"
528                 + "        min-device-sdk=\"" + (Build.VERSION.SDK_INT + 1) + "\"\n"
529                 + "     />\n\n"
530                 + " </permissions>";
531         parseSharedLibraries(contents);
532         assertThat(mSysConfig.getSharedLibraries()).isEmpty();
533     }
534 
535     /**
536      * Tests that readPermissions for a library with {@code max-device-sdk} less than the current
537      * SDK results in the library being ignored.
538      */
539     @Test
readPermissions_allowLibs_ignoredOldMaxSdk()540     public void readPermissions_allowLibs_ignoredOldMaxSdk() throws IOException {
541         String contents =
542                 "<permissions>\n"
543                 + "    <library \n"
544                 + "        name=\"foo\"\n"
545                 + "        file=\"" + mFooJar + "\"\n"
546                 + "        max-device-sdk=\"30\"\n"
547                 + "     />\n\n"
548                 + " </permissions>";
549         parseSharedLibraries(contents);
550         assertThat(mSysConfig.getSharedLibraries()).isEmpty();
551     }
552 
553     /**
554      * Tests that readPermissions for a library with {@code max-device-sdk} equal to the current
555      * SDK results in the library being added to the shared libraries.
556      */
557     @Test
readPermissions_allowLibs_allowsCurrentMaxSdk()558     public void readPermissions_allowLibs_allowsCurrentMaxSdk() throws IOException {
559         // depending on whether this test is running before or after finalization, we need to
560         // pass a different parameter
561         String parameter;
562         if ("REL".equals(Build.VERSION.CODENAME)) {
563             parameter = "" + Build.VERSION.SDK_INT;
564         } else {
565             parameter = "ZZZ";
566         }
567         String contents =
568                 "<permissions>\n"
569                 + "    <library \n"
570                 + "        name=\"foo\"\n"
571                 + "        file=\"" + mFooJar + "\"\n"
572                 + "        max-device-sdk=\"" + parameter + "\"\n"
573                 + "     />\n\n"
574                 + " </permissions>";
575         parseSharedLibraries(contents);
576         assertFooIsOnlySharedLibrary();
577     }
578 
579     /**
580      * Tests that readPermissions for a library with {@code max-device-sdk} greater than the current
581      * SDK results in the library being added to the shared libraries.
582      */
583     @Test
readPermissions_allowLibs_allowsMaxSdkInFuture()584     public void readPermissions_allowLibs_allowsMaxSdkInFuture() throws IOException {
585         String contents =
586                 "<permissions>\n"
587                 + "    <library \n"
588                 + "        name=\"foo\"\n"
589                 + "        file=\"" + mFooJar + "\"\n"
590                 + "        max-device-sdk=\"" + (Build.VERSION.SDK_INT + 1) + "\"\n"
591                 + "     />\n\n"
592                 + " </permissions>";
593         parseSharedLibraries(contents);
594         assertFooIsOnlySharedLibrary();
595     }
596 
597     /**
598      * Test that getRollbackDenylistedPackages works correctly for the tag:
599      * {@code automatic-rollback-denylisted-app}.
600      */
601     @Test
automaticRollbackDeny_vending()602     public void automaticRollbackDeny_vending() throws IOException {
603         final String contents =
604                 "<config>\n"
605                 + "    <automatic-rollback-denylisted-app package=\"com.android.vending\" />\n"
606                 + "</config>";
607         final File folder = createTempSubfolder("folder");
608         createTempFile(folder, "automatic-rollback-denylisted-app.xml", contents);
609 
610         readPermissions(folder, /* Grant all permission flags */ ~0);
611 
612         assertThat(mSysConfig.getAutomaticRollbackDenylistedPackages())
613             .containsExactly("com.android.vending");
614     }
615 
616     /**
617      * Test that getRollbackDenylistedPackages works correctly for the tag:
618      * {@code automatic-rollback-denylisted-app} without any packages.
619      */
620     @Test
automaticRollbackDeny_empty()621     public void automaticRollbackDeny_empty() throws IOException {
622         final String contents =
623                 "<config>\n"
624                 + "    <automatic-rollback-denylisted-app />\n"
625                 + "</config>";
626         final File folder = createTempSubfolder("folder");
627         createTempFile(folder, "automatic-rollback-denylisted-app.xml", contents);
628 
629         readPermissions(folder, /* Grant all permission flags */ ~0);
630 
631         assertThat(mSysConfig.getAutomaticRollbackDenylistedPackages()).isEmpty();
632     }
633 
634     /**
635      * Test that getRollbackDenylistedPackages works correctly for the tag:
636      * {@code automatic-rollback-denylisted-app} without the corresponding config.
637      */
638     @Test
automaticRollbackDeny_noConfig()639     public void automaticRollbackDeny_noConfig() throws IOException {
640         final File folder = createTempSubfolder("folder");
641 
642         readPermissions(folder, /* Grant all permission flags */ ~0);
643 
644         assertThat(mSysConfig.getAutomaticRollbackDenylistedPackages()).isEmpty();
645     }
646 
647     /**
648      * Tests that readPermissions works correctly for the tag: {@code update-ownership}.
649      */
650     @Test
readPermissions_updateOwnership_successful()651     public void readPermissions_updateOwnership_successful() throws IOException {
652         final String contents =
653                 "<config>\n"
654                         + "    <update-ownership package=\"com.foo\" installer=\"com.bar\" />\n"
655                         + "</config>";
656         final File folder = createTempSubfolder("folder");
657         createTempFile(folder, "update_ownership.xml", contents);
658 
659         readPermissions(folder, /* Grant all permission flags */ ~0);
660 
661         assertThat(mSysConfig.getSystemAppUpdateOwnerPackageName("com.foo"))
662                 .isEqualTo("com.bar");
663     }
664 
665     /**
666      * Tests that readPermissions works correctly for the tag: {@code update-ownership}.
667      */
668     @Test
readPermissions_updateOwnership_noPackage()669     public void readPermissions_updateOwnership_noPackage() throws IOException {
670         final String contents =
671                 "<config>\n"
672                         + "    <update-ownership />\n"
673                         + "</config>";
674         final File folder = createTempSubfolder("folder");
675         createTempFile(folder, "update_ownership.xml", contents);
676 
677         readPermissions(folder, /* Grant all permission flags */ ~0);
678 
679         assertThat(mSysConfig.getSystemAppUpdateOwnerPackageName("com.foo")).isNull();
680     }
681 
682     /**
683      * Tests that readPermissions works correctly for the tag: {@code update-ownership}.
684      */
685     @Test
readPermissions_updateOwnership_noInstaller()686     public void readPermissions_updateOwnership_noInstaller() throws IOException {
687         final String contents =
688                 "<config>\n"
689                         + "    <update-ownership package=\"com.foo\" />\n"
690                         + "</config>";
691         final File folder = createTempSubfolder("folder");
692         createTempFile(folder, "update_ownership.xml", contents);
693 
694         readPermissions(folder, /* Grant all permission flags */ ~0);
695 
696         assertThat(mSysConfig.getSystemAppUpdateOwnerPackageName("com.foo")).isNull();
697     }
698 
parseSharedLibraries(String contents)699     private void parseSharedLibraries(String contents) throws IOException {
700         File folder = createTempSubfolder("permissions_folder");
701         createTempFile(folder, "permissions.xml", contents);
702         readPermissions(folder, /* permissionFlag = ALLOW_LIBS */ 0x02);
703     }
704 
705     /**
706      * Create an {@link XmlPullParser} for {@param permissionFile} and begin parsing it until
707      * reaching the root tag.
708      */
readXmlUntilStartTag(File permissionFile)709     private XmlPullParser readXmlUntilStartTag(File permissionFile)
710             throws IOException, XmlPullParserException {
711         FileReader permReader = new FileReader(permissionFile);
712         XmlPullParser parser = Xml.newPullParser();
713         parser.setInput(permReader);
714         int type;
715         do {
716             type = parser.next();
717         } while (type != parser.START_TAG && type != parser.END_DOCUMENT);
718         if (type != parser.START_TAG) {
719             throw new XmlPullParserException("No start tag found");
720         }
721         return parser;
722     }
723 
724     /**
725      * Creates folderName/fileName in the mTemporaryFolder and fills it with the contents.
726      *
727      * @param folderName subdirectory of mTemporaryFolder to put the file, creating if needed
728      * @return the folder
729      */
createTempSubfolder(String folderName)730     private File createTempSubfolder(String folderName)
731             throws IOException {
732         File folder = new File(mTemporaryFolder.getRoot(), folderName);
733         folder.mkdirs();
734         return folder;
735     }
736 
737     /**
738      * Creates folderName/fileName in the mTemporaryFolder and fills it with the contents.
739      *
740      * @param folder   pre-existing subdirectory of mTemporaryFolder to put the file
741      * @param fileName name of the file (e.g. filename.xml) to create
742      * @param contents contents to write to the file
743      * @return the newly created file
744      */
createTempFile(File folder, String fileName, String contents)745     private File createTempFile(File folder, String fileName, String contents)
746             throws IOException {
747         File file = new File(folder, fileName);
748         BufferedWriter bw = new BufferedWriter(new FileWriter(file));
749         bw.write(contents);
750         bw.close();
751 
752         // Print to logcat for test debugging.
753         Log.d(LOG_TAG, "Contents of file " + file.getAbsolutePath());
754         Scanner input = new Scanner(file);
755         while (input.hasNextLine()) {
756             Log.d(LOG_TAG, input.nextLine());
757         }
758 
759         return file;
760     }
761 
assertFooIsOnlySharedLibrary()762     private void assertFooIsOnlySharedLibrary() {
763         assertThat(mSysConfig.getSharedLibraries().size()).isEqualTo(1);
764         SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get("foo");
765         assertThat(entry.name).isEqualTo("foo");
766         assertThat(entry.filename).isEqualTo(mFooJar.toString());
767     }
768 }
769