| 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 |