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.pm.parsing.PackageParser2; 26 import com.android.internal.pm.parsing.PackageParserException; 27 import com.android.internal.pm.parsing.pkg.ParsedPackage; 28 import com.android.internal.util.ConcurrentUtils; 29 30 import java.io.File; 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 ParallelPackageParser(PackageParser2 packageParser, ExecutorService executorService)58 ParallelPackageParser(PackageParser2 packageParser, ExecutorService executorService) { 59 mPackageParser = packageParser; 60 mExecutorService = executorService; 61 } 62 63 static class ParseResult { 64 65 ParsedPackage parsedPackage; // Parsed package 66 File scanFile; // File that was parsed 67 Throwable throwable; // Set if an error occurs during parsing 68 69 @Override toString()70 public String toString() { 71 return "ParseResult{" + 72 "parsedPackage=" + parsedPackage + 73 ", scanFile=" + scanFile + 74 ", throwable=" + throwable + 75 '}'; 76 } 77 } 78 79 /** 80 * Take the parsed package from the parsing queue, waiting if necessary until the element 81 * appears in the queue. 82 * @return parsed package 83 */ take()84 public ParseResult take() { 85 try { 86 if (mInterruptedInThread != null) { 87 throw new InterruptedException("Interrupted in " + mInterruptedInThread); 88 } 89 return mQueue.take(); 90 } catch (InterruptedException e) { 91 // We cannot recover from interrupt here 92 Thread.currentThread().interrupt(); 93 throw new IllegalStateException(e); 94 } 95 } 96 97 /** 98 * Submits the file for parsing 99 * @param scanFile file to scan 100 * @param parseFlags parse flags 101 */ submit(File scanFile, int parseFlags)102 public void submit(File scanFile, int parseFlags) { 103 mExecutorService.submit(() -> { 104 ParseResult pr = new ParseResult(); 105 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parallel parsePackage [" + scanFile + "]"); 106 try { 107 pr.scanFile = scanFile; 108 pr.parsedPackage = parsePackage(scanFile, parseFlags); 109 } catch (Throwable e) { 110 pr.throwable = e; 111 } finally { 112 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); 113 } 114 try { 115 mQueue.put(pr); 116 } catch (InterruptedException e) { 117 Thread.currentThread().interrupt(); 118 // Propagate result to callers of take(). 119 // This is helpful to prevent main thread from getting stuck waiting on 120 // ParallelPackageParser to finish in case of interruption 121 mInterruptedInThread = Thread.currentThread().getName(); 122 } 123 }); 124 } 125 126 @VisibleForTesting parsePackage(File scanFile, int parseFlags)127 protected ParsedPackage parsePackage(File scanFile, int parseFlags) 128 throws PackageManagerException { 129 try { 130 return mPackageParser.parsePackage(scanFile, parseFlags, true); 131 } catch (PackageParserException e) { 132 throw new PackageManagerException(e.error, e.getMessage(), e); 133 } 134 } 135 } 136