OLD | NEW |
(Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 package org.chromium.base; |
| 6 |
| 7 import android.app.Service; |
| 8 import android.content.Intent; |
| 9 import android.os.Handler; |
| 10 import android.os.IBinder; |
| 11 import android.os.Process; |
| 12 |
| 13 import org.chromium.base.annotations.CalledByNative; |
| 14 import org.chromium.base.annotations.JNINamespace; |
| 15 import org.chromium.base.annotations.SuppressFBWarnings; |
| 16 import org.chromium.base.library_loader.LibraryLoader; |
| 17 import org.chromium.base.library_loader.LibraryProcessType; |
| 18 import org.chromium.base.library_loader.ProcessInitException; |
| 19 |
| 20 import javax.annotation.concurrent.GuardedBy; |
| 21 |
| 22 /** |
| 23 * The service implementation used to host all multiprocess test client code. |
| 24 */ |
| 25 @JNINamespace("base::android") |
| 26 public class MultiprocessTestClientService extends Service { |
| 27 private static final String TAG = "cr_TestClient"; |
| 28 |
| 29 private static boolean sAlreadyInitialized = false; |
| 30 |
| 31 private final Handler mHandler = new Handler(); |
| 32 |
| 33 private final Object mResultLock = new Object(); |
| 34 |
| 35 @GuardedBy("mResultLock") |
| 36 private MainReturnCodeResult mResult; |
| 37 |
| 38 private final ITestClient.Stub mBinder = new ITestClient.Stub() { |
| 39 @Override |
| 40 public int launch(final String[] commandLine, FileDescriptorInfo[] fdsTo
Map) { |
| 41 final int[] fdKeys = new int[fdsToMap.length]; |
| 42 final int[] fdFds = new int[fdsToMap.length]; |
| 43 for (int i = 0; i < fdsToMap.length; i++) { |
| 44 fdKeys[i] = fdsToMap[i].key; |
| 45 // Take ownership of the file descriptor so they outlive the Fil
eDescriptorInfo |
| 46 // instances. Native code will own them. |
| 47 fdFds[i] = fdsToMap[i].fd.detachFd(); |
| 48 } |
| 49 // Don't run main directly, it would block and the response would no
t be returned. |
| 50 // We post to the main thread as this thread does not have a Looper. |
| 51 mHandler.post(new Runnable() { |
| 52 @Override |
| 53 public void run() { |
| 54 nativeRunMain(commandLine, fdKeys, fdFds); |
| 55 } |
| 56 }); |
| 57 return Process.myPid(); |
| 58 } |
| 59 |
| 60 @SuppressFBWarnings("RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE") |
| 61 @Override |
| 62 public MainReturnCodeResult waitForMainToReturn(int timeoutMs) { |
| 63 synchronized (mResultLock) { |
| 64 while (mResult == null) { |
| 65 try { |
| 66 mResultLock.wait(timeoutMs); |
| 67 } catch (InterruptedException ie) { |
| 68 continue; |
| 69 } |
| 70 // Check if we timed-out. |
| 71 if (mResult == null) { |
| 72 Log.e(TAG, "Failed to wait for main return value."); |
| 73 return new MainReturnCodeResult(0, true /* timed-out */)
; |
| 74 } |
| 75 } |
| 76 return mResult; |
| 77 } |
| 78 } |
| 79 |
| 80 @SuppressFBWarnings("DM_EXIT") |
| 81 @Override |
| 82 public boolean forceStopSynchronous(int exitCode) { |
| 83 System.exit(exitCode); |
| 84 return true; |
| 85 } |
| 86 |
| 87 @SuppressFBWarnings("DM_EXIT") |
| 88 @Override |
| 89 public void forceStop(int exitCode) { |
| 90 System.exit(exitCode); |
| 91 } |
| 92 }; |
| 93 |
| 94 @SuppressFBWarnings("DM_EXIT") |
| 95 @Override |
| 96 public void onCreate() { |
| 97 super.onCreate(); |
| 98 |
| 99 if (sAlreadyInitialized) { |
| 100 // The framework controls how services are reused and even though no
thing is bound to a |
| 101 // service it might be kept around. Since we really want to fork a n
ew process when we |
| 102 // bind, we'll kill the process early when a service is reused, forc
ing the framework to |
| 103 // recreate the service in a new process. |
| 104 // This is not ideal, but there are no clear alternatives at this po
int. |
| 105 Log.e(TAG, "Service being reused, forcing stoppage."); |
| 106 System.exit(0); |
| 107 return; |
| 108 } |
| 109 markInitialized(); |
| 110 |
| 111 ContextUtils.initApplicationContext(getApplicationContext()); |
| 112 |
| 113 PathUtils.setPrivateDataDirectorySuffix("chrome_multiprocess_test_client
_service"); |
| 114 |
| 115 loadLibraries(); |
| 116 } |
| 117 |
| 118 @Override |
| 119 public IBinder onBind(Intent intent) { |
| 120 return mBinder; |
| 121 } |
| 122 |
| 123 private void loadLibraries() { |
| 124 try { |
| 125 LibraryLoader.get(LibraryProcessType.PROCESS_CHILD).loadNow(); |
| 126 } catch (ProcessInitException pie) { |
| 127 Log.e(TAG, "Unable to load native libraries.", pie); |
| 128 } |
| 129 ContextUtils.initApplicationContextForNative(); |
| 130 } |
| 131 |
| 132 @CalledByNative |
| 133 private void setMainReturnValue(int result) { |
| 134 synchronized (mResultLock) { |
| 135 mResult = new MainReturnCodeResult(result, false /* timed-out */); |
| 136 mResultLock.notifyAll(); |
| 137 } |
| 138 } |
| 139 |
| 140 private static void markInitialized() { |
| 141 // We don't set sAlreadyInitialized directly in onCreate to avoid FindBu
gs complaining about |
| 142 // a static member been set from a non-static function. |
| 143 sAlreadyInitialized = true; |
| 144 } |
| 145 |
| 146 private native void nativeRunMain(String[] commandLine, int[] fdsToMapIds, i
nt[] fdsToMapFds); |
| 147 } |
OLD | NEW |