OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package org.chromium.content.browser; | 5 package org.chromium.content.browser; |
6 | 6 |
7 import android.content.ComponentName; | 7 import android.content.ComponentName; |
8 import android.content.Context; | 8 import android.content.Context; |
9 import android.content.Intent; | 9 import android.content.Intent; |
10 import android.content.ServiceConnection; | 10 import android.content.ServiceConnection; |
11 import android.content.pm.PackageManager; | 11 import android.content.pm.PackageManager; |
12 import android.content.pm.ServiceInfo; | 12 import android.content.pm.ServiceInfo; |
13 import android.os.Build; | 13 import android.os.Build; |
14 import android.os.Bundle; | 14 import android.os.Bundle; |
15 import android.os.IBinder; | 15 import android.os.IBinder; |
16 import android.os.RemoteException; | 16 import android.os.RemoteException; |
17 | 17 |
18 import org.chromium.base.Log; | 18 import org.chromium.base.Log; |
19 import org.chromium.base.TraceEvent; | 19 import org.chromium.base.TraceEvent; |
20 import org.chromium.base.VisibleForTesting; | 20 import org.chromium.base.VisibleForTesting; |
21 import org.chromium.base.process_launcher.ChildProcessCreationParams; | 21 import org.chromium.base.process_launcher.ChildProcessCreationParams; |
22 import org.chromium.base.process_launcher.FileDescriptorInfo; | 22 import org.chromium.base.process_launcher.FileDescriptorInfo; |
23 import org.chromium.base.process_launcher.ICallbackInt; | 23 import org.chromium.base.process_launcher.ICallbackInt; |
24 import org.chromium.base.process_launcher.IChildProcessService; | 24 import org.chromium.base.process_launcher.IChildProcessService; |
25 | 25 |
26 import java.io.IOException; | 26 import java.io.IOException; |
27 | 27 |
28 import javax.annotation.Nullable; | 28 import javax.annotation.Nullable; |
| 29 import javax.annotation.concurrent.GuardedBy; |
29 | 30 |
30 /** | 31 /** |
31 * Manages a connection between the browser activity and a child service. | 32 * Manages a connection between the browser activity and a child service. |
32 */ | 33 */ |
33 public class ChildProcessConnectionImpl implements ChildProcessConnection { | 34 public abstract class BaseChildProcessConnection { |
34 private final Context mContext; | 35 private static final String TAG = "BaseChildProcessConn"; |
35 private final int mServiceNumber; | |
36 private final boolean mInSandbox; | |
37 private final ChildProcessConnection.DeathCallback mDeathCallback; | |
38 private final ComponentName mServiceName; | |
39 | 36 |
40 // Synchronization: While most internal flow occurs on the UI thread, the pu
blic API | 37 /** |
41 // (specifically start and stop) may be called from any thread, hence all en
try point methods | 38 * Used to notify the consumer about disconnection of the service. This call
back is provided |
42 // into the class are synchronized on the lock to protect access to these me
mbers. | 39 * earlier than ConnectionCallbacks below, as a child process might die befo
re the connection is |
43 private final Object mLock = new Object(); | 40 * fully set up. |
44 private IChildProcessService mService; | 41 */ |
45 // Set to true when the service connection callback runs. This differs from | 42 interface DeathCallback { |
46 // mServiceConnectComplete, which tracks that the connection completed succe
ssfully. | 43 // Called on Launcher thread. |
47 private boolean mDidOnServiceConnected; | 44 void onChildProcessDied(BaseChildProcessConnection connection); |
48 // Set to true when the service connected successfully. | |
49 private boolean mServiceConnectComplete; | |
50 // Set to true when the service disconnects, as opposed to being properly cl
osed. This happens | |
51 // when the process crashes or gets killed by the system out-of-memory kille
r. | |
52 private boolean mServiceDisconnected; | |
53 // When the service disconnects (i.e. mServiceDisconnected is set to true),
the status of the | |
54 // oom bindings is stashed here for future inspection. | |
55 private boolean mWasOomProtected; | |
56 private int mPid; // Process ID of the corresponding child process. | |
57 // Initial binding protects the newly spawned process from being killed befo
re it is put to use, | |
58 // it is maintained between calls to start() and removeInitialBinding(). | |
59 private ChildServiceConnection mInitialBinding; | |
60 // Strong binding will make the service priority equal to the priority of th
e activity. We want | |
61 // the OS to be able to kill background renderers as it kills other backgrou
nd apps, so strong | |
62 // bindings are maintained only for services that are active at the moment (
between | |
63 // addStrongBinding() and removeStrongBinding()). | |
64 private ChildServiceConnection mStrongBinding; | |
65 // Low priority binding maintained in the entire lifetime of the connection,
i.e. between calls | |
66 // to start() and stop(). | |
67 private ChildServiceConnection mWaivedBinding; | |
68 // Incremented on addStrongBinding(), decremented on removeStrongBinding(). | |
69 private int mStrongBindingCount; | |
70 // Moderate binding will make the service priority equal to the priority of
a visible process | |
71 // while the app is in the foreground. It will stay bound only while the app
is in the | |
72 // foreground to protect a background process from the system out-of-memory
killer. | |
73 private ChildServiceConnection mModerateBinding; | |
74 | |
75 // Parameters passed to the child process through the service binding intent
. | |
76 // If the service gets recreated by the framework the intent will be reused,
so these parameters | |
77 // should be common to all processes of that type. | |
78 private final Bundle mChildProcessCommonParameters; | |
79 | |
80 private final boolean mAlwaysInForeground; | |
81 private final ChildProcessCreationParams mCreationParams; | |
82 | |
83 // Caches whether non-sandboxed and sandboxed services require an extra | |
84 // binding flag provided via ChildProcessCreationParams. | |
85 // TODO(mnaganov): Get rid of it after the release of the next Android SDK. | |
86 private static Boolean sNeedsExtrabindFlags[] = new Boolean[2]; | |
87 | |
88 private static final String TAG = "ChildProcessConnect"; | |
89 | |
90 private static class ConnectionParams { | |
91 final String[] mCommandLine; | |
92 final FileDescriptorInfo[] mFilesToBeMapped; | |
93 final IBinder mCallback; | |
94 | |
95 ConnectionParams( | |
96 String[] commandLine, FileDescriptorInfo[] filesToBeMapped, IBin
der callback) { | |
97 mCommandLine = commandLine; | |
98 mFilesToBeMapped = filesToBeMapped; | |
99 mCallback = callback; | |
100 } | |
101 } | 45 } |
102 | 46 |
103 // This is set in start() and is used in onServiceConnected(). | 47 /** |
104 private ChildProcessConnection.StartCallback mStartCallback; | 48 * Used to notify the consumer about the process start. These callbacks will
be invoked before |
| 49 * the ConnectionCallbacks. |
| 50 */ |
| 51 interface StartCallback { |
| 52 /** |
| 53 * Called when the child process has successfully started and is ready f
or connection |
| 54 * setup. |
| 55 */ |
| 56 void onChildStarted(); |
105 | 57 |
106 // This is set in setupConnection() and is later used in doConnectionSetupLo
cked(), after which | 58 /** |
107 // the variable is cleared. Therefore this is only valid while the connectio
n is being set up. | 59 * Called when the child process failed to start. This can happen if the
process is already |
108 private ConnectionParams mConnectionParams; | 60 * in use by another client. |
| 61 */ |
| 62 void onChildStartFailed(); |
| 63 } |
109 | 64 |
110 // Callback provided in setupConnection() that will communicate the result t
o the caller. This | 65 /** |
111 // has to be called exactly once after setupConnection(), even if setup fail
s, so that the | 66 * Used to notify the consumer about the connection being established. |
112 // caller can free up resources associated with the setup attempt. This is s
et to null after the | 67 */ |
113 // call. | 68 interface ConnectionCallback { |
114 private ChildProcessConnection.ConnectionCallback mConnectionCallback; | 69 /** |
| 70 * Called when the connection to the service is established. |
| 71 * @param connecion the connection object to the child process |
| 72 */ |
| 73 void onConnected(BaseChildProcessConnection connection); |
| 74 } |
115 | 75 |
116 private class ChildServiceConnection implements ServiceConnection { | 76 /** Used to create specialization connection instances. */ |
| 77 interface Factory { |
| 78 BaseChildProcessConnection create(Context context, int number, boolean s
andboxed, |
| 79 DeathCallback deathCallback, String serviceClassName, |
| 80 Bundle childProcessCommonParameters, ChildProcessCreationParams
creationParams); |
| 81 } |
| 82 |
| 83 /** Interface representing a connection to the Android service. Can be mocke
d in unit-tests. */ |
| 84 protected interface ChildServiceConnection { |
| 85 boolean bind(); |
| 86 void unbind(); |
| 87 boolean isBound(); |
| 88 } |
| 89 |
| 90 /** Implementation of ChildServiceConnection that does connect to a service.
*/ |
| 91 protected class ChildServiceConnectionImpl |
| 92 implements ChildServiceConnection, ServiceConnection { |
| 93 private final int mBindFlags; |
117 private boolean mBound; | 94 private boolean mBound; |
118 | 95 |
119 private final int mBindFlags; | |
120 | |
121 private Intent createServiceBindIntent() { | 96 private Intent createServiceBindIntent() { |
122 Intent intent = new Intent(); | 97 Intent intent = new Intent(); |
123 if (mCreationParams != null) { | 98 if (mCreationParams != null) { |
124 mCreationParams.addIntentExtras(intent); | 99 mCreationParams.addIntentExtras(intent); |
125 } | 100 } |
126 intent.setComponent(mServiceName); | 101 intent.setComponent(mServiceName); |
127 return intent; | 102 return intent; |
128 } | 103 } |
129 | 104 |
130 public ChildServiceConnection(int bindFlags) { | 105 private ChildServiceConnectionImpl(int bindFlags) { |
131 mBindFlags = bindFlags; | 106 mBindFlags = bindFlags; |
132 } | 107 } |
133 | 108 |
134 boolean bind() { | 109 @Override |
| 110 public boolean bind() { |
135 if (!mBound) { | 111 if (!mBound) { |
136 try { | 112 try { |
137 TraceEvent.begin("ChildProcessConnectionImpl.ChildServiceCon
nection.bind"); | 113 TraceEvent.begin("BaseChildProcessConnection.ChildServiceCon
nection.bind"); |
138 Intent intent = createServiceBindIntent(); | 114 Intent intent = createServiceBindIntent(); |
139 if (mChildProcessCommonParameters != null) { | 115 if (mChildProcessCommonParameters != null) { |
140 intent.putExtras(mChildProcessCommonParameters); | 116 intent.putExtras(mChildProcessCommonParameters); |
141 } | 117 } |
142 mBound = mContext.bindService(intent, this, mBindFlags); | 118 mBound = mContext.bindService(intent, this, mBindFlags); |
143 } finally { | 119 } finally { |
144 TraceEvent.end("ChildProcessConnectionImpl.ChildServiceConne
ction.bind"); | 120 TraceEvent.end("BaseChildProcessConnection.ChildServiceConne
ction.bind"); |
145 } | 121 } |
146 } | 122 } |
147 return mBound; | 123 return mBound; |
148 } | 124 } |
149 | 125 |
150 void unbind() { | 126 @Override |
| 127 public void unbind() { |
151 if (mBound) { | 128 if (mBound) { |
152 mContext.unbindService(this); | 129 mContext.unbindService(this); |
153 mBound = false; | 130 mBound = false; |
154 } | 131 } |
155 } | 132 } |
156 | 133 |
157 boolean isBound() { | 134 @Override |
| 135 public boolean isBound() { |
158 return mBound; | 136 return mBound; |
159 } | 137 } |
160 | 138 |
161 @Override | 139 @Override |
162 public void onServiceConnected(ComponentName className, final IBinder se
rvice) { | 140 public void onServiceConnected(ComponentName className, final IBinder se
rvice) { |
163 LauncherThread.post(new Runnable() { | 141 LauncherThread.post(new Runnable() { |
164 @Override | 142 @Override |
165 public void run() { | 143 public void run() { |
166 ChildProcessConnectionImpl.this.onServiceConnectedOnLauncher
Thread(service); | 144 BaseChildProcessConnection.this.onServiceConnectedOnLauncher
Thread(service); |
167 } | 145 } |
168 }); | 146 }); |
169 } | 147 } |
170 | 148 |
171 // Called on the main thread to notify that the child service did not di
sconnect gracefully. | 149 // Called on the main thread to notify that the child service did not di
sconnect gracefully. |
172 @Override | 150 @Override |
173 public void onServiceDisconnected(ComponentName className) { | 151 public void onServiceDisconnected(ComponentName className) { |
174 LauncherThread.post(new Runnable() { | 152 LauncherThread.post(new Runnable() { |
175 @Override | 153 @Override |
176 public void run() { | 154 public void run() { |
177 ChildProcessConnectionImpl.this.onServiceDisconnectedOnLaunc
herThread(); | 155 BaseChildProcessConnection.this.onServiceDisconnectedOnLaunc
herThread(); |
178 } | 156 } |
179 }); | 157 }); |
180 } | 158 } |
181 } | 159 } |
182 | 160 |
183 ChildProcessConnectionImpl(Context context, int number, boolean inSandbox, | 161 // Caches whether non-sandboxed and sandboxed services require an extra |
184 ChildProcessConnection.DeathCallback deathCallback, String serviceCl
assName, | 162 // binding flag provided via ChildProcessCreationParams. |
185 Bundle childProcessCommonParameters, boolean alwaysInForeground, | 163 // TODO(mnaganov): Get rid of it after the release of the next Android SDK. |
186 ChildProcessCreationParams creationParams) { | 164 private static Boolean sNeedsExtrabindFlags[] = new Boolean[2]; |
| 165 private final Context mContext; |
| 166 private final int mServiceNumber; |
| 167 private final boolean mSandboxed; |
| 168 private final BaseChildProcessConnection.DeathCallback mDeathCallback; |
| 169 private final ComponentName mServiceName; |
| 170 |
| 171 // Parameters passed to the child process through the service binding intent
. |
| 172 // If the service gets recreated by the framework the intent will be reused,
so these parameters |
| 173 // should be common to all processes of that type. |
| 174 private final Bundle mChildProcessCommonParameters; |
| 175 |
| 176 private final ChildProcessCreationParams mCreationParams; |
| 177 |
| 178 private static class ConnectionParams { |
| 179 final String[] mCommandLine; |
| 180 final FileDescriptorInfo[] mFilesToBeMapped; |
| 181 final IBinder mCallback; |
| 182 |
| 183 ConnectionParams( |
| 184 String[] commandLine, FileDescriptorInfo[] filesToBeMapped, IBin
der callback) { |
| 185 mCommandLine = commandLine; |
| 186 mFilesToBeMapped = filesToBeMapped; |
| 187 mCallback = callback; |
| 188 } |
| 189 } |
| 190 |
| 191 // Synchronization: While most internal flow occurs on the UI thread, the pu
blic API |
| 192 // (specifically start and stop) may be called from any thread, hence all en
try point methods |
| 193 // into the class are synchronized on the lock to protect access to these me
mbers. |
| 194 // TODO(jcivelli): crbug.com/714657 remove this lock. |
| 195 private final Object mLock = new Object(); |
| 196 |
| 197 // This is set in start() and is used in onServiceConnected(). |
| 198 @GuardedBy("mLock") |
| 199 private StartCallback mStartCallback; |
| 200 |
| 201 // This is set in setupConnection() and is later used in doConnectionSetupLo
cked(), after which |
| 202 // the variable is cleared. Therefore this is only valid while the connectio
n is being set up. |
| 203 @GuardedBy("mLock") |
| 204 private ConnectionParams mConnectionParams; |
| 205 |
| 206 // Callback provided in setupConnection() that will communicate the result t
o the caller. This |
| 207 // has to be called exactly once after setupConnection(), even if setup fail
s, so that the |
| 208 // caller can free up resources associated with the setup attempt. This is s
et to null after the |
| 209 // call. |
| 210 @GuardedBy("mLock") |
| 211 private ConnectionCallback mConnectionCallback; |
| 212 |
| 213 @GuardedBy("mLock") |
| 214 private IChildProcessService mService; |
| 215 |
| 216 // Set to true when the service connection callback runs. This differs from |
| 217 // mServiceConnectComplete, which tracks that the connection completed succe
ssfully. |
| 218 @GuardedBy("mLock") |
| 219 private boolean mDidOnServiceConnected; |
| 220 |
| 221 // Set to true when the service connected successfully. |
| 222 @GuardedBy("mLock") |
| 223 private boolean mServiceConnectComplete; |
| 224 |
| 225 // Set to true when the service disconnects, as opposed to being properly cl
osed. This happens |
| 226 // when the process crashes or gets killed by the system out-of-memory kille
r. |
| 227 @GuardedBy("mLock") |
| 228 private boolean mServiceDisconnected; |
| 229 |
| 230 // Process ID of the corresponding child process. |
| 231 @GuardedBy("mLock") |
| 232 private int mPid; |
| 233 |
| 234 protected BaseChildProcessConnection(Context context, int number, boolean sa
ndboxed, |
| 235 DeathCallback deathCallback, String serviceClassName, |
| 236 Bundle childProcessCommonParameters, ChildProcessCreationParams crea
tionParams) { |
187 mContext = context; | 237 mContext = context; |
188 mServiceNumber = number; | 238 mServiceNumber = number; |
189 mInSandbox = inSandbox; | 239 mSandboxed = sandboxed; |
190 mDeathCallback = deathCallback; | 240 mDeathCallback = deathCallback; |
191 String packageName = | 241 String packageName = |
192 creationParams != null ? creationParams.getPackageName() : conte
xt.getPackageName(); | 242 creationParams != null ? creationParams.getPackageName() : conte
xt.getPackageName(); |
193 mServiceName = new ComponentName(packageName, serviceClassName + mServic
eNumber); | 243 mServiceName = new ComponentName(packageName, serviceClassName + mServic
eNumber); |
194 mChildProcessCommonParameters = childProcessCommonParameters; | 244 mChildProcessCommonParameters = childProcessCommonParameters; |
195 mAlwaysInForeground = alwaysInForeground; | |
196 mCreationParams = creationParams; | 245 mCreationParams = creationParams; |
197 int initialFlags = Context.BIND_AUTO_CREATE; | |
198 if (mAlwaysInForeground) initialFlags |= Context.BIND_IMPORTANT; | |
199 int extraBindFlags = 0; | |
200 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && mCreationParams !=
null | |
201 && mCreationParams.getIsExternalService() | |
202 && isExportedService(inSandbox, mContext, mServiceName)) { | |
203 extraBindFlags = Context.BIND_EXTERNAL_SERVICE; | |
204 } | |
205 mInitialBinding = new ChildServiceConnection(initialFlags | extraBindFla
gs); | |
206 mStrongBinding = new ChildServiceConnection( | |
207 Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT | extraBindFla
gs); | |
208 mWaivedBinding = new ChildServiceConnection( | |
209 Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY | extraBi
ndFlags); | |
210 mModerateBinding = new ChildServiceConnection(Context.BIND_AUTO_CREATE |
extraBindFlags); | |
211 } | 246 } |
212 | 247 |
213 private static boolean isExportedService(boolean inSandbox, Context context, | 248 public final Context getContext() { |
214 ComponentName serviceName) { | 249 return mContext; |
215 // Check for the cached value first. It is assumed that all pooled child
services | |
216 // have identical attributes in the manifest. | |
217 final int arrayIndex = inSandbox ? 1 : 0; | |
218 if (sNeedsExtrabindFlags[arrayIndex] != null) { | |
219 return sNeedsExtrabindFlags[arrayIndex].booleanValue(); | |
220 } | |
221 boolean result = false; | |
222 try { | |
223 PackageManager packageManager = context.getPackageManager(); | |
224 ServiceInfo serviceInfo = packageManager.getServiceInfo(serviceName,
0); | |
225 result = serviceInfo.exported; | |
226 } catch (PackageManager.NameNotFoundException e) { | |
227 Log.e(TAG, "Could not retrieve info about service %s", serviceName,
e); | |
228 } | |
229 sNeedsExtrabindFlags[arrayIndex] = Boolean.valueOf(result); | |
230 return result; | |
231 } | 250 } |
232 | 251 |
233 @Override | 252 public final int getServiceNumber() { |
234 public int getServiceNumber() { | |
235 return mServiceNumber; | 253 return mServiceNumber; |
236 } | 254 } |
237 | 255 |
238 @Override | 256 public final boolean isSandboxed() { |
239 public boolean isInSandbox() { | 257 return mSandboxed; |
240 return mInSandbox; | |
241 } | 258 } |
242 | 259 |
243 @Override | 260 public final String getPackageName() { |
244 public String getPackageName() { | |
245 return mCreationParams != null ? mCreationParams.getPackageName() | 261 return mCreationParams != null ? mCreationParams.getPackageName() |
246 : mContext.getPackageName(); | 262 : mContext.getPackageName(); |
247 } | 263 } |
248 | 264 |
249 @Override | 265 public final ChildProcessCreationParams getCreationParams() { |
250 public ChildProcessCreationParams getCreationParams() { | |
251 return mCreationParams; | 266 return mCreationParams; |
252 } | 267 } |
253 | 268 |
254 @Override | 269 public final IChildProcessService getService() { |
255 public IChildProcessService getService() { | |
256 synchronized (mLock) { | 270 synchronized (mLock) { |
257 return mService; | 271 return mService; |
258 } | 272 } |
259 } | 273 } |
260 | 274 |
261 @Override | 275 public final ComponentName getServiceName() { |
| 276 return mServiceName; |
| 277 } |
| 278 |
| 279 /** |
| 280 * @return the connection pid, or 0 if not yet connected |
| 281 */ |
262 public int getPid() { | 282 public int getPid() { |
263 synchronized (mLock) { | 283 synchronized (mLock) { |
264 return mPid; | 284 return mPid; |
265 } | 285 } |
266 } | 286 } |
267 | 287 |
268 @Override | 288 /** |
269 public void start(ChildProcessConnection.StartCallback startCallback) { | 289 * Starts a connection to an IChildProcessService. This must be followed by
a call to |
| 290 * setupConnection() to setup the connection parameters. start() and setupCo
nnection() are |
| 291 * separate to allow to pass whatever parameters are available in start(), a
nd complete the |
| 292 * remainder later while reducing the connection setup latency. |
| 293 * @param startCallback (optional) callback when the child process starts or
fails to start. |
| 294 */ |
| 295 public void start(StartCallback startCallback) { |
270 try { | 296 try { |
271 TraceEvent.begin("ChildProcessConnectionImpl.start"); | 297 TraceEvent.begin("BaseChildProcessConnection.start"); |
272 assert LauncherThread.runningOnLauncherThread(); | 298 assert LauncherThread.runningOnLauncherThread(); |
273 synchronized (mLock) { | 299 synchronized (mLock) { |
274 assert mConnectionParams == null : | 300 assert mConnectionParams |
275 "setupConnection() called before start() in ChildProcess
ConnectionImpl."; | 301 == null |
| 302 : "setupConnection() called before start() in BaseChildP
rocessConnection."; |
276 | 303 |
277 mStartCallback = startCallback; | 304 mStartCallback = startCallback; |
278 | 305 |
279 if (!mInitialBinding.bind()) { | 306 if (!bind()) { |
280 Log.e(TAG, "Failed to establish the service connection."); | 307 Log.e(TAG, "Failed to establish the service connection."); |
281 // We have to notify the caller so that they can free-up ass
ociated resources. | 308 // We have to notify the caller so that they can free-up ass
ociated resources. |
282 // TODO(ppi): Can we hard-fail here? | 309 // TODO(ppi): Can we hard-fail here? |
283 mDeathCallback.onChildProcessDied(ChildProcessConnectionImpl
.this); | 310 mDeathCallback.onChildProcessDied(BaseChildProcessConnection
.this); |
284 } else { | |
285 mWaivedBinding.bind(); | |
286 } | 311 } |
287 } | 312 } |
288 } finally { | 313 } finally { |
289 TraceEvent.end("ChildProcessConnectionImpl.start"); | 314 TraceEvent.end("BaseChildProcessConnection.start"); |
290 } | 315 } |
291 } | 316 } |
292 | 317 |
293 @Override | 318 /** |
| 319 * Setups the connection after it was started with start(). |
| 320 * @param commandLine (optional) will be ignored if the command line was alr
eady sent in start() |
| 321 * @param filesToBeMapped a list of file descriptors that should be register
ed |
| 322 * @param callback optional client specified callbacks that the child can us
e to communicate |
| 323 * with the parent process |
| 324 * @param connectionCallback will be called exactly once after the connectio
n is set up or the |
| 325 * setup fails |
| 326 */ |
294 public void setupConnection(String[] commandLine, FileDescriptorInfo[] files
ToBeMapped, | 327 public void setupConnection(String[] commandLine, FileDescriptorInfo[] files
ToBeMapped, |
295 @Nullable IBinder callback, ConnectionCallback connectionCallback) { | 328 @Nullable IBinder callback, ConnectionCallback connectionCallback) { |
296 assert LauncherThread.runningOnLauncherThread(); | 329 assert LauncherThread.runningOnLauncherThread(); |
297 synchronized (mLock) { | 330 synchronized (mLock) { |
298 assert mConnectionParams == null; | 331 assert mConnectionParams == null; |
299 if (mServiceDisconnected) { | 332 if (mServiceDisconnected) { |
300 Log.w(TAG, "Tried to setup a connection that already disconnecte
d."); | 333 Log.w(TAG, "Tried to setup a connection that already disconnecte
d."); |
301 connectionCallback.onConnected(0); | 334 connectionCallback.onConnected(null); |
302 return; | 335 return; |
303 } | 336 } |
304 try { | 337 try { |
305 TraceEvent.begin("ChildProcessConnectionImpl.setupConnection"); | 338 TraceEvent.begin("BaseChildProcessConnection.setupConnection"); |
306 mConnectionCallback = connectionCallback; | 339 mConnectionCallback = connectionCallback; |
307 mConnectionParams = new ConnectionParams(commandLine, filesToBeM
apped, callback); | 340 mConnectionParams = new ConnectionParams(commandLine, filesToBeM
apped, callback); |
308 // Run the setup if the service is already connected. If not, | 341 // Run the setup if the service is already connected. If not, |
309 // doConnectionSetupLocked() will be called from onServiceConnec
ted(). | 342 // doConnectionSetupLocked() will be called from onServiceConnec
ted(). |
310 if (mServiceConnectComplete) { | 343 if (mServiceConnectComplete) { |
311 doConnectionSetupLocked(); | 344 doConnectionSetupLocked(); |
312 } | 345 } |
313 } finally { | 346 } finally { |
314 TraceEvent.end("ChildProcessConnectionImpl.setupConnection"); | 347 TraceEvent.end("BaseChildProcessConnection.setupConnection"); |
315 } | 348 } |
316 } | 349 } |
317 } | 350 } |
318 | 351 |
319 @Override | 352 /** |
| 353 * Terminates the connection to IChildProcessService, closing all bindings.
It is safe to call |
| 354 * this multiple times. |
| 355 */ |
320 public void stop() { | 356 public void stop() { |
321 synchronized (mLock) { | 357 synchronized (mLock) { |
322 mInitialBinding.unbind(); | 358 unbind(); |
323 mStrongBinding.unbind(); | 359 mService = null; |
324 mWaivedBinding.unbind(); | |
325 mModerateBinding.unbind(); | |
326 mStrongBindingCount = 0; | |
327 if (mService != null) { | |
328 mService = null; | |
329 } | |
330 mConnectionParams = null; | 360 mConnectionParams = null; |
331 } | 361 } |
332 } | 362 } |
333 | 363 |
334 private void onServiceConnectedOnLauncherThread(IBinder service) { | 364 private void onServiceConnectedOnLauncherThread(IBinder service) { |
335 assert LauncherThread.runningOnLauncherThread(); | 365 assert LauncherThread.runningOnLauncherThread(); |
336 synchronized (mLock) { | 366 synchronized (mLock) { |
337 // A flag from the parent class ensures we run the post-connection l
ogic only once | 367 // A flag from the parent class ensures we run the post-connection l
ogic only once |
338 // (instead of once per each ChildServiceConnection). | 368 // (instead of once per each ChildServiceConnection). |
339 if (mDidOnServiceConnected) { | 369 if (mDidOnServiceConnected) { |
340 return; | 370 return; |
341 } | 371 } |
342 try { | 372 try { |
343 TraceEvent.begin( | 373 TraceEvent.begin( |
344 "ChildProcessConnectionImpl.ChildServiceConnection.onSer
viceConnected"); | 374 "BaseChildProcessConnection.ChildServiceConnection.onSer
viceConnected"); |
345 mDidOnServiceConnected = true; | 375 mDidOnServiceConnected = true; |
346 mService = IChildProcessService.Stub.asInterface(service); | 376 mService = IChildProcessService.Stub.asInterface(service); |
347 | 377 |
348 StartCallback startCallback = mStartCallback; | 378 StartCallback startCallback = mStartCallback; |
349 mStartCallback = null; | 379 mStartCallback = null; |
350 | 380 |
351 final boolean bindCheck = | 381 final boolean bindCheck = |
352 mCreationParams != null && mCreationParams.getBindToCall
erCheck(); | 382 mCreationParams != null && mCreationParams.getBindToCall
erCheck(); |
353 boolean boundToUs = false; | 383 boolean boundToUs = false; |
354 try { | 384 try { |
(...skipping 19 matching lines...) Expand all Loading... |
374 | 404 |
375 mServiceConnectComplete = true; | 405 mServiceConnectComplete = true; |
376 | 406 |
377 // Run the setup if the connection parameters have already been
provided. If | 407 // Run the setup if the connection parameters have already been
provided. If |
378 // not, doConnectionSetupLocked() will be called from setupConne
ction(). | 408 // not, doConnectionSetupLocked() will be called from setupConne
ction(). |
379 if (mConnectionParams != null) { | 409 if (mConnectionParams != null) { |
380 doConnectionSetupLocked(); | 410 doConnectionSetupLocked(); |
381 } | 411 } |
382 } finally { | 412 } finally { |
383 TraceEvent.end( | 413 TraceEvent.end( |
384 "ChildProcessConnectionImpl.ChildServiceConnection.onSer
viceConnected"); | 414 "BaseChildProcessConnection.ChildServiceConnection.onSer
viceConnected"); |
385 } | 415 } |
386 } | 416 } |
387 } | 417 } |
388 | 418 |
389 private void onServiceDisconnectedOnLauncherThread() { | 419 private void onServiceDisconnectedOnLauncherThread() { |
390 assert LauncherThread.runningOnLauncherThread(); | 420 assert LauncherThread.runningOnLauncherThread(); |
391 synchronized (mLock) { | 421 synchronized (mLock) { |
392 // Ensure that the disconnection logic runs only once (instead of on
ce per each | 422 // Ensure that the disconnection logic runs only once (instead of on
ce per each |
393 // ChildServiceConnection). | 423 // ChildServiceConnection). |
394 if (mServiceDisconnected) { | 424 if (mServiceDisconnected) { |
395 return; | 425 return; |
396 } | 426 } |
397 // Stash the status of the oom bindings, since stop() will release a
ll bindings. | |
398 mWasOomProtected = isCurrentlyOomProtected(); | |
399 mServiceDisconnected = true; | 427 mServiceDisconnected = true; |
400 Log.w(TAG, "onServiceDisconnected (crash or killed by oom): pid=%d",
mPid); | 428 Log.w(TAG, "onServiceDisconnected (crash or killed by oom): pid=%d",
mPid); |
401 stop(); // We don't want to auto-restart on crash. Let the browser d
o that. | 429 stop(); // We don't want to auto-restart on crash. Let the browser d
o that. |
402 mDeathCallback.onChildProcessDied(ChildProcessConnectionImpl.this); | 430 mDeathCallback.onChildProcessDied(BaseChildProcessConnection.this); |
403 // If we have a pending connection callback, we need to communicate
the failure to | 431 // If we have a pending connection callback, we need to communicate
the failure to |
404 // the caller. | 432 // the caller. |
405 if (mConnectionCallback != null) { | 433 if (mConnectionCallback != null) { |
406 mConnectionCallback.onConnected(0); | 434 mConnectionCallback.onConnected(null); |
407 } | 435 } |
408 mConnectionCallback = null; | 436 mConnectionCallback = null; |
409 } | 437 } |
410 } | 438 } |
411 | 439 |
412 private void onSetupConnectionResult(int pid) { | 440 private void onSetupConnectionResult(int pid) { |
413 synchronized (mLock) { | 441 synchronized (mLock) { |
414 mPid = pid; | 442 mPid = pid; |
415 assert mPid != 0 : "Child service claims to be run by a process of p
id=0."; | 443 assert mPid != 0 : "Child service claims to be run by a process of p
id=0."; |
416 | 444 |
417 if (mConnectionCallback != null) { | 445 if (mConnectionCallback != null) { |
418 mConnectionCallback.onConnected(mPid); | 446 mConnectionCallback.onConnected(this); |
419 } | 447 } |
420 mConnectionCallback = null; | 448 mConnectionCallback = null; |
421 } | 449 } |
422 } | 450 } |
423 | 451 |
424 /** | 452 /** |
425 * Called after the connection parameters have been set (in setupConnection(
)) *and* a | 453 * Called after the connection parameters have been set (in setupConnection(
)) *and* a |
426 * connection has been established (as signaled by onServiceConnected()). Th
ese two events can | 454 * connection has been established (as signaled by onServiceConnected()). Th
ese two events can |
427 * happen in any order. Has to be called with mLock. | 455 * happen in any order. Has to be called with mLock. |
428 */ | 456 */ |
| 457 @GuardedBy("mLock") |
429 private void doConnectionSetupLocked() { | 458 private void doConnectionSetupLocked() { |
430 try { | 459 try { |
431 TraceEvent.begin("ChildProcessConnectionImpl.doConnectionSetupLocked
"); | 460 TraceEvent.begin("BaseChildProcessConnection.doConnectionSetupLocked
"); |
432 assert mServiceConnectComplete && mService != null; | 461 assert mServiceConnectComplete && mService != null; |
433 assert mConnectionParams != null; | 462 assert mConnectionParams != null; |
434 | 463 |
435 Bundle bundle = ChildProcessLauncher.createsServiceBundle( | 464 Bundle bundle = ChildProcessLauncher.createsServiceBundle( |
436 mConnectionParams.mCommandLine, mConnectionParams.mFilesToBe
Mapped); | 465 mConnectionParams.mCommandLine, mConnectionParams.mFilesToBe
Mapped); |
437 ICallbackInt pidCallback = new ICallbackInt.Stub() { | 466 ICallbackInt pidCallback = new ICallbackInt.Stub() { |
438 @Override | 467 @Override |
439 public void call(final int pid) { | 468 public void call(final int pid) { |
440 LauncherThread.post(new Runnable() { | 469 LauncherThread.post(new Runnable() { |
441 @Override | 470 @Override |
(...skipping 11 matching lines...) Expand all Loading... |
453 // We proactively close the FDs rather than wait for GC & finalizer. | 482 // We proactively close the FDs rather than wait for GC & finalizer. |
454 try { | 483 try { |
455 for (FileDescriptorInfo fileInfo : mConnectionParams.mFilesToBeM
apped) { | 484 for (FileDescriptorInfo fileInfo : mConnectionParams.mFilesToBeM
apped) { |
456 fileInfo.fd.close(); | 485 fileInfo.fd.close(); |
457 } | 486 } |
458 } catch (IOException ioe) { | 487 } catch (IOException ioe) { |
459 Log.w(TAG, "Failed to close FD.", ioe); | 488 Log.w(TAG, "Failed to close FD.", ioe); |
460 } | 489 } |
461 mConnectionParams = null; | 490 mConnectionParams = null; |
462 } finally { | 491 } finally { |
463 TraceEvent.end("ChildProcessConnectionImpl.doConnectionSetupLocked")
; | 492 TraceEvent.end("BaseChildProcessConnection.doConnectionSetupLocked")
; |
464 } | 493 } |
465 } | 494 } |
466 | 495 |
467 @Override | 496 /** Subclasses should implement this method to bind/unbind to the actual ser
vice. */ |
468 public boolean isInitialBindingBound() { | 497 protected abstract boolean bind(); |
469 synchronized (mLock) { | 498 protected abstract void unbind(); |
470 return mInitialBinding.isBound(); | 499 |
471 } | 500 protected ChildServiceConnection createServiceConnection(int bindFlags) { |
| 501 return new ChildServiceConnectionImpl(bindFlags); |
472 } | 502 } |
473 | 503 |
474 @Override | 504 protected boolean shouldBindAsExportedService() { |
475 public boolean isStrongBindingBound() { | 505 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && getCreationPara
ms() != null |
476 synchronized (mLock) { | 506 && getCreationParams().getIsExternalService() |
477 return mStrongBinding.isBound(); | 507 && isExportedService(isSandboxed(), getContext(), getServiceName
()); |
478 } | |
479 } | 508 } |
480 | 509 |
481 @Override | 510 private static boolean isExportedService( |
482 public void removeInitialBinding() { | 511 boolean inSandbox, Context context, ComponentName serviceName) { |
483 synchronized (mLock) { | 512 // Check for the cached value first. It is assumed that all pooled child
services |
484 assert !mAlwaysInForeground; | 513 // have identical attributes in the manifest. |
485 mInitialBinding.unbind(); | 514 final int arrayIndex = inSandbox ? 1 : 0; |
| 515 if (sNeedsExtrabindFlags[arrayIndex] != null) { |
| 516 return sNeedsExtrabindFlags[arrayIndex].booleanValue(); |
486 } | 517 } |
| 518 boolean result = false; |
| 519 try { |
| 520 PackageManager packageManager = context.getPackageManager(); |
| 521 ServiceInfo serviceInfo = packageManager.getServiceInfo(serviceName,
0); |
| 522 result = serviceInfo.exported; |
| 523 } catch (PackageManager.NameNotFoundException e) { |
| 524 Log.e(TAG, "Could not retrieve info about service %s", serviceName,
e); |
| 525 } |
| 526 sNeedsExtrabindFlags[arrayIndex] = Boolean.valueOf(result); |
| 527 return result; |
487 } | 528 } |
488 | 529 |
489 @Override | 530 @VisibleForTesting |
490 public boolean isOomProtectedOrWasWhenDied() { | 531 public void crashServiceForTesting() throws RemoteException { |
491 synchronized (mLock) { | 532 synchronized (mLock) { |
492 if (mServiceDisconnected) { | 533 mService.crashIntentionallyForTesting(); |
493 return mWasOomProtected; | |
494 } else { | |
495 return isCurrentlyOomProtected(); | |
496 } | |
497 } | |
498 } | |
499 | |
500 private boolean isCurrentlyOomProtected() { | |
501 synchronized (mLock) { | |
502 assert !mServiceDisconnected; | |
503 if (mAlwaysInForeground) return ChildProcessLauncher.isApplicationIn
Foreground(); | |
504 return mInitialBinding.isBound() || mStrongBinding.isBound(); | |
505 } | |
506 } | |
507 | |
508 @Override | |
509 public void dropOomBindings() { | |
510 synchronized (mLock) { | |
511 assert !mAlwaysInForeground; | |
512 mInitialBinding.unbind(); | |
513 | |
514 mStrongBindingCount = 0; | |
515 mStrongBinding.unbind(); | |
516 | |
517 mModerateBinding.unbind(); | |
518 } | |
519 } | |
520 | |
521 @Override | |
522 public void addStrongBinding() { | |
523 synchronized (mLock) { | |
524 if (mService == null) { | |
525 Log.w(TAG, "The connection is not bound for %d", mPid); | |
526 return; | |
527 } | |
528 if (mStrongBindingCount == 0) { | |
529 mStrongBinding.bind(); | |
530 } | |
531 mStrongBindingCount++; | |
532 } | |
533 } | |
534 | |
535 @Override | |
536 public void removeStrongBinding() { | |
537 synchronized (mLock) { | |
538 if (mService == null) { | |
539 Log.w(TAG, "The connection is not bound for %d", mPid); | |
540 return; | |
541 } | |
542 assert mStrongBindingCount > 0; | |
543 mStrongBindingCount--; | |
544 if (mStrongBindingCount == 0) { | |
545 mStrongBinding.unbind(); | |
546 } | |
547 } | |
548 } | |
549 | |
550 @Override | |
551 public boolean isModerateBindingBound() { | |
552 synchronized (mLock) { | |
553 return mModerateBinding.isBound(); | |
554 } | |
555 } | |
556 | |
557 @Override | |
558 public void addModerateBinding() { | |
559 synchronized (mLock) { | |
560 if (mService == null) { | |
561 Log.w(TAG, "The connection is not bound for %d", mPid); | |
562 return; | |
563 } | |
564 mModerateBinding.bind(); | |
565 } | |
566 } | |
567 | |
568 @Override | |
569 public void removeModerateBinding() { | |
570 synchronized (mLock) { | |
571 if (mService == null) { | |
572 Log.w(TAG, "The connection is not bound for %d", mPid); | |
573 return; | |
574 } | |
575 mModerateBinding.unbind(); | |
576 } | 534 } |
577 } | 535 } |
578 | 536 |
579 @VisibleForTesting | 537 @VisibleForTesting |
580 public void crashServiceForTesting() throws RemoteException { | |
581 mService.crashIntentionallyForTesting(); | |
582 } | |
583 | |
584 @VisibleForTesting | |
585 public boolean isConnected() { | 538 public boolean isConnected() { |
586 return mService != null; | 539 synchronized (mLock) { |
| 540 return mService != null; |
| 541 } |
587 } | 542 } |
588 } | 543 } |
OLD | NEW |