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

Side by Side 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.chromium.content.browser;
6
7 import android.content.ComponentName;
8 import android.content.Context;
9 import android.content.pm.ApplicationInfo;
10 import android.content.pm.PackageManager;
11 import android.os.Bundle;
12 import android.text.TextUtils;
13
14 import org.chromium.base.Log;
15 import org.chromium.base.VisibleForTesting;
16 import org.chromium.content.app.PrivilegedProcessService;
17 import org.chromium.content.app.SandboxedProcessService;
18
19 import java.util.ArrayList;
20 import java.util.LinkedList;
21 import java.util.Map;
22 import java.util.Queue;
23 import java.util.concurrent.ConcurrentHashMap;
24
25 import javax.annotation.concurrent.GuardedBy;
26
27 /**
28 * This class is responsible for allocating and managing connections to child
29 * process services. These connections are in a pool (the services are defined
30 * in the AndroidManifest.xml).
31 */
32 public class ChildConnectionAllocator {
33 private static final String TAG = "ChildConnAllocator";
34
35 private static final String NUM_SANDBOXED_SERVICES_KEY =
36 "org.chromium.content.browser.NUM_SANDBOXED_SERVICES";
37 private static final String NUM_PRIVILEGED_SERVICES_KEY =
38 "org.chromium.content.browser.NUM_PRIVILEGED_SERVICES";
39 private static final String SANDBOXED_SERVICES_NAME_KEY =
40 "org.chromium.content.browser.SANDBOXED_SERVICES_NAME";
41
42 private static final Object sAllocatorLock = new Object();
43
44 // Map from package name to ChildConnectionAllocator.
45 @GuardedBy("sAllocatorLock")
46 private static Map<String, ChildConnectionAllocator> sSandboxedChildConnecti onAllocatorMap;
47
48 // Allocator used for non-sandboxed services.
49 @GuardedBy("sAllocatorLock")
50 private static ChildConnectionAllocator sPrivilegedChildConnectionAllocator;
51
52 // Used by test to override the default sandboxed service settings.
53 private static int sSandboxedServicesCountForTesting = -1;
54 private static String sSandboxedServicesNameForTesting;
55
56 // Connections to services. Indices of the array correspond to the service n umbers.
57 private final ChildProcessConnection[] mChildProcessConnections;
58
59 private final String mChildClassName;
60 private final boolean mInSandbox;
61
62 private final Object mConnectionLock = new Object();
63
64 // The list of free (not bound) service indices.
65 @GuardedBy("mConnectionLock")
66 private final ArrayList<Integer> mFreeConnectionIndices;
67
68 // Each Allocator keeps a queue for the pending spawn data. Once a connectio n is free, we
69 // dequeue the pending spawn data from the same allocator as the connection.
70 @GuardedBy("mConnectionLock")
71 private final Queue<ChildSpawnData> mPendingSpawnQueue = new LinkedList<>();
72
73 public static ChildConnectionAllocator getAllocator(
74 Context context, String packageName, boolean inSandbox) {
75 synchronized (sAllocatorLock) {
76 if (!inSandbox) {
77 if (sPrivilegedChildConnectionAllocator == null) {
78 sPrivilegedChildConnectionAllocator = new ChildConnectionAll ocator(false,
79 getNumberOfServices(context, false, packageName),
80 getClassNameOfService(context, false, packageName));
81 }
82 return sPrivilegedChildConnectionAllocator;
83 }
84
85 if (sSandboxedChildConnectionAllocatorMap == null) {
86 sSandboxedChildConnectionAllocatorMap =
87 new ConcurrentHashMap<String, ChildConnectionAllocator>( );
88 }
89 if (!sSandboxedChildConnectionAllocatorMap.containsKey(packageName)) {
90 Log.w(TAG,
91 "Create a new ChildConnectionAllocator with package name = %s,"
92 + " inSandbox = true",
93 packageName);
94 sSandboxedChildConnectionAllocatorMap.put(packageName,
95 new ChildConnectionAllocator(true,
96 getNumberOfServices(context, true, packageName),
97 getClassNameOfService(context, true, packageName )));
98 }
99 return sSandboxedChildConnectionAllocatorMap.get(packageName);
100 // TODO(pkotwicz|hanxi): Figure out when old allocators should be re moved from
101 // {@code sSandboxedChildConnectionAllocatorMap}.
102 }
103 }
104
105 private static String getClassNameOfService(
106 Context context, boolean inSandbox, String packageName) {
107 if (!inSandbox) {
108 return PrivilegedProcessService.class.getName();
109 }
110
111 if (!TextUtils.isEmpty(sSandboxedServicesNameForTesting)) {
112 return sSandboxedServicesNameForTesting;
113 }
114
115 String serviceName = null;
116 try {
117 PackageManager packageManager = context.getPackageManager();
118 ApplicationInfo appInfo =
119 packageManager.getApplicationInfo(packageName, PackageManage r.GET_META_DATA);
120 if (appInfo.metaData != null) {
121 serviceName = appInfo.metaData.getString(SANDBOXED_SERVICES_NAME _KEY);
122 }
123 } catch (PackageManager.NameNotFoundException e) {
124 throw new RuntimeException("Could not get application info.");
125 }
126
127 if (serviceName != null) {
128 // Check that the service exists.
129 try {
130 PackageManager packageManager = context.getPackageManager();
131 // PackageManager#getServiceInfo() throws an exception if the se rvice does not
132 // exist.
133 packageManager.getServiceInfo(new ComponentName(packageName, ser viceName + "0"), 0);
134 return serviceName;
135 } catch (PackageManager.NameNotFoundException e) {
136 throw new RuntimeException(
137 "Illegal meta data value: the child service doesn't exis t");
138 }
139 }
140 return SandboxedProcessService.class.getName();
141 }
142
143 static int getNumberOfServices(Context context, boolean inSandbox, String pa ckageName) {
144 int numServices = -1;
145 if (inSandbox && sSandboxedServicesCountForTesting != -1) {
146 numServices = sSandboxedServicesCountForTesting;
147 } else {
148 try {
149 PackageManager packageManager = context.getPackageManager();
150 ApplicationInfo appInfo = packageManager.getApplicationInfo(
151 packageName, PackageManager.GET_META_DATA);
152 if (appInfo.metaData != null) {
153 numServices = appInfo.metaData.getInt(
154 inSandbox ? NUM_SANDBOXED_SERVICES_KEY : NUM_PRIVILE GED_SERVICES_KEY,
155 -1);
156 }
157 } catch (PackageManager.NameNotFoundException e) {
158 throw new RuntimeException("Could not get application info");
159 }
160 }
161 if (numServices < 0) {
162 throw new RuntimeException("Illegal meta data value for number of ch ild services");
163 }
164 return numServices;
165 }
166
167 @VisibleForTesting
168 public static void setSanboxServicesSettingsForTesting(int serviceCount, Str ing serviceName) {
169 sSandboxedServicesCountForTesting = serviceCount;
170 sSandboxedServicesNameForTesting = serviceName;
171 }
172
173 private ChildConnectionAllocator(
174 boolean inSandbox, int numChildServices, String serviceClassName) {
175 mChildProcessConnections = new ChildProcessConnectionImpl[numChildServic es];
176 mFreeConnectionIndices = new ArrayList<Integer>(numChildServices);
177 for (int i = 0; i < numChildServices; i++) {
178 mFreeConnectionIndices.add(i);
179 }
180 mChildClassName = serviceClassName;
181 mInSandbox = inSandbox;
182 }
183
184 // Allocates or enqueues. If there are no free slots, returns null and enque ues the spawn data.
185 public ChildProcessConnection allocate(ChildSpawnData spawnData,
186 ChildProcessConnection.DeathCallback deathCallback, Bundle childProc essCommonParameters,
187 boolean queueIfNoSlotAvailable) {
188 assert spawnData.isInSandbox() == mInSandbox;
189 synchronized (mConnectionLock) {
190 if (mFreeConnectionIndices.isEmpty()) {
191 Log.d(TAG, "Ran out of services to allocate.");
192 if (queueIfNoSlotAvailable) {
193 mPendingSpawnQueue.add(spawnData);
194 }
195 return null;
196 }
197 int slot = mFreeConnectionIndices.remove(0);
198 assert mChildProcessConnections[slot] == null;
199 mChildProcessConnections[slot] = new ChildProcessConnectionImpl(spaw nData.getContext(),
200 slot, mInSandbox, deathCallback, mChildClassName, childProce ssCommonParameters,
201 spawnData.isAlwaysInForeground(), spawnData.getCreationParam s());
202 Log.d(TAG, "Allocator allocated a connection, sandbox: %b, slot: %d" , mInSandbox, slot);
203 return mChildProcessConnections[slot];
204 }
205 }
206
207 // Also return the first ChildSpawnData in the pending queue, if any.
208 public ChildSpawnData free(ChildProcessConnection connection) {
209 synchronized (mConnectionLock) {
210 int slot = connection.getServiceNumber();
211 if (mChildProcessConnections[slot] != connection) {
212 int occupier = mChildProcessConnections[slot] == null
213 ? -1
214 : mChildProcessConnections[slot].getServiceNumber();
215 Log.e(TAG,
216 "Unable to find connection to free in slot: %d "
217 + "already occupied by service: %d",
218 slot, occupier);
219 assert false;
220 } else {
221 mChildProcessConnections[slot] = null;
222 assert !mFreeConnectionIndices.contains(slot);
223 mFreeConnectionIndices.add(slot);
224 Log.d(TAG, "Allocator freed a connection, sandbox: %b, slot: %d" , mInSandbox, slot);
225 }
226 return mPendingSpawnQueue.poll();
227 }
228 }
229
230 public boolean isFreeConnectionAvailable() {
231 synchronized (mConnectionLock) {
232 return !mFreeConnectionIndices.isEmpty();
233 }
234 }
235
236 /** @return the count of connections managed by the allocator */
237 @VisibleForTesting
238 int allocatedConnectionsCountForTesting() {
239 synchronized (mConnectionLock) {
240 return mChildProcessConnections.length - mFreeConnectionIndices.size ();
241 }
242 }
243
244 @VisibleForTesting
245 ChildProcessConnection[] connectionArrayForTesting() {
246 return mChildProcessConnections;
247 }
248
249 @VisibleForTesting
250 void enqueuePendingQueueForTesting(ChildSpawnData spawnData) {
251 synchronized (mConnectionLock) {
252 mPendingSpawnQueue.add(spawnData);
253 }
254 }
255
256 @VisibleForTesting
257 int pendingSpawnsCountForTesting() {
258 synchronized (mConnectionLock) {
259 return mPendingSpawnQueue.size();
260 }
261 }
262 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698