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

Commit aef1ff05 authored by Gavin Corkery's avatar Gavin Corkery Committed by Android (Google) Code Review
Browse files

Merge "Allow PackageManager to retrieve inactive/factory APEXs." into qt-dev

parents 0edeaa36 ef441722
Loading
Loading
Loading
Loading
+14 −4
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ import android.annotation.Nullable;
import android.annotation.StringRes;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.apex.ApexInfo;
import android.app.ActivityTaskManager;
import android.app.ActivityThread;
import android.app.ResourcesManager;
@@ -8382,20 +8383,29 @@ public class PackageParser {
     * PackageInfo parser specifically for apex files.
     * NOTE: It will collect certificates
     *
     * @param apexFile
     * @param apexInfo
     * @return PackageInfo
     * @throws PackageParserException
     */
    public static PackageInfo generatePackageInfoFromApex(File apexFile, int flags)
    public static PackageInfo generatePackageInfoFromApex(ApexInfo apexInfo, int flags)
            throws PackageParserException {
        PackageParser pp = new PackageParser();
        File apexFile = new File(apexInfo.packagePath);
        final Package p = pp.parsePackage(apexFile, flags, false);
        PackageUserState state = new PackageUserState();
        PackageInfo pi = generatePackageInfo(p, EmptyArray.INT, flags, 0, 0,
                Collections.emptySet(), state);

        pi.applicationInfo.sourceDir = apexFile.getPath();
        pi.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_INSTALLED;
        if (apexInfo.isFactory) {
            pi.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
        } else {
            pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
        }
        if (apexInfo.isActive) {
            pi.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED;
        } else {
            pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_INSTALLED;
        }
        pi.isApex = true;

        // Collect certificates
+12 −2
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import android.apex.ApexInfo;
import android.content.Context;
import android.content.pm.PackageParser.Component;
import android.content.pm.PackageParser.Package;
@@ -497,10 +498,17 @@ public class PackageParserTest {

    @Test
    public void testApexPackageInfoGeneration() throws Exception {
        File apexFile = copyRawResourceToFile("com.android.tzdata.apex",
        String apexPackageName = "com.android.tzdata.apex";
        File apexFile = copyRawResourceToFile(apexPackageName,
                R.raw.com_android_tzdata);
        ApexInfo apexInfo = new ApexInfo();
        apexInfo.isActive = true;
        apexInfo.isFactory = false;
        apexInfo.packageName = apexPackageName;
        apexInfo.packagePath = apexFile.getPath();
        apexInfo.versionCode = 191000070;
        int flags = PackageManager.GET_META_DATA | PackageManager.GET_SIGNING_CERTIFICATES;
        PackageInfo pi = PackageParser.generatePackageInfoFromApex(apexFile, flags);
        PackageInfo pi = PackageParser.generatePackageInfoFromApex(apexInfo, flags);
        assertEquals("com.google.android.tzdata", pi.applicationInfo.packageName);
        assertTrue(pi.applicationInfo.enabled);
        assertEquals(28, pi.applicationInfo.targetSdkVersion);
@@ -515,5 +523,7 @@ public class PackageParserTest {
        assertNotNull(pi.signingInfo);
        assertTrue(pi.signingInfo.getApkContentsSigners().length > 0);
        assertTrue(pi.isApex);
        assertTrue((pi.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0);
        assertTrue((pi.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0);
    }
}
+168 −42
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.pm;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.apex.ApexInfo;
@@ -26,6 +27,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
@@ -42,8 +44,13 @@ import com.android.internal.util.IndentingPrintWriter;

import java.io.File;
import java.io.PrintWriter;
import java.util.Collection;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;

/**
 * ApexManager class handles communications with the apex service to perform operation and queries,
@@ -59,10 +66,10 @@ class ApexManager {
     * AndroidManifest.xml}
     *
     * <p>Note that key of this map is {@code packageName} field of the corresponding {@code
     * AndroidManfiset.xml}.
     * AndroidManifest.xml}.
      */
    @GuardedBy("mLock")
    private ArrayMap<String, PackageInfo> mActivePackagesCache;
    private List<PackageInfo> mAllPackagesCache;
    /**
     * A map from {@code apexName} to the {@Link PackageInfo} generated from the {@code
     * AndroidManifest.xml}.
@@ -74,6 +81,7 @@ class ApexManager {
    @GuardedBy("mLock")
    private ArrayMap<String, PackageInfo> mApexNameToPackageInfoCache;


    ApexManager(Context context) {
        try {
            mApexService = IApexService.Stub.asInterface(
@@ -84,6 +92,15 @@ class ApexManager {
        mContext = context;
    }

    static final int MATCH_ACTIVE_PACKAGE = 1 << 0;
    static final int MATCH_FACTORY_PACKAGE = 1 << 1;
    @IntDef(
            flag = true,
            prefix = { "MATCH_"},
            value = {MATCH_ACTIVE_PACKAGE, MATCH_FACTORY_PACKAGE})
    @Retention(RetentionPolicy.SOURCE)
    @interface PackageInfoFlags{}

    void systemReady() {
        mContext.registerReceiver(new BroadcastReceiver() {
            @Override
@@ -94,16 +111,18 @@ class ApexManager {
        }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
    }

    private void populateActivePackagesCacheIfNeeded() {
    private void populateAllPackagesCacheIfNeeded() {
        synchronized (mLock) {
            if (mActivePackagesCache != null) {
            if (mAllPackagesCache != null) {
                return;
            }
            mActivePackagesCache = new ArrayMap<>();
            mApexNameToPackageInfoCache = new ArrayMap<>();
            try {
                final ApexInfo[] activePkgs = mApexService.getActivePackages();
                for (ApexInfo ai : activePkgs) {
                mAllPackagesCache = new ArrayList<>();
                HashSet<String> activePackagesSet = new HashSet<>();
                HashSet<String> factoryPackagesSet = new HashSet<>();
                final ApexInfo[] allPkgs = mApexService.getAllPackages();
                for (ApexInfo ai : allPkgs) {
                    // If the device is using flattened APEX, don't report any APEX
                    // packages since they won't be managed or updated by PackageManager.
                    if ((new File(ai.packagePath)).isDirectory()) {
@@ -111,11 +130,27 @@ class ApexManager {
                    }
                    try {
                        final PackageInfo pkg = PackageParser.generatePackageInfoFromApex(
                                new File(ai.packagePath), PackageManager.GET_META_DATA
                                ai, PackageManager.GET_META_DATA
                                        | PackageManager.GET_SIGNING_CERTIFICATES);
                        mActivePackagesCache.put(pkg.packageName, pkg);
                        mAllPackagesCache.add(pkg);
                        if (ai.isActive) {
                            if (activePackagesSet.contains(pkg.packageName)) {
                                throw new IllegalStateException(
                                        "Two active packages have the same name: "
                                                + pkg.packageName);
                            }
                            activePackagesSet.add(ai.packageName);
                            // TODO(b/132324953): remove.
                            mApexNameToPackageInfoCache.put(ai.packageName, pkg);
                        }
                        if (ai.isFactory) {
                            if (factoryPackagesSet.contains(pkg.packageName)) {
                                throw new IllegalStateException(
                                        "Two factory packages have the same name: "
                                                + pkg.packageName);
                            }
                            factoryPackagesSet.add(ai.packageName);
                        }
                    } catch (PackageParserException pe) {
                        throw new IllegalStateException("Unable to parse: " + ai, pe);
                    }
@@ -128,41 +163,85 @@ class ApexManager {
    }

    /**
     * Retrieves information about an active APEX package.
     * Retrieves information about an APEX package.
     *
     * @param packageName the package name to look for. Note that this is the package name reported
     *                    in the APK container manifest (i.e. AndroidManifest.xml), which might
     *                    differ from the one reported in the APEX manifest (i.e.
     *                    apex_manifest.json).
     * @param flags the type of package to return. This may match to active packages
     *              and factory (pre-installed) packages.
     * @return a PackageInfo object with the information about the package, or null if the package
     *         is not found.
     */
    @Nullable PackageInfo getActivePackage(String packageName) {
        populateActivePackagesCacheIfNeeded();
        return mActivePackagesCache.get(packageName);
    @Nullable PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags) {
        populateAllPackagesCacheIfNeeded();
        boolean matchActive = (flags & MATCH_ACTIVE_PACKAGE) != 0;
        boolean matchFactory = (flags & MATCH_FACTORY_PACKAGE) != 0;
        for (PackageInfo packageInfo: mAllPackagesCache) {
            if (!packageInfo.packageName.equals(packageName)) {
                continue;
            }
            if ((!matchActive || isActive(packageInfo))
                    && (!matchFactory || isFactory(packageInfo))) {
                return packageInfo;
            }
        }
        return null;
    }

    /**
     * Returns a {@link PackageInfo} for an APEX package keyed by it's {@code apexName}.
     * Returns a {@link PackageInfo} for an active APEX package keyed by it's {@code apexName}.
     *
     * @deprecated this API will soon be deleted, please don't depend on it.
     */
    // TODO(b/132324953): delete.
    @Deprecated
    @Nullable PackageInfo getPackageInfoForApexName(String apexName) {
        populateActivePackagesCacheIfNeeded();
        populateAllPackagesCacheIfNeeded();
        return mApexNameToPackageInfoCache.get(apexName);
    }

    /**
     * Retrieves information about all active APEX packages.
     *
     * @return a Collection of PackageInfo object, each one containing information about a different
     * @return a List of PackageInfo object, each one containing information about a different
     *         active package.
     */
    Collection<PackageInfo> getActivePackages() {
        populateActivePackagesCacheIfNeeded();
        return mActivePackagesCache.values();
    List<PackageInfo> getActivePackages() {
        populateAllPackagesCacheIfNeeded();
        return mAllPackagesCache
                .stream()
                .filter(item -> isActive(item))
                .collect(Collectors.toList());
    }

    /**
     * Retrieves information about all active pre-installed APEX packages.
     *
     * @return a List of PackageInfo object, each one containing information about a different
     *         active pre-installed package.
     */
    List<PackageInfo> getFactoryPackages() {
        populateAllPackagesCacheIfNeeded();
        return mAllPackagesCache
                .stream()
                .filter(item -> isFactory(item))
                .collect(Collectors.toList());
    }

    /**
     * Retrieves information about all inactive APEX packages.
     *
     * @return a List of PackageInfo object, each one containing information about a different
     *         inactive package.
     */
    List<PackageInfo> getInactivePackages() {
        populateAllPackagesCacheIfNeeded();
        return mAllPackagesCache
                .stream()
                .filter(item -> !isActive(item))
                .collect(Collectors.toList());
    }

    /**
@@ -172,8 +251,13 @@ class ApexManager {
     * @return {@code true} if {@code packageName} is an apex package.
     */
    boolean isApexPackage(String packageName) {
        populateActivePackagesCacheIfNeeded();
        return mActivePackagesCache.containsKey(packageName);
        populateAllPackagesCacheIfNeeded();
        for (PackageInfo packageInfo : mAllPackagesCache) {
            if (packageInfo.packageName.equals(packageName)) {
                return true;
            }
        }
        return false;
    }

    /**
@@ -301,20 +385,39 @@ class ApexManager {
    }

    /**
     * Dumps various state information to the provided {@link PrintWriter} object.
     * Whether an APEX package is active or not.
     *
     * @param pw the {@link PrintWriter} object to send information to.
     * @param packageInfo the package to check
     * @return {@code true} if this package is active, {@code false} otherwise.
     */
    private static boolean isActive(PackageInfo packageInfo) {
        return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0;
    }

    /**
     * Whether the APEX package is pre-installed or not.
     *
     * @param packageInfo the package to check
     * @return {@code true} if this package is pre-installed, {@code false} otherwise.
     */
    private static boolean isFactory(PackageInfo packageInfo) {
        return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
    }

    /**
     * Dump information about the packages contained in a particular cache
     * @param packagesCache the cache to print information about.
     * @param packageName a {@link String} containing a package name, or {@code null}. If set, only
     *                    information about that specific package will be dumped.
     * @param ipw the {@link IndentingPrintWriter} object to send information to.
     */
    void dump(PrintWriter pw, @Nullable String packageName) {
        final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", 120);
    void dumpFromPackagesCache(
            List<PackageInfo> packagesCache,
            @Nullable String packageName,
            IndentingPrintWriter ipw) {
        ipw.println();
        ipw.println("Active APEX packages:");
        ipw.increaseIndent();
        try {
            populateActivePackagesCacheIfNeeded();
            for (PackageInfo pi : mActivePackagesCache.values()) {
        for (PackageInfo pi : packagesCache) {
            if (packageName != null && !packageName.equals(pi.packageName)) {
                continue;
            }
@@ -322,10 +425,33 @@ class ApexManager {
            ipw.increaseIndent();
            ipw.println("Version: " + pi.versionCode);
            ipw.println("Path: " + pi.applicationInfo.sourceDir);
            ipw.println("IsActive: " + isActive(pi));
            ipw.println("IsFactory: " + isFactory(pi));
            ipw.decreaseIndent();
        }
        ipw.decreaseIndent();
        ipw.println();
    }

    /**
     * Dumps various state information to the provided {@link PrintWriter} object.
     *
     * @param pw the {@link PrintWriter} object to send information to.
     * @param packageName a {@link String} containing a package name, or {@code null}. If set, only
     *                    information about that specific package will be dumped.
     */
    void dump(PrintWriter pw, @Nullable String packageName) {
        final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", 120);
        try {
            populateAllPackagesCacheIfNeeded();
            ipw.println();
            ipw.println("Active APEX packages:");
            dumpFromPackagesCache(getActivePackages(), packageName, ipw);
            ipw.println("Inactive APEX packages:");
            dumpFromPackagesCache(getInactivePackages(), packageName, ipw);
            ipw.println("Factory APEX packages:");
            dumpFromPackagesCache(getFactoryPackages(), packageName, ipw);
            ipw.increaseIndent();
            ipw.println("APEX session state:");
            ipw.increaseIndent();
            final ApexSessionInfo[] sessions = mApexService.getSessions();
@@ -360,6 +486,6 @@ class ApexManager {
    }

    public void onBootCompleted() {
        populateActivePackagesCacheIfNeeded();
        populateAllPackagesCacheIfNeeded();
    }
}
 No newline at end of file
+17 −7
Original line number Diff line number Diff line
@@ -4220,6 +4220,11 @@ public class PackageManagerService extends IPackageManager.Stub
            final boolean matchFactoryOnly = (flags & MATCH_FACTORY_ONLY) != 0;
            if (matchFactoryOnly) {
                // Instant app filtering for APEX modules is ignored
                if ((flags & MATCH_APEX) != 0) {
                    return mApexManager.getPackageInfo(packageName,
                            ApexManager.MATCH_FACTORY_PACKAGE);
                }
                final PackageSetting ps = mSettings.getDisabledSystemPkgLPr(packageName);
                if (ps != null) {
                    if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
@@ -4230,7 +4235,6 @@ public class PackageManagerService extends IPackageManager.Stub
                    }
                    return generatePackageInfo(ps, flags, userId);
                }
                // TODO(b/123680735): support MATCH_APEX|MATCH_FACTORY_ONLY case
            }
            PackageParser.Package p = mPackages.get(packageName);
@@ -4260,9 +4264,8 @@ public class PackageManagerService extends IPackageManager.Stub
                }
                return generatePackageInfo(ps, flags, userId);
            }
            //
            if (!matchFactoryOnly && (flags & MATCH_APEX) != 0) {
                return mApexManager.getActivePackage(packageName);
                return mApexManager.getPackageInfo(packageName, ApexManager.MATCH_ACTIVE_PACKAGE);
            }
        }
        return null;
@@ -8558,6 +8561,7 @@ public class PackageManagerService extends IPackageManager.Stub
        flags = updateFlagsForPackage(flags, userId, null);
        final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
        final boolean listApex = (flags & MATCH_APEX) != 0;
        final boolean listFactory = (flags & MATCH_FACTORY_ONLY) != 0;
        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                false /* requireFullPermission */, false /* checkShell */,
@@ -8598,10 +8602,15 @@ public class PackageManagerService extends IPackageManager.Stub
                }
            }
            if (listApex) {
                // TODO(b/119767311): include uninstalled/inactive APEX if
                //  MATCH_UNINSTALLED_PACKAGES is set.
                if (listFactory) {
                    list.addAll(mApexManager.getFactoryPackages());
                } else {
                    list.addAll(mApexManager.getActivePackages());
                }
                if (listUninstalled) {
                    list.addAll(mApexManager.getInactivePackages());
                }
            }
            return new ParceledListSlice<>(list);
        }
    }
@@ -24890,7 +24899,8 @@ public class PackageManagerService extends IPackageManager.Stub
                return;
            }
            final ApexManager am = PackageManagerService.this.mApexManager;
            PackageInfo activePackage = am.getActivePackage(packageName);
            PackageInfo activePackage = am.getPackageInfo(packageName,
                    ApexManager.MATCH_ACTIVE_PACKAGE);
            if (activePackage == null) {
                adapter.onPackageDeleted(packageName, PackageManager.DELETE_FAILED_ABORTED,
                        packageName + " is not an apex package");