Merge "Combine package parsing thread pools"

This commit is contained in:
TreeHugger Robot
2020-01-24 23:18:57 +00:00
committed by Android (Google) Code Review
4 changed files with 112 additions and 103 deletions

View File

@@ -41,6 +41,7 @@ import static android.os.Build.VERSION_CODES.O;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
import android.annotation.AnyThread;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -1080,6 +1081,7 @@ public class PackageParser {
*
* @see #parsePackage(File, int, boolean)
*/
@AnyThread
public ParsedPackage parseParsedPackage(File packageFile, int flags, boolean useCaches)
throws PackageParserException {
ParsedPackage parsed = useCaches ? getCachedResult(packageFile, flags) : null;

View File

@@ -387,6 +387,7 @@ import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -2868,23 +2869,34 @@ public class PackageManagerService extends IPackageManager.Stub
scanFlags = scanFlags | SCAN_FIRST_BOOT_OR_UPGRADE;
}
final int systemParseFlags = mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR;
final int systemScanFlags = scanFlags | SCAN_AS_SYSTEM;
PackageParser packageParser = new PackageParser();
packageParser.setSeparateProcesses(mSeparateProcesses);
packageParser.setOnlyCoreApps(mOnlyCore);
packageParser.setDisplayMetrics(mMetrics);
packageParser.setCacheDir(mCacheDir);
packageParser.setCallback(mPackageParserCallback);
ExecutorService executorService = ParallelPackageParser.makeExecutorService();
// Collect vendor/product/system_ext overlay packages. (Do this before scanning
// any apps.)
// For security and version matching reason, only consider overlay packages if they
// reside in the right directory.
final int systemParseFlags = mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR;
final int systemScanFlags = scanFlags | SCAN_AS_SYSTEM;
for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {
final SystemPartition partition = mDirsToScanAsSystem.get(i);
if (partition.overlayFolder == null) {
continue;
}
scanDirTracedLI(partition.overlayFolder, systemParseFlags,
systemScanFlags | partition.scanFlag, 0);
systemScanFlags | partition.scanFlag, 0,
packageParser, executorService);
}
scanDirTracedLI(frameworkDir, systemParseFlags,
systemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0);
systemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0,
packageParser, executorService);
if (!mPackages.containsKey("android")) {
throw new IllegalStateException(
"Failed to load frameworks package; check log for warnings");
@@ -2893,10 +2905,12 @@ public class PackageManagerService extends IPackageManager.Stub
final SystemPartition partition = mDirsToScanAsSystem.get(i);
if (partition.privAppFolder != null) {
scanDirTracedLI(partition.privAppFolder, systemParseFlags,
systemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0);
systemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0,
packageParser, executorService);
}
scanDirTracedLI(partition.appFolder, systemParseFlags,
systemScanFlags | partition.scanFlag, 0);
systemScanFlags | partition.scanFlag, 0,
packageParser, executorService);
}
@@ -3000,8 +3014,18 @@ public class PackageManagerService extends IPackageManager.Stub
if (!mOnlyCore) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
SystemClock.uptimeMillis());
scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0,
packageParser, executorService);
}
List<Runnable> unfinishedTasks = executorService.shutdownNow();
if (!unfinishedTasks.isEmpty()) {
throw new IllegalStateException("Not all tasks finished before calling close: "
+ unfinishedTasks);
}
if (!mOnlyCore) {
// Remove disable package settings for updated system apps that were
// removed via an OTA. If the update is no longer present, remove the
// app completely. Otherwise, revoke their system privileges.
@@ -8578,16 +8602,18 @@ public class PackageManagerService extends IPackageManager.Stub
return finalList;
}
private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags, long currentTime) {
private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags,
long currentTime, PackageParser packageParser, ExecutorService executorService) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
try {
scanDirLI(scanDir, parseFlags, scanFlags, currentTime);
scanDirLI(scanDir, parseFlags, scanFlags, currentTime, packageParser, executorService);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime) {
private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime,
PackageParser packageParser, ExecutorService executorService) {
final File[] files = scanDir.listFiles();
if (ArrayUtils.isEmpty(files)) {
Log.d(TAG, "No files in app dir " + scanDir);
@@ -8598,58 +8624,58 @@ public class PackageManagerService extends IPackageManager.Stub
Log.d(TAG, "Scanning app dir " + scanDir + " scanFlags=" + scanFlags
+ " flags=0x" + Integer.toHexString(parseFlags));
}
try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(
mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir,
mPackageParserCallback)) {
// Submit files for parsing in parallel
int fileCount = 0;
for (File file : files) {
final boolean isPackage = (isApkFile(file) || file.isDirectory())
&& !PackageInstallerService.isStageName(file.getName());
if (!isPackage) {
// Ignore entries which are not packages
continue;
ParallelPackageParser parallelPackageParser =
new ParallelPackageParser(packageParser, executorService);
// Submit files for parsing in parallel
int fileCount = 0;
for (File file : files) {
final boolean isPackage = (isApkFile(file) || file.isDirectory())
&& !PackageInstallerService.isStageName(file.getName());
if (!isPackage) {
// Ignore entries which are not packages
continue;
}
parallelPackageParser.submit(file, parseFlags);
fileCount++;
}
// Process results one by one
for (; fileCount > 0; fileCount--) {
ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
Throwable throwable = parseResult.throwable;
int errorCode = PackageManager.INSTALL_SUCCEEDED;
if (throwable == null) {
// TODO(toddke): move lower in the scan chain
// Static shared libraries have synthetic package names
if (parseResult.parsedPackage.isStaticSharedLibrary()) {
renameStaticSharedLibraryPackage(parseResult.parsedPackage);
}
parallelPackageParser.submit(file, parseFlags);
fileCount++;
try {
addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
currentTime, null);
} catch (PackageManagerException e) {
errorCode = e.error;
Slog.w(TAG, "Failed to scan " + parseResult.scanFile + ": " + e.getMessage());
}
} else if (throwable instanceof PackageParserException) {
PackageParserException e = (PackageParserException)
throwable;
errorCode = e.error;
Slog.w(TAG, "Failed to parse " + parseResult.scanFile + ": " + e.getMessage());
} else {
throw new IllegalStateException("Unexpected exception occurred while parsing "
+ parseResult.scanFile, throwable);
}
// Process results one by one
for (; fileCount > 0; fileCount--) {
ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
Throwable throwable = parseResult.throwable;
int errorCode = PackageManager.INSTALL_SUCCEEDED;
if (throwable == null) {
// TODO(toddke): move lower in the scan chain
// Static shared libraries have synthetic package names
if (parseResult.parsedPackage.isStaticSharedLibrary()) {
renameStaticSharedLibraryPackage(parseResult.parsedPackage);
}
try {
addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
currentTime, null);
} catch (PackageManagerException e) {
errorCode = e.error;
Slog.w(TAG, "Failed to scan " + parseResult.scanFile + ": " + e.getMessage());
}
} else if (throwable instanceof PackageParserException) {
PackageParserException e = (PackageParserException)
throwable;
errorCode = e.error;
Slog.w(TAG, "Failed to parse " + parseResult.scanFile + ": " + e.getMessage());
} else {
throw new IllegalStateException("Unexpected exception occurred while parsing "
+ parseResult.scanFile, throwable);
}
// Delete invalid userdata apps
if ((scanFlags & SCAN_AS_SYSTEM) == 0 &&
errorCode != PackageManager.INSTALL_SUCCEEDED) {
logCriticalInfo(Log.WARN,
"Deleting invalid package at " + parseResult.scanFile);
removeCodePathLI(parseResult.scanFile);
}
// Delete invalid userdata apps
if ((scanFlags & SCAN_AS_SYSTEM) == 0
&& errorCode != PackageManager.INSTALL_SUCCEEDED) {
logCriticalInfo(Log.WARN,
"Deleting invalid package at " + parseResult.scanFile);
removeCodePathLI(parseResult.scanFile);
}
}
}

View File

@@ -22,13 +22,11 @@ import android.content.pm.PackageParser;
import android.content.pm.parsing.ParsedPackage;
import android.os.Process;
import android.os.Trace;
import android.util.DisplayMetrics;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ConcurrentUtils;
import java.io.File;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
@@ -38,30 +36,27 @@ import java.util.concurrent.ExecutorService;
* <p>Parsing requests are processed by a thread-pool of {@link #MAX_THREADS}.
* At any time, at most {@link #QUEUE_CAPACITY} results are kept in RAM</p>
*/
class ParallelPackageParser implements AutoCloseable {
class ParallelPackageParser {
private static final int QUEUE_CAPACITY = 10;
private static final int QUEUE_CAPACITY = 30;
private static final int MAX_THREADS = 4;
private final String[] mSeparateProcesses;
private final boolean mOnlyCore;
private final DisplayMetrics mMetrics;
private final File mCacheDir;
private final PackageParser.Callback mPackageParserCallback;
private volatile String mInterruptedInThread;
private final BlockingQueue<ParseResult> mQueue = new ArrayBlockingQueue<>(QUEUE_CAPACITY);
private final ExecutorService mService = ConcurrentUtils.newFixedThreadPool(MAX_THREADS,
"package-parsing-thread", Process.THREAD_PRIORITY_FOREGROUND);
static ExecutorService makeExecutorService() {
return ConcurrentUtils.newFixedThreadPool(MAX_THREADS, "package-parsing-thread",
Process.THREAD_PRIORITY_FOREGROUND);
}
ParallelPackageParser(String[] separateProcesses, boolean onlyCoreApps,
DisplayMetrics metrics, File cacheDir, PackageParser.Callback callback) {
mSeparateProcesses = separateProcesses;
mOnlyCore = onlyCoreApps;
mMetrics = metrics;
mCacheDir = cacheDir;
mPackageParserCallback = callback;
private final PackageParser mPackageParser;
private final ExecutorService mExecutorService;
ParallelPackageParser(PackageParser packageParser, ExecutorService executorService) {
mPackageParser = packageParser;
mExecutorService = executorService;
}
static class ParseResult {
@@ -104,18 +99,12 @@ class ParallelPackageParser implements AutoCloseable {
* @param parseFlags parse flags
*/
public void submit(File scanFile, int parseFlags) {
mService.submit(() -> {
mExecutorService.submit(() -> {
ParseResult pr = new ParseResult();
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parallel parsePackage [" + scanFile + "]");
try {
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setOnlyCoreApps(mOnlyCore);
pp.setDisplayMetrics(mMetrics);
pp.setCacheDir(mCacheDir);
pp.setCallback(mPackageParserCallback);
pr.scanFile = scanFile;
pr.parsedPackage = parsePackage(pp, scanFile, parseFlags);
pr.parsedPackage = parsePackage(scanFile, parseFlags);
} catch (Throwable e) {
pr.throwable = e;
} finally {
@@ -134,17 +123,8 @@ class ParallelPackageParser implements AutoCloseable {
}
@VisibleForTesting
protected ParsedPackage parsePackage(PackageParser packageParser, File scanFile,
int parseFlags) throws PackageParser.PackageParserException {
return packageParser.parseParsedPackage(scanFile, parseFlags, true);
}
@Override
public void close() {
List<Runnable> unfinishedTasks = mService.shutdownNow();
if (!unfinishedTasks.isEmpty()) {
throw new IllegalStateException("Not all tasks finished before calling close: "
+ unfinishedTasks);
}
protected ParsedPackage parsePackage(File scanFile, int parseFlags)
throws PackageParser.PackageParserException {
return mPackageParser.parseParsedPackage(scanFile, parseFlags, true);
}
}

View File

@@ -31,6 +31,7 @@ import org.junit.runner.RunWith;
import java.io.File;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutorService;
/**
* Tests for {@link ParallelPackageParser}
@@ -43,7 +44,8 @@ public class ParallelPackageParserTest {
@Before
public void setUp() {
mParser = new TestParallelPackageParser();
mParser = new TestParallelPackageParser(new PackageParser(),
ParallelPackageParser.makeExecutorService());
}
@Test(timeout = 1000)
@@ -68,15 +70,14 @@ public class ParallelPackageParserTest {
}
}
class TestParallelPackageParser extends ParallelPackageParser {
private class TestParallelPackageParser extends ParallelPackageParser {
TestParallelPackageParser() {
super(null, false, null, null, null);
TestParallelPackageParser(PackageParser packageParser, ExecutorService executorService) {
super(packageParser, executorService);
}
@Override
protected ParsedPackage parsePackage(PackageParser packageParser, File scanFile,
int parseFlags) throws PackageParser.PackageParserException {
protected ParsedPackage parsePackage(File scanFile, int parseFlags) {
// Do not actually parse the package for testing
return null;
}