Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(450)

Unified Diff: content/public/android/java/src/org/chromium/content/browser/ChildConnectionAllocator.java

Issue 2795113003: Factor out inner-classes out of ChildProcessLauncher. (Closed)
Patch Set: Addressed boliu@'s comments. Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/public/android/java/src/org/chromium/content/browser/ChildConnectionAllocator.java
diff --git a/content/public/android/java/src/org/chromium/content/browser/ChildConnectionAllocator.java b/content/public/android/java/src/org/chromium/content/browser/ChildConnectionAllocator.java
new file mode 100644
index 0000000000000000000000000000000000000000..25872326fa838d1cbfa6e7fb4de6292205c75bb3
--- /dev/null
+++ b/content/public/android/java/src/org/chromium/content/browser/ChildConnectionAllocator.java
@@ -0,0 +1,262 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.content.browser;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.text.TextUtils;
+
+import org.chromium.base.Log;
+import org.chromium.base.VisibleForTesting;
+import org.chromium.content.app.PrivilegedProcessService;
+import org.chromium.content.app.SandboxedProcessService;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.annotation.concurrent.GuardedBy;
+
+/**
+ * This class is responsible for allocating and managing connections to child
+ * process services. These connections are in a pool (the services are defined
+ * in the AndroidManifest.xml).
+ */
+public class ChildConnectionAllocator {
+ private static final String TAG = "ChildConnAllocator";
+
+ private static final String NUM_SANDBOXED_SERVICES_KEY =
+ "org.chromium.content.browser.NUM_SANDBOXED_SERVICES";
+ private static final String NUM_PRIVILEGED_SERVICES_KEY =
+ "org.chromium.content.browser.NUM_PRIVILEGED_SERVICES";
+ private static final String SANDBOXED_SERVICES_NAME_KEY =
+ "org.chromium.content.browser.SANDBOXED_SERVICES_NAME";
+
+ private static final Object sAllocatorLock = new Object();
+
+ // Map from package name to ChildConnectionAllocator.
+ @GuardedBy("sAllocatorLock")
+ private static Map<String, ChildConnectionAllocator> sSandboxedChildConnectionAllocatorMap;
+
+ // Allocator used for non-sandboxed services.
+ @GuardedBy("sAllocatorLock")
+ private static ChildConnectionAllocator sPrivilegedChildConnectionAllocator;
+
+ // Used by test to override the default sandboxed service settings.
+ private static int sSandboxedServicesCountForTesting = -1;
+ private static String sSandboxedServicesNameForTesting;
+
+ // Connections to services. Indices of the array correspond to the service numbers.
+ private final ChildProcessConnection[] mChildProcessConnections;
+
+ private final String mChildClassName;
+ private final boolean mInSandbox;
+
+ private final Object mConnectionLock = new Object();
+
+ // The list of free (not bound) service indices.
+ @GuardedBy("mConnectionLock")
+ private final ArrayList<Integer> mFreeConnectionIndices;
+
+ // Each Allocator keeps a queue for the pending spawn data. Once a connection is free, we
+ // dequeue the pending spawn data from the same allocator as the connection.
+ @GuardedBy("mConnectionLock")
+ private final Queue<ChildSpawnData> mPendingSpawnQueue = new LinkedList<>();
+
+ public static ChildConnectionAllocator getAllocator(
+ Context context, String packageName, boolean inSandbox) {
+ synchronized (sAllocatorLock) {
+ if (!inSandbox) {
+ if (sPrivilegedChildConnectionAllocator == null) {
+ sPrivilegedChildConnectionAllocator = new ChildConnectionAllocator(false,
+ getNumberOfServices(context, false, packageName),
+ getClassNameOfService(context, false, packageName));
+ }
+ return sPrivilegedChildConnectionAllocator;
+ }
+
+ if (sSandboxedChildConnectionAllocatorMap == null) {
+ sSandboxedChildConnectionAllocatorMap =
+ new ConcurrentHashMap<String, ChildConnectionAllocator>();
+ }
+ if (!sSandboxedChildConnectionAllocatorMap.containsKey(packageName)) {
+ Log.w(TAG,
+ "Create a new ChildConnectionAllocator with package name = %s,"
+ + " inSandbox = true",
+ packageName);
+ sSandboxedChildConnectionAllocatorMap.put(packageName,
+ new ChildConnectionAllocator(true,
+ getNumberOfServices(context, true, packageName),
+ getClassNameOfService(context, true, packageName)));
+ }
+ return sSandboxedChildConnectionAllocatorMap.get(packageName);
+ // TODO(pkotwicz|hanxi): Figure out when old allocators should be removed from
+ // {@code sSandboxedChildConnectionAllocatorMap}.
+ }
+ }
+
+ private static String getClassNameOfService(
+ Context context, boolean inSandbox, String packageName) {
+ if (!inSandbox) {
+ return PrivilegedProcessService.class.getName();
+ }
+
+ if (!TextUtils.isEmpty(sSandboxedServicesNameForTesting)) {
+ return sSandboxedServicesNameForTesting;
+ }
+
+ String serviceName = null;
+ try {
+ PackageManager packageManager = context.getPackageManager();
+ ApplicationInfo appInfo =
+ packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
+ if (appInfo.metaData != null) {
+ serviceName = appInfo.metaData.getString(SANDBOXED_SERVICES_NAME_KEY);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RuntimeException("Could not get application info.");
+ }
+
+ if (serviceName != null) {
+ // Check that the service exists.
+ try {
+ PackageManager packageManager = context.getPackageManager();
+ // PackageManager#getServiceInfo() throws an exception if the service does not
+ // exist.
+ packageManager.getServiceInfo(new ComponentName(packageName, serviceName + "0"), 0);
+ return serviceName;
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RuntimeException(
+ "Illegal meta data value: the child service doesn't exist");
+ }
+ }
+ return SandboxedProcessService.class.getName();
+ }
+
+ static int getNumberOfServices(Context context, boolean inSandbox, String packageName) {
+ int numServices = -1;
+ if (inSandbox && sSandboxedServicesCountForTesting != -1) {
+ numServices = sSandboxedServicesCountForTesting;
+ } else {
+ try {
+ PackageManager packageManager = context.getPackageManager();
+ ApplicationInfo appInfo = packageManager.getApplicationInfo(
+ packageName, PackageManager.GET_META_DATA);
+ if (appInfo.metaData != null) {
+ numServices = appInfo.metaData.getInt(
+ inSandbox ? NUM_SANDBOXED_SERVICES_KEY : NUM_PRIVILEGED_SERVICES_KEY,
+ -1);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RuntimeException("Could not get application info");
+ }
+ }
+ if (numServices < 0) {
+ throw new RuntimeException("Illegal meta data value for number of child services");
+ }
+ return numServices;
+ }
+
+ @VisibleForTesting
+ public static void setSanboxServicesSettingsForTesting(int serviceCount, String serviceName) {
+ sSandboxedServicesCountForTesting = serviceCount;
+ sSandboxedServicesNameForTesting = serviceName;
+ }
+
+ private ChildConnectionAllocator(
+ boolean inSandbox, int numChildServices, String serviceClassName) {
+ mChildProcessConnections = new ChildProcessConnectionImpl[numChildServices];
+ mFreeConnectionIndices = new ArrayList<Integer>(numChildServices);
+ for (int i = 0; i < numChildServices; i++) {
+ mFreeConnectionIndices.add(i);
+ }
+ mChildClassName = serviceClassName;
+ mInSandbox = inSandbox;
+ }
+
+ // Allocates or enqueues. If there are no free slots, returns null and enqueues the spawn data.
+ public ChildProcessConnection allocate(ChildSpawnData spawnData,
+ ChildProcessConnection.DeathCallback deathCallback, Bundle childProcessCommonParameters,
+ boolean queueIfNoSlotAvailable) {
+ assert spawnData.isInSandbox() == mInSandbox;
+ synchronized (mConnectionLock) {
+ if (mFreeConnectionIndices.isEmpty()) {
+ Log.d(TAG, "Ran out of services to allocate.");
+ if (queueIfNoSlotAvailable) {
+ mPendingSpawnQueue.add(spawnData);
+ }
+ return null;
+ }
+ int slot = mFreeConnectionIndices.remove(0);
+ assert mChildProcessConnections[slot] == null;
+ mChildProcessConnections[slot] = new ChildProcessConnectionImpl(spawnData.getContext(),
+ slot, mInSandbox, deathCallback, mChildClassName, childProcessCommonParameters,
+ spawnData.isAlwaysInForeground(), spawnData.getCreationParams());
+ Log.d(TAG, "Allocator allocated a connection, sandbox: %b, slot: %d", mInSandbox, slot);
+ return mChildProcessConnections[slot];
+ }
+ }
+
+ // Also return the first ChildSpawnData in the pending queue, if any.
+ public ChildSpawnData free(ChildProcessConnection connection) {
+ synchronized (mConnectionLock) {
+ int slot = connection.getServiceNumber();
+ if (mChildProcessConnections[slot] != connection) {
+ int occupier = mChildProcessConnections[slot] == null
+ ? -1
+ : mChildProcessConnections[slot].getServiceNumber();
+ Log.e(TAG,
+ "Unable to find connection to free in slot: %d "
+ + "already occupied by service: %d",
+ slot, occupier);
+ assert false;
+ } else {
+ mChildProcessConnections[slot] = null;
+ assert !mFreeConnectionIndices.contains(slot);
+ mFreeConnectionIndices.add(slot);
+ Log.d(TAG, "Allocator freed a connection, sandbox: %b, slot: %d", mInSandbox, slot);
+ }
+ return mPendingSpawnQueue.poll();
+ }
+ }
+
+ public boolean isFreeConnectionAvailable() {
+ synchronized (mConnectionLock) {
+ return !mFreeConnectionIndices.isEmpty();
+ }
+ }
+
+ /** @return the count of connections managed by the allocator */
+ @VisibleForTesting
+ int allocatedConnectionsCountForTesting() {
+ synchronized (mConnectionLock) {
+ return mChildProcessConnections.length - mFreeConnectionIndices.size();
+ }
+ }
+
+ @VisibleForTesting
+ ChildProcessConnection[] connectionArrayForTesting() {
+ return mChildProcessConnections;
+ }
+
+ @VisibleForTesting
+ void enqueuePendingQueueForTesting(ChildSpawnData spawnData) {
+ synchronized (mConnectionLock) {
+ mPendingSpawnQueue.add(spawnData);
+ }
+ }
+
+ @VisibleForTesting
+ int pendingSpawnsCountForTesting() {
+ synchronized (mConnectionLock) {
+ return mPendingSpawnQueue.size();
+ }
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698