Merge "Combine package parsing thread pools"
This commit is contained in:
committed by
Android (Google) Code Review
commit
f61162a002
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user