summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/com/android/internal/util/ConcurrentUtils.java89
-rw-r--r--services/core/java/com/android/server/PersistentDataBlockService.java4
-rw-r--r--services/core/java/com/android/server/SystemServerInitThreadPool.java96
-rw-r--r--services/core/java/com/android/server/UiModeManagerService.java10
-rw-r--r--services/core/java/com/android/server/fingerprint/FingerprintService.java4
-rw-r--r--services/core/java/com/android/server/pm/ParallelPackageParser.java21
-rw-r--r--services/java/com/android/server/SystemServer.java3
7 files changed, 201 insertions, 26 deletions
diff --git a/core/java/com/android/internal/util/ConcurrentUtils.java b/core/java/com/android/internal/util/ConcurrentUtils.java
new file mode 100644
index 000000000000..e35f9f45acfe
--- /dev/null
+++ b/core/java/com/android/internal/util/ConcurrentUtils.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.internal.util;
+
+import android.os.Process;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Utility methods for common functionality using java.util.concurrent package
+ *
+ * @hide
+ */
+public class ConcurrentUtils {
+
+ private ConcurrentUtils() {
+ }
+
+ /**
+ * Creates a thread pool using
+ * {@link java.util.concurrent.Executors#newFixedThreadPool(int, ThreadFactory)}
+ *
+ * @param nThreads the number of threads in the pool
+ * @param poolName base name of the threads in the pool
+ * @param linuxThreadPriority a Linux priority level. see {@link Process#setThreadPriority(int)}
+ * @return the newly created thread pool
+ */
+ public static ExecutorService newFixedThreadPool(int nThreads, String poolName,
+ int linuxThreadPriority) {
+ return Executors.newFixedThreadPool(nThreads,
+ new ThreadFactory() {
+ private final AtomicInteger threadNum = new AtomicInteger(0);
+
+ @Override
+ public Thread newThread(final Runnable r) {
+ return new Thread(poolName + threadNum.incrementAndGet()) {
+ @Override
+ public void run() {
+ Process.setThreadPriority(linuxThreadPriority);
+ r.run();
+ }
+ };
+ }
+ });
+ }
+
+ /**
+ * Waits if necessary for the computation to complete, and then retrieves its result.
+ * <p>If {@code InterruptedException} occurs, this method will interrupt the current thread
+ * and throw {@code IllegalStateException}</p>
+ *
+ * @param future future to wait for result
+ * @param description short description of the operation
+ * @return the computed result
+ * @throws IllegalStateException if interrupted during wait
+ * @throws RuntimeException if an error occurs while waiting for {@link Future#get()}
+ * @see Future#get()
+ */
+ public static <T> T waitForFutureNoInterrupt(Future<T> future, String description) {
+ try {
+ return future.get();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new IllegalStateException(description + " interrupted");
+ } catch (ExecutionException e) {
+ throw new RuntimeException(description + " failed", e);
+ }
+ }
+
+}
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index 8a0d4df8ec27..417d375f302a 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -116,12 +116,12 @@ public class PersistentDataBlockService extends SystemService {
@Override
public void onStart() {
// Do init on a separate thread, will join in PHASE_ACTIVITY_MANAGER_READY
- FgThread.getHandler().post(() -> {
+ SystemServerInitThreadPool.get().submit(() -> {
enforceChecksumValidity();
formatIfOemUnlockEnabled();
publishBinderService(Context.PERSISTENT_DATA_BLOCK_SERVICE, mService);
mInitDoneSignal.countDown();
- });
+ }, TAG + ".onStart");
}
@Override
diff --git a/services/core/java/com/android/server/SystemServerInitThreadPool.java b/services/core/java/com/android/server/SystemServerInitThreadPool.java
new file mode 100644
index 000000000000..d1968507508a
--- /dev/null
+++ b/services/core/java/com/android/server/SystemServerInitThreadPool.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server;
+
+import android.os.Build;
+import android.os.Process;
+import android.util.Slog;
+
+import com.android.internal.util.ConcurrentUtils;
+import com.android.internal.util.Preconditions;
+
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Thread pool used during initialization of system server.
+ * <p>System services can {@link #submit(Runnable)} tasks for execution during boot.
+ * The pool will be shut down after {@link SystemService#PHASE_BOOT_COMPLETED}.
+ * New tasks <em>should not</em> be submitted afterwards.
+ *
+ * @hide
+ */
+public class SystemServerInitThreadPool {
+ private static final String TAG = SystemServerInitThreadPool.class.getSimpleName();
+ private static final int SHUTDOWN_TIMEOUT_MILLIS = 20000;
+ private static final boolean IS_DEBUGGABLE = Build.IS_DEBUGGABLE;
+
+ private static SystemServerInitThreadPool sInstance;
+
+ private ExecutorService mService = ConcurrentUtils.newFixedThreadPool(2,
+ "system-server-init-thread", Process.THREAD_PRIORITY_FOREGROUND);
+
+ public static synchronized SystemServerInitThreadPool get() {
+ if (sInstance == null) {
+ sInstance = new SystemServerInitThreadPool();
+ }
+ Preconditions.checkState(sInstance.mService != null, "Cannot get " + TAG
+ + " - it has been shut down");
+ return sInstance;
+ }
+
+ public Future<?> submit(Runnable runnable, String description) {
+ if (IS_DEBUGGABLE) {
+ return mService.submit(() -> {
+ Slog.d(TAG, "Started executing " + description);
+ try {
+ runnable.run();
+ } catch (RuntimeException e) {
+ Slog.e(TAG, "Failure in " + description + ": " + e, e);
+ throw e;
+ }
+ Slog.d(TAG, "Finished executing " + description);
+ });
+ }
+ return mService.submit(runnable);
+ }
+
+ static synchronized void shutdown() {
+ if (sInstance != null && sInstance.mService != null) {
+ sInstance.mService.shutdown();
+ boolean terminated;
+ try {
+ terminated = sInstance.mService.awaitTermination(SHUTDOWN_TIMEOUT_MILLIS,
+ TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new IllegalStateException(TAG + " init interrupted");
+ }
+ List<Runnable> unstartedRunnables = sInstance.mService.shutdownNow();
+ if (!terminated) {
+ throw new IllegalStateException("Cannot shutdown. Unstarted tasks "
+ + unstartedRunnables);
+ }
+ sInstance.mService = null; // Make mService eligible for GC
+ Slog.d(TAG, "Shutdown successful");
+ }
+ }
+
+}
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 6ea6fb7c9c03..5e1e1e0a5925 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -208,11 +208,13 @@ final class UiModeManagerService extends SystemService {
Settings.Secure.UI_NIGHT_MODE, defaultNightMode);
// Update the initial, static configurations.
- synchronized (this) {
- updateConfigurationLocked();
- sendConfigurationLocked();
- }
+ SystemServerInitThreadPool.get().submit(() -> {
+ synchronized (mLock) {
+ updateConfigurationLocked();
+ sendConfigurationLocked();
+ }
+ }, TAG + ".onStart");
publishBinderService(Context.UI_MODE_SERVICE, mService);
}
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 3f0ebf28bbfd..b0f67a81db28 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -54,7 +54,7 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
-import com.android.server.FgThread;
+import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
import org.json.JSONArray;
@@ -1077,7 +1077,7 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
@Override
public void onStart() {
publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
- FgThread.getHandler().post(() -> getFingerprintDaemon());
+ SystemServerInitThreadPool.get().submit(this::getFingerprintDaemon, TAG + ".onStart");
listenForUserSwitches();
}
diff --git a/services/core/java/com/android/server/pm/ParallelPackageParser.java b/services/core/java/com/android/server/pm/ParallelPackageParser.java
index 158cfc946b73..73125473df03 100644
--- a/services/core/java/com/android/server/pm/ParallelPackageParser.java
+++ b/services/core/java/com/android/server/pm/ParallelPackageParser.java
@@ -22,15 +22,13 @@ 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;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.atomic.AtomicInteger;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
@@ -51,21 +49,8 @@ class ParallelPackageParser implements AutoCloseable {
private final BlockingQueue<ParseResult> mQueue = new ArrayBlockingQueue<>(QUEUE_CAPACITY);
- private final ExecutorService mService = Executors.newFixedThreadPool(MAX_THREADS,
- new ThreadFactory() {
- private final AtomicInteger threadNum = new AtomicInteger(0);
-
- @Override
- public Thread newThread(final Runnable r) {
- return new Thread("package-parsing-thread" + threadNum.incrementAndGet()) {
- @Override
- public void run() {
- Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
- r.run();
- }
- };
- }
- });
+ private final ExecutorService mService = ConcurrentUtils.newFixedThreadPool(MAX_THREADS,
+ "package-parsing-thread", Process.THREAD_PRIORITY_FOREGROUND);
ParallelPackageParser(String[] separateProcesses, boolean onlyCoreApps,
DisplayMetrics metrics) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 1d550d294560..5b46f5144fd5 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -345,6 +345,8 @@ public final class SystemServer {
// Create the system service manager.
mSystemServiceManager = new SystemServiceManager(mSystemContext);
LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
+ // Prepare the thread pool for init tasks that can be parallelized
+ SystemServerInitThreadPool.get();
} finally {
traceEnd(); // InitBeforeStartServices
}
@@ -362,6 +364,7 @@ public final class SystemServer {
} finally {
traceEnd();
}
+ SystemServerInitThreadPool.shutdown();
// For debug builds, log event loop stalls to dropbox for analysis.
if (StrictMode.conditionallyEnableDebugLogging()) {