OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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.base; | 5 package org.chromium.base; |
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.os.Bundle; |
11 import android.os.IBinder; | 12 import android.os.IBinder; |
12 import android.os.ParcelFileDescriptor; | 13 import android.os.ParcelFileDescriptor; |
13 import android.os.RemoteException; | 14 import android.os.RemoteException; |
14 | 15 |
15 import org.chromium.base.annotations.CalledByNative; | 16 import org.chromium.base.annotations.CalledByNative; |
16 import org.chromium.base.annotations.JNINamespace; | 17 import org.chromium.base.annotations.JNINamespace; |
17 import org.chromium.base.process_launcher.FileDescriptorInfo; | 18 import org.chromium.base.process_launcher.FileDescriptorInfo; |
18 | 19 |
19 import java.io.IOException; | 20 import java.io.IOException; |
20 import java.util.ArrayList; | 21 import java.util.ArrayList; |
21 import java.util.LinkedList; | 22 import java.util.LinkedList; |
22 import java.util.List; | 23 import java.util.List; |
23 import java.util.Queue; | 24 import java.util.Queue; |
| 25 import java.util.concurrent.CountDownLatch; |
24 | 26 |
25 import javax.annotation.concurrent.GuardedBy; | 27 import javax.annotation.concurrent.GuardedBy; |
26 | 28 |
27 /** | 29 /** |
28 * Helper class for launching test client processes for multiprocess unit tests. | 30 * Helper class for launching test client processes for multiprocess unit tests. |
29 */ | 31 */ |
30 @JNINamespace("base::android") | 32 @JNINamespace("base::android") |
31 public final class MultiprocessTestClientLauncher { | 33 public final class MultiprocessTestClientLauncher { |
32 private static final String TAG = "cr_MProcTCLauncher"; | 34 private static final String TAG = "cr_MProcTCLauncher"; |
33 | 35 |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
92 if (connection.getPid() == pid) { | 94 if (connection.getPid() == pid) { |
93 return connection; | 95 return connection; |
94 } | 96 } |
95 } | 97 } |
96 } | 98 } |
97 return null; | 99 return null; |
98 } | 100 } |
99 } | 101 } |
100 | 102 |
101 private static class ClientServiceConnection implements ServiceConnection { | 103 private static class ClientServiceConnection implements ServiceConnection { |
102 private final String[] mCommandLine; | 104 private final Bundle mSetupBundle; |
103 private final FileDescriptorInfo[] mFilesToMap; | |
104 private final Object mConnectedLock = new Object(); | 105 private final Object mConnectedLock = new Object(); |
| 106 private final CountDownLatch mPidReceived = new CountDownLatch(1); |
105 private final int mSlot; | 107 private final int mSlot; |
106 private ITestClient mService = null; | 108 private ITestClient mService = null; |
107 @GuardedBy("mConnectedLock") | 109 @GuardedBy("mConnectedLock") |
108 private boolean mConnected; | 110 private boolean mConnected; |
109 private int mPid; | 111 private int mPid; |
| 112 private ITestController mTestController; |
| 113 private final ITestCallback.Stub mCallback = new ITestCallback.Stub() { |
| 114 public void childConnected(ITestController controller) { |
| 115 mTestController = controller; |
| 116 // This method can be called before onServiceConnected below has
set the PID. |
| 117 // Wait for mPid to be set before notifying. |
| 118 try { |
| 119 mPidReceived.await(); |
| 120 } catch (InterruptedException ie) { |
| 121 Log.e(TAG, "Interrupted while waiting for connection PID."); |
| 122 return; |
| 123 } |
| 124 // Now we are fully initialized, notify clients. |
| 125 synchronized (mConnectedLock) { |
| 126 mConnected = true; |
| 127 mConnectedLock.notifyAll(); |
| 128 } |
| 129 } |
| 130 }; |
110 | 131 |
111 ClientServiceConnection(int slot, String[] commandLine, FileDescriptorIn
fo[] filesToMap) { | 132 ClientServiceConnection(int slot, String[] commandLine, FileDescriptorIn
fo[] filesToMap) { |
112 mSlot = slot; | 133 mSlot = slot; |
113 mCommandLine = commandLine; | 134 mSetupBundle = new Bundle(); |
114 mFilesToMap = filesToMap; | 135 mSetupBundle.putStringArray(ChildProcessConstants.EXTRA_COMMAND_LINE
, commandLine); |
| 136 mSetupBundle.putParcelableArray(ChildProcessConstants.EXTRA_FILES, f
ilesToMap); |
115 } | 137 } |
116 | 138 |
117 public void waitForConnection() { | 139 public void waitForConnection() { |
118 synchronized (mConnectedLock) { | 140 synchronized (mConnectedLock) { |
119 while (!mConnected) { | 141 while (!mConnected) { |
120 try { | 142 try { |
121 mConnectedLock.wait(); | 143 mConnectedLock.wait(); |
122 } catch (InterruptedException ie) { | 144 } catch (InterruptedException ie) { |
123 Log.e(TAG, "Interrupted while waiting for connection."); | 145 Log.e(TAG, "Interrupted while waiting for connection."); |
124 } | 146 } |
125 } | 147 } |
126 } | 148 } |
127 } | 149 } |
128 | 150 |
129 @Override | 151 @Override |
130 public void onServiceConnected(ComponentName className, IBinder service)
{ | 152 public void onServiceConnected(ComponentName className, IBinder service)
{ |
131 try { | 153 try { |
132 mService = ITestClient.Stub.asInterface(service); | 154 mService = ITestClient.Stub.asInterface(service); |
133 mPid = mService.launch(mCommandLine, mFilesToMap); | 155 if (!mService.bindToCaller()) { |
134 synchronized (mConnectedLock) { | 156 Log.e(TAG, "Failed to bind to child service"); |
135 mConnected = true; | 157 return; |
136 mConnectedLock.notifyAll(); | |
137 } | 158 } |
| 159 mPid = mService.setupConnection(mSetupBundle, mCallback); |
| 160 mPidReceived.countDown(); |
138 } catch (RemoteException e) { | 161 } catch (RemoteException e) { |
139 Log.e(TAG, "Connect failed"); | 162 Log.e(TAG, "Connect failed"); |
140 } | 163 } |
141 } | 164 } |
142 | 165 |
143 @Override | 166 @Override |
144 public void onServiceDisconnected(ComponentName className) { | 167 public void onServiceDisconnected(ComponentName className) { |
145 if (mPid == 0) { | 168 if (mPid == 0) { |
146 Log.e(TAG, "Early ClientServiceConnection disconnection."); | 169 Log.e(TAG, "Early ClientServiceConnection disconnection."); |
147 return; | 170 return; |
148 } | 171 } |
149 sConnectionAllocator.freeConnection(this); | 172 sConnectionAllocator.freeConnection(this); |
150 } | 173 } |
151 | 174 |
152 public ITestClient getService() { | 175 public ITestController getTestController() { |
153 return mService; | 176 return mTestController; |
154 } | 177 } |
155 | 178 |
156 public String getServiceClassName() { | 179 public String getServiceClassName() { |
157 // In order to use different processes, we have to declare multiple
services in the | 180 // In order to use different processes, we have to declare multiple
services in the |
158 // AndroidManifest.xml file, each service associated with its own pr
ocess. The various | 181 // AndroidManifest.xml file, each service associated with its own pr
ocess. The various |
159 // services are functionnaly identical but need to each have their o
wn class. | 182 // services are functionnaly identical but need to each have their o
wn class. |
160 // We differentiate them by their class name having a trailing numbe
r. | 183 // We differentiate them by their class name having a trailing numbe
r. |
161 return MultiprocessTestClientService.class.getName() + mSlot; | 184 return MultiprocessTestClientService.class.getName() + mSlot; |
162 } | 185 } |
163 | 186 |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
221 * @return the return code returned by the main method or whether it timed-o
ut. | 244 * @return the return code returned by the main method or whether it timed-o
ut. |
222 */ | 245 */ |
223 @CalledByNative | 246 @CalledByNative |
224 private static MainReturnCodeResult waitForMainToReturn(int pid, int timeout
Ms) { | 247 private static MainReturnCodeResult waitForMainToReturn(int pid, int timeout
Ms) { |
225 ClientServiceConnection connection = sConnectionAllocator.getConnectionB
yPid(pid); | 248 ClientServiceConnection connection = sConnectionAllocator.getConnectionB
yPid(pid); |
226 if (connection == null) { | 249 if (connection == null) { |
227 Log.e(TAG, "waitForMainToReturn called on unknown connection for pid
" + pid); | 250 Log.e(TAG, "waitForMainToReturn called on unknown connection for pid
" + pid); |
228 return null; | 251 return null; |
229 } | 252 } |
230 try { | 253 try { |
231 return connection.getService().waitForMainToReturn(timeoutMs); | 254 return connection.getTestController().waitForMainToReturn(timeoutMs)
; |
232 } catch (RemoteException e) { | 255 } catch (RemoteException e) { |
233 Log.e(TAG, "Remote call to waitForMainToReturn failed."); | 256 Log.e(TAG, "Remote call to waitForMainToReturn failed."); |
234 return null; | 257 return null; |
235 } finally { | 258 } finally { |
236 freeConnection(connection); | 259 freeConnection(connection); |
237 } | 260 } |
238 } | 261 } |
239 | 262 |
240 @CalledByNative | 263 @CalledByNative |
241 private static boolean terminate(int pid, int exitCode, boolean wait) { | 264 private static boolean terminate(int pid, int exitCode, boolean wait) { |
242 ClientServiceConnection connection = sConnectionAllocator.getConnectionB
yPid(pid); | 265 ClientServiceConnection connection = sConnectionAllocator.getConnectionB
yPid(pid); |
243 if (connection == null) { | 266 if (connection == null) { |
244 Log.e(TAG, "terminate called on unknown connection for pid " + pid); | 267 Log.e(TAG, "terminate called on unknown connection for pid " + pid); |
245 return false; | 268 return false; |
246 } | 269 } |
247 try { | 270 try { |
248 if (wait) { | 271 if (wait) { |
249 connection.getService().forceStopSynchronous(exitCode); | 272 connection.getTestController().forceStopSynchronous(exitCode); |
250 } else { | 273 } else { |
251 connection.getService().forceStop(exitCode); | 274 connection.getTestController().forceStop(exitCode); |
252 } | 275 } |
253 } catch (RemoteException e) { | 276 } catch (RemoteException e) { |
254 // We expect this failure, since the forceStop's service implementat
ion calls | 277 // We expect this failure, since the forceStop's service implementat
ion calls |
255 // System.exit(). | 278 // System.exit(). |
256 } finally { | 279 } finally { |
257 freeConnection(connection); | 280 freeConnection(connection); |
258 } | 281 } |
259 return true; | 282 return true; |
260 } | 283 } |
261 | 284 |
(...skipping 21 matching lines...) Expand all Loading... |
283 ParcelFileDescriptor parcelableFd = null; | 306 ParcelFileDescriptor parcelableFd = null; |
284 try { | 307 try { |
285 parcelableFd = ParcelFileDescriptor.fromFd(fd); | 308 parcelableFd = ParcelFileDescriptor.fromFd(fd); |
286 } catch (IOException e) { | 309 } catch (IOException e) { |
287 Log.e(TAG, "Invalid FD provided for process connection, aborting con
nection.", e); | 310 Log.e(TAG, "Invalid FD provided for process connection, aborting con
nection.", e); |
288 return null; | 311 return null; |
289 } | 312 } |
290 return new FileDescriptorInfo(id, parcelableFd, 0 /* offset */, 0 /* siz
e */); | 313 return new FileDescriptorInfo(id, parcelableFd, 0 /* offset */, 0 /* siz
e */); |
291 } | 314 } |
292 } | 315 } |
OLD | NEW |