Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fb2afbf7 authored by Narayan Kamath's avatar Narayan Kamath
Browse files

PackageParser: Add serialization mechanism for parse results.

Also adds unit tests that assert that the cached value is equivalent
to the parsed value.

bug: 30792387
Test: PackageParserTest
Change-Id: Ibf6dfd1225243b436e3d7473170c2ccc31cfbbd7
parent 9b011937
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -1883,7 +1883,8 @@ public class IntentFilter implements Parcelable {
        */
    }

    private IntentFilter(Parcel source) {
    /** @hide */
    public IntentFilter(Parcel source) {
        mActions = new ArrayList<String>();
        source.readStringList(mActions);
        if (source.readInt() != 0) {
+618 −19

File changed.

Preview size limit exceeded, changes collapsed.

+424 −0
Original line number Diff line number Diff line
@@ -15,14 +15,37 @@
 */
package com.android.server.pm;

import android.annotation.TestApi;
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;
@@ -87,6 +110,36 @@ public class PackageParserTest {
        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);
    }

    /**
     * A trivial subclass of package parser that only caches the package name, and throws away
     * all other information.
@@ -102,4 +155,375 @@ public class PackageParserTest {
            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.mOverlayPriority, b.mOverlayPriority);
        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.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.deviceEncryptedDataDir, that.deviceEncryptedDataDir);
        assertEquals(a.credentialProtectedDataDir, that.credentialProtectedDataDir);
        assertEquals(a.credentialEncryptedDataDir, that.credentialEncryptedDataDir);
        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.mOverlayPriority = 100;
        pkg.mTrustedOverlay = true;
        pkg.use32bitAbi = true;
        pkg.packageName = "foo";
        pkg.splitNames = new String[] { "foo" };
        pkg.volumeUuid = "foo";
        pkg.codePath = "foo";
        pkg.baseCodePath = "foo";
        pkg.splitCodePaths = new String[] { "foo" };
        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("foo");

        pkg.protectedBroadcasts = new ArrayList<>();
        pkg.protectedBroadcasts.add("foo");

        pkg.parentPackage = new PackageParser.Package("foo");

        pkg.childPackages = new ArrayList<>();
        pkg.childPackages.add(new PackageParser.Package("bar"));

        pkg.libraryNames = new ArrayList<>();
        pkg.libraryNames.add("foo");

        pkg.usesLibraries = new ArrayList<>();
        pkg.usesLibraries.add("foo");

        pkg.usesOptionalLibraries = new ArrayList<>();
        pkg.usesOptionalLibraries.add("foo");

        pkg.usesLibraryFiles = new String[] { "foo "};

        pkg.mOriginalPackages = new ArrayList<>();
        pkg.mOriginalPackages.add("foo");

        pkg.mRealPackage = "foo";

        pkg.mAdoptPermissions = new ArrayList<>();
        pkg.mAdoptPermissions.add("foo");

        pkg.mAppMetaData = new Bundle();
        pkg.mVersionName = "foo";
        pkg.mSharedUserId = "foo";
        pkg.mSignatures = new Signature[] { new Signature(new byte[16]) };
        pkg.mCertificates = new Certificate[][] { new Certificate[] { null }};
        pkg.mExtras = new Bundle();
        pkg.mRestrictedAccountType = "foo";
        pkg.mRequiredAccountType = "foo";
        pkg.mOverlayTarget = "foo";
        pkg.mSigningKeys = new ArraySet<>();
        pkg.mUpgradeKeySets = new ArraySet<>();
        pkg.mKeySetMapping = new ArrayMap<>();
        pkg.cpuAbiOverride = "foo";
        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<String> 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);
                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);
                assertEquals("foo", value);
            } else if (fieldType == int.class) {
                // int fields: Check that they're set to 100.
                int value = (int) f.get(pkg);
                assertEquals(100, value);
            } else {
                // All other fields: Check that they're set.
                Object o = f.get(pkg);
                assertNotNull("Field was null: " + f.getName(), o);
            }
        }
    }
}