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

Commit efbb3196 authored by Winson's avatar Winson
Browse files

Move package parsing to services.jar

This facilitates exposing the Parsed_ classes as
@SystemApi(client = SYSTEM_SERVER) while keeping everything inside
services.jar rather than having it split across both jars.

This reverts getPackageArchiveInfo to use legacy PackageParser, since
framework.jar can no longer access the moved classes.

Bug: 214038417

Test: presubmit, no logic changes

Change-Id: I152d70fb4f643d32efb012cfb20b0fbc5f88f2d8
parent 40b0a8e0
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -10,7 +10,10 @@ package {
android_test {
    name: "PackageManagerPerfTests",

    srcs: ["src/**/*.java"],
    srcs: [
        "src/**/*.java",
        "src/**/*.kt",
    ],

    static_libs: [
        "platform-compat-test-rules",
@@ -21,6 +24,7 @@ android_test {
        "apct-perftests-utils",
        "collector-device-lib-platform",
        "cts-install-lib-java",
        "services.core",
    ],

    libs: ["android.test.base"],
+18 −9
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 * Copyright (C) 2022 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.
@@ -19,9 +19,6 @@ package android.os
import android.content.pm.PackageParser
import android.content.pm.PackageParserCacheHelper.ReadHelper
import android.content.pm.PackageParserCacheHelper.WriteHelper
import android.content.pm.parsing.ParsingPackageImpl
import android.content.pm.parsing.ParsingPackageRead
import android.content.pm.parsing.ParsingPackageUtils
import android.content.pm.parsing.result.ParseInput
import android.content.pm.parsing.result.ParseTypeImpl
import android.content.res.TypedArray
@@ -29,6 +26,8 @@ import android.perftests.utils.BenchmarkState
import android.perftests.utils.PerfStatusReporter
import androidx.test.filters.LargeTest
import com.android.internal.util.ConcurrentUtils
import com.android.server.pm.pkg.parsing.ParsingPackageImpl
import com.android.server.pm.pkg.parsing.ParsingPackageUtils
import libcore.io.IoUtils
import org.junit.Rule
import org.junit.Test
@@ -42,7 +41,7 @@ import java.util.concurrent.TimeUnit

@LargeTest
@RunWith(Parameterized::class)
class PackageParsingPerfTest {
public class PackageParsingPerfTest {

    companion object {
        private const val PARALLEL_QUEUE_CAPACITY = 10
@@ -196,8 +195,12 @@ class PackageParsingPerfTest {
            // For testing, just disable enforcement to avoid hooking up to compat framework
            ParseTypeImpl(ParseInput.Callback { _, _, _ -> false })
        }
        val parser = ParsingPackageUtils(false, null, null, emptyList(),
            object : ParsingPackageUtils.Callback {
        val parser = ParsingPackageUtils(false,
            null,
            null,
            emptyList(),
            object :
                ParsingPackageUtils.Callback {
                override fun hasFeature(feature: String) = true

                override fun startParsingPackage(
@@ -206,7 +209,12 @@ class PackageParsingPerfTest {
                    path: String,
                    manifestArray: TypedArray,
                    isCoreApp: Boolean
                ) = ParsingPackageImpl(packageName, baseApkPath, path, manifestArray)
                ) = ParsingPackageImpl(
                    packageName,
                    baseApkPath,
                    path,
                    manifestArray
                )
            })

        override fun parseImpl(file: File) =
@@ -268,6 +276,7 @@ class PackageParsingPerfTest {
     * Re-implementation of the server side PackageCacher, as it's inaccessible here.
     */
    class PackageCacher2(cacheDir: File) : PackageCacher<ParsingPackageImpl>(cacheDir) {
        override fun fromParcel(parcel: Parcel) = ParsingPackageImpl(parcel)
        override fun fromParcel(parcel: Parcel) =
            ParsingPackageImpl(parcel)
    }
}
+1 −37
Original line number Diff line number Diff line
@@ -62,6 +62,7 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
@@ -73,12 +74,6 @@ import android.content.pm.SuspendDialogInfo;
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VersionedPackage;
import android.content.pm.dex.ArtManager;
import android.content.pm.parsing.PackageInfoWithoutStateUtils;
import android.content.pm.parsing.ParsingPackage;
import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.result.ParseInput;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
import android.content.pm.pkg.FrameworkPackageUserState;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -2335,37 +2330,6 @@ public class ApplicationPackageManager extends PackageManager {
        return info.loadLabel(this);
    }

    @Nullable
    public PackageInfo getPackageArchiveInfo(@NonNull String archiveFilePath, int flags) {
        return getPackageArchiveInfo(archiveFilePath, PackageInfoFlags.of(flags));
    }

    @Nullable
    public PackageInfo getPackageArchiveInfo(@NonNull String archiveFilePath,
            PackageInfoFlags flags) {
        long flagsBits = flags.getValue();
        if ((flagsBits & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE
                | PackageManager.MATCH_DIRECT_BOOT_AWARE)) == 0) {
            // Caller expressed no opinion about what encryption
            // aware/unaware components they want to see, so match both
            flagsBits |= PackageManager.MATCH_DIRECT_BOOT_AWARE
                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
        }

        boolean collectCertificates = (flagsBits & PackageManager.GET_SIGNATURES) != 0
                || (flagsBits & PackageManager.GET_SIGNING_CERTIFICATES) != 0;

        ParseInput input = ParseTypeImpl.forParsingWithoutPlatformCompat().reset();
        ParseResult<ParsingPackage> result = ParsingPackageUtils.parseDefault(input,
                new File(archiveFilePath), 0, getPermissionManager().getSplitPermissions(),
                collectCertificates);
        if (result.isError()) {
            return null;
        }
        return PackageInfoWithoutStateUtils.generate(result.getResult(), null, flagsBits, 0, 0,
                null, FrameworkPackageUserState.DEFAULT, UserHandle.getCallingUserId());
    }

    @Override
    public int installExistingPackage(String packageName) throws NameNotFoundException {
        return installExistingPackage(packageName, INSTALL_REASON_UNKNOWN);
+26 −4
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.dex.ArtManager;
import android.content.pm.pkg.FrameworkPackageUserState;
import android.content.pm.verify.domain.DomainVerificationManager;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -88,6 +89,7 @@ import com.android.internal.util.DataClass;

import dalvik.system.VMRuntime;

import java.io.File;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.security.cert.Certificate;
@@ -7891,8 +7893,7 @@ public abstract class PackageManager {
    @Deprecated
    @Nullable
    public PackageInfo getPackageArchiveInfo(@NonNull String archiveFilePath, int flags) {
        throw new UnsupportedOperationException(
                "getPackageArchiveInfo() not implemented in subclass");
        return getPackageArchiveInfo(archiveFilePath, PackageInfoFlags.of(flags));
    }

    /**
@@ -7901,8 +7902,29 @@ public abstract class PackageManager {
    @Nullable
    public PackageInfo getPackageArchiveInfo(@NonNull String archiveFilePath,
            @NonNull PackageInfoFlags flags) {
        throw new UnsupportedOperationException(
                "getPackageArchiveInfo() not implemented in subclass");
        long flagsBits = flags.getValue();
        final PackageParser parser = new PackageParser();
        parser.setCallback(new PackageParser.CallbackImpl(this));
        final File apkFile = new File(archiveFilePath);
        try {
            if ((flagsBits & (MATCH_DIRECT_BOOT_UNAWARE | MATCH_DIRECT_BOOT_AWARE)) != 0) {
                // Caller expressed an explicit opinion about what encryption
                // aware/unaware components they want to see, so fall through and
                // give them what they want
            } else {
                // Caller expressed no opinion, so match everything
                flagsBits |= MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
            }

            PackageParser.Package pkg = parser.parsePackage(apkFile, 0, false);
            if ((flagsBits & GET_SIGNATURES) != 0) {
                PackageParser.collectCertificates(pkg, false /* skipVerify */);
            }
            return PackageParser.generatePackageInfo(pkg, null, (int) flagsBits, 0, 0, null,
                    FrameworkPackageUserState.DEFAULT);
        } catch (PackageParser.PackageParserException e) {
            return null;
        }
    }

    /**
+206 −13
Original line number Diff line number Diff line
/*
 * Copyright (C) 2007 The Android Open Source Project
 * Copyright (C) 2022 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.
@@ -37,6 +37,8 @@ import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTEN
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
import static android.os.Build.VERSION_CODES.O;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
@@ -55,11 +57,9 @@ import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.overlay.OverlayPaths;
import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.pkg.FrameworkPackageUserState;
import android.content.pm.pkg.PackageUserStateUtils;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
import android.content.pm.pkg.FrameworkPackageUserState;
import android.content.res.ApkAssets;
import android.content.res.AssetManager;
import android.content.res.Configuration;
@@ -68,6 +68,7 @@ import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
import android.os.FileUtils;
import android.os.Parcel;
import android.os.Parcelable;
@@ -83,6 +84,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.Base64;
import android.util.DebugUtils;
import android.util.DisplayMetrics;
import android.util.IntArray;
import android.util.Log;
@@ -128,6 +130,7 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

@@ -148,7 +151,7 @@ import java.util.UUID;
 * </ul>
 *
 * @deprecated This class is mostly unused and no new changes should be added to it. Use
 * {@link android.content.pm.parsing.ParsingPackageUtils} and related parsing v2 infrastructure in
 * ParsingPackageUtils and related parsing v2 infrastructure in
 * the core/services parsing subpackages. Or for a quick parse of a provided APK, use
 * {@link PackageManager#getPackageArchiveInfo(String, int)}.
 *
@@ -655,7 +658,7 @@ public class PackageParser {

        // If available for the target user, or trying to match uninstalled packages and it's
        // a system app.
        return PackageUserStateUtils.isAvailable(state, flags)
        return isAvailable(state, flags)
                || (appInfo != null && appInfo.isSystemApp()
                        && ((flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0
                        || (flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) != 0));
@@ -765,7 +768,7 @@ public class PackageParser {
                final ActivityInfo[] res = new ActivityInfo[N];
                for (int i = 0; i < N; i++) {
                    final Activity a = p.activities.get(i);
                    if (PackageUserStateUtils.isMatch(state, a.info, flags)) {
                    if (isMatch(state, a.info, flags)) {
                        if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(a.className)) {
                            continue;
                        }
@@ -782,7 +785,7 @@ public class PackageParser {
                final ActivityInfo[] res = new ActivityInfo[N];
                for (int i = 0; i < N; i++) {
                    final Activity a = p.receivers.get(i);
                    if (PackageUserStateUtils.isMatch(state, a.info, flags)) {
                    if (isMatch(state, a.info, flags)) {
                        res[num++] = generateActivityInfo(a, flags, state, userId);
                    }
                }
@@ -796,7 +799,7 @@ public class PackageParser {
                final ServiceInfo[] res = new ServiceInfo[N];
                for (int i = 0; i < N; i++) {
                    final Service s = p.services.get(i);
                    if (PackageUserStateUtils.isMatch(state, s.info, flags)) {
                    if (isMatch(state, s.info, flags)) {
                        res[num++] = generateServiceInfo(s, flags, state, userId);
                    }
                }
@@ -810,7 +813,7 @@ public class PackageParser {
                final ProviderInfo[] res = new ProviderInfo[N];
                for (int i = 0; i < N; i++) {
                    final Provider pr = p.providers.get(i);
                    if (PackageUserStateUtils.isMatch(state, pr.info, flags)) {
                    if (isMatch(state, pr.info, flags)) {
                        res[num++] = generateProviderInfo(pr, flags, state, userId);
                    }
                }
@@ -7428,7 +7431,7 @@ public class PackageParser {
            mCompileSdkVersionCodename = dest.readString();
            mUpgradeKeySets = (ArraySet<String>) dest.readArraySet(boot);

            mKeySetMapping = ParsingPackageUtils.readKeySetMapping(dest);
            mKeySetMapping = readKeySetMapping(dest);

            cpuAbiOverride = dest.readString();
            use32bitAbi = (dest.readInt() == 1);
@@ -7554,7 +7557,7 @@ public class PackageParser {
            dest.writeInt(mCompileSdkVersion);
            dest.writeString(mCompileSdkVersionCodename);
            dest.writeArraySet(mUpgradeKeySets);
            ParsingPackageUtils.writeKeySetMapping(dest, mKeySetMapping);
            writeKeySetMapping(dest, mKeySetMapping);
            dest.writeString(cpuAbiOverride);
            dest.writeInt(use32bitAbi ? 1 : 0);
            dest.writeByteArray(restrictUpdateHash);
@@ -7977,7 +7980,7 @@ public class PackageParser {
        if (ai.category == ApplicationInfo.CATEGORY_UNDEFINED) {
            ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName);
        }
        ai.seInfoUser = SELinuxUtil.getSeinfoUser(state);
        ai.seInfoUser = getSeinfoUser(state);
        final OverlayPaths overlayPaths = state.getAllOverlayPaths();
        if (overlayPaths != null) {
            ai.resourceDirs = overlayPaths.getResourceDirs().toArray(new String[0]);
@@ -9074,4 +9077,194 @@ public class PackageParser {
            return mCachedSplitApks[0][0];
        }
    }



    public static boolean isMatch(@NonNull FrameworkPackageUserState state,
            ComponentInfo componentInfo, long flags) {
        return isMatch(state, componentInfo.applicationInfo.isSystemApp(),
                componentInfo.applicationInfo.enabled, componentInfo.enabled,
                componentInfo.directBootAware, componentInfo.name, flags);
    }

    public static boolean isMatch(@NonNull FrameworkPackageUserState state, boolean isSystem,
            boolean isPackageEnabled, ComponentInfo component, long flags) {
        return isMatch(state, isSystem, isPackageEnabled, component.isEnabled(),
                component.directBootAware, component.name, flags);
    }

    /**
     * Test if the given component is considered installed, enabled and a match for the given
     * flags.
     *
     * <p>
     * Expects at least one of {@link PackageManager#MATCH_DIRECT_BOOT_AWARE} and {@link
     * PackageManager#MATCH_DIRECT_BOOT_UNAWARE} are specified in {@code flags}.
     * </p>
     */
    public static boolean isMatch(@NonNull FrameworkPackageUserState state, boolean isSystem,
            boolean isPackageEnabled, boolean isComponentEnabled,
            boolean isComponentDirectBootAware, String componentName, long flags) {
        final boolean matchUninstalled = (flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0;
        if (!isAvailable(state, flags) && !(isSystem && matchUninstalled)) {
            return reportIfDebug(false, flags);
        }

        if (!isEnabled(state, isPackageEnabled, isComponentEnabled, componentName, flags)) {
            return reportIfDebug(false, flags);
        }

        if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) {
            if (!isSystem) {
                return reportIfDebug(false, flags);
            }
        }

        final boolean matchesUnaware = ((flags & PackageManager.MATCH_DIRECT_BOOT_UNAWARE) != 0)
                && !isComponentDirectBootAware;
        final boolean matchesAware = ((flags & PackageManager.MATCH_DIRECT_BOOT_AWARE) != 0)
                && isComponentDirectBootAware;
        return reportIfDebug(matchesUnaware || matchesAware, flags);
    }

    public static boolean isAvailable(@NonNull FrameworkPackageUserState state, long flags) {
        // True if it is installed for this user and it is not hidden. If it is hidden,
        // still return true if the caller requested MATCH_UNINSTALLED_PACKAGES
        final boolean matchAnyUser = (flags & PackageManager.MATCH_ANY_USER) != 0;
        final boolean matchUninstalled = (flags & PackageManager.MATCH_UNINSTALLED_PACKAGES) != 0;
        return matchAnyUser
                || (state.isInstalled()
                && (!state.isHidden() || matchUninstalled));
    }

    public static boolean reportIfDebug(boolean result, long flags) {
        if (DEBUG_PARSER && !result) {
            Slog.i(TAG, "No match!; flags: "
                    + DebugUtils.flagsToString(PackageManager.class, "MATCH_", flags) + " "
                    + Debug.getCaller());
        }
        return result;
    }

    public static boolean isEnabled(@NonNull FrameworkPackageUserState state, ComponentInfo componentInfo,
            long flags) {
        return isEnabled(state, componentInfo.applicationInfo.enabled, componentInfo.enabled,
                componentInfo.name, flags);
    }

    public static boolean isEnabled(@NonNull FrameworkPackageUserState state, boolean isPackageEnabled,
            ComponentInfo parsedComponent, long flags) {
        return isEnabled(state, isPackageEnabled, parsedComponent.isEnabled(),
                parsedComponent.name, flags);
    }

    /**
     * Test if the given component is considered enabled.
     */
    public static boolean isEnabled(@NonNull FrameworkPackageUserState state,
            boolean isPackageEnabled, boolean isComponentEnabled, String componentName,
            long flags) {
        if ((flags & MATCH_DISABLED_COMPONENTS) != 0) {
            return true;
        }

        // First check if the overall package is disabled; if the package is
        // enabled then fall through to check specific component
        switch (state.getEnabledState()) {
            case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
            case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER:
                return false;
            case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED:
                if ((flags & MATCH_DISABLED_UNTIL_USED_COMPONENTS) == 0) {
                    return false;
                }
                // fallthrough
            case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
                if (!isPackageEnabled) {
                    return false;
                }
                // fallthrough
            case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
                break;
        }

        // Check if component has explicit state before falling through to
        // the manifest default
        if (state.isComponentEnabled(componentName)) {
            return true;
        } else if (state.isComponentDisabled(componentName)) {
            return false;
        }

        return isComponentEnabled;
    }

    /**
     * Writes the keyset mapping to the provided package. {@code null} mappings are permitted.
     */
    public static void writeKeySetMapping(@NonNull Parcel dest,
            @NonNull Map<String, ArraySet<PublicKey>> keySetMapping) {
        if (keySetMapping == null) {
            dest.writeInt(-1);
            return;
        }

        final int N = keySetMapping.size();
        dest.writeInt(N);

        for (String key : keySetMapping.keySet()) {
            dest.writeString(key);
            ArraySet<PublicKey> keys = keySetMapping.get(key);
            if (keys == null) {
                dest.writeInt(-1);
                continue;
            }

            final int M = keys.size();
            dest.writeInt(M);
            for (int j = 0; j < M; j++) {
                dest.writeSerializable(keys.valueAt(j));
            }
        }
    }

    /**
     * Reads a keyset mapping from the given parcel at the given data position. May return
     * {@code null} if the serialized mapping was {@code null}.
     */
    @NonNull
    public static ArrayMap<String, ArraySet<PublicKey>> readKeySetMapping(@NonNull Parcel in) {
        final int N = in.readInt();
        if (N == -1) {
            return null;
        }

        ArrayMap<String, ArraySet<PublicKey>> keySetMapping = new ArrayMap<>();
        for (int i = 0; i < N; ++i) {
            String key = in.readString();
            final int M = in.readInt();
            if (M == -1) {
                keySetMapping.put(key, null);
                continue;
            }

            ArraySet<PublicKey> keys = new ArraySet<>(M);
            for (int j = 0; j < M; ++j) {
                PublicKey pk =
                        in.readSerializable(PublicKey.class.getClassLoader(), PublicKey.class);
                keys.add(pk);
            }

            keySetMapping.put(key, keys);
        }

        return keySetMapping;
    }

    public static String getSeinfoUser(FrameworkPackageUserState userState) {
        if (userState.isInstantApp()) {
            return ":ephemeralapp:complete";
        }
        return ":complete";
    }
}
Loading