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
17package android.content.pm;
18
19import static org.junit.Assert.assertEquals;
20import static org.junit.Assert.assertNotNull;
21import static org.junit.Assert.assertNull;
22import static org.junit.Assert.assertTrue;
23
24import android.content.Context;
25import android.content.pm.PackageParser.Component;
26import android.content.pm.PackageParser.Package;
27import android.content.pm.PackageParser.Permission;
28import android.os.Build;
29import android.os.Bundle;
30import android.os.FileUtils;
31import android.os.SystemProperties;
32import android.support.test.InstrumentationRegistry;
33import android.support.test.runner.AndroidJUnit4;
34import android.test.suitebuilder.annotation.SmallTest;
35
36import com.android.frameworks.coretests.R;
37
38import org.junit.Test;
39import org.junit.runner.RunWith;
40
41import java.io.File;
42import java.io.InputStream;
43import java.util.Arrays;
44import java.util.function.Function;
45
46@SmallTest
47@RunWith(AndroidJUnit4.class)
48public 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
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
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
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
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
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
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
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
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     */
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     */
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     */
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
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
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
377    public void testPackageWithComponents_no_cache() throws Exception {
378        checkPackageWithComponents(p -> p);
379    }
380
381    @Test
382    public void testPackageWithComponents_cached() throws Exception {
383        checkPackageWithComponents(p ->
384                PackageParser.fromCacheEntryStatic(PackageParser.toCacheEntryStatic(p)));
385    }
386
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     */
422    private boolean supportsMultiPackageApk() {
423        return SystemProperties.getBoolean("persist.sys.child_packages_enabled", false);
424    }
425
426    @Test
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