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..112bdbeca84c9f1949dc150e8ea498282bd352e6 |
--- /dev/null |
+++ b/base/test/android/java/src/org/chromium/base/MultiprocessTestClientService.java |
@@ -0,0 +1,147 @@ |
+// 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.LibraryLoader; |
+import org.chromium.base.library_loader.LibraryProcessType; |
+import org.chromium.base.library_loader.ProcessInitException; |
+ |
+import javax.annotation.concurrent.GuardedBy; |
+ |
+/** |
+ * The service implementation used to host all multiprocess test client code. |
+ */ |
+@JNINamespace("base::android") |
+public class MultiprocessTestClientService extends Service { |
+ private static final String TAG = "cr_TestClient"; |
+ |
+ private static boolean sAlreadyInitialized = false; |
+ |
+ private final Handler mHandler = new Handler(); |
+ |
+ private final Object mResultLock = new Object(); |
+ |
+ @GuardedBy("mResultLock") |
+ private MainReturnCodeResult mResult; |
+ |
+ private final ITestClient.Stub mBinder = new ITestClient.Stub() { |
+ @Override |
+ public int launch(final String[] commandLine, FileDescriptorInfo[] fdsToMap) { |
+ final int[] fdKeys = new int[fdsToMap.length]; |
+ final int[] fdFds = new int[fdsToMap.length]; |
+ for (int i = 0; i < fdsToMap.length; i++) { |
+ fdKeys[i] = fdsToMap[i].key; |
+ // Take ownership of the file descriptor so they outlive the FileDescriptorInfo |
+ // instances. Native code will own them. |
+ fdFds[i] = fdsToMap[i].fd.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, fdKeys, fdFds); |
+ } |
+ }); |
+ return Process.myPid(); |
+ } |
+ |
+ @SuppressFBWarnings("RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE") |
+ @Override |
+ public MainReturnCodeResult waitForMainToReturn(int timeoutMs) { |
+ synchronized (mResultLock) { |
+ while (mResult == null) { |
+ try { |
+ mResultLock.wait(timeoutMs); |
+ } catch (InterruptedException ie) { |
+ continue; |
+ } |
+ // Check if we timed-out. |
+ if (mResult == null) { |
+ Log.e(TAG, "Failed to wait for main return value."); |
+ return new MainReturnCodeResult(0, true /* timed-out */); |
+ } |
+ } |
+ 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); |
+ } |
+ }; |
+ |
+ @SuppressFBWarnings("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); |
+ return; |
+ } |
+ markInitialized(); |
+ |
+ ContextUtils.initApplicationContext(getApplicationContext()); |
+ |
+ PathUtils.setPrivateDataDirectorySuffix("chrome_multiprocess_test_client_service"); |
+ |
+ loadLibraries(); |
+ } |
+ |
+ @Override |
+ public IBinder onBind(Intent intent) { |
+ return mBinder; |
+ } |
+ |
+ private void loadLibraries() { |
+ try { |
+ LibraryLoader.get(LibraryProcessType.PROCESS_CHILD).loadNow(); |
+ } catch (ProcessInitException pie) { |
+ Log.e(TAG, "Unable to load native libraries.", pie); |
+ } |
+ ContextUtils.initApplicationContextForNative(); |
+ } |
+ |
+ @CalledByNative |
+ private void setMainReturnValue(int result) { |
+ synchronized (mResultLock) { |
+ mResult = new MainReturnCodeResult(result, false /* timed-out */); |
+ mResultLock.notifyAll(); |
+ } |
+ } |
+ |
+ private static void markInitialized() { |
+ // We don't set sAlreadyInitialized directly in onCreate to avoid FindBugs complaining about |
+ // a static member been set from a non-static function. |
+ sAlreadyInitialized = true; |
+ } |
+ |
+ private native void nativeRunMain(String[] commandLine, int[] fdsToMapIds, int[] fdsToMapFds); |
+} |