From 9e99a1a5f252989af46c2ada90b6defb691ac9e0 Mon Sep 17 00:00:00 2001 From: Winson Chiu Date: Wed, 19 Oct 2022 17:00:52 +0000 Subject: [PATCH] Finalize ENABLE_FEATURE_SCAN_APEX Removes all of the legacy code and commits this feature as the only branch for scanning APEXes. Bug: 225756739 Test: atest ApexManagerTest Change-Id: I20dbc0a1d51f2ebc7020dc6c5c7b57a7c746886f --- .../android/server/pm/ApexPackageInfo.java | 419 ------------------ .../com/android/server/pm/AppDataHelper.java | 2 +- .../com/android/server/pm/ComputerEngine.java | 174 +++----- .../com/android/server/pm/InitAppsHelper.java | 17 +- .../android/server/pm/InstallingSession.java | 22 +- .../server/pm/PackageManagerService.java | 7 +- .../tests/mockingservicestests/Android.bp | 6 + .../android/server/pm/ApexManagerTest.java | 315 ++++++------- .../android/server/pm/InitAppsHelperTest.kt | 8 +- .../src/com/android/server/pm/MockSystem.kt | 21 +- .../extendedtestutils/ExtendedMockitoUtils.kt | 5 + services/tests/servicestests/Android.bp | 3 - 12 files changed, 253 insertions(+), 746 deletions(-) delete mode 100644 services/core/java/com/android/server/pm/ApexPackageInfo.java rename services/tests/{servicestests => mockingservicestests}/src/com/android/server/pm/ApexManagerTest.java (64%) diff --git a/services/core/java/com/android/server/pm/ApexPackageInfo.java b/services/core/java/com/android/server/pm/ApexPackageInfo.java deleted file mode 100644 index 672ae2eb0fb26..0000000000000 --- a/services/core/java/com/android/server/pm/ApexPackageInfo.java +++ /dev/null @@ -1,419 +0,0 @@ -/* - * 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. - * 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 static com.android.server.pm.ApexManager.MATCH_ACTIVE_PACKAGE; -import static com.android.server.pm.ApexManager.MATCH_FACTORY_PACKAGE; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.apex.ApexInfo; -import android.content.pm.PackageManager; -import android.util.ArrayMap; -import android.util.Pair; -import android.util.PrintWriterPrinter; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.IndentingPrintWriter; -import com.android.internal.util.Preconditions; -import com.android.server.pm.parsing.PackageParser2; -import com.android.server.pm.parsing.pkg.AndroidPackageUtils; -import com.android.server.pm.parsing.pkg.ParsedPackage; -import com.android.server.pm.pkg.AndroidPackage; -import com.android.server.pm.pkg.PackageStateInternal; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; - -import java.io.File; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.concurrent.ExecutorService; - -/** - * A temporary holder to store PackageInfo for scanned apex packages. We will unify the scan/install - * flows of APK and APEX and PMS will be the only source of truth for all package information - * including both APK and APEX. This class will no longer be needed when the migration is done. - */ -class ApexPackageInfo { - public static final boolean ENABLE_FEATURE_SCAN_APEX = true; - - private static final String TAG = "ApexManager"; - private static final String VNDK_APEX_MODULE_NAME_PREFIX = "com.android.vndk."; - - private final Object mLock = new Object(); - - @GuardedBy("mLock") - private List> mAllPackagesCache; - - @Nullable - private final PackageManagerService mPackageManager; - - ApexPackageInfo() { - mPackageManager = null; - } - - ApexPackageInfo(@NonNull PackageManagerService pms) { - mPackageManager = pms; - } - - /** - * Called by package manager service to scan apex package files when device boots up. - * - * @param allPackages All apex packages to scan. - * @param packageParser The package parser to support apex package parsing and caching parsed - * results. - * @param executorService An executor to support parallel package parsing. - */ - List scanApexPackages(ApexInfo[] allPackages, - @NonNull PackageParser2 packageParser, @NonNull ExecutorService executorService) { - synchronized (mLock) { - return scanApexPackagesInternalLocked(allPackages, packageParser, executorService); - } - } - - void notifyScanResult(List scanResults) { - synchronized (mLock) { - notifyScanResultLocked(scanResults); - } - } - - /** - * 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 - Pair getPackageInfo(String packageName, - @ApexManager.PackageInfoFlags int flags) { - synchronized (mLock) { - Preconditions.checkState(mAllPackagesCache != null, - "APEX packages have not been scanned"); - boolean matchActive = (flags & MATCH_ACTIVE_PACKAGE) != 0; - boolean matchFactory = (flags & MATCH_FACTORY_PACKAGE) != 0; - for (int i = 0, size = mAllPackagesCache.size(); i < size; i++) { - final Pair pair = mAllPackagesCache.get(i); - var apexInfo = pair.first; - var pkg = pair.second; - if (!pkg.getPackageName().equals(packageName)) { - continue; - } - if ((matchActive && apexInfo.isActive) - || (matchFactory && apexInfo.isFactory)) { - return pair; - } - } - return null; - } - } - - /** - * Retrieves information about all active APEX packages. - * - * @return list containing information about different active packages. - */ - @NonNull - List> getActivePackages() { - synchronized (mLock) { - Preconditions.checkState(mAllPackagesCache != null, - "APEX packages have not been scanned"); - final List> activePackages = new ArrayList<>(); - for (int i = 0; i < mAllPackagesCache.size(); i++) { - final var pair = mAllPackagesCache.get(i); - if (pair.first.isActive) { - activePackages.add(pair); - } - } - return activePackages; - } - } - - /** - * Retrieves information about all pre-installed APEX packages. - * - * @return list containing information about different pre-installed packages. - */ - @NonNull - List> getFactoryPackages() { - synchronized (mLock) { - Preconditions.checkState(mAllPackagesCache != null, - "APEX packages have not been scanned"); - final List> factoryPackages = new ArrayList<>(); - for (int i = 0; i < mAllPackagesCache.size(); i++) { - final var pair = mAllPackagesCache.get(i); - if (pair.first.isFactory) { - factoryPackages.add(pair); - } - } - return factoryPackages; - } - } - - /** - * Retrieves information about all inactive APEX packages. - * - * @return list containing information about different inactive packages. - */ - @NonNull - List> getInactivePackages() { - synchronized (mLock) { - Preconditions.checkState(mAllPackagesCache != null, - "APEX packages have not been scanned"); - final List> inactivePackages = new ArrayList<>(); - for (int i = 0; i < mAllPackagesCache.size(); i++) { - final var pair = mAllPackagesCache.get(i); - if (!pair.first.isActive) { - inactivePackages.add(pair); - } - } - return inactivePackages; - } - } - - /** - * Checks if {@code packageName} is an apex package. - * - * @param packageName package to check. - * @return {@code true} if {@code packageName} is an apex package. - */ - boolean isApexPackage(String packageName) { - synchronized (mLock) { - Preconditions.checkState(mAllPackagesCache != null, - "APEX packages have not been scanned"); - for (int i = 0, size = mAllPackagesCache.size(); i < size; i++) { - final var pair = mAllPackagesCache.get(i); - if (pair.second.getPackageName().equals(packageName)) { - return true; - } - } - } - return false; - } - - /** - * Called to update cached PackageInfo when installing rebootless APEX. - */ - void notifyPackageInstalled(ApexInfo apexInfo, PackageParser2 packageParser) - throws PackageManagerException { - final int flags = PackageManager.GET_META_DATA - | PackageManager.GET_SIGNING_CERTIFICATES - | PackageManager.GET_SIGNATURES; - final ParsedPackage parsedPackage = packageParser.parsePackage( - new File(apexInfo.modulePath), flags, /* useCaches= */ false); - notifyPackageInstalled(apexInfo, parsedPackage.hideAsFinal()); - } - - void notifyPackageInstalled(ApexInfo apexInfo, AndroidPackage pkg) { - final String packageName = pkg.getPackageName(); - synchronized (mLock) { - for (int i = 0, size = mAllPackagesCache.size(); i < size; i++) { - var pair = mAllPackagesCache.get(i); - var oldApexInfo = pair.first; - var oldApexPkg = pair.second; - if (oldApexInfo.isActive && oldApexPkg.getPackageName().equals(packageName)) { - if (oldApexInfo.isFactory) { - oldApexInfo.isActive = false; - mAllPackagesCache.add(Pair.create(apexInfo, pkg)); - } else { - mAllPackagesCache.set(i, Pair.create(apexInfo, pkg)); - } - break; - } - } - } - } - - /** - * 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); - synchronized (mLock) { - if (mAllPackagesCache == null) { - ipw.println("APEX packages have not been scanned"); - return; - } - } - ipw.println("Active APEX packages:"); - dumpPackages(getActivePackages(), packageName, ipw); - ipw.println("Inactive APEX packages:"); - dumpPackages(getInactivePackages(), packageName, ipw); - ipw.println("Factory APEX packages:"); - dumpPackages(getFactoryPackages(), packageName, ipw); - } - - @GuardedBy("mLock") - private void notifyScanResultLocked(List scanResults) { - mAllPackagesCache = new ArrayList<>(); - final int flags = PackageManager.GET_META_DATA - | PackageManager.GET_SIGNING_CERTIFICATES - | PackageManager.GET_SIGNATURES; - - HashSet activePackagesSet = new HashSet<>(); - HashSet factoryPackagesSet = new HashSet<>(); - for (ApexManager.ScanResult result : scanResults) { - ApexInfo ai = result.apexInfo; - String packageName = result.pkg.getPackageName(); - if (!packageName.equals(result.packageName)) { - throw new IllegalStateException("Unmatched package name: " - + result.packageName + " != " + packageName - + ", path=" + ai.modulePath); - } - mAllPackagesCache.add(Pair.create(ai, result.pkg)); - if (ai.isActive) { - if (!activePackagesSet.add(packageName)) { - throw new IllegalStateException( - "Two active packages have the same name: " + packageName); - } - } - if (ai.isFactory) { - // Don't throw when the duplicating APEX is VNDK APEX - if (!factoryPackagesSet.add(packageName) - && !ai.moduleName.startsWith(VNDK_APEX_MODULE_NAME_PREFIX)) { - throw new IllegalStateException( - "Two factory packages have the same name: " + packageName); - } - } - } - } - - @GuardedBy("mLock") - private List scanApexPackagesInternalLocked(final ApexInfo[] allPkgs, - PackageParser2 packageParser, ExecutorService executorService) { - if (allPkgs == null || allPkgs.length == 0) { - notifyScanResultLocked(Collections.EMPTY_LIST); - return Collections.EMPTY_LIST; - } - - ArrayMap parsingApexInfo = new ArrayMap<>(); - ParallelPackageParser parallelPackageParser = - new ParallelPackageParser(packageParser, executorService); - for (ApexInfo ai : allPkgs) { - File apexFile = new File(ai.modulePath); - parallelPackageParser.submit(apexFile, - ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES); - parsingApexInfo.put(apexFile, ai); - } - - List results = new ArrayList<>(parsingApexInfo.size()); - // Process results one by one - for (int i = 0; i < parsingApexInfo.size(); i++) { - ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take(); - Throwable throwable = parseResult.throwable; - ApexInfo ai = parsingApexInfo.get(parseResult.scanFile); - - if (throwable == null) { - // TODO: When ENABLE_FEATURE_SCAN_APEX is finalized, remove this and the entire - // calling path code - ScanPackageUtils.applyPolicy(parseResult.parsedPackage, - PackageManagerService.SCAN_AS_SYSTEM, - mPackageManager == null ? null : mPackageManager.getPlatformPackage(), - false); - // Calling hideAsFinal to assign derived fields for the app info flags. - AndroidPackage finalPkg = parseResult.parsedPackage.hideAsFinal(); - results.add(new ApexManager.ScanResult(ai, finalPkg, finalPkg.getPackageName())); - } else if (throwable instanceof PackageManagerException) { - throw new IllegalStateException("Unable to parse: " + ai.modulePath, throwable); - } else { - throw new IllegalStateException("Unexpected exception occurred while parsing " - + ai.modulePath, throwable); - } - } - - notifyScanResultLocked(results); - return results; - } - - /** - * @see #dumpPackages(List, String, IndentingPrintWriter) - */ - static void dumpPackageStates(List packageStates, boolean isActive, - @Nullable String packageName, IndentingPrintWriter ipw) { - ipw.println(); - ipw.increaseIndent(); - for (int i = 0, size = packageStates.size(); i < size; i++) { - final var packageState = packageStates.get(i); - var pkg = packageState.getPkg(); - if (packageName != null && !packageName.equals(pkg.getPackageName())) { - continue; - } - ipw.println(pkg.getPackageName()); - ipw.increaseIndent(); - ipw.println("Version: " + pkg.getLongVersionCode()); - ipw.println("Path: " + pkg.getBaseApkPath()); - ipw.println("IsActive: " + isActive); - ipw.println("IsFactory: " + !packageState.isUpdatedSystemApp()); - ipw.println("ApplicationInfo: "); - ipw.increaseIndent(); - // TODO: Dump the package manually - AndroidPackageUtils.generateAppInfoWithoutState(pkg) - .dump(new PrintWriterPrinter(ipw), ""); - ipw.decreaseIndent(); - ipw.decreaseIndent(); - } - ipw.decreaseIndent(); - ipw.println(); - } - - /** - * 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. - */ - static void dumpPackages(List> packagesCache, - @Nullable String packageName, IndentingPrintWriter ipw) { - ipw.println(); - ipw.increaseIndent(); - for (int i = 0, size = packagesCache.size(); i < size; i++) { - final var pair = packagesCache.get(i); - var apexInfo = pair.first; - var pkg = pair.second; - if (packageName != null && !packageName.equals(pkg.getPackageName())) { - continue; - } - ipw.println(pkg.getPackageName()); - ipw.increaseIndent(); - ipw.println("Version: " + pkg.getLongVersionCode()); - ipw.println("Path: " + pkg.getBaseApkPath()); - ipw.println("IsActive: " + apexInfo.isActive); - ipw.println("IsFactory: " + apexInfo.isFactory); - ipw.println("ApplicationInfo: "); - ipw.increaseIndent(); - // TODO: Dump the package manually - AndroidPackageUtils.generateAppInfoWithoutState(pkg) - .dump(new PrintWriterPrinter(ipw), ""); - ipw.decreaseIndent(); - ipw.decreaseIndent(); - } - ipw.decreaseIndent(); - ipw.println(); - } -} \ No newline at end of file diff --git a/services/core/java/com/android/server/pm/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java index b8e1e9ae18329..f3cfa95ff86db 100644 --- a/services/core/java/com/android/server/pm/AppDataHelper.java +++ b/services/core/java/com/android/server/pm/AppDataHelper.java @@ -61,7 +61,7 @@ import java.util.concurrent.Future; /** * Prepares app data for users */ -final class AppDataHelper { +public class AppDataHelper { private static final boolean DEBUG_APP_DATA = false; private final PackageManagerService mPm; diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java index b9967f94876a1..d381e32ed5da4 100644 --- a/services/core/java/com/android/server/pm/ComputerEngine.java +++ b/services/core/java/com/android/server/pm/ComputerEngine.java @@ -63,7 +63,6 @@ import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; -import android.apex.ApexInfo; import android.app.ActivityManager; import android.content.ComponentName; import android.content.Context; @@ -112,6 +111,7 @@ import android.util.LogPrinter; import android.util.LongSparseLongArray; import android.util.MathUtils; import android.util.Pair; +import android.util.PrintWriterPrinter; import android.util.Slog; import android.util.SparseArray; import android.util.Xml; @@ -396,7 +396,6 @@ public class ComputerEngine implements Computer { private final UserManagerService mUserManager; private final PermissionManagerServiceInternal mPermissionManager; private final ApexManager mApexManager; - private final ApexPackageInfo mApexPackageInfo; private final PackageManagerServiceInjector mInjector; private final ComponentResolverApi mComponentResolver; private final InstantAppResolverConnection mInstantAppResolverConnection; @@ -452,7 +451,6 @@ public class ComputerEngine implements Computer { mContext = args.service.mContext; mInjector = args.service.mInjector; mApexManager = args.service.mApexManager; - mApexPackageInfo = args.service.mApexPackageInfo; mInstantAppResolverConnection = args.service.mInstantAppResolverConnection; mDefaultAppProvider = args.service.getDefaultAppProvider(); mDomainVerificationManager = args.service.mDomainVerificationManager; @@ -968,10 +966,8 @@ public class ComputerEngine implements Computer { if (p != null) { PackageStateInternal ps = mSettings.getPackage(packageName); if (ps == null) return null; - if (ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) { - if (!matchApex && p.isApex()) { - return null; - } + if (!matchApex && p.isApex()) { + return null; } if (filterSharedLibPackage(ps, filterCallingUid, userId, flags)) { return null; @@ -987,24 +983,6 @@ public class ComputerEngine implements Computer { } return ai; } - if (!ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) { - if (matchApex) { - // For APKs, PackageInfo.applicationInfo is not exactly the same as ApplicationInfo - // returned from getApplicationInfo, but for APEX packages difference shouldn't be - // very big. - // TODO(b/155328545): generate proper application info for APEXes as well. - int apexFlags = ApexManager.MATCH_ACTIVE_PACKAGE; - if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) { - apexFlags = ApexManager.MATCH_FACTORY_PACKAGE; - } - final var pair = mApexPackageInfo.getPackageInfo(packageName, apexFlags); - if (pair == null) { - return null; - } - return PackageInfoUtils.generateApplicationInfo(pair.second, flags, - PackageUserStateInternal.DEFAULT, userId, null); - } - } if ("android".equals(packageName) || "system".equals(packageName)) { return androidApplication(); } @@ -1553,22 +1531,10 @@ public class ComputerEngine implements Computer { final boolean matchApex = (flags & MATCH_APEX) != 0; if (matchFactoryOnly) { // Instant app filtering for APEX modules is ignored - if (!ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) { - if (matchApex) { - final var pair = mApexPackageInfo.getPackageInfo(packageName, - ApexManager.MATCH_FACTORY_PACKAGE); - if (pair == null) { - return null; - } - return PackageInfoUtils.generate(pair.second, pair.first, flags, null, userId); - } - } final PackageStateInternal ps = mSettings.getDisabledSystemPkg(packageName); if (ps != null) { - if (ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) { - if (!matchApex && ps.getPkg() != null && ps.getPkg().isApex()) { - return null; - } + if (!matchApex && ps.getPkg() != null && ps.getPkg().isApex()) { + return null; } if (filterSharedLibPackage(ps, filterCallingUid, userId, flags)) { return null; @@ -1589,10 +1555,8 @@ public class ComputerEngine implements Computer { } if (p != null) { final PackageStateInternal ps = getPackageStateInternal(p.getPackageName()); - if (ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) { - if (!matchApex && p.isApex()) { - return null; - } + if (!matchApex && p.isApex()) { + return null; } if (filterSharedLibPackage(ps, filterCallingUid, userId, flags)) { return null; @@ -1614,16 +1578,6 @@ public class ComputerEngine implements Computer { } return generatePackageInfo(ps, flags, userId); } - if (!ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) { - if (matchApex) { - final var pair = mApexPackageInfo.getPackageInfo(packageName, - ApexManager.MATCH_ACTIVE_PACKAGE); - if (pair == null) { - return null; - } - return PackageInfoUtils.generate(pair.second, pair.first, flags, null, userId); - } - } return null; } @@ -1692,10 +1646,8 @@ public class ComputerEngine implements Computer { ps = psDisabled; } } - if (ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) { - if (!listApex && ps.getPkg() != null && ps.getPkg().isApex()) { - continue; - } + if (!listApex && ps.getPkg() != null && ps.getPkg().isApex()) { + continue; } if (filterSharedLibPackage(ps, callingUid, userId, flags)) { continue; @@ -1722,10 +1674,8 @@ public class ComputerEngine implements Computer { ps = psDisabled; } } - if (ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) { - if (!listApex && p.isApex()) { - continue; - } + if (!listApex && p.isApex()) { + continue; } if (filterSharedLibPackage(ps, callingUid, userId, flags)) { continue; @@ -1739,22 +1689,6 @@ public class ComputerEngine implements Computer { } } } - if (!ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) { - if (listApex) { - List> pairs; - if (listFactory) { - pairs = mApexPackageInfo.getFactoryPackages(); - } else { - pairs = mApexPackageInfo.getActivePackages(); - } - - for (int index = 0; index < pairs.size(); index++) { - var pair = pairs.get(index); - list.add(PackageInfoUtils.generate(pair.second, pair.first, flags, null, - userId)); - } - } - } return new ParceledListSlice<>(list); } @@ -3155,24 +3089,56 @@ public class ComputerEngine implements Computer { } private void dumpApex(PrintWriter pw, String packageName) { - if (ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) { - final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120); - List activePackages = new ArrayList<>(); - List inactivePackages = new ArrayList<>(); - List factoryActivePackages = new ArrayList<>(); - List factoryInactivePackages = new ArrayList<>(); - generateApexPackageInfo(activePackages, inactivePackages, factoryActivePackages, - factoryInactivePackages); - ipw.println("Active APEX packages:"); - ApexPackageInfo.dumpPackageStates(activePackages, true, packageName, ipw); - ipw.println("Inactive APEX packages:"); - ApexPackageInfo.dumpPackageStates(inactivePackages, false, packageName, ipw); - ipw.println("Factory APEX packages:"); - ApexPackageInfo.dumpPackageStates(factoryActivePackages, true, packageName, ipw); - ApexPackageInfo.dumpPackageStates(factoryInactivePackages, false, packageName, ipw); - } else { - mApexPackageInfo.dump(pw, packageName); + final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120); + List activePackages = new ArrayList<>(); + List inactivePackages = new ArrayList<>(); + List factoryActivePackages = new ArrayList<>(); + List factoryInactivePackages = new ArrayList<>(); + generateApexPackageInfo(activePackages, inactivePackages, factoryActivePackages, + factoryInactivePackages); + ipw.println("Active APEX packages:"); + dumpApexPackageStates(activePackages, true, packageName, ipw); + ipw.println("Inactive APEX packages:"); + dumpApexPackageStates(inactivePackages, false, packageName, ipw); + ipw.println("Factory APEX packages:"); + dumpApexPackageStates(factoryActivePackages, true, packageName, ipw); + dumpApexPackageStates(factoryInactivePackages, false, packageName, ipw); + } + + + /** + * Dump information about the packages contained in a particular cache + * @param packageStates the states 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. + */ + private static void dumpApexPackageStates(List packageStates, + boolean isActive, @Nullable String packageName, IndentingPrintWriter ipw) { + ipw.println(); + ipw.increaseIndent(); + for (int i = 0, size = packageStates.size(); i < size; i++) { + final var packageState = packageStates.get(i); + var pkg = packageState.getPkg(); + if (packageName != null && !packageName.equals(pkg.getPackageName())) { + continue; + } + ipw.println(pkg.getPackageName()); + ipw.increaseIndent(); + ipw.println("Version: " + pkg.getLongVersionCode()); + ipw.println("Path: " + pkg.getBaseApkPath()); + ipw.println("IsActive: " + isActive); + ipw.println("IsFactory: " + !packageState.isUpdatedSystemApp()); + ipw.println("ApplicationInfo: "); + ipw.increaseIndent(); + // TODO: Dump the package manually + AndroidPackageUtils.generateAppInfoWithoutState(pkg) + .dump(new PrintWriterPrinter(ipw), ""); + ipw.decreaseIndent(); + ipw.decreaseIndent(); } + ipw.decreaseIndent(); + ipw.println(); } // The body of findPreferredActivity. @@ -3551,12 +3517,8 @@ public class ComputerEngine implements Computer { @Override public boolean isApexPackage(String packageName) { - if (!ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) { - return mApexPackageInfo.isApexPackage(packageName); - } else { - final AndroidPackage pkg = mPackages.get(packageName); - return pkg != null && pkg.isApex(); - } + final AndroidPackage pkg = mPackages.get(packageName); + return pkg != null && pkg.isApex(); } @Override @@ -4563,10 +4525,8 @@ public class ComputerEngine implements Computer { effectiveFlags |= PackageManager.MATCH_ANY_USER; } if (ps.getPkg() != null) { - if (ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) { - if (!listApex && ps.getPkg().isApex()) { - continue; - } + if (!listApex && ps.getPkg().isApex()) { + continue; } if (filterSharedLibPackage(ps, callingUid, userId, flags)) { continue; @@ -4596,10 +4556,8 @@ public class ComputerEngine implements Computer { if (pkg == null) { continue; } - if (ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) { - if (!listApex && pkg.isApex()) { - continue; - } + if (!listApex && pkg.isApex()) { + continue; } if (filterSharedLibPackage(packageState, Binder.getCallingUid(), userId, flags)) { continue; diff --git a/services/core/java/com/android/server/pm/InitAppsHelper.java b/services/core/java/com/android/server/pm/InitAppsHelper.java index 6f5909615db28..12b5ab8a3160c 100644 --- a/services/core/java/com/android/server/pm/InitAppsHelper.java +++ b/services/core/java/com/android/server/pm/InitAppsHelper.java @@ -72,7 +72,6 @@ final class InitAppsHelper { private final int mSystemScanFlags; private final InstallPackageHelper mInstallPackageHelper; private final ApexManager mApexManager; - private final ApexPackageInfo mApexPackageInfo; private final ExecutorService mExecutorService; /* Tracks how long system scan took */ private long mSystemScanTime; @@ -96,13 +95,11 @@ final class InitAppsHelper { private final List mStubSystemApps = new ArrayList<>(); // TODO(b/198166813): remove PMS dependency - InitAppsHelper(PackageManagerService pm, - ApexManager apexManager, ApexPackageInfo apexPackageInfo, + InitAppsHelper(PackageManagerService pm, ApexManager apexManager, InstallPackageHelper installPackageHelper, List systemPartitions) { mPm = pm; mApexManager = apexManager; - mApexPackageInfo = apexPackageInfo; mInstallPackageHelper = installPackageHelper; mSystemPartitions = systemPartitions; mDirsToScanAsSystem = getSystemScanPartitions(); @@ -165,16 +162,8 @@ final class InitAppsHelper { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanApexPackages"); try { - final List apexScanResults; - if (ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) { - apexScanResults = mInstallPackageHelper.scanApexPackages( - mApexManager.getAllApexInfos(), mSystemParseFlags, mSystemScanFlags, - packageParser, mExecutorService); - } else { - apexScanResults = mApexPackageInfo.scanApexPackages( - mApexManager.getAllApexInfos(), packageParser, mExecutorService); - } - return apexScanResults; + return mInstallPackageHelper.scanApexPackages(mApexManager.getAllApexInfos(), + mSystemParseFlags, mSystemScanFlags, packageParser, mExecutorService); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } diff --git a/services/core/java/com/android/server/pm/InstallingSession.java b/services/core/java/com/android/server/pm/InstallingSession.java index 16b3a815c0754..88469f539f940 100644 --- a/services/core/java/com/android/server/pm/InstallingSession.java +++ b/services/core/java/com/android/server/pm/InstallingSession.java @@ -572,19 +572,15 @@ class InstallingSession { } try (PackageParser2 packageParser = mPm.mInjector.getScanningPackageParser()) { ApexInfo apexInfo = mPm.mApexManager.installPackage(apexes[0]); - if (ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) { - // APEX has been handled successfully by apexd. Let's continue the install flow - // so it will be scanned and registered with the system. - // TODO(b/225756739): Improve atomicity of rebootless APEX install. - // The newly installed APEX will not be reverted even if - // processApkInstallRequests() fails. Need a way to keep info stored in apexd - // and PMS in sync in the face of install failures. - request.setApexInfo(apexInfo); - mPm.mHandler.post(() -> processApkInstallRequests(true, requests)); - return; - } else { - mPm.mApexPackageInfo.notifyPackageInstalled(apexInfo, packageParser); - } + // APEX has been handled successfully by apexd. Let's continue the install flow + // so it will be scanned and registered with the system. + // TODO(b/225756739): Improve atomicity of rebootless APEX install. + // The newly installed APEX will not be reverted even if + // processApkInstallRequests() fails. Need a way to keep info stored in apexd + // and PMS in sync in the face of install failures. + request.setApexInfo(apexInfo); + mPm.mHandler.post(() -> processApkInstallRequests(true, requests)); + return; } } catch (PackageManagerException e) { request.setError("APEX installation failed", e); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index dfbe68a8e9975..68019daf2e66f 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -690,7 +690,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService private final ModuleInfoProvider mModuleInfoProvider; final ApexManager mApexManager; - final ApexPackageInfo mApexPackageInfo; final PackageManagerServiceInjector mInjector; @@ -1641,7 +1640,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService mSharedLibraries = injector.getSharedLibrariesImpl(); mApexManager = testParams.apexManager; - mApexPackageInfo = new ApexPackageInfo(this); mArtManagerService = testParams.artManagerService; mAvailableFeatures = testParams.availableFeatures; mBackgroundDexOptService = testParams.backgroundDexOptService; @@ -1841,7 +1839,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService mProtectedPackages = new ProtectedPackages(mContext); mApexManager = injector.getApexManager(); - mApexPackageInfo = new ApexPackageInfo(this); mAppsFilter = mInjector.getAppsFilter(); mInstantAppRegistry = new InstantAppRegistry(mContext, mPermissionManager, @@ -1979,8 +1976,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService + ver.fingerprint + " to " + PackagePartitions.FINGERPRINT); } - mInitAppsHelper = new InitAppsHelper(this, mApexManager, mApexPackageInfo, - mInstallPackageHelper, mInjector.getSystemPartitions()); + mInitAppsHelper = new InitAppsHelper(this, mApexManager, mInstallPackageHelper, + mInjector.getSystemPartitions()); // when upgrading from pre-M, promote system app permissions from install to runtime mPromoteSystemApps = diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp index 16317fedc583a..73b1907c9f429 100644 --- a/services/tests/mockingservicestests/Android.bp +++ b/services/tests/mockingservicestests/Android.bp @@ -78,6 +78,12 @@ android_test { "servicestests-core-utils", ], + java_resources: [ + ":apex.test", + ":test.rebootless_apex_v1", + ":test.rebootless_apex_v2", + ], + jni_libs: [ "libpsi", ], diff --git a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java similarity index 64% rename from services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java rename to services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java index a7739ed2c169f..aabec22063331 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 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. @@ -31,25 +31,29 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.testng.Assert.assertThrows; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.apex.ApexInfo; import android.apex.ApexSessionInfo; import android.apex.ApexSessionParams; import android.apex.IApexService; -import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.os.Build; import android.os.Environment; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; -import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.android.server.pm.parsing.PackageParser2; -import com.android.server.pm.parsing.TestPackageParser2; import com.android.server.pm.pkg.AndroidPackage; +import com.android.server.pm.pkg.parsing.ParsingPackageUtils; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -60,88 +64,139 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.List; +import java.util.Objects; @SmallTest @Presubmit @RunWith(AndroidJUnit4.class) public class ApexManagerTest { + + @Rule + public final MockSystemRule mMockSystem = new MockSystemRule(); + private static final String TEST_APEX_PKG = "com.android.apex.test"; private static final String TEST_APEX_FILE_NAME = "apex.test.apex"; private static final int TEST_SESSION_ID = 99999999; private static final int[] TEST_CHILD_SESSION_ID = {8888, 7777}; private ApexManager mApexManager; - private Context mContext; private PackageParser2 mPackageParser2; private IApexService mApexService = mock(IApexService.class); + private PackageManagerService mPmService; + + private InstallPackageHelper mInstallPackageHelper; + @Before - public void setUp() throws RemoteException { - mContext = InstrumentationRegistry.getInstrumentation().getContext(); + public void setUp() throws Exception { ApexManager.ApexManagerImpl managerImpl = spy(new ApexManager.ApexManagerImpl()); doReturn(mApexService).when(managerImpl).waitForApexService(); + when(mApexService.getActivePackages()).thenReturn(new ApexInfo[0]); mApexManager = managerImpl; - mPackageParser2 = new TestPackageParser2(); + mPackageParser2 = new PackageParser2(null, null, null, new PackageParser2.Callback() { + @Override + public boolean isChangeEnabled(long changeId, @NonNull ApplicationInfo appInfo) { + return true; + } + + @Override + public boolean hasFeature(String feature) { + return true; + } + }); + + mMockSystem.system().stageNominalSystemState(); + mPmService = new PackageManagerService(mMockSystem.mocks().getInjector(), + false /*factoryTest*/, + MockSystem.Companion.getDEFAULT_VERSION_INFO().fingerprint, + false /*isEngBuild*/, + false /*isUserDebugBuild*/, + Build.VERSION_CODES.CUR_DEVELOPMENT, + Build.VERSION.INCREMENTAL); + mMockSystem.system().validateFinalState(); + mInstallPackageHelper = new InstallPackageHelper(mPmService, mock(AppDataHelper.class)); + } + + @NonNull + private List scanApexInfos(ApexInfo[] apexInfos) { + return mInstallPackageHelper.scanApexPackages(apexInfos, + ParsingPackageUtils.PARSE_IS_SYSTEM_DIR, + PackageManagerService.SCAN_AS_SYSTEM, mPackageParser2, + ParallelPackageParser.makeExecutorService()); + } + + @Nullable + private ApexManager.ScanResult findActive(@NonNull List results) { + return results.stream() + .filter(it -> it.apexInfo.isActive) + .filter(it -> Objects.equals(it.packageName, TEST_APEX_PKG)) + .findFirst() + .orElse(null); + } + + @Nullable + private ApexManager.ScanResult findFactory(@NonNull List results, + @NonNull String packageName) { + return results.stream() + .filter(it -> it.apexInfo.isFactory) + .filter(it -> Objects.equals(it.packageName, packageName)) + .findFirst() + .orElse(null); + } + + @NonNull + private AndroidPackage mockParsePackage(@NonNull PackageParser2 parser, + @NonNull ApexInfo apexInfo) { + var flags = PackageManager.GET_META_DATA | PackageManager.GET_SIGNING_CERTIFICATES; + try { + var parsedPackage = parser.parsePackage(new File(apexInfo.modulePath), flags, + /* useCaches= */ false); + ScanPackageUtils.applyPolicy(parsedPackage, + PackageManagerService.SCAN_AS_APEX | PackageManagerService.SCAN_AS_SYSTEM, + mPmService.getPlatformPackage(), /* isUpdatedSystemApp */ false); + // isUpdatedSystemApp is ignoreable above, only used for shared library adjustment + return parsedPackage.hideAsFinal(); + } catch (PackageManagerException e) { + throw new RuntimeException(e); + } } @Test - public void testGetPackageInfo_setFlagsMatchActivePackage() throws RemoteException { - ApexInfo[] apexInfo = createApexInfoForTestPkg(true, false); - ApexPackageInfo apexPackageInfo = new ApexPackageInfo(); - apexPackageInfo.scanApexPackages( - apexInfo, mPackageParser2, ParallelPackageParser.makeExecutorService()); - final var activePair = apexPackageInfo.getPackageInfo(TEST_APEX_PKG, - ApexManager.MATCH_ACTIVE_PACKAGE); + public void testScanActivePackage() { + var apexInfos = createApexInfoForTestPkg(true, false); + var results = scanApexInfos(apexInfos); + var active = findActive(results); + var factory = findFactory(results, TEST_APEX_PKG); - assertThat(activePair).isNotNull(); - assertThat(activePair.second.getPackageName()).contains(TEST_APEX_PKG); + assertThat(active).isNotNull(); + assertThat(active.packageName).isEqualTo(TEST_APEX_PKG); - final var factoryPair = apexPackageInfo.getPackageInfo(TEST_APEX_PKG, - ApexManager.MATCH_FACTORY_PACKAGE); - - assertThat(factoryPair).isNull(); + assertThat(factory).isNull(); } @Test - public void testGetPackageInfo_setFlagsMatchFactoryPackage() throws RemoteException { - ApexInfo[] apexInfo = createApexInfoForTestPkg(false, true); - ApexPackageInfo apexPackageInfo = new ApexPackageInfo(); - apexPackageInfo.scanApexPackages( - apexInfo, mPackageParser2, ParallelPackageParser.makeExecutorService()); - var factoryPair = apexPackageInfo.getPackageInfo(TEST_APEX_PKG, - ApexManager.MATCH_FACTORY_PACKAGE); + public void testScanFactoryPackage() { + var apexInfos = createApexInfoForTestPkg(false, true); + var results = scanApexInfos(apexInfos); + var active = findActive(results); + var factory = findFactory(results, TEST_APEX_PKG); - assertThat(factoryPair).isNotNull(); - assertThat(factoryPair.second.getPackageName()).contains(TEST_APEX_PKG); + assertThat(factory).isNotNull(); + assertThat(factory.packageName).contains(TEST_APEX_PKG); - final var activePair = apexPackageInfo.getPackageInfo(TEST_APEX_PKG, - ApexManager.MATCH_ACTIVE_PACKAGE); - - assertThat(activePair).isNull(); + assertThat(active).isNull(); } @Test - public void testGetPackageInfo_setFlagsNone() throws RemoteException { - ApexInfo[] apexInfo = createApexInfoForTestPkg(false, true); - ApexPackageInfo apexPackageInfo = new ApexPackageInfo(); - apexPackageInfo.scanApexPackages( - apexInfo, mPackageParser2, ParallelPackageParser.makeExecutorService()); - - assertThat(apexPackageInfo.getPackageInfo(TEST_APEX_PKG, 0)).isNull(); - } - - @Test - public void testGetApexSystemServices() throws RemoteException { - ApexInfo[] apexInfo = new ApexInfo[] { + public void testGetApexSystemServices() { + ApexInfo[] apexInfo = new ApexInfo[]{ createApexInfoForTestPkg(false, true, 1), // only active apex reports apex-system-service createApexInfoForTestPkg(true, false, 2), }; - ApexPackageInfo apexPackageInfo = new ApexPackageInfo(); - List scanResults = apexPackageInfo.scanApexPackages( - apexInfo, mPackageParser2, ParallelPackageParser.makeExecutorService()); + List scanResults = scanApexInfos(apexInfo); mApexManager.notifyScanResult(scanResults); List services = mApexManager.getApexSystemServices(); @@ -151,73 +206,11 @@ public class ApexManagerTest { } @Test - public void testGetActivePackages() throws RemoteException { - ApexInfo[] apexInfo = createApexInfoForTestPkg(true, true); - ApexPackageInfo apexPackageInfo = new ApexPackageInfo(); - apexPackageInfo.scanApexPackages( - apexInfo, mPackageParser2, ParallelPackageParser.makeExecutorService()); - - assertThat(apexPackageInfo.getActivePackages()).isNotEmpty(); - } - - @Test - public void testGetActivePackages_noneActivePackages() throws RemoteException { - ApexInfo[] apexInfo = createApexInfoForTestPkg(false, true); - ApexPackageInfo apexPackageInfo = new ApexPackageInfo(); - apexPackageInfo.scanApexPackages( - apexInfo, mPackageParser2, ParallelPackageParser.makeExecutorService()); - - assertThat(apexPackageInfo.getActivePackages()).isEmpty(); - } - - @Test - public void testGetFactoryPackages() throws RemoteException { - ApexInfo [] apexInfo = createApexInfoForTestPkg(false, true); - ApexPackageInfo apexPackageInfo = new ApexPackageInfo(); - apexPackageInfo.scanApexPackages( - apexInfo, mPackageParser2, ParallelPackageParser.makeExecutorService()); - - assertThat(apexPackageInfo.getFactoryPackages()).isNotEmpty(); - } - - @Test - public void testGetFactoryPackages_noneFactoryPackages() throws RemoteException { - ApexInfo[] apexInfo = createApexInfoForTestPkg(true, false); - ApexPackageInfo apexPackageInfo = new ApexPackageInfo(); - apexPackageInfo.scanApexPackages( - apexInfo, mPackageParser2, ParallelPackageParser.makeExecutorService()); - - assertThat(apexPackageInfo.getFactoryPackages()).isEmpty(); - } - - @Test - public void testGetInactivePackages() throws RemoteException { - ApexInfo[] apexInfo = createApexInfoForTestPkg(false, true); - ApexPackageInfo apexPackageInfo = new ApexPackageInfo(); - apexPackageInfo.scanApexPackages( - apexInfo, mPackageParser2, ParallelPackageParser.makeExecutorService()); - - assertThat(apexPackageInfo.getInactivePackages()).isNotEmpty(); - } - - @Test - public void testGetInactivePackages_noneInactivePackages() throws RemoteException { - ApexInfo[] apexInfo = createApexInfoForTestPkg(true, false); - ApexPackageInfo apexPackageInfo = new ApexPackageInfo(); - apexPackageInfo.scanApexPackages( - apexInfo, mPackageParser2, ParallelPackageParser.makeExecutorService()); - - assertThat(apexPackageInfo.getInactivePackages()).isEmpty(); - } - - @Test - public void testIsApexPackage() throws RemoteException { - ApexInfo[] apexInfo = createApexInfoForTestPkg(false, true); - ApexPackageInfo apexPackageInfo = new ApexPackageInfo(); - apexPackageInfo.scanApexPackages( - apexInfo, mPackageParser2, ParallelPackageParser.makeExecutorService()); - - assertThat(apexPackageInfo.isApexPackage(TEST_APEX_PKG)).isTrue(); + public void testIsApexPackage() { + var apexInfos = createApexInfoForTestPkg(false, true); + var results = scanApexInfos(apexInfos); + var factory = findFactory(results, TEST_APEX_PKG); + assertThat(factory.pkg.isApex()).isTrue(); } @Test @@ -328,16 +321,14 @@ public class ApexManagerTest { assertThat(activeApex.apexModuleName).isEqualTo(TEST_APEX_PKG); ApexInfo[] apexInfo = createApexInfoForTestPkg(true, true); - ApexPackageInfo apexPackageInfo = new ApexPackageInfo(); - List scanResults = apexPackageInfo.scanApexPackages( - apexInfo, mPackageParser2, ParallelPackageParser.makeExecutorService()); + List scanResults = scanApexInfos(apexInfo); mApexManager.notifyScanResult(scanResults); assertThat(mApexManager.getApkInApexInstallError(activeApex.apexModuleName)).isNull(); mApexManager.reportErrorWithApkInApex(activeApex.apexDirectory.getAbsolutePath(), "Some random error"); assertThat(mApexManager.getApkInApexInstallError(activeApex.apexModuleName)) - .isEqualTo("Some random error"); + .isEqualTo("Some random error"); } /** @@ -357,9 +348,7 @@ public class ApexManagerTest { when(fakeApkInApex.getPackageName()).thenReturn("randomPackageName"); ApexInfo[] apexInfo = createApexInfoForTestPkg(true, true); - ApexPackageInfo apexPackageInfo = new ApexPackageInfo(); - List scanResults = apexPackageInfo.scanApexPackages( - apexInfo, mPackageParser2, ParallelPackageParser.makeExecutorService()); + List scanResults = scanApexInfos(apexInfo); mApexManager.notifyScanResult(scanResults); assertThat(mApexManager.getApksInApex(activeApex.apexModuleName)).isEmpty(); @@ -371,11 +360,9 @@ public class ApexManagerTest { public void testInstallPackage_activeOnSystem() throws Exception { ApexInfo activeApexInfo = createApexInfo("test.apex_rebootless", 1, /* isActive= */ true, /* isFactory= */ true, extractResource("test.apex_rebootless_v1", - "test.rebootless_apex_v1.apex")); + "test.rebootless_apex_v1.apex")); ApexInfo[] apexInfo = new ApexInfo[]{activeApexInfo}; - ApexPackageInfo apexPackageInfo = new ApexPackageInfo(); - apexPackageInfo.scanApexPackages( - apexInfo, mPackageParser2, ParallelPackageParser.makeExecutorService()); + var results = scanApexInfos(apexInfo); File finalApex = extractResource("test.rebootles_apex_v2", "test.rebootless_apex_v2.apex"); ApexInfo newApexInfo = createApexInfo("test.apex_rebootless", 2, /* isActive= */ true, @@ -384,32 +371,28 @@ public class ApexManagerTest { File installedApex = extractResource("installed", "test.rebootless_apex_v2.apex"); newApexInfo = mApexManager.installPackage(installedApex); - apexPackageInfo.notifyPackageInstalled(newApexInfo, mPackageParser2); - var newInfo = apexPackageInfo.getPackageInfo("test.apex.rebootless", - ApexManager.MATCH_ACTIVE_PACKAGE); - assertThat(newInfo.second.getBaseApkPath()).isEqualTo(finalApex.getAbsolutePath()); - assertThat(newInfo.second.getLongVersionCode()).isEqualTo(2); + var newPkg = mockParsePackage(mPackageParser2, newApexInfo); + assertThat(newPkg.getBaseApkPath()).isEqualTo(finalApex.getAbsolutePath()); + assertThat(newPkg.getLongVersionCode()).isEqualTo(2); - var factoryInfo = apexPackageInfo.getPackageInfo("test.apex.rebootless", - ApexManager.MATCH_FACTORY_PACKAGE); - assertThat(factoryInfo.second.getBaseApkPath()).isEqualTo(activeApexInfo.modulePath); - assertThat(factoryInfo.second.getLongVersionCode()).isEqualTo(1); - assertThat(factoryInfo.second.isSystem()).isTrue(); + var factoryPkg = mockParsePackage(mPackageParser2, + findFactory(results, "test.apex.rebootless").apexInfo); + assertThat(factoryPkg.getBaseApkPath()).isEqualTo(activeApexInfo.modulePath); + assertThat(factoryPkg.getLongVersionCode()).isEqualTo(1); + assertThat(factoryPkg.isSystem()).isTrue(); } @Test public void testInstallPackage_activeOnData() throws Exception { ApexInfo factoryApexInfo = createApexInfo("test.apex_rebootless", 1, /* isActive= */ false, /* isFactory= */ true, extractResource("test.apex_rebootless_v1", - "test.rebootless_apex_v1.apex")); + "test.rebootless_apex_v1.apex")); ApexInfo activeApexInfo = createApexInfo("test.apex_rebootless", 1, /* isActive= */ true, /* isFactory= */ false, extractResource("test.apex.rebootless@1", - "test.rebootless_apex_v1.apex")); + "test.rebootless_apex_v1.apex")); ApexInfo[] apexInfo = new ApexInfo[]{factoryApexInfo, activeApexInfo}; - ApexPackageInfo apexPackageInfo = new ApexPackageInfo(); - apexPackageInfo.scanApexPackages( - apexInfo, mPackageParser2, ParallelPackageParser.makeExecutorService()); + var results = scanApexInfos(apexInfo); File finalApex = extractResource("test.rebootles_apex_v2", "test.rebootless_apex_v2.apex"); ApexInfo newApexInfo = createApexInfo("test.apex_rebootless", 2, /* isActive= */ true, @@ -418,30 +401,20 @@ public class ApexManagerTest { File installedApex = extractResource("installed", "test.rebootless_apex_v2.apex"); newApexInfo = mApexManager.installPackage(installedApex); - apexPackageInfo.notifyPackageInstalled(newApexInfo, mPackageParser2); - var newInfo = apexPackageInfo.getPackageInfo("test.apex.rebootless", - ApexManager.MATCH_ACTIVE_PACKAGE); - assertThat(newInfo.second.getBaseApkPath()).isEqualTo(finalApex.getAbsolutePath()); - assertThat(newInfo.second.getLongVersionCode()).isEqualTo(2); + var newPkg = mockParsePackage(mPackageParser2, newApexInfo); + assertThat(newPkg.getBaseApkPath()).isEqualTo(finalApex.getAbsolutePath()); + assertThat(newPkg.getLongVersionCode()).isEqualTo(2); - var factoryInfo = apexPackageInfo.getPackageInfo("test.apex.rebootless", - ApexManager.MATCH_FACTORY_PACKAGE); - assertThat(factoryInfo.second.getBaseApkPath()).isEqualTo(factoryApexInfo.modulePath); - assertThat(factoryInfo.second.getLongVersionCode()).isEqualTo(1); - assertThat(factoryInfo.second.isSystem()).isTrue(); + var factoryPkg = mockParsePackage(mPackageParser2, + findFactory(results, "test.apex.rebootless").apexInfo); + assertThat(factoryPkg.getBaseApkPath()).isEqualTo(factoryApexInfo.modulePath); + assertThat(factoryPkg.getLongVersionCode()).isEqualTo(1); + assertThat(factoryPkg.isSystem()).isTrue(); } @Test public void testInstallPackageBinderCallFails() throws Exception { - ApexInfo activeApexInfo = createApexInfo("test.apex_rebootless", 1, /* isActive= */ true, - /* isFactory= */ false, extractResource("test.apex_rebootless_v1", - "test.rebootless_apex_v1.apex")); - ApexInfo[] apexInfo = new ApexInfo[]{activeApexInfo}; - ApexPackageInfo apexPackageInfo = new ApexPackageInfo(); - apexPackageInfo.scanApexPackages( - apexInfo, mPackageParser2, ParallelPackageParser.makeExecutorService()); - when(mApexService.installAndActivatePackage(anyString())).thenThrow( new RuntimeException("install failed :(")); @@ -452,14 +425,12 @@ public class ApexManagerTest { } @Test - public void testGetActivePackageNameForApexModuleName() throws Exception { + public void testGetActivePackageNameForApexModuleName() { final String moduleName = "com.android.module_name"; ApexInfo[] apexInfo = createApexInfoForTestPkg(true, false); apexInfo[0].moduleName = moduleName; - ApexPackageInfo apexPackageInfo = new ApexPackageInfo(); - List scanResults = apexPackageInfo.scanApexPackages( - apexInfo, mPackageParser2, ParallelPackageParser.makeExecutorService()); + List scanResults = scanApexInfos(apexInfo); mApexManager.notifyScanResult(scanResults); assertThat(mApexManager.getActivePackageNameForApexModuleName(moduleName)) @@ -472,12 +443,13 @@ public class ApexManagerTest { when(mApexService.getActivePackages()).thenReturn(new ApexInfo[]{apex}); final File backingApexFile = mApexManager.getBackingApexFile( - new File("/apex/" + TEST_APEX_PKG + "/apk/App/App.apk")); + new File(mMockSystem.system().getApexDirectory(), + TEST_APEX_PKG + "/apk/App/App.apk")); assertThat(backingApexFile.getAbsolutePath()).isEqualTo(apex.modulePath); } @Test - public void testGetBackingApexFile_fileNotOnApexMountPoint_returnsNull() throws Exception { + public void testGetBackingApexFile_fileNotOnApexMountPoint_returnsNull() { File result = mApexManager.getBackingApexFile( new File("/data/local/tmp/whatever/does-not-matter")); assertThat(result).isNull(); @@ -489,22 +461,23 @@ public class ApexManagerTest { when(mApexService.getActivePackages()).thenReturn(new ApexInfo[]{apex}); final File backingApexFile = mApexManager.getBackingApexFile( - new File("/apex/com.wrong.apex/apk/App")); + new File(mMockSystem.system().getApexDirectory(), "com.wrong.apex/apk/App")); assertThat(backingApexFile).isNull(); } @Test - public void testGetBackingApexFiles_topLevelApexDir_returnsNull() throws Exception { + public void testGetBackingApexFiles_topLevelApexDir_returnsNull() { assertThat(mApexManager.getBackingApexFile(Environment.getApexDirectory())).isNull(); assertThat(mApexManager.getBackingApexFile(new File("/apex/"))).isNull(); assertThat(mApexManager.getBackingApexFile(new File("/apex//"))).isNull(); } @Test - public void testGetBackingApexFiles_flattenedApex() throws Exception { + public void testGetBackingApexFiles_flattenedApex() { ApexManager flattenedApexManager = new ApexManager.ApexManagerFlattenedApex(); final File backingApexFile = flattenedApexManager.getBackingApexFile( - new File("/apex/com.android.apex.cts.shim/app/CtsShim/CtsShim.apk")); + new File(mMockSystem.system().getApexDirectory(), + "com.android.apex.cts.shim/app/CtsShim/CtsShim.apk")); assertThat(backingApexFile).isNull(); } @@ -521,7 +494,7 @@ public class ApexManagerTest { } private ApexInfo createApexInfoForTestPkg(boolean isActive, boolean isFactory, int version) { - File apexFile = extractResource(TEST_APEX_PKG, TEST_APEX_FILE_NAME); + File apexFile = extractResource(TEST_APEX_PKG, TEST_APEX_FILE_NAME); ApexInfo apexInfo = new ApexInfo(); apexInfo.isActive = isActive; apexInfo.isFactory = isFactory; diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/InitAppsHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/InitAppsHelperTest.kt index 2165301f56c24..15b4975bb2b92 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/InitAppsHelperTest.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/InitAppsHelperTest.kt @@ -83,7 +83,7 @@ class InitAppsHelperTest { val pms = createPackageManagerService() assertThat(pms.isFirstBoot).isEqualTo(true) assertThat(pms.isDeviceUpgrading).isEqualTo(false) - val initAppsHelper = InitAppsHelper(pms, rule.mocks().apexManager, null, null, + val initAppsHelper = InitAppsHelper(pms, rule.mocks().apexManager, null, listOf()) assertThat( initAppsHelper.systemScanFlags and PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE) @@ -98,7 +98,7 @@ class InitAppsHelperTest { val pms = createPackageManagerService() assertThat(pms.isFirstBoot).isEqualTo(false) assertThat(pms.isDeviceUpgrading).isEqualTo(true) - val initAppsHelper = InitAppsHelper(pms, rule.mocks().apexManager, null, null, + val initAppsHelper = InitAppsHelper(pms, rule.mocks().apexManager, null, listOf()) assertThat( initAppsHelper.systemScanFlags and PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE) @@ -112,7 +112,7 @@ class InitAppsHelperTest { val pms = createPackageManagerService() assertThat(pms.isFirstBoot).isEqualTo(false) assertThat(pms.isDeviceUpgrading).isEqualTo(false) - val initAppsHelper = InitAppsHelper(pms, rule.mocks().apexManager, null, null, + val initAppsHelper = InitAppsHelper(pms, rule.mocks().apexManager, null, listOf()) assertThat( initAppsHelper.systemScanFlags and PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE) @@ -126,7 +126,7 @@ class InitAppsHelperTest { val pms = createPackageManagerService() assertThat(pms.isFirstBoot).isEqualTo(false) assertThat(pms.isDeviceUpgrading).isEqualTo(true) - val initAppsHelper = InitAppsHelper(pms, rule.mocks().apexManager, null, null, + val initAppsHelper = InitAppsHelper(pms, rule.mocks().apexManager, null, listOf()) assertThat( initAppsHelper.systemScanFlags and PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE) diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt index dc7bcd625ee3a..dd6c7334a45af 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt @@ -78,14 +78,6 @@ import com.android.server.testutils.mock import com.android.server.testutils.nullable import com.android.server.testutils.whenever import com.android.server.utils.WatchedArrayMap -import java.io.File -import java.io.IOException -import java.nio.file.Files -import java.security.PublicKey -import java.security.cert.CertificateException -import java.util.Arrays -import java.util.Random -import java.util.concurrent.FutureTask import libcore.util.HexEncoding import org.junit.Assert import org.junit.rules.TestRule @@ -94,6 +86,14 @@ import org.junit.runners.model.Statement import org.mockito.AdditionalMatchers.or import org.mockito.Mockito import org.mockito.quality.Strictness +import java.io.File +import java.io.IOException +import java.nio.file.Files +import java.security.PublicKey +import java.security.cert.CertificateException +import java.util.Arrays +import java.util.Random +import java.util.concurrent.FutureTask /** * A utility for mocking behavior of the system and dependencies when testing PackageManagerService @@ -104,6 +104,9 @@ import org.mockito.quality.Strictness class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) { private val random = Random() val mocks = Mocks() + + // TODO: getBackingApexFile does not handle paths that aren't /apex + val apexDirectory = File("/apex") val packageCacheDirectory: File = Files.createTempDirectory("packageCache").toFile() val rootDirectory: File = @@ -297,7 +300,9 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) { whenever(mocks.systemConfig.sharedLibraries).thenReturn(DEFAULT_SHARED_LIBRARIES_LIST) whenever(mocks.systemConfig.defaultVrComponents).thenReturn(ArraySet()) whenever(mocks.systemConfig.hiddenApiWhitelistedApps).thenReturn(ArraySet()) + wheneverStatic { SystemProperties.set(anyString(), anyString()) }.thenDoNothing() wheneverStatic { SystemProperties.getBoolean("fw.free_cache_v2", true) }.thenReturn(true) + wheneverStatic { Environment.getApexDirectory() }.thenReturn(apexDirectory) wheneverStatic { Environment.getPackageCacheDirectory() }.thenReturn(packageCacheDirectory) wheneverStatic { SystemProperties.digestOf("ro.build.fingerprint") }.thenReturn("cacheName") wheneverStatic { Environment.getRootDirectory() }.thenReturn(rootDirectory) diff --git a/services/tests/mockingservicestests/utils-mockito/com/android/server/extendedtestutils/ExtendedMockitoUtils.kt b/services/tests/mockingservicestests/utils-mockito/com/android/server/extendedtestutils/ExtendedMockitoUtils.kt index 72ae77e48e25e..a0a6743903ad8 100644 --- a/services/tests/mockingservicestests/utils-mockito/com/android/server/extendedtestutils/ExtendedMockitoUtils.kt +++ b/services/tests/mockingservicestests/utils-mockito/com/android/server/extendedtestutils/ExtendedMockitoUtils.kt @@ -33,9 +33,14 @@ fun wheneverStatic(mockedMethod: MockedMethod) = object : CustomStaticStu override fun thenReturn(value: T) { ExtendedMockito.doReturn(value).wheneverStatic(mockedMethod) } + + override fun thenDoNothing() { + ExtendedMockito.doNothing().wheneverStatic(mockedMethod) + } } interface CustomStaticStubber { fun thenAnswer(answer: Answer) fun thenReturn(value: T) + fun thenDoNothing() } diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index 8a932f165ca66..61bb57e145f83 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -124,9 +124,6 @@ android_test { ":PackageParserTestApp4", ":PackageParserTestApp5", ":PackageParserTestApp6", - ":apex.test", - ":test.rebootless_apex_v1", - ":test.rebootless_apex_v2", ":com.android.apex.cts.shim.v1_prebuilt", ":com.android.apex.cts.shim.v2_different_certificate_prebuilt", ":com.android.apex.cts.shim.v2_unsigned_apk_container_prebuilt",