Index: base/test/android/java/src/org/chromium/base/MultiprocessTestClientService.java |
diff --git a/base/test/android/java/src/org/chromium/base/MultiprocessTestClientService.java b/base/test/android/java/src/org/chromium/base/MultiprocessTestClientService.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..e75182da43b1149e1856207543d28723f1d1ab00 |
--- /dev/null |
+++ b/base/test/android/java/src/org/chromium/base/MultiprocessTestClientService.java |
@@ -0,0 +1,139 @@ |
+// Copyright 2016 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.base; |
+ |
+import android.app.Service; |
+import android.content.Intent; |
+import android.os.Handler; |
+import android.os.IBinder; |
+import android.os.Process; |
+ |
+import org.chromium.base.annotations.CalledByNative; |
+import org.chromium.base.annotations.JNINamespace; |
+import org.chromium.base.annotations.SuppressFBWarnings; |
+import org.chromium.base.library_loader.NativeLibraries; |
+ |
+/** |
+ * The service implementation used to host all multiprocess test client code. |
+ */ |
+@JNINamespace("base::android") |
+public class MultiprocessTestClientService extends Service { |
+ private static final String TAG = "TestClient"; |
+ |
+ private final Handler mHandler = new Handler(); |
+ |
+ private final Object mResultLock = new Object(); |
+ private MainReturnCodeResult mResult; |
+ |
+ private final ITestClient.Stub mBinder = new ITestClient.Stub() { |
+ @Override |
+ public int launch(final String[] commandLine, FileDescriptorInfo[] fdsToMap) { |
+ final int[] fdIds = new int[fdsToMap.length]; |
+ final int[] fdFds = new int[fdsToMap.length]; |
+ for (int i = 0; i < fdsToMap.length; i++) { |
+ fdIds[i] = fdsToMap[i].mId; |
+ fdFds[i] = fdsToMap[i].mFd.detachFd(); |
+ } |
+ // Don't run main directly, it would block and the response would not be returned. |
+ // We post to the main thread as this thread does not have a Looper. |
+ mHandler.post(new Runnable() { |
+ @Override |
+ public void run() { |
+ nativeRunMain(commandLine, fdIds, fdFds); |
+ } |
+ }); |
+ return Process.myPid(); |
+ } |
+ |
+ @SuppressFBWarnings("RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE") |
+ @Override |
+ public MainReturnCodeResult waitForMainToReturn(int timeoutMs) { |
+ synchronized (mResultLock) { |
+ if (mResult != null) return mResult; |
+ try { |
+ mResultLock.wait(timeoutMs); |
+ } catch (InterruptedException ie) { |
+ Log.e(TAG, "Failed to wait for main return value."); |
+ return new MainReturnCodeResult(0, true /* timed-out */); |
+ } |
+ assert mResult != null; |
+ return mResult; |
+ } |
+ } |
+ |
+ @SuppressFBWarnings("DM_EXIT") |
+ @Override |
+ public boolean forceStopSynchronous(int exitCode) { |
+ System.exit(exitCode); |
+ return true; |
+ } |
+ |
+ @SuppressFBWarnings("DM_EXIT") |
+ @Override |
+ public void forceStop(int exitCode) { |
+ System.exit(exitCode); |
+ } |
+ }; |
+ |
+ static boolean sAlreadyInitialized = false; |
+ |
+ @SuppressFBWarnings({"ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD", "DM_EXIT"}) |
+ @Override |
+ public void onCreate() { |
+ super.onCreate(); |
+ |
+ if (sAlreadyInitialized) { |
+ // The framework controls how services are reused and even though nothing is bound to a |
+ // service it might be kept around. Since we really want to fork a new process when we |
+ // bind, we'll kill the process early when a service is reused, forcing the framework to |
+ // recreate the service in a new process. |
+ // This is not ideal, but there are no clear alternatives at this point. |
+ Log.e(TAG, "Service being reused, forcing stoppage."); |
+ System.exit(0); |
+ } |
+ sAlreadyInitialized = true; |
+ |
+ ContextUtils.initApplicationContext(getApplicationContext()); |
+ |
+ PathUtils.setPrivateDataDirectorySuffix("chrome_multiprocess_test_client_service"); |
+ |
+ loadLibraries(); |
+ |
+ Log.e(TAG, "onCreate"); |
+ } |
+ |
+ @Override |
+ public void onDestroy() { |
+ Log.e(TAG, "onDestroy"); |
+ super.onDestroy(); |
+ } |
+ |
+ @Override |
+ public IBinder onBind(Intent intent) { |
+ // Only bind the first incoming request. |
+ stopSelf(); |
+ |
+ return mBinder; |
+ } |
+ |
+ private void loadLibraries() { |
+ for (String library : NativeLibraries.LIBRARIES) { |
+ Log.i(TAG, "loading: %s", library); |
+ System.loadLibrary(library); |
+ Log.i(TAG, "loaded: %s", library); |
+ } |
+ ContextUtils.initApplicationContextForNative(); |
+ } |
+ |
+ @CalledByNative |
+ private void setMainReturnValue(int result) { |
+ synchronized (mResultLock) { |
+ mResult = new MainReturnCodeResult(result, false /* timed-out */); |
+ mResultLock.notifyAll(); |
+ } |
+ } |
+ |
+ private native void nativeRunMain(String[] commandLine, int[] fdsToMapIds, int[] fdsToMapFds); |
+} |