1 /*
2  * Copyright (C) 2016 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 android.content.pm;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertNotNull;
21 import static org.junit.Assert.assertNull;
22 import static org.junit.Assert.assertTrue;
23 
24 import android.content.Context;
25 import android.content.pm.PackageParser.Component;
26 import android.content.pm.PackageParser.Package;
27 import android.content.pm.PackageParser.Permission;
28 import android.os.Build;
29 import android.os.Bundle;
30 import android.os.FileUtils;
31 import android.os.SystemProperties;
32 import android.support.test.InstrumentationRegistry;
33 import android.support.test.runner.AndroidJUnit4;
34 import android.test.suitebuilder.annotation.SmallTest;
35 
36 import com.android.frameworks.coretests.R;
37 
38 import org.junit.Test;
39 import org.junit.runner.RunWith;
40 
41 import java.io.File;
42 import java.io.InputStream;
43 import java.util.Arrays;
44 import java.util.function.Function;
45 
46 @SmallTest
47 @RunWith(AndroidJUnit4.class)
48 public class PackageParserTest {
49     private static final String RELEASED = null;
50     private static final String OLDER_PRE_RELEASE = "A";
51     private static final String PRE_RELEASE = "B";
52     private static final String NEWER_PRE_RELEASE = "C";
53 
54     private static final String[] CODENAMES_RELEASED = { /* empty */ };
55     private static final String[] CODENAMES_PRE_RELEASE = { PRE_RELEASE };
56 
57     private static final int OLDER_VERSION = 10;
58     private static final int PLATFORM_VERSION = 20;
59     private static final int NEWER_VERSION = 30;
60 
verifyComputeMinSdkVersion(int minSdkVersion, String minSdkCodename, boolean isPlatformReleased, int expectedMinSdk)61     private void verifyComputeMinSdkVersion(int minSdkVersion, String minSdkCodename,
62             boolean isPlatformReleased, int expectedMinSdk) {
63         final String[] outError = new String[1];
64         final int result = PackageParser.computeMinSdkVersion(
65                 minSdkVersion,
66                 minSdkCodename,
67                 PLATFORM_VERSION,
68                 isPlatformReleased ? CODENAMES_RELEASED : CODENAMES_PRE_RELEASE,
69                 outError);
70 
71         assertEquals(result, expectedMinSdk);
72 
73         if (expectedMinSdk == -1) {
74             assertNotNull(outError[0]);
75         } else {
76             assertNull(outError[0]);
77         }
78     }
79 
80     @Test
testComputeMinSdkVersion_preReleasePlatform()81     public void testComputeMinSdkVersion_preReleasePlatform() {
82         // Do allow older release minSdkVersion on pre-release platform.
83         // APP: Released API 10
84         // DEV: Pre-release API 20
85         verifyComputeMinSdkVersion(OLDER_VERSION, RELEASED, false, OLDER_VERSION);
86 
87         // Do allow same release minSdkVersion on pre-release platform.
88         // APP: Released API 20
89         // DEV: Pre-release API 20
90         verifyComputeMinSdkVersion(PLATFORM_VERSION, RELEASED, false, PLATFORM_VERSION);
91 
92         // Don't allow newer release minSdkVersion on pre-release platform.
93         // APP: Released API 30
94         // DEV: Pre-release API 20
95         verifyComputeMinSdkVersion(NEWER_VERSION, RELEASED, false, -1);
96 
97         // Don't allow older pre-release minSdkVersion on pre-release platform.
98         // APP: Pre-release API 10
99         // DEV: Pre-release API 20
100         verifyComputeMinSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, false, -1);
101 
102         // Do allow same pre-release minSdkVersion on pre-release platform,
103         // but overwrite the specified version with CUR_DEVELOPMENT.
104         // APP: Pre-release API 20
105         // DEV: Pre-release API 20
106         verifyComputeMinSdkVersion(PLATFORM_VERSION, PRE_RELEASE, false,
107                 Build.VERSION_CODES.CUR_DEVELOPMENT);
108 
109         // Don't allow newer pre-release minSdkVersion on pre-release platform.
110         // APP: Pre-release API 30
111         // DEV: Pre-release API 20
112         verifyComputeMinSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, false, -1);
113     }
114 
115     @Test
testComputeMinSdkVersion_releasedPlatform()116     public void testComputeMinSdkVersion_releasedPlatform() {
117         // Do allow older release minSdkVersion on released platform.
118         // APP: Released API 10
119         // DEV: Released API 20
120         verifyComputeMinSdkVersion(OLDER_VERSION, RELEASED, true, OLDER_VERSION);
121 
122         // Do allow same release minSdkVersion on released platform.
123         // APP: Released API 20
124         // DEV: Released API 20
125         verifyComputeMinSdkVersion(PLATFORM_VERSION, RELEASED, true, PLATFORM_VERSION);
126 
127         // Don't allow newer release minSdkVersion on released platform.
128         // APP: Released API 30
129         // DEV: Released API 20
130         verifyComputeMinSdkVersion(NEWER_VERSION, RELEASED, true, -1);
131 
132         // Don't allow older pre-release minSdkVersion on released platform.
133         // APP: Pre-release API 10
134         // DEV: Released API 20
135         verifyComputeMinSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, true, -1);
136 
137         // Don't allow same pre-release minSdkVersion on released platform.
138         // APP: Pre-release API 20
139         // DEV: Released API 20
140         verifyComputeMinSdkVersion(PLATFORM_VERSION, PRE_RELEASE, true, -1);
141 
142         // Don't allow newer pre-release minSdkVersion on released platform.
143         // APP: Pre-release API 30
144         // DEV: Released API 20
145         verifyComputeMinSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, true, -1);
146     }
147 
verifyComputeTargetSdkVersion(int targetSdkVersion, String targetSdkCodename, boolean isPlatformReleased, int expectedTargetSdk, boolean forceCurrentDev)148     private void verifyComputeTargetSdkVersion(int targetSdkVersion, String targetSdkCodename,
149             boolean isPlatformReleased, int expectedTargetSdk, boolean forceCurrentDev) {
150         final String[] outError = new String[1];
151         final int result = PackageParser.computeTargetSdkVersion(
152                 targetSdkVersion,
153                 targetSdkCodename,
154                 isPlatformReleased ? CODENAMES_RELEASED : CODENAMES_PRE_RELEASE,
155                 outError,
156                 forceCurrentDev);
157 
158         assertEquals(result, expectedTargetSdk);
159 
160         if (expectedTargetSdk == -1) {
161             assertNotNull(outError[0]);
162         } else {
163             assertNull(outError[0]);
164         }
165     }
166 
167     @Test
testComputeTargetSdkVersion_preReleasePlatform()168     public void testComputeTargetSdkVersion_preReleasePlatform() {
169         // Do allow older release targetSdkVersion on pre-release platform.
170         // APP: Released API 10
171         // DEV: Pre-release API 20
172         verifyComputeTargetSdkVersion(OLDER_VERSION, RELEASED, false, OLDER_VERSION,
173                 false /* forceCurrentDev */);
174 
175         // Do allow same release targetSdkVersion on pre-release platform.
176         // APP: Released API 20
177         // DEV: Pre-release API 20
178         verifyComputeTargetSdkVersion(PLATFORM_VERSION, RELEASED, false, PLATFORM_VERSION,
179                 false /* forceCurrentDev */);
180 
181         // Do allow newer release targetSdkVersion on pre-release platform.
182         // APP: Released API 30
183         // DEV: Pre-release API 20
184         verifyComputeTargetSdkVersion(NEWER_VERSION, RELEASED, false, NEWER_VERSION,
185                 false /* forceCurrentDev */);
186 
187         // Don't allow older pre-release targetSdkVersion on pre-release platform.
188         // APP: Pre-release API 10
189         // DEV: Pre-release API 20
190         verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, false, -1,
191                 false /* forceCurrentDev */);
192 
193         // Do allow same pre-release targetSdkVersion on pre-release platform,
194         // but overwrite the specified version with CUR_DEVELOPMENT.
195         // APP: Pre-release API 20
196         // DEV: Pre-release API 20
197         verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE, false,
198                 Build.VERSION_CODES.CUR_DEVELOPMENT, false /* forceCurrentDev */);
199 
200         // Don't allow newer pre-release targetSdkVersion on pre-release platform.
201         // APP: Pre-release API 30
202         // DEV: Pre-release API 20
203         verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, false, -1,
204                 false /* forceCurrentDev */);
205 
206         // Force newer pre-release targetSdkVersion to current pre-release platform.
207         // APP: Pre-release API 30
208         // DEV: Pre-release API 20
209         verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, false,
210                 Build.VERSION_CODES.CUR_DEVELOPMENT, true /* forceCurrentDev */);
211     }
212 
213     @Test
testComputeTargetSdkVersion_releasedPlatform()214     public void testComputeTargetSdkVersion_releasedPlatform() {
215         // Do allow older release targetSdkVersion on released platform.
216         // APP: Released API 10
217         // DEV: Released API 20
218         verifyComputeTargetSdkVersion(OLDER_VERSION, RELEASED, true, OLDER_VERSION,
219                 false /* forceCurrentDev */);
220 
221         // Do allow same release targetSdkVersion on released platform.
222         // APP: Released API 20
223         // DEV: Released API 20
224         verifyComputeTargetSdkVersion(PLATFORM_VERSION, RELEASED, true, PLATFORM_VERSION,
225                 false /* forceCurrentDev */);
226 
227         // Do allow newer release targetSdkVersion on released platform.
228         // APP: Released API 30
229         // DEV: Released API 20
230         verifyComputeTargetSdkVersion(NEWER_VERSION, RELEASED, true, NEWER_VERSION,
231                 false /* forceCurrentDev */);
232 
233         // Don't allow older pre-release targetSdkVersion on released platform.
234         // APP: Pre-release API 10
235         // DEV: Released API 20
236         verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, true, -1,
237                 false /* forceCurrentDev */);
238 
239         // Don't allow same pre-release targetSdkVersion on released platform.
240         // APP: Pre-release API 20
241         // DEV: Released API 20
242         verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE, true, -1,
243                 false /* forceCurrentDev */);
244 
245         // Don't allow newer pre-release targetSdkVersion on released platform.
246         // APP: Pre-release API 30
247         // DEV: Released API 20
248         verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, true, -1,
249                 false /* forceCurrentDev */);
250     }
251 
252     /**
253      * Unit test for PackageParser.getActivityConfigChanges().
254      * If the bit is 1 in the original configChanges, it is still 1 in the final configChanges.
255      * If the bit is 0 in the original configChanges and the bit is not set to 1 in
256      * recreateOnConfigChanges, the bit is changed to 1 in the final configChanges by default.
257      */
258     @Test
testGetActivityConfigChanges()259     public void testGetActivityConfigChanges() {
260         // Not set in either configChanges or recreateOnConfigChanges.
261         int configChanges = 0x0000; // 00000000.
262         int recreateOnConfigChanges = 0x0000; // 00000000.
263         int finalConfigChanges =
264                 PackageParser.getActivityConfigChanges(configChanges, recreateOnConfigChanges);
265         assertEquals(0x0003, finalConfigChanges); // Should be 00000011.
266 
267         // Not set in configChanges, but set in recreateOnConfigChanges.
268         configChanges = 0x0000; // 00000000.
269         recreateOnConfigChanges = 0x0003; // 00000011.
270         finalConfigChanges =
271                 PackageParser.getActivityConfigChanges(configChanges, recreateOnConfigChanges);
272         assertEquals(0x0000, finalConfigChanges); // Should be 00000000.
273 
274         // Set in configChanges.
275         configChanges = 0x0003; // 00000011.
276         recreateOnConfigChanges = 0X0000; // 00000000.
277         finalConfigChanges =
278                 PackageParser.getActivityConfigChanges(configChanges, recreateOnConfigChanges);
279         assertEquals(0x0003, finalConfigChanges); // Should be 00000011.
280 
281         recreateOnConfigChanges = 0x0003; // 00000011.
282         finalConfigChanges =
283                 PackageParser.getActivityConfigChanges(configChanges, recreateOnConfigChanges);
284         assertEquals(0x0003, finalConfigChanges); // Should still be 00000011.
285 
286         // Other bit set in configChanges.
287         configChanges = 0x0080; // 10000000, orientation.
288         recreateOnConfigChanges = 0x0000; // 00000000.
289         finalConfigChanges =
290                 PackageParser.getActivityConfigChanges(configChanges, recreateOnConfigChanges);
291         assertEquals(0x0083, finalConfigChanges); // Should be 10000011.
292     }
293 
parsePackage(String apkFileName, int apkResourceId)294     Package parsePackage(String apkFileName, int apkResourceId) throws Exception {
295         return parsePackage(apkFileName, apkResourceId, p -> p);
296     }
297 
298     /**
299      * Attempts to parse a package.
300      *
301      * APKs are put into coretests/apks/packageparser_*.
302      *
303      * @param apkFileName temporary file name to store apk extracted from resources
304      * @param apkResourceId identifier of the apk as a resource
305      */
parsePackage(String apkFileName, int apkResourceId, Function<Package, Package> converter)306     Package parsePackage(String apkFileName, int apkResourceId,
307             Function<Package, Package> converter) throws Exception {
308         // Copy the resource to a file.
309         Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
310         File outFile = new File(context.getFilesDir(), apkFileName);
311         try {
312             InputStream is = context.getResources().openRawResource(apkResourceId);
313             assertTrue(FileUtils.copyToFile(is, outFile));
314             return converter.apply(new PackageParser().parsePackage(outFile, 0 /* flags */));
315         } finally {
316             outFile.delete();
317         }
318     }
319 
320     /**
321      * Asserts basic properties about a component.
322      */
assertComponent(String className, String packageName, int numIntents, Component<?> component)323     private void assertComponent(String className, String packageName, int numIntents,
324             Component<?> component) {
325         assertEquals(className, component.className);
326         assertEquals(packageName, component.owner.packageName);
327         assertEquals(numIntents, component.intents.size());
328     }
329 
330     /**
331      * Asserts four regularly-named components of each type: one Activity, one Service, one
332      * Provider, and one Receiver.
333      * @param template templated string with %s subbed with Activity, Service, Provider, Receiver
334      */
assertOneComponentOfEachType(String template, Package p)335     private void assertOneComponentOfEachType(String template, Package p) {
336         String packageName = p.packageName;
337 
338         assertEquals(1, p.activities.size());
339         assertComponent(String.format(template, "Activity"),
340                 packageName, 0 /* intents */, p.activities.get(0));
341         assertEquals(1, p.services.size());
342         assertComponent(String.format(template, "Service"),
343                 packageName, 0 /* intents */, p.services.get(0));
344         assertEquals(1, p.providers.size());
345         assertComponent(String.format(template, "Provider"),
346                 packageName, 0 /* intents */, p.providers.get(0));
347         assertEquals(1, p.receivers.size());
348         assertComponent(String.format(template, "Receiver"),
349                 packageName, 0 /* intents */, p.receivers.get(0));
350     }
351 
assertPermission(String name, String packageName, int protectionLevel, Permission permission)352     private void assertPermission(String name, String packageName, int protectionLevel,
353             Permission permission) {
354         assertEquals(packageName, permission.owner.packageName);
355         assertEquals(name, permission.info.name);
356         assertEquals(protectionLevel, permission.info.protectionLevel);
357     }
358 
assertMetadata(Bundle b, String... keysAndValues)359     private void assertMetadata(Bundle b, String... keysAndValues) {
360         assertTrue("Odd number of elements in keysAndValues", (keysAndValues.length % 2) == 0);
361 
362         assertNotNull(b);
363         assertEquals(keysAndValues.length / 2, b.size());
364 
365         for (int i = 0; i < keysAndValues.length; i += 2) {
366             final String key = keysAndValues[i];
367             final String value = keysAndValues[i + 1];
368 
369             assertEquals(value, b.getString(key));
370         }
371     }
372 
373     // TODO Add a "_cached" test for testMultiPackageComponents() too, after fixing b/64295061.
374     // Package.writeToParcel can't handle circular package references.
375 
376     @Test
testPackageWithComponents_no_cache()377     public void testPackageWithComponents_no_cache() throws Exception {
378         checkPackageWithComponents(p -> p);
379     }
380 
381     @Test
testPackageWithComponents_cached()382     public void testPackageWithComponents_cached() throws Exception {
383         checkPackageWithComponents(p ->
384                 PackageParser.fromCacheEntryStatic(PackageParser.toCacheEntryStatic(p)));
385     }
386 
checkPackageWithComponents( Function<Package, Package> converter)387     private void checkPackageWithComponents(
388             Function<Package, Package> converter) throws Exception {
389         Package p = parsePackage(
390                 "install_complete_package_info.apk", R.raw.install_complete_package_info,
391                 converter);
392         String packageName = "com.android.frameworks.coretests.install_complete_package_info";
393 
394         assertEquals(packageName, p.packageName);
395         assertEquals(1, p.permissions.size());
396         assertPermission(
397                 "com.android.frameworks.coretests.install_complete_package_info.test_permission",
398                 packageName, PermissionInfo.PROTECTION_NORMAL, p.permissions.get(0));
399 
400         assertOneComponentOfEachType("com.android.frameworks.coretests.Test%s", p);
401 
402         assertMetadata(p.mAppMetaData,
403                 "key1", "value1",
404                 "key2", "this_is_app");
405         assertMetadata(p.activities.get(0).metaData,
406                 "key1", "value1",
407                 "key2", "this_is_activity");
408         assertMetadata(p.services.get(0).metaData,
409                 "key1", "value1",
410                 "key2", "this_is_service");
411         assertMetadata(p.receivers.get(0).metaData,
412                 "key1", "value1",
413                 "key2", "this_is_receiver");
414         assertMetadata(p.providers.get(0).metaData,
415                 "key1", "value1",
416                 "key2", "this_is_provider");
417     }
418 
419     /**
420      * Determines if the current device supports multi-package APKs.
421      */
supportsMultiPackageApk()422     private boolean supportsMultiPackageApk() {
423         return SystemProperties.getBoolean("persist.sys.child_packages_enabled", false);
424     }
425 
426     @Test
testMultiPackageComponents()427     public void testMultiPackageComponents() throws Exception {
428         // TODO(gboyer): Remove once we decide to launch multi-package APKs.
429         if (!supportsMultiPackageApk()) {
430             return;
431         }
432         String parentName = "com.android.frameworks.coretests.install_multi_package";
433         String firstChildName =
434                 "com.android.frameworks.coretests.install_multi_package.first_child";
435         String secondChildName =  // NOTE: intentionally inconsistent!
436                 "com.android.frameworks.coretests.blah.second_child";
437 
438         Package parent = parsePackage("install_multi_package.apk", R.raw.install_multi_package);
439         assertEquals(parentName, parent.packageName);
440         assertEquals(2, parent.childPackages.size());
441         assertOneComponentOfEachType("com.android.frameworks.coretests.Test%s", parent);
442         assertEquals(1, parent.permissions.size());
443         assertPermission(parentName + ".test_permission", parentName,
444                 PermissionInfo.PROTECTION_NORMAL, parent.permissions.get(0));
445         assertEquals(Arrays.asList("android.permission.INTERNET"),
446                 parent.requestedPermissions);
447 
448         Package firstChild = parent.childPackages.get(0);
449         assertEquals(firstChildName, firstChild.packageName);
450         assertOneComponentOfEachType(
451                 "com.android.frameworks.coretests.FirstChildTest%s", firstChild);
452         assertEquals(0, firstChild.permissions.size());  // Child APKs cannot declare permissions.
453         assertEquals(Arrays.asList("android.permission.NFC"),
454                 firstChild.requestedPermissions);
455 
456         Package secondChild = parent.childPackages.get(1);
457         assertEquals(secondChildName, secondChild.packageName);
458         assertOneComponentOfEachType(
459                 "com.android.frameworks.coretests.SecondChildTest%s", secondChild);
460         assertEquals(0, secondChild.permissions.size());  // Child APKs cannot declare permissions.
461         assertEquals(
462                 Arrays.asList(
463                         "android.permission.ACCESS_NETWORK_STATE",
464                         "android.permission.READ_CONTACTS"),
465                 secondChild.requestedPermissions);
466     }
467 }
468