Revert framework-res splits from apexes

Test: presubmit

Bug: 195732042
Change-Id: I64f1640f72b18dcbda43be5a673872fd6c12ab7e
This commit is contained in:
Pedro Loureiro
2022-09-16 17:07:37 +00:00
parent bcd00b3150
commit ad7bf54043
9 changed files with 53 additions and 148 deletions

View File

@@ -28,16 +28,16 @@ import androidx.test.filters.LargeTest
import com.android.internal.util.ConcurrentUtils
import com.android.server.pm.parsing.pkg.PackageImpl
import com.android.server.pm.pkg.parsing.ParsingPackageUtils
import java.io.File
import java.io.FileOutputStream
import java.util.concurrent.ArrayBlockingQueue
import java.util.concurrent.TimeUnit
import libcore.io.IoUtils
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import java.io.File
import java.io.FileOutputStream
import java.util.concurrent.ArrayBlockingQueue
import java.util.concurrent.TimeUnit
@LargeTest
@RunWith(Parameterized::class)
@@ -180,8 +180,8 @@ public class PackageParsingPerfTest {
protected abstract fun parseImpl(file: File): PackageType
}
class ParallelParser1(private val cacher: PackageCacher1? = null)
: ParallelParser<PackageParser.Package>(cacher) {
class ParallelParser1(private val cacher: PackageCacher1? = null) :
ParallelParser<PackageParser.Package>(cacher) {
val parser = PackageParser().apply {
setCallback { true }
}
@@ -189,8 +189,8 @@ public class PackageParsingPerfTest {
override fun parseImpl(file: File) = parser.parsePackage(file, 0, cacher != null)
}
class ParallelParser2(cacher: PackageCacher2? = null)
: ParallelParser<PackageImpl>(cacher) {
class ParallelParser2(cacher: PackageCacher2? = null) :
ParallelParser<PackageImpl>(cacher) {
val input = ThreadLocal.withInitial {
// For testing, just disable enforcement to avoid hooking up to compat framework
ParseTypeImpl(ParseInput.Callback { _, _, _ -> false })
@@ -218,7 +218,7 @@ public class PackageParsingPerfTest {
})
override fun parseImpl(file: File) =
parser.parsePackage(input.get()!!.reset(), file, 0, null).result
parser.parsePackage(input.get()!!.reset(), file, 0).result
as PackageImpl
}

View File

@@ -78,7 +78,6 @@ public class ApkLiteParseUtils {
public static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
private static final int PARSE_IS_SYSTEM_DIR = 1 << 4;
private static final int PARSE_COLLECT_CERTIFICATES = 1 << 5;
private static final int PARSE_FRAMEWORK_RES_SPLITS = 1 << 8;
private static final String TAG_APPLICATION = "application";
private static final String TAG_PACKAGE_VERIFIER = "package-verifier";
private static final String TAG_PROFILEABLE = "profileable";
@@ -103,7 +102,7 @@ public class ApkLiteParseUtils {
public static ParseResult<PackageLite> parsePackageLite(ParseInput input,
File packageFile, int flags) {
if (packageFile.isDirectory()) {
return parseClusterPackageLite(input, packageFile, /* frameworkSplits= */ null, flags);
return parseClusterPackageLite(input, packageFile, flags);
} else {
return parseMonolithicPackageLite(input, packageFile, flags);
}
@@ -137,38 +136,19 @@ public class ApkLiteParseUtils {
/**
* Parse lightweight details about a directory of APKs.
*
* @param packageDirOrApk is the folder that contains split apks for a regular app or the
* framework-res.apk for framwork-res splits (in which case the
* splits come in the <code>frameworkSplits</code> parameter)
* @param packageDir is the folder that contains split apks for a regular app
*/
public static ParseResult<PackageLite> parseClusterPackageLite(ParseInput input,
File packageDirOrApk, List<File> frameworkSplits, int flags) {
File packageDir, int flags) {
final File[] files;
final boolean parsingFrameworkSplits = (flags & PARSE_FRAMEWORK_RES_SPLITS) != 0;
if (parsingFrameworkSplits) {
if (ArrayUtils.isEmpty(frameworkSplits)) {
return input.error(PackageManager.INSTALL_PARSE_FAILED_NOT_APK,
"No packages found in split");
}
files = frameworkSplits.toArray(new File[frameworkSplits.size() + 1]);
// we also want to process the base apk so add it to the array
files[files.length - 1] = packageDirOrApk;
} else {
files = packageDirOrApk.listFiles();
if (ArrayUtils.isEmpty(files)) {
return input.error(PackageManager.INSTALL_PARSE_FAILED_NOT_APK,
"No packages found in split");
}
// Apk directory is directly nested under the current directory
if (files.length == 1 && files[0].isDirectory()) {
return parseClusterPackageLite(input, files[0], frameworkSplits, flags);
}
files = packageDir.listFiles();
if (ArrayUtils.isEmpty(files)) {
return input.error(PackageManager.INSTALL_PARSE_FAILED_NOT_APK,
"No packages found in split");
}
if (parsingFrameworkSplits) {
// disable the flag for checking the certificates of the splits. We know they
// won't match, but we rely on the mainline apex to be safe if it was installed
flags = flags & ~PARSE_COLLECT_CERTIFICATES;
// Apk directory is directly nested under the current directory
if (files.length == 1 && files[0].isDirectory()) {
return parseClusterPackageLite(input, files[0], flags);
}
String packageName = null;
@@ -186,10 +166,6 @@ public class ApkLiteParseUtils {
}
final ApkLite lite = result.getResult();
if (parsingFrameworkSplits && file == files[files.length - 1]) {
baseApk = lite;
break;
}
// Assert that all package names and version codes are
// consistent with the first one we encounter.
if (packageName == null) {
@@ -201,8 +177,7 @@ public class ApkLiteParseUtils {
"Inconsistent package " + lite.getPackageName() + " in " + file
+ "; expected " + packageName);
}
// we allow version codes that do not match for framework splits
if (!parsingFrameworkSplits && versionCode != lite.getVersionCode()) {
if (versionCode != lite.getVersionCode()) {
return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Inconsistent version " + lite.getVersionCode() + " in " + file
+ "; expected " + versionCode);
@@ -217,15 +192,11 @@ public class ApkLiteParseUtils {
}
}
}
// baseApk is set in the last iteration of the for each loop when we are parsing
// frameworkRes splits or needs to be done now otherwise
if (!parsingFrameworkSplits) {
baseApk = apks.remove(null);
}
baseApk = apks.remove(null);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
return composePackageLiteFromApks(input, packageDirOrApk, baseApk, apks);
return composePackageLiteFromApks(input, packageDir, baseApk, apks);
}
/**

View File

@@ -33,11 +33,9 @@ import static com.android.server.pm.PackageManagerService.SCAN_REQUIRE_KNOWN;
import static com.android.server.pm.PackageManagerService.SYSTEM_PARTITIONS;
import static com.android.server.pm.PackageManagerService.TAG;
import static com.android.server.pm.pkg.parsing.ParsingPackageUtils.PARSE_APK_IN_APEX;
import static com.android.server.pm.pkg.parsing.ParsingPackageUtils.PARSE_FRAMEWORK_RES_SPLITS;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.os.Environment;
import android.os.SystemClock;
import android.os.Trace;
@@ -121,29 +119,6 @@ final class InitAppsHelper {
mExecutorService = ParallelPackageParser.makeExecutorService();
}
private List<File> getFrameworkResApkSplitFiles() {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanFrameworkResApkSplits");
try {
final List<File> splits = new ArrayList<>();
final List<ApexManager.ActiveApexInfo> activeApexInfos =
mPm.mApexManager.getActiveApexInfos();
for (int i = 0; i < activeApexInfos.size(); i++) {
ApexManager.ActiveApexInfo apexInfo = activeApexInfos.get(i);
File splitsFolder = new File(apexInfo.apexDirectory, "etc/splits");
if (splitsFolder.isDirectory()) {
for (File file : splitsFolder.listFiles()) {
if (ApkLiteParseUtils.isApkFile(file)) {
splits.add(file);
}
}
}
}
return splits;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
private List<ScanPartition> getSystemScanPartitions() {
final List<ScanPartition> scanPartitions = new ArrayList<>();
scanPartitions.addAll(mSystemPartitions);
@@ -270,7 +245,7 @@ final class InitAppsHelper {
long startTime) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
SystemClock.uptimeMillis());
scanDirTracedLI(mPm.getAppInstallDir(), /* frameworkSplits= */ null, 0,
scanDirTracedLI(mPm.getAppInstallDir(), 0,
mScanFlags | SCAN_REQUIRE_KNOWN, packageParser, mExecutorService);
List<Runnable> unfinishedTasks = mExecutorService.shutdownNow();
@@ -338,15 +313,13 @@ final class InitAppsHelper {
if (partition.getOverlayFolder() == null) {
continue;
}
scanDirTracedLI(partition.getOverlayFolder(), /* frameworkSplits= */ null,
scanDirTracedLI(partition.getOverlayFolder(),
mSystemParseFlags, mSystemScanFlags | partition.scanFlag,
packageParser, executorService);
}
List<File> frameworkSplits = getFrameworkResApkSplitFiles();
scanDirTracedLI(frameworkDir, frameworkSplits,
mSystemParseFlags | PARSE_FRAMEWORK_RES_SPLITS,
mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED,
scanDirTracedLI(frameworkDir,
mSystemParseFlags, mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED,
packageParser, executorService);
if (!mPm.mPackages.containsKey("android")) {
throw new IllegalStateException(
@@ -356,12 +329,12 @@ final class InitAppsHelper {
for (int i = 0, size = mDirsToScanAsSystem.size(); i < size; i++) {
final ScanPartition partition = mDirsToScanAsSystem.get(i);
if (partition.getPrivAppFolder() != null) {
scanDirTracedLI(partition.getPrivAppFolder(), /* frameworkSplits= */ null,
scanDirTracedLI(partition.getPrivAppFolder(),
mSystemParseFlags,
mSystemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag,
packageParser, executorService);
}
scanDirTracedLI(partition.getAppFolder(), /* frameworkSplits= */ null,
scanDirTracedLI(partition.getAppFolder(),
mSystemParseFlags, mSystemScanFlags | partition.scanFlag,
packageParser, executorService);
}
@@ -379,7 +352,7 @@ final class InitAppsHelper {
}
@GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
private void scanDirTracedLI(File scanDir, List<File> frameworkSplits,
private void scanDirTracedLI(File scanDir,
int parseFlags, int scanFlags,
PackageParser2 packageParser, ExecutorService executorService) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
@@ -388,7 +361,7 @@ final class InitAppsHelper {
// when scanning apk in apexes, we want to check the maxSdkVersion
parseFlags |= PARSE_APK_IN_APEX;
}
mInstallPackageHelper.installPackagesFromDir(scanDir, frameworkSplits, parseFlags,
mInstallPackageHelper.installPackagesFromDir(scanDir, parseFlags,
scanFlags, packageParser, executorService);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);

View File

@@ -3467,7 +3467,7 @@ final class InstallPackageHelper {
}
@GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
public void installPackagesFromDir(File scanDir, List<File> frameworkSplits, int parseFlags,
public void installPackagesFromDir(File scanDir, int parseFlags,
int scanFlags, PackageParser2 packageParser,
ExecutorService executorService) {
final File[] files = scanDir.listFiles();
@@ -3481,7 +3481,7 @@ final class InstallPackageHelper {
+ " flags=0x" + Integer.toHexString(parseFlags));
}
ParallelPackageParser parallelPackageParser =
new ParallelPackageParser(packageParser, executorService, frameworkSplits);
new ParallelPackageParser(packageParser, executorService);
// Submit files for parsing in parallel
int fileCount = 0;

View File

@@ -27,7 +27,6 @@ import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.pkg.ParsedPackage;
import java.io.File;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
@@ -55,17 +54,9 @@ class ParallelPackageParser {
private final ExecutorService mExecutorService;
private final List<File> mFrameworkSplits;
ParallelPackageParser(PackageParser2 packageParser, ExecutorService executorService) {
this(packageParser, executorService, /* frameworkSplits= */ null);
}
ParallelPackageParser(PackageParser2 packageParser, ExecutorService executorService,
List<File> frameworkSplits) {
mPackageParser = packageParser;
mExecutorService = executorService;
mFrameworkSplits = frameworkSplits;
}
static class ParseResult {
@@ -134,6 +125,6 @@ class ParallelPackageParser {
@VisibleForTesting
protected ParsedPackage parsePackage(File scanFile, int parseFlags)
throws PackageManagerException {
return mPackageParser.parsePackage(scanFile, parseFlags, true, mFrameworkSplits);
return mPackageParser.parsePackage(scanFile, parseFlags, true);
}
}

View File

@@ -143,15 +143,6 @@ public class PackageParser2 implements AutoCloseable {
@AnyThread
public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches)
throws PackageManagerException {
return parsePackage(packageFile, flags, useCaches, /* frameworkSplits= */ null);
}
/**
* TODO(b/135203078): Document new package parsing
*/
@AnyThread
public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches,
List<File> frameworkSplits) throws PackageManagerException {
var files = packageFile.listFiles();
// Apk directory is directly nested under the current directory
if (ArrayUtils.size(files) == 1 && files[0].isDirectory()) {
@@ -167,8 +158,7 @@ public class PackageParser2 implements AutoCloseable {
long parseTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
ParseInput input = mSharedResult.get().reset();
ParseResult<ParsingPackage> result = parsingUtils.parsePackage(input, packageFile, flags,
frameworkSplits);
ParseResult<ParsingPackage> result = parsingUtils.parsePackage(input, packageFile, flags);
if (result.isError()) {
throw new PackageManagerException(result.getErrorCode(), result.getErrorMessage(),
result.getException());

View File

@@ -238,7 +238,6 @@ public class ParsingPackageUtils {
* of required system property within the overlay tag.
*/
public static final int PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY = 1 << 7;
public static final int PARSE_FRAMEWORK_RES_SPLITS = 1 << 8;
public static final int PARSE_APK_IN_APEX = 1 << 9;
public static final int PARSE_CHATTY = 1 << 31;
@@ -257,7 +256,6 @@ public class ParsingPackageUtils {
PARSE_IS_SYSTEM_DIR,
PARSE_MUST_BE_APK,
PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY,
PARSE_FRAMEWORK_RES_SPLITS
})
@Retention(RetentionPolicy.SOURCE)
public @interface ParseFlags {}
@@ -307,7 +305,7 @@ public class ParsingPackageUtils {
isCoreApp);
}
});
var parseResult = parser.parsePackage(input, file, parseFlags, /* frameworkSplits= */ null);
var parseResult = parser.parsePackage(input, file, parseFlags);
if (parseResult.isError()) {
return input.error(parseResult);
}
@@ -356,14 +354,9 @@ public class ParsingPackageUtils {
* not check whether {@code packageFile} has changed since the last parse, it's up to callers to
* do so.
*/
public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile, int flags,
List<File> frameworkSplits) {
if (((flags & PARSE_FRAMEWORK_RES_SPLITS) != 0)
&& frameworkSplits.size() > 0
&& packageFile.getAbsolutePath().endsWith("/framework-res.apk")) {
return parseClusterPackage(input, packageFile, frameworkSplits, flags);
} else if (packageFile.isDirectory()) {
return parseClusterPackage(input, packageFile, /* frameworkSplits= */null, flags);
public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile, int flags) {
if (packageFile.isDirectory()) {
return parseClusterPackage(input, packageFile, flags);
} else {
return parseMonolithicPackage(input, packageFile, flags);
}
@@ -375,28 +368,17 @@ public class ParsingPackageUtils {
* identical package name and version codes, a single base APK, and unique
* split names.
* <p>
* Can also be passed the framework-res.apk file and a list of split apks coming from apexes
* (via {@code frameworkSplits}) in which case they will be parsed similar to cluster packages
* even if they are in different folders. Note that this code path may have other behaviour
* differences.
* <p>
* Note that this <em>does not</em> perform signature verification; that must be done separately
* in {@link #getSigningDetails(ParseInput, ParsedPackage, boolean)}.
*/
private ParseResult<ParsingPackage> parseClusterPackage(ParseInput input, File packageDir,
List<File> frameworkSplits, int flags) {
// parseClusterPackageLite should receive no flags (0) for regular splits but we want to
// pass the flags for framework splits
int flags) {
int liteParseFlags = 0;
if ((flags & PARSE_FRAMEWORK_RES_SPLITS) != 0) {
liteParseFlags = flags;
}
if ((flags & PARSE_APK_IN_APEX) != 0) {
liteParseFlags |= PARSE_APK_IN_APEX;
}
final ParseResult<PackageLite> liteResult =
ApkLiteParseUtils.parseClusterPackageLite(input, packageDir, frameworkSplits,
liteParseFlags);
ApkLiteParseUtils.parseClusterPackageLite(input, packageDir, liteParseFlags);
if (liteResult.isError()) {
return input.error(liteResult);
}
@@ -647,7 +629,7 @@ public class ParsingPackageUtils {
final TypedArray manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest);
try {
final boolean isCoreApp = parser.getAttributeBooleanValue(null /*namespace*/,
"coreApp",false);
"coreApp", false);
final ParsingPackage pkg = mCallback.startParsingPackage(
pkgName, apkPath, codePath, manifestArray, isCoreApp);
final ParseResult<ParsingPackage> result =

View File

@@ -62,10 +62,10 @@ import com.android.server.compat.PlatformCompat
import com.android.server.extendedtestutils.wheneverStatic
import com.android.server.pm.dex.DexManager
import com.android.server.pm.parsing.PackageParser2
import com.android.server.pm.pkg.AndroidPackage
import com.android.server.pm.parsing.pkg.PackageImpl
import com.android.server.pm.parsing.pkg.ParsedPackage
import com.android.server.pm.permission.PermissionManagerServiceInternal
import com.android.server.pm.pkg.AndroidPackage
import com.android.server.pm.pkg.parsing.ParsingPackage
import com.android.server.pm.pkg.parsing.ParsingPackageUtils
import com.android.server.pm.resolution.ComponentResolver
@@ -77,14 +77,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 libcore.util.HexEncoding
import org.junit.Assert
import org.junit.rules.TestRule
import org.junit.runner.Description
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
@@ -93,6 +85,14 @@ 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
import org.junit.runner.Description
import org.junit.runners.model.Statement
import org.mockito.AdditionalMatchers.or
import org.mockito.Mockito
import org.mockito.quality.Strictness
/**
* A utility for mocking behavior of the system and dependencies when testing PackageManagerService
@@ -522,7 +522,7 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) {
whenever(mocks.packageParser.parsePackage(
or(eq(path), eq(basePath)), anyInt(), anyBoolean())) { parsedPackage }
whenever(mocks.packageParser.parsePackage(
or(eq(path), eq(basePath)), anyInt(), anyBoolean(), any())) { parsedPackage }
or(eq(path), eq(basePath)), anyInt(), anyBoolean())) { parsedPackage }
return parsedPackage
}

View File

@@ -23,6 +23,7 @@ import android.os.Process
import android.util.Log
import com.android.server.pm.pkg.AndroidPackage
import com.android.server.testutils.whenever
import java.io.File
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
import org.hamcrest.Matchers.notNullValue
@@ -33,13 +34,11 @@ import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.argThat
import org.mockito.Mockito
import org.mockito.Mockito.verify
import java.io.File
@RunWith(JUnit4::class)
class PackageManagerServiceBootTest {
@@ -120,8 +119,7 @@ class PackageManagerServiceBootTest {
whenever(rule.mocks().packageParser.parsePackage(
argThat { path: File -> path.path.contains("a.data.package") },
anyInt(),
anyBoolean(),
any()))
anyBoolean()))
.thenThrow(PackageManagerException(
PackageManager.INSTALL_FAILED_INVALID_APK, "Oh no!"))
val pm = createPackageManagerService()