1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.server.pm; 18 19 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; 20 21 import android.os.Process; 22 import android.os.Trace; 23 24 import com.android.internal.annotations.VisibleForTesting; 25 import com.android.internal.util.ConcurrentUtils; 26 import com.android.server.pm.parsing.PackageParser2; 27 import com.android.server.pm.parsing.pkg.ParsedPackage; 28 29 import java.io.File; 30 import java.util.List; 31 import java.util.concurrent.ArrayBlockingQueue; 32 import java.util.concurrent.BlockingQueue; 33 import java.util.concurrent.ExecutorService; 34 35 /** 36 * Helper class for parallel parsing of packages using {@link PackageParser2}. 37 * <p>Parsing requests are processed by a thread-pool of {@link #MAX_THREADS}. 38 * At any time, at most {@link #QUEUE_CAPACITY} results are kept in RAM</p> 39 */ 40 class ParallelPackageParser { 41 42 private static final int QUEUE_CAPACITY = 30; 43 private static final int MAX_THREADS = 4; 44 45 private volatile String mInterruptedInThread; 46 47 private final BlockingQueue<ParseResult> mQueue = new ArrayBlockingQueue<>(QUEUE_CAPACITY); 48 makeExecutorService()49 static ExecutorService makeExecutorService() { 50 return ConcurrentUtils.newFixedThreadPool(MAX_THREADS, "package-parsing-thread", 51 Process.THREAD_PRIORITY_FOREGROUND); 52 } 53 54 private final PackageParser2 mPackageParser; 55 56 private final ExecutorService mExecutorService; 57 58 private final List<File> mFrameworkSplits; 59 ParallelPackageParser(PackageParser2 packageParser, ExecutorService executorService)60 ParallelPackageParser(PackageParser2 packageParser, ExecutorService executorService) { 61 this(packageParser, executorService, /* frameworkSplits= */ null); 62 } 63 ParallelPackageParser(PackageParser2 packageParser, ExecutorService executorService, List<File> frameworkSplits)64 ParallelPackageParser(PackageParser2 packageParser, ExecutorService executorService, 65 List<File> frameworkSplits) { 66 mPackageParser = packageParser; 67 mExecutorService = executorService; 68 mFrameworkSplits = frameworkSplits; 69 } 70 71 static class ParseResult { 72 73 ParsedPackage parsedPackage; // Parsed package 74 File scanFile; // File that was parsed 75 Throwable throwable; // Set if an error occurs during parsing 76 77 @Override toString()78 public String toString() { 79 return "ParseResult{" + 80 "parsedPackage=" + parsedPackage + 81 ", scanFile=" + scanFile + 82 ", throwable=" + throwable + 83 '}'; 84 } 85 } 86 87 /** 88 * Take the parsed package from the parsing queue, waiting if necessary until the element 89 * appears in the queue. 90 * @return parsed package 91 */ take()92 public ParseResult take() { 93 try { 94 if (mInterruptedInThread != null) { 95 throw new InterruptedException("Interrupted in " + mInterruptedInThread); 96 } 97 return mQueue.take(); 98 } catch (InterruptedException e) { 99 // We cannot recover from interrupt here 100 Thread.currentThread().interrupt(); 101 throw new IllegalStateException(e); 102 } 103 } 104 105 /** 106 * Submits the file for parsing 107 * @param scanFile file to scan 108 * @param parseFlags parse flags 109 */ submit(File scanFile, int parseFlags)110 public void submit(File scanFile, int parseFlags) { 111 mExecutorService.submit(() -> { 112 ParseResult pr = new ParseResult(); 113 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parallel parsePackage [" + scanFile + "]"); 114 try { 115 pr.scanFile = scanFile; 116 pr.parsedPackage = parsePackage(scanFile, parseFlags); 117 } catch (Throwable e) { 118 pr.throwable = e; 119 } finally { 120 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); 121 } 122 try { 123 mQueue.put(pr); 124 } catch (InterruptedException e) { 125 Thread.currentThread().interrupt(); 126 // Propagate result to callers of take(). 127 // This is helpful to prevent main thread from getting stuck waiting on 128 // ParallelPackageParser to finish in case of interruption 129 mInterruptedInThread = Thread.currentThread().getName(); 130 } 131 }); 132 } 133 134 @VisibleForTesting parsePackage(File scanFile, int parseFlags)135 protected ParsedPackage parsePackage(File scanFile, int parseFlags) 136 throws PackageManagerException { 137 return mPackageParser.parsePackage(scanFile, parseFlags, true, mFrameworkSplits); 138 } 139 } 140