/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.pm; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ConfigurationInfo; import android.content.pm.FeatureGroupInfo; import android.content.pm.FeatureInfo; import android.content.pm.InstrumentationInfo; import android.content.pm.PackageParser; import android.content.pm.ProviderInfo; import android.content.pm.ServiceInfo; import android.content.pm.Signature; import android.os.Bundle; import android.os.Parcel; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.MediumTest; import java.io.File; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.nio.charset.StandardCharsets; import java.security.cert.Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import static org.junit.Assert.*; import android.util.ArrayMap; import android.util.ArraySet; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import libcore.io.IoUtils; @RunWith(AndroidJUnit4.class) @MediumTest public class PackageParserTest { private File mTmpDir; private static final File FRAMEWORK = new File("/system/framework/framework-res.apk"); @Before public void setUp() { // Create a new temporary directory for each of our tests. mTmpDir = IoUtils.createTemporaryDirectory("PackageParserTest"); } @Test public void testParse_noCache() throws Exception { PackageParser pp = new CachePackageNameParser(); PackageParser.Package pkg = pp.parsePackage(FRAMEWORK, 0 /* parseFlags */, false /* useCaches */); assertNotNull(pkg); pp.setCacheDir(mTmpDir); pkg = pp.parsePackage(FRAMEWORK, 0 /* parseFlags */, false /* useCaches */); assertNotNull(pkg); // Make sure that we always write out a cache entry for future reference, // whether or not we're asked to use caches. assertEquals(1, mTmpDir.list().length); } @Test public void testParse_withCache() throws Exception { PackageParser pp = new CachePackageNameParser(); pp.setCacheDir(mTmpDir); // The first parse will write this package to the cache. pp.parsePackage(FRAMEWORK, 0 /* parseFlags */, true /* useCaches */); // Now attempt to parse the package again, should return the // cached result. PackageParser.Package pkg = pp.parsePackage(FRAMEWORK, 0 /* parseFlags */, true /* useCaches */); assertEquals("cache_android", pkg.packageName); // Try again, with useCaches == false, shouldn't return the parsed // result. pkg = pp.parsePackage(FRAMEWORK, 0 /* parseFlags */, false /* useCaches */); assertEquals("android", pkg.packageName); // We haven't set a cache directory here : the parse should still succeed, // just not using the cached results. pp = new CachePackageNameParser(); pkg = pp.parsePackage(FRAMEWORK, 0 /* parseFlags */, true /* useCaches */); assertEquals("android", pkg.packageName); pkg = pp.parsePackage(FRAMEWORK, 0 /* parseFlags */, false /* useCaches */); assertEquals("android", pkg.packageName); } @Test public void test_serializePackage() throws Exception { PackageParser pp = new PackageParser(); pp.setCacheDir(mTmpDir); PackageParser.Package pkg = pp.parsePackage(FRAMEWORK, 0 /* parseFlags */, true /* useCaches */); Parcel p = Parcel.obtain(); pkg.writeToParcel(p, 0 /* flags */); p.setDataPosition(0); PackageParser.Package deserialized = new PackageParser.Package(p); assertPackagesEqual(pkg, deserialized); } @Test public void test_roundTripKnownFields() throws Exception { PackageParser.Package pkg = new PackageParser.Package("foo"); setKnownFields(pkg); Parcel p = Parcel.obtain(); pkg.writeToParcel(p, 0 /* flags */); p.setDataPosition(0); PackageParser.Package deserialized = new PackageParser.Package(p); assertAllFieldsExist(deserialized); } @Test public void test_stringInterning() throws Exception { PackageParser.Package pkg = new PackageParser.Package("foo"); setKnownFields(pkg); Parcel p = Parcel.obtain(); pkg.writeToParcel(p, 0 /* flags */); p.setDataPosition(0); PackageParser.Package deserialized = new PackageParser.Package(p); p.setDataPosition(0); PackageParser.Package deserialized2 = new PackageParser.Package(p); assertSame(deserialized.packageName, deserialized2.packageName); assertSame(deserialized.applicationInfo.permission, deserialized2.applicationInfo.permission); assertSame(deserialized.requestedPermissions.get(0), deserialized2.requestedPermissions.get(0)); assertSame(deserialized.protectedBroadcasts.get(0), deserialized2.protectedBroadcasts.get(0)); assertSame(deserialized.usesLibraries.get(0), deserialized2.usesLibraries.get(0)); assertSame(deserialized.usesOptionalLibraries.get(0), deserialized2.usesOptionalLibraries.get(0)); assertSame(deserialized.mVersionName, deserialized2.mVersionName); assertSame(deserialized.mSharedUserId, deserialized2.mSharedUserId); } /** * A trivial subclass of package parser that only caches the package name, and throws away * all other information. */ public static class CachePackageNameParser extends PackageParser { @Override public byte[] toCacheEntry(Package pkg) { return ("cache_" + pkg.packageName).getBytes(StandardCharsets.UTF_8); } @Override public Package fromCacheEntry(byte[] cacheEntry) { return new Package(new String(cacheEntry, StandardCharsets.UTF_8)); } } // NOTE: The equality assertions below are based on code autogenerated by IntelliJ. public static void assertPackagesEqual(PackageParser.Package a, PackageParser.Package b) { assertEquals(a.baseRevisionCode, b.baseRevisionCode); assertEquals(a.baseHardwareAccelerated, b.baseHardwareAccelerated); assertEquals(a.mVersionCode, b.mVersionCode); assertEquals(a.mSharedUserLabel, b.mSharedUserLabel); assertEquals(a.mPreferredOrder, b.mPreferredOrder); assertEquals(a.installLocation, b.installLocation); assertEquals(a.coreApp, b.coreApp); assertEquals(a.mRequiredForAllUsers, b.mRequiredForAllUsers); assertEquals(a.mTrustedOverlay, b.mTrustedOverlay); assertEquals(a.use32bitAbi, b.use32bitAbi); assertEquals(a.packageName, b.packageName); assertTrue(Arrays.equals(a.splitNames, b.splitNames)); assertEquals(a.volumeUuid, b.volumeUuid); assertEquals(a.codePath, b.codePath); assertEquals(a.baseCodePath, b.baseCodePath); assertTrue(Arrays.equals(a.splitCodePaths, b.splitCodePaths)); assertTrue(Arrays.equals(a.splitRevisionCodes, b.splitRevisionCodes)); assertTrue(Arrays.equals(a.splitFlags, b.splitFlags)); assertTrue(Arrays.equals(a.splitPrivateFlags, b.splitPrivateFlags)); assertApplicationInfoEqual(a.applicationInfo, b.applicationInfo); assertEquals(a.permissions.size(), b.permissions.size()); for (int i = 0; i < a.permissions.size(); ++i) { assertPermissionsEqual(a.permissions.get(i), b.permissions.get(i)); assertSame(a.permissions.get(i).owner, a); assertSame(b.permissions.get(i).owner, b); } assertEquals(a.permissionGroups.size(), b.permissionGroups.size()); for (int i = 0; i < a.permissionGroups.size(); ++i) { assertPermissionGroupsEqual(a.permissionGroups.get(i), b.permissionGroups.get(i)); } assertEquals(a.activities.size(), b.activities.size()); for (int i = 0; i < a.activities.size(); ++i) { assertActivitiesEqual(a.activities.get(i), b.activities.get(i)); } assertEquals(a.receivers.size(), b.receivers.size()); for (int i = 0; i < a.receivers.size(); ++i) { assertActivitiesEqual(a.receivers.get(i), b.receivers.get(i)); } assertEquals(a.providers.size(), b.providers.size()); for (int i = 0; i < a.providers.size(); ++i) { assertProvidersEqual(a.providers.get(i), b.providers.get(i)); } assertEquals(a.services.size(), b.services.size()); for (int i = 0; i < a.services.size(); ++i) { assertServicesEqual(a.services.get(i), b.services.get(i)); } assertEquals(a.instrumentation.size(), b.instrumentation.size()); for (int i = 0; i < a.instrumentation.size(); ++i) { assertInstrumentationEqual(a.instrumentation.get(i), b.instrumentation.get(i)); } assertEquals(a.requestedPermissions, b.requestedPermissions); assertEquals(a.protectedBroadcasts, b.protectedBroadcasts); assertEquals(a.parentPackage, b.parentPackage); assertEquals(a.childPackages, b.childPackages); assertEquals(a.libraryNames, b.libraryNames); assertEquals(a.usesLibraries, b.usesLibraries); assertEquals(a.usesOptionalLibraries, b.usesOptionalLibraries); assertTrue(Arrays.equals(a.usesLibraryFiles, b.usesLibraryFiles)); assertEquals(a.mOriginalPackages, b.mOriginalPackages); assertEquals(a.mRealPackage, b.mRealPackage); assertEquals(a.mAdoptPermissions, b.mAdoptPermissions); assertBundleApproximateEquals(a.mAppMetaData, b.mAppMetaData); assertEquals(a.mVersionName, b.mVersionName); assertEquals(a.mSharedUserId, b.mSharedUserId); assertTrue(Arrays.equals(a.mSignatures, b.mSignatures)); assertTrue(Arrays.equals(a.mCertificates, b.mCertificates)); assertTrue(Arrays.equals(a.mLastPackageUsageTimeInMills, b.mLastPackageUsageTimeInMills)); assertEquals(a.mExtras, b.mExtras); assertEquals(a.mRestrictedAccountType, b.mRestrictedAccountType); assertEquals(a.mRequiredAccountType, b.mRequiredAccountType); assertEquals(a.mOverlayTarget, b.mOverlayTarget); assertEquals(a.mSigningKeys, b.mSigningKeys); assertEquals(a.mUpgradeKeySets, b.mUpgradeKeySets); assertEquals(a.mKeySetMapping, b.mKeySetMapping); assertEquals(a.cpuAbiOverride, b.cpuAbiOverride); assertTrue(Arrays.equals(a.restrictUpdateHash, b.restrictUpdateHash)); } private static void assertBundleApproximateEquals(Bundle a, Bundle b) { if (a == b) { return; } // Force the bundles to be unparceled. a.getBoolean("foo"); b.getBoolean("foo"); assertEquals(a.toString(), b.toString()); } private static void assertComponentsEqual(PackageParser.Component a, PackageParser.Component b) { assertEquals(a.className, b.className); assertBundleApproximateEquals(a.metaData, b.metaData); assertEquals(a.getComponentName(), b.getComponentName()); if (a.intents != null && b.intents != null) { assertEquals(a.intents.size(), b.intents.size()); } else if (a.intents == null || b.intents == null) { return; } for (int i = 0; i < a.intents.size(); ++i) { PackageParser.IntentInfo aIntent = a.intents.get(i); PackageParser.IntentInfo bIntent = b.intents.get(i); assertEquals(aIntent.hasDefault, bIntent.hasDefault); assertEquals(aIntent.labelRes, bIntent.labelRes); assertEquals(aIntent.nonLocalizedLabel, bIntent.nonLocalizedLabel); assertEquals(aIntent.icon, bIntent.icon); assertEquals(aIntent.logo, bIntent.logo); assertEquals(aIntent.banner, bIntent.banner); assertEquals(aIntent.preferred, bIntent.preferred); } } private static void assertPermissionsEqual(PackageParser.Permission a, PackageParser.Permission b) { assertComponentsEqual(a, b); assertEquals(a.tree, b.tree); // Verify basic flags in PermissionInfo to make sure they're consistent. We don't perform // a full structural equality here because the code that serializes them isn't parser // specific and is tested elsewhere. assertEquals(a.info.protectionLevel, b.info.protectionLevel); assertEquals(a.info.group, b.info.group); assertEquals(a.info.flags, b.info.flags); if (a.group != null && b.group != null) { assertPermissionGroupsEqual(a.group, b.group); } else if (a.group != null || b.group != null) { throw new AssertionError(); } } private static void assertInstrumentationEqual(PackageParser.Instrumentation a, PackageParser.Instrumentation b) { assertComponentsEqual(a, b); // Sanity check for InstrumentationInfo. assertEquals(a.info.targetPackage, b.info.targetPackage); assertEquals(a.info.targetProcesses, b.info.targetProcesses); assertEquals(a.info.sourceDir, b.info.sourceDir); assertEquals(a.info.publicSourceDir, b.info.publicSourceDir); } private static void assertServicesEqual(PackageParser.Service a, PackageParser.Service b) { assertComponentsEqual(a, b); // Sanity check for ServiceInfo. assertApplicationInfoEqual(a.info.applicationInfo, b.info.applicationInfo); assertEquals(a.info.name, b.info.name); } private static void assertProvidersEqual(PackageParser.Provider a, PackageParser.Provider b) { assertComponentsEqual(a, b); // Sanity check for ProviderInfo assertApplicationInfoEqual(a.info.applicationInfo, b.info.applicationInfo); assertEquals(a.info.name, b.info.name); } private static void assertActivitiesEqual(PackageParser.Activity a, PackageParser.Activity b) { assertComponentsEqual(a, b); // Sanity check for ActivityInfo. assertApplicationInfoEqual(a.info.applicationInfo, b.info.applicationInfo); assertEquals(a.info.name, b.info.name); } private static void assertPermissionGroupsEqual(PackageParser.PermissionGroup a, PackageParser.PermissionGroup b) { assertComponentsEqual(a, b); // Sanity check for PermissionGroupInfo. assertEquals(a.info.name, b.info.name); assertEquals(a.info.descriptionRes, b.info.descriptionRes); } private static void assertApplicationInfoEqual(ApplicationInfo a, ApplicationInfo that) { assertEquals(a.descriptionRes, that.descriptionRes); assertEquals(a.theme, that.theme); assertEquals(a.fullBackupContent, that.fullBackupContent); assertEquals(a.uiOptions, that.uiOptions); assertEquals(a.flags, that.flags); assertEquals(a.privateFlags, that.privateFlags); assertEquals(a.requiresSmallestWidthDp, that.requiresSmallestWidthDp); assertEquals(a.compatibleWidthLimitDp, that.compatibleWidthLimitDp); assertEquals(a.largestWidthLimitDp, that.largestWidthLimitDp); assertEquals(a.nativeLibraryRootRequiresIsa, that.nativeLibraryRootRequiresIsa); assertEquals(a.uid, that.uid); assertEquals(a.minSdkVersion, that.minSdkVersion); assertEquals(a.targetSdkVersion, that.targetSdkVersion); assertEquals(a.versionCode, that.versionCode); assertEquals(a.enabled, that.enabled); assertEquals(a.enabledSetting, that.enabledSetting); assertEquals(a.installLocation, that.installLocation); assertEquals(a.networkSecurityConfigRes, that.networkSecurityConfigRes); assertEquals(a.taskAffinity, that.taskAffinity); assertEquals(a.permission, that.permission); assertEquals(a.processName, that.processName); assertEquals(a.className, that.className); assertEquals(a.manageSpaceActivityName, that.manageSpaceActivityName); assertEquals(a.backupAgentName, that.backupAgentName); assertEquals(a.volumeUuid, that.volumeUuid); assertEquals(a.scanSourceDir, that.scanSourceDir); assertEquals(a.scanPublicSourceDir, that.scanPublicSourceDir); assertEquals(a.sourceDir, that.sourceDir); assertEquals(a.publicSourceDir, that.publicSourceDir); assertTrue(Arrays.equals(a.splitSourceDirs, that.splitSourceDirs)); assertTrue(Arrays.equals(a.splitPublicSourceDirs, that.splitPublicSourceDirs)); assertTrue(Arrays.equals(a.resourceDirs, that.resourceDirs)); assertEquals(a.seInfo, that.seInfo); assertTrue(Arrays.equals(a.sharedLibraryFiles, that.sharedLibraryFiles)); assertEquals(a.dataDir, that.dataDir); assertEquals(a.deviceProtectedDataDir, that.deviceProtectedDataDir); assertEquals(a.credentialProtectedDataDir, that.credentialProtectedDataDir); assertEquals(a.nativeLibraryDir, that.nativeLibraryDir); assertEquals(a.secondaryNativeLibraryDir, that.secondaryNativeLibraryDir); assertEquals(a.nativeLibraryRootDir, that.nativeLibraryRootDir); assertEquals(a.primaryCpuAbi, that.primaryCpuAbi); assertEquals(a.secondaryCpuAbi, that.secondaryCpuAbi); } public static void setKnownFields(PackageParser.Package pkg) { pkg.baseRevisionCode = 100; pkg.baseHardwareAccelerated = true; pkg.mVersionCode = 100; pkg.mSharedUserLabel = 100; pkg.mPreferredOrder = 100; pkg.installLocation = 100; pkg.coreApp = true; pkg.mRequiredForAllUsers = true; pkg.mTrustedOverlay = true; pkg.use32bitAbi = true; pkg.packageName = "foo"; pkg.splitNames = new String[] { "foo2" }; pkg.volumeUuid = "foo3"; pkg.codePath = "foo4"; pkg.baseCodePath = "foo5"; pkg.splitCodePaths = new String[] { "foo6" }; pkg.splitRevisionCodes = new int[] { 100 }; pkg.splitFlags = new int[] { 100 }; pkg.splitPrivateFlags = new int[] { 100 }; pkg.applicationInfo = new ApplicationInfo(); pkg.permissions.add(new PackageParser.Permission(pkg)); pkg.permissionGroups.add(new PackageParser.PermissionGroup(pkg)); final PackageParser.ParseComponentArgs dummy = new PackageParser.ParseComponentArgs( pkg, new String[1], 0, 0, 0, 0, 0, 0, null, 0, 0, 0); pkg.activities.add(new PackageParser.Activity(dummy, new ActivityInfo())); pkg.receivers.add(new PackageParser.Activity(dummy, new ActivityInfo())); pkg.providers.add(new PackageParser.Provider(dummy, new ProviderInfo())); pkg.services.add(new PackageParser.Service(dummy, new ServiceInfo())); pkg.instrumentation.add(new PackageParser.Instrumentation(dummy, new InstrumentationInfo())); pkg.requestedPermissions.add("foo7"); pkg.protectedBroadcasts = new ArrayList<>(); pkg.protectedBroadcasts.add("foo8"); pkg.parentPackage = new PackageParser.Package("foo9"); pkg.childPackages = new ArrayList<>(); pkg.childPackages.add(new PackageParser.Package("bar")); pkg.staticSharedLibName = "foo23"; pkg.staticSharedLibVersion = 100; pkg.usesStaticLibraries = new ArrayList<>(); pkg.usesStaticLibraries.add("foo23"); pkg.usesStaticLibrariesCertDigests = new String[] { "digest" }; pkg.usesStaticLibrariesVersions = new int[] { 100 }; pkg.libraryNames = new ArrayList<>(); pkg.libraryNames.add("foo10"); pkg.usesLibraries = new ArrayList<>(); pkg.usesLibraries.add("foo11"); pkg.usesOptionalLibraries = new ArrayList<>(); pkg.usesOptionalLibraries.add("foo12"); pkg.usesLibraryFiles = new String[] { "foo13"}; pkg.mOriginalPackages = new ArrayList<>(); pkg.mOriginalPackages.add("foo14"); pkg.mRealPackage = "foo15"; pkg.mAdoptPermissions = new ArrayList<>(); pkg.mAdoptPermissions.add("foo16"); pkg.mAppMetaData = new Bundle(); pkg.mVersionName = "foo17"; pkg.mSharedUserId = "foo18"; pkg.mSignatures = new Signature[] { new Signature(new byte[16]) }; pkg.mCertificates = new Certificate[][] { new Certificate[] { null }}; pkg.mExtras = new Bundle(); pkg.mRestrictedAccountType = "foo19"; pkg.mRequiredAccountType = "foo20"; pkg.mOverlayTarget = "foo21"; pkg.mOverlayPriority = 100; pkg.mSigningKeys = new ArraySet<>(); pkg.mUpgradeKeySets = new ArraySet<>(); pkg.mKeySetMapping = new ArrayMap<>(); pkg.cpuAbiOverride = "foo22"; pkg.restrictUpdateHash = new byte[16]; pkg.preferredActivityFilters = new ArrayList<>(); pkg.preferredActivityFilters.add(new PackageParser.ActivityIntentInfo( new PackageParser.Activity(dummy, new ActivityInfo()))); pkg.configPreferences = new ArrayList<>(); pkg.configPreferences.add(new ConfigurationInfo()); pkg.reqFeatures = new ArrayList<>(); pkg.reqFeatures.add(new FeatureInfo()); pkg.featureGroups = new ArrayList<>(); pkg.featureGroups.add(new FeatureGroupInfo()); } private static void assertAllFieldsExist(PackageParser.Package pkg) throws Exception { Field[] fields = PackageParser.Package.class.getDeclaredFields(); Set nonSerializedFields = new HashSet<>(); nonSerializedFields.add("mExtras"); nonSerializedFields.add("packageUsageTimeMillis"); for (Field f : fields) { final Class fieldType = f.getType(); if (nonSerializedFields.contains(f.getName())) { continue; } if (List.class.isAssignableFrom(fieldType)) { // Sanity check for list fields: Assume they're non-null and contain precisely // one element. List list = (List) f.get(pkg); assertNotNull("List was null: " + f, list); assertEquals(1, list.size()); } else if (fieldType.getComponentType() != null) { // Sanity check for array fields: Assume they're non-null and contain precisely // one element. Object array = f.get(pkg); assertNotNull(Array.get(array, 0)); } else if (fieldType == String.class) { // String fields: Check that they're set to "foo". String value = (String) f.get(pkg); assertTrue("Bad value for field: " + f, value != null && value.startsWith("foo")); } else if (fieldType == int.class) { // int fields: Check that they're set to 100. int value = (int) f.get(pkg); assertEquals("Bad value for field: " + f, 100, value); } else { // All other fields: Check that they're set. Object o = f.get(pkg); assertNotNull("Field was null: " + f, o); } } } }