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
This commit is contained in:
Winson Chiu
2022-10-19 17:00:52 +00:00
parent 7a5652f7b1
commit 9e99a1a5f2
12 changed files with 253 additions and 746 deletions

View File

@@ -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<Pair<ApexInfo, AndroidPackage>> 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<ApexManager.ScanResult> scanApexPackages(ApexInfo[] allPackages,
@NonNull PackageParser2 packageParser, @NonNull ExecutorService executorService) {
synchronized (mLock) {
return scanApexPackagesInternalLocked(allPackages, packageParser, executorService);
}
}
void notifyScanResult(List<ApexManager.ScanResult> 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<ApexInfo, AndroidPackage> 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<ApexInfo, AndroidPackage> 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<Pair<ApexInfo, AndroidPackage>> getActivePackages() {
synchronized (mLock) {
Preconditions.checkState(mAllPackagesCache != null,
"APEX packages have not been scanned");
final List<Pair<ApexInfo, AndroidPackage>> 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<Pair<ApexInfo, AndroidPackage>> getFactoryPackages() {
synchronized (mLock) {
Preconditions.checkState(mAllPackagesCache != null,
"APEX packages have not been scanned");
final List<Pair<ApexInfo, AndroidPackage>> 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<Pair<ApexInfo, AndroidPackage>> getInactivePackages() {
synchronized (mLock) {
Preconditions.checkState(mAllPackagesCache != null,
"APEX packages have not been scanned");
final List<Pair<ApexInfo, AndroidPackage>> 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<ApexManager.ScanResult> scanResults) {
mAllPackagesCache = new ArrayList<>();
final int flags = PackageManager.GET_META_DATA
| PackageManager.GET_SIGNING_CERTIFICATES
| PackageManager.GET_SIGNATURES;
HashSet<String> activePackagesSet = new HashSet<>();
HashSet<String> 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<ApexManager.ScanResult> scanApexPackagesInternalLocked(final ApexInfo[] allPkgs,
PackageParser2 packageParser, ExecutorService executorService) {
if (allPkgs == null || allPkgs.length == 0) {
notifyScanResultLocked(Collections.EMPTY_LIST);
return Collections.EMPTY_LIST;
}
ArrayMap<File, ApexInfo> 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<ApexManager.ScanResult> 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<PackageStateInternal> 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<Pair<ApexInfo, AndroidPackage>> 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();
}
}

View File

@@ -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;

View File

@@ -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<Pair<ApexInfo, AndroidPackage>> 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<PackageStateInternal> activePackages = new ArrayList<>();
List<PackageStateInternal> inactivePackages = new ArrayList<>();
List<PackageStateInternal> factoryActivePackages = new ArrayList<>();
List<PackageStateInternal> 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<PackageStateInternal> activePackages = new ArrayList<>();
List<PackageStateInternal> inactivePackages = new ArrayList<>();
List<PackageStateInternal> factoryActivePackages = new ArrayList<>();
List<PackageStateInternal> 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<PackageStateInternal> 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;

View File

@@ -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<String> mStubSystemApps = new ArrayList<>();
// TODO(b/198166813): remove PMS dependency
InitAppsHelper(PackageManagerService pm,
ApexManager apexManager, ApexPackageInfo apexPackageInfo,
InitAppsHelper(PackageManagerService pm, ApexManager apexManager,
InstallPackageHelper installPackageHelper,
List<ScanPartition> 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<ApexManager.ScanResult> 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);
}

View File

@@ -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);

View File

@@ -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 =

View File

@@ -78,6 +78,12 @@ android_test {
"servicestests-core-utils",
],
java_resources: [
":apex.test",
":test.rebootless_apex_v1",
":test.rebootless_apex_v2",
],
jni_libs: [
"libpsi",
],

View File

@@ -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<ApexManager.ScanResult> 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<ApexManager.ScanResult> 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<ApexManager.ScanResult> 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<ApexManager.ScanResult> scanResults = apexPackageInfo.scanApexPackages(
apexInfo, mPackageParser2, ParallelPackageParser.makeExecutorService());
List<ApexManager.ScanResult> scanResults = scanApexInfos(apexInfo);
mApexManager.notifyScanResult(scanResults);
List<ApexSystemServiceInfo> 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<ApexManager.ScanResult> scanResults = apexPackageInfo.scanApexPackages(
apexInfo, mPackageParser2, ParallelPackageParser.makeExecutorService());
List<ApexManager.ScanResult> 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<ApexManager.ScanResult> scanResults = apexPackageInfo.scanApexPackages(
apexInfo, mPackageParser2, ParallelPackageParser.makeExecutorService());
List<ApexManager.ScanResult> 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<ApexManager.ScanResult> scanResults = apexPackageInfo.scanApexPackages(
apexInfo, mPackageParser2, ParallelPackageParser.makeExecutorService());
List<ApexManager.ScanResult> 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;

View File

@@ -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<ScanPartition>())
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<ScanPartition>())
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<ScanPartition>())
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<ScanPartition>())
assertThat(
initAppsHelper.systemScanFlags and PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE)

View File

@@ -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)

View File

@@ -33,9 +33,14 @@ fun <T> wheneverStatic(mockedMethod: MockedMethod<T>) = object : CustomStaticStu
override fun thenReturn(value: T) {
ExtendedMockito.doReturn(value).wheneverStatic(mockedMethod)
}
override fun thenDoNothing() {
ExtendedMockito.doNothing().wheneverStatic(mockedMethod)
}
}
interface CustomStaticStubber<T> {
fun thenAnswer(answer: Answer<T>)
fun thenReturn(value: T)
fun thenDoNothing()
}

View File

@@ -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",