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.concurrent.ArrayBlockingQueue; 31 import java.util.concurrent.BlockingQueue; 32 import java.util.concurrent.ExecutorService; 33 34 /** 35 * Helper class for parallel parsing of packages using {@link PackageParser2}. 36 * <p>Parsing requests are processed by a thread-pool of {@link #MAX_THREADS}. 37 * At any time, at most {@link #QUEUE_CAPACITY} results are kept in RAM</p> 38 */ 39 class ParallelPackageParser { 40 41 private static final int QUEUE_CAPACITY = 30; 42 private static final int MAX_THREADS = 4; 43 44 private volatile String mInterruptedInThread; 45 46 private final BlockingQueue<ParseResult> mQueue = new ArrayBlockingQueue<>(QUEUE_CAPACITY); 47 makeExecutorService()48 static ExecutorService makeExecutorService() { 49 return ConcurrentUtils.newFixedThreadPool(MAX_THREADS, "package-parsing-thread", 50 Process.THREAD_PRIORITY_FOREGROUND); 51 } 52 53 private final PackageParser2 mPackageParser; 54 55 private final ExecutorService mExecutorService; 56 ParallelPackageParser(PackageParser2 packageParser, ExecutorService executorService)57 ParallelPackageParser(PackageParser2 packageParser, ExecutorService executorService) { 58 mPackageParser = packageParser; 59 mExecutorService = executorService; 60 } 61 62 static class ParseResult { 63 64 ParsedPackage parsedPackage; // Parsed package 65 File scanFile; // File that was parsed 66 Throwable throwable; // Set if an error occurs during parsing 67 68 @Override toString()69 public String toString() { 70 return "ParseResult{" + 71 "parsedPackage=" + parsedPackage + 72 ", scanFile=" + scanFile + 73 ", throwable=" + throwable + 74 '}'; 75 } 76 } 77 78 /** 79 * Take the parsed package from the parsing queue, waiting if necessary until the element 80 * appears in the queue. 81 * @return parsed package 82 */ take()83 public ParseResult take() { 84 try { 85 if (mInterruptedInThread != null) { 86 throw new InterruptedException("Interrupted in " + mInterruptedInThread); 87 } 88 return mQueue.take(); 89 } catch (InterruptedException e) { 90 // We cannot recover from interrupt here 91 Thread.currentThread().interrupt(); 92 throw new IllegalStateException(e); 93 } 94 } 95 96 /** 97 * Submits the file for parsing 98 * @param scanFile file to scan 99 * @param parseFlags parse flags 100 */ submit(File scanFile, int parseFlags)101 public void submit(File scanFile, int parseFlags) { 102 mExecutorService.submit(() -> { 103 ParseResult pr = new ParseResult(); 104 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parallel parsePackage [" + scanFile + "]"); 105 try { 106 pr.scanFile = scanFile; 107 pr.parsedPackage = parsePackage(scanFile, parseFlags); 108 } catch (Throwable e) { 109 pr.throwable = e; 110 } finally { 111 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); 112 } 113 try { 114 mQueue.put(pr); 115 } catch (InterruptedException e) { 116 Thread.currentThread().interrupt(); 117 // Propagate result to callers of take(). 118 // This is helpful to prevent main thread from getting stuck waiting on 119 // ParallelPackageParser to finish in case of interruption 120 mInterruptedInThread = Thread.currentThread().getName(); 121 } 122 }); 123 } 124 125 @VisibleForTesting parsePackage(File scanFile, int parseFlags)126 protected ParsedPackage parsePackage(File scanFile, int parseFlags) 127 throws PackageManagerException { 128 return mPackageParser.parsePackage(scanFile, parseFlags, true); 129 } 130 } 131