mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 23:31:56 +00:00
Bug 1314466 - part 5, Add service process manager r=snorp
This commit is contained in:
parent
26e454ec14
commit
de17af98b5
@ -0,0 +1,12 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.process;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
|
||||
interface IChildProcess {
|
||||
void stop();
|
||||
int getPid();
|
||||
void start(in String[] args, in ParcelFileDescriptor crashReporterPfd, in ParcelFileDescriptor ipcPfd);
|
||||
}
|
@ -34,6 +34,8 @@ import org.mozilla.gecko.gfx.BitmapUtils;
|
||||
import org.mozilla.gecko.gfx.LayerView;
|
||||
import org.mozilla.gecko.gfx.PanZoomController;
|
||||
import org.mozilla.gecko.permissions.Permissions;
|
||||
import org.mozilla.gecko.process.GeckoProcessManager;
|
||||
import org.mozilla.gecko.process.GeckoServiceChildProcess;
|
||||
import org.mozilla.gecko.util.EventCallback;
|
||||
import org.mozilla.gecko.util.GeckoRequest;
|
||||
import org.mozilla.gecko.util.HardwareCodecCapabilityUtils;
|
||||
@ -84,6 +86,7 @@ import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.os.Looper;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.SystemClock;
|
||||
import android.os.Vibrator;
|
||||
import android.provider.Settings;
|
||||
@ -2274,4 +2277,9 @@ public class GeckoAppShell
|
||||
}
|
||||
return sScreenSize;
|
||||
}
|
||||
|
||||
@WrapForJNI
|
||||
private static int startGeckoServiceChildProcess(String type, String[] args, int crashFd, int ipcFd) {
|
||||
return GeckoProcessManager.getInstance().start(type, args, crashFd, ipcFd);
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.MessageQueue;
|
||||
import android.os.SystemClock;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
@ -32,6 +33,7 @@ import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
public class GeckoThread extends Thread {
|
||||
private static final String LOGTAG = "GeckoThread";
|
||||
@ -134,11 +136,18 @@ public class GeckoThread extends Thread {
|
||||
private final String mAction;
|
||||
private final boolean mDebugging;
|
||||
|
||||
private String[] mChildProcessArgs;
|
||||
private int mCrashFileDescriptor;
|
||||
private int mIPCFileDescriptor;
|
||||
|
||||
GeckoThread(GeckoProfile profile, String args, String action, boolean debugging) {
|
||||
mProfile = profile;
|
||||
mArgs = args;
|
||||
mAction = action;
|
||||
mDebugging = debugging;
|
||||
mChildProcessArgs = null;
|
||||
mCrashFileDescriptor = -1;
|
||||
mIPCFileDescriptor = -1;
|
||||
|
||||
setName("Gecko");
|
||||
}
|
||||
@ -152,6 +161,16 @@ public class GeckoThread extends Thread {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean initChildProcess(GeckoProfile profile, String[] args, int crashFd, int ipcFd, boolean debugging) {
|
||||
if (init(profile, null, null, debugging)) {
|
||||
sGeckoThread.mChildProcessArgs = args;
|
||||
sGeckoThread.mCrashFileDescriptor = crashFd;
|
||||
sGeckoThread.mIPCFileDescriptor = ipcFd;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean canUseProfile(final Context context, final GeckoProfile profile,
|
||||
final String profileName, final File profileDir) {
|
||||
if (profileDir != null && !profileDir.isDirectory()) {
|
||||
@ -403,35 +422,45 @@ public class GeckoThread extends Thread {
|
||||
return resourcePath;
|
||||
}
|
||||
|
||||
private String addCustomProfileArg(String args) {
|
||||
String profileArg = "";
|
||||
|
||||
private void addCustomProfileArg(String args, ArrayList<String> list) {
|
||||
// Make sure a profile exists.
|
||||
final GeckoProfile profile = getProfile();
|
||||
profile.getDir(); // call the lazy initializer
|
||||
|
||||
// If args don't include the profile, make sure it's included.
|
||||
if (args == null || !args.matches(".*\\B-(P|profile)\\s+\\S+.*")) {
|
||||
if (profile.isCustomProfile()) {
|
||||
profileArg = " -profile " + profile.getDir().getAbsolutePath();
|
||||
} else {
|
||||
profileArg = " -P " + profile.getName();
|
||||
boolean needsProfile = true;
|
||||
|
||||
if (args != null) {
|
||||
StringTokenizer st = new StringTokenizer(args);
|
||||
while (st.hasMoreTokens()) {
|
||||
String token = st.nextToken();
|
||||
if ("-P".equals(token) || "-profile".equals(token)) {
|
||||
needsProfile = false;
|
||||
}
|
||||
list.add(token);
|
||||
}
|
||||
}
|
||||
|
||||
return (args != null ? args : "") + profileArg;
|
||||
// If args don't include the profile, make sure it's included.
|
||||
if (args == null || needsProfile) {
|
||||
if (profile.isCustomProfile()) {
|
||||
list.add("-profile");
|
||||
list.add(profile.getDir().getAbsolutePath());
|
||||
} else {
|
||||
list.add("-P");
|
||||
list.add(profile.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getGeckoArgs(final String apkPath) {
|
||||
private String[] getGeckoArgs(final String apkPath) {
|
||||
// argv[0] is the program name, which for us is the package name.
|
||||
final Context context = GeckoAppShell.getApplicationContext();
|
||||
final StringBuilder args = new StringBuilder(context.getPackageName());
|
||||
args.append(" -greomni ").append(apkPath);
|
||||
final ArrayList<String> args = new ArrayList<String>();
|
||||
args.add(context.getPackageName());
|
||||
args.add("-greomni");
|
||||
args.add(apkPath);
|
||||
|
||||
final String userArgs = addCustomProfileArg(mArgs);
|
||||
if (userArgs != null) {
|
||||
args.append(' ').append(userArgs);
|
||||
}
|
||||
addCustomProfileArg(mArgs, args);
|
||||
|
||||
// In un-official builds, we want to load Javascript resources fresh
|
||||
// with each build. In official builds, the startup cache is purged by
|
||||
@ -440,10 +469,10 @@ public class GeckoThread extends Thread {
|
||||
if (!AppConstants.MOZILLA_OFFICIAL) {
|
||||
Log.w(LOGTAG, "STARTUP PERFORMANCE WARNING: un-official build: purging the " +
|
||||
"startup (JavaScript) caches.");
|
||||
args.append(" -purgecaches");
|
||||
args.add("-purgecaches");
|
||||
}
|
||||
|
||||
return args.toString();
|
||||
return args.toArray(new String[args.size()]);
|
||||
}
|
||||
|
||||
public static GeckoProfile getActiveProfile() {
|
||||
@ -493,7 +522,13 @@ public class GeckoThread extends Thread {
|
||||
}
|
||||
}
|
||||
|
||||
final String args = getGeckoArgs(initGeckoEnvironment());
|
||||
final String[] args;
|
||||
if (mChildProcessArgs != null) {
|
||||
initGeckoEnvironment();
|
||||
args = mChildProcessArgs;
|
||||
} else {
|
||||
args = getGeckoArgs(initGeckoEnvironment());
|
||||
}
|
||||
|
||||
// This can only happen after the call to initGeckoEnvironment
|
||||
// above, because otherwise the JNI code hasn't been loaded yet.
|
||||
@ -506,11 +541,12 @@ public class GeckoThread extends Thread {
|
||||
Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - runGecko");
|
||||
|
||||
if (!AppConstants.MOZILLA_OFFICIAL) {
|
||||
Log.i(LOGTAG, "RunGecko - args = " + args);
|
||||
String msg = new String("RunGecko - args =" + TextUtils.join(" ", args));
|
||||
Log.i(LOGTAG, msg);
|
||||
}
|
||||
|
||||
// And go.
|
||||
GeckoLoader.nativeRun(args);
|
||||
GeckoLoader.nativeRun(args, mCrashFileDescriptor, mIPCFileDescriptor);
|
||||
|
||||
// And... we're done.
|
||||
setState(State.EXITED);
|
||||
|
@ -16,8 +16,10 @@ import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import java.util.ArrayList;
|
||||
import android.util.Log;
|
||||
|
||||
import org.mozilla.gecko.annotation.JNITarget;
|
||||
@ -35,6 +37,7 @@ public final class GeckoLoader {
|
||||
private static boolean sSQLiteLibsLoaded;
|
||||
private static boolean sNSSLibsLoaded;
|
||||
private static boolean sMozGlueLoaded;
|
||||
private static String[] sEnvList;
|
||||
|
||||
private GeckoLoader() {
|
||||
// prevent instantiation
|
||||
@ -123,18 +126,31 @@ public final class GeckoLoader {
|
||||
sIntent = intent;
|
||||
}
|
||||
|
||||
public static void addEnvironmentToIntent(Intent intent) {
|
||||
if (sEnvList != null) {
|
||||
for (int ix = 0; ix < sEnvList.length; ix++) {
|
||||
intent.putExtra("env" + ix, sEnvList[ix]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void setupGeckoEnvironment(Context context, String[] pluginDirs, String profilePath) {
|
||||
// if we have an intent (we're being launched by an activity)
|
||||
// read in any environmental variables from it here
|
||||
final SafeIntent intent = sIntent;
|
||||
if (intent != null) {
|
||||
final ArrayList<String> envList = new ArrayList<String>();
|
||||
String env = intent.getStringExtra("env0");
|
||||
Log.d(LOGTAG, "Gecko environment env0: " + env);
|
||||
for (int c = 1; env != null; c++) {
|
||||
envList.add(env);
|
||||
putenv(env);
|
||||
env = intent.getStringExtra("env" + c);
|
||||
Log.d(LOGTAG, "env" + c + ": " + env);
|
||||
}
|
||||
if (envList.size() > 0) {
|
||||
sEnvList = envList.toArray(new String[envList.size()]);
|
||||
}
|
||||
}
|
||||
|
||||
putenv("MOZ_ANDROID_PACKAGE_NAME=" + context.getPackageName());
|
||||
@ -548,7 +564,7 @@ public final class GeckoLoader {
|
||||
private static native void putenv(String map);
|
||||
|
||||
// These methods are implemented in mozglue/android/APKOpen.cpp
|
||||
public static native void nativeRun(String args);
|
||||
public static native void nativeRun(String[] args, int crashFd, int ipcFd);
|
||||
private static native void loadGeckoLibsNative(String apkName);
|
||||
private static native void loadSQLiteLibsNative(String apkName);
|
||||
private static native void loadNSSLibsNative(String apkName);
|
||||
|
@ -0,0 +1,175 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.process;
|
||||
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.mozglue.GeckoLoader;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.DeadObjectException;
|
||||
import android.os.IBinder;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteException;
|
||||
import android.support.v4.util.SimpleArrayMap;
|
||||
import android.view.Surface;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.Map;
|
||||
|
||||
public final class GeckoProcessManager {
|
||||
private static final String LOGTAG = "GeckoProcessManager";
|
||||
private static final GeckoProcessManager INSTANCE = new GeckoProcessManager();
|
||||
|
||||
public static GeckoProcessManager getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private static final class ChildConnection implements ServiceConnection, IBinder.DeathRecipient {
|
||||
public final String mType;
|
||||
private boolean mWait = false;
|
||||
public IChildProcess mChild = null;
|
||||
public int mPid = 0;
|
||||
public ChildConnection(String type) {
|
||||
mType = type;
|
||||
}
|
||||
|
||||
void prepareToWait() {
|
||||
mWait = true;
|
||||
}
|
||||
|
||||
void waitForChild() {
|
||||
ThreadUtils.assertNotOnUiThread();
|
||||
synchronized(this) {
|
||||
if (mWait) {
|
||||
try {
|
||||
this.wait(5000); // 5 seconds
|
||||
} catch (final InterruptedException e) {
|
||||
Log.e(LOGTAG, "Interrupted waiting for child service to start", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clearWait() {
|
||||
mWait = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
try {
|
||||
service.linkToDeath(this, 0);
|
||||
} catch (final RemoteException e) {
|
||||
Log.e(LOGTAG, "Failed to link ChildConnection to death of service.", e);
|
||||
}
|
||||
mChild = IChildProcess.Stub.asInterface(service);
|
||||
try {
|
||||
mPid = mChild.getPid();
|
||||
} catch (final RemoteException e) {
|
||||
Log.e(LOGTAG, "Failed to get child " + mType + " process PID. Process may have died.", e);
|
||||
}
|
||||
synchronized(this) {
|
||||
if (mWait) {
|
||||
mWait = false;
|
||||
this.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
if (mChild != null) {
|
||||
synchronized(INSTANCE.mConnections) {
|
||||
INSTANCE.mConnections.remove(mType);
|
||||
}
|
||||
mChild.asBinder().unlinkToDeath(this, 0);
|
||||
mChild = null;
|
||||
}
|
||||
synchronized(this) {
|
||||
if (mWait) {
|
||||
mWait = false;
|
||||
this.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void binderDied() {
|
||||
Log.e(LOGTAG,"Binder died. Attempt to unbind service: " + mType + " " + mPid);
|
||||
try {
|
||||
GeckoAppShell.getApplicationContext().unbindService(this);
|
||||
} catch (final java.lang.IllegalArgumentException e) {
|
||||
Log.e(LOGTAG,"Looks like connection was already unbound", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SimpleArrayMap<String, ChildConnection> mConnections;
|
||||
|
||||
private GeckoProcessManager() {
|
||||
mConnections = new SimpleArrayMap<String, ChildConnection>();
|
||||
}
|
||||
|
||||
public int start(String type, String[] args, int crashFd, int ipcFd) {
|
||||
ChildConnection connection = null;
|
||||
synchronized(mConnections) {
|
||||
connection = mConnections.get(type);
|
||||
}
|
||||
if (connection != null) {
|
||||
Log.w(LOGTAG, "Attempting to start a child process service that is already running. Attempting to kill existing process first");
|
||||
connection.prepareToWait();
|
||||
try {
|
||||
connection.mChild.stop();
|
||||
connection.waitForChild();
|
||||
} catch (final RemoteException e) {
|
||||
connection.clearWait();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
connection = new ChildConnection(type);
|
||||
Intent intent = new Intent();
|
||||
intent.setClassName(GeckoAppShell.getApplicationContext(),
|
||||
"org.mozilla.gecko.process.GeckoServiceChildProcess$" + type);
|
||||
GeckoLoader.addEnvironmentToIntent(intent);
|
||||
connection.prepareToWait();
|
||||
GeckoAppShell.getApplicationContext().bindService(intent, connection, Context.BIND_AUTO_CREATE);
|
||||
connection.waitForChild();
|
||||
if (connection.mChild == null) {
|
||||
// FAILED TO CONNECT.
|
||||
Log.e(LOGTAG, "Failed to connect to child process of '" + type + "'");
|
||||
GeckoAppShell.getApplicationContext().unbindService(connection);
|
||||
return 0;
|
||||
}
|
||||
ParcelFileDescriptor crashPfd = null;
|
||||
if (crashFd >= 0) {
|
||||
crashPfd = ParcelFileDescriptor.fromFd(crashFd);
|
||||
}
|
||||
ParcelFileDescriptor ipcPfd = ParcelFileDescriptor.fromFd(ipcFd);
|
||||
connection.mChild.start(args, crashPfd, ipcPfd);
|
||||
if (crashPfd != null) {
|
||||
crashPfd.close();
|
||||
}
|
||||
ipcPfd.close();
|
||||
synchronized(mConnections) {
|
||||
mConnections.put(type, connection);
|
||||
}
|
||||
return connection.mPid;
|
||||
} catch (final RemoteException e) {
|
||||
Log.e(LOGTAG, "Unable to create child process for: '" + type + "'. Remote Exception:", e);
|
||||
} catch (final IOException e) {
|
||||
Log.e(LOGTAG, "Unable to create child process for: '" + type + "'. Error creating ParcelFileDescriptor needed to create intent:", e);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // GeckoProcessManager
|
@ -0,0 +1,101 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.process;
|
||||
|
||||
import org.mozilla.gecko.annotation.JNITarget;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.mozglue.GeckoLoader;
|
||||
import org.mozilla.gecko.GeckoThread;
|
||||
import org.mozilla.gecko.mozglue.SafeIntent;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.Process;
|
||||
import android.util.Log;
|
||||
|
||||
public class GeckoServiceChildProcess extends Service {
|
||||
|
||||
static private String LOGTAG = "GeckoServiceChildProcess";
|
||||
|
||||
private boolean serviceStarted;
|
||||
|
||||
static private void stop() {
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Process.killProcess(Process.myPid());;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
}
|
||||
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
public int onStartCommand(final Intent intent, final int flags, final int startId) {
|
||||
return Service.START_STICKY;
|
||||
}
|
||||
|
||||
private Binder mBinder = new IChildProcess.Stub() {
|
||||
@Override
|
||||
public void stop() {
|
||||
GeckoServiceChildProcess.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPid() {
|
||||
return Process.myPid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(final String[] args,
|
||||
final ParcelFileDescriptor crashReporterPfd,
|
||||
final ParcelFileDescriptor ipcPfd) {
|
||||
if (serviceStarted) {
|
||||
Log.e(LOGTAG, "Attempting to start a service that has already been started.");
|
||||
return;
|
||||
}
|
||||
serviceStarted = true;
|
||||
final int crashReporterFd = crashReporterPfd != null ? crashReporterPfd.detachFd() : -1;
|
||||
final int ipcFd = ipcPfd != null ? ipcPfd.detachFd() : -1;
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
GeckoAppShell.ensureCrashHandling();
|
||||
GeckoAppShell.setApplicationContext(getApplicationContext());
|
||||
if (GeckoThread.initChildProcess(null, args, crashReporterFd, ipcFd, false)) {
|
||||
GeckoThread.launch();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
public IBinder onBind(final Intent intent) {
|
||||
GeckoLoader.setLastIntent(new SafeIntent(intent));
|
||||
return mBinder;
|
||||
}
|
||||
|
||||
public boolean onUnbind(Intent intent) {
|
||||
Log.i(LOGTAG, "Service has been unbound. Stopping.");
|
||||
stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
@JNITarget
|
||||
static public final class geckomediaplugin extends GeckoServiceChildProcess {}
|
||||
|
||||
@JNITarget
|
||||
static public final class tab extends GeckoServiceChildProcess {}
|
||||
}
|
@ -405,30 +405,86 @@ Java_org_mozilla_gecko_mozglue_GeckoLoader_loadNSSLibsNative(JNIEnv *jenv, jclas
|
||||
jenv->ReleaseStringUTFChars(jApkName, str);
|
||||
}
|
||||
|
||||
typedef void (*GeckoStart_t)(JNIEnv*, char*, const nsXREAppData*);
|
||||
|
||||
extern "C" NS_EXPORT void MOZ_JNICALL
|
||||
Java_org_mozilla_gecko_mozglue_GeckoLoader_nativeRun(JNIEnv *jenv, jclass jc, jstring jargs)
|
||||
static char**
|
||||
CreateArgvFromObjectArray(JNIEnv *jenv, jobjectArray jargs, int* length)
|
||||
{
|
||||
GeckoStart_t GeckoStart;
|
||||
xul_dlsym("GeckoStart", &GeckoStart);
|
||||
if (GeckoStart == nullptr)
|
||||
return;
|
||||
// XXX: java doesn't give us true UTF8, we should figure out something
|
||||
// better to do here
|
||||
int len = jenv->GetStringUTFLength(jargs);
|
||||
// GeckoStart needs to write in the args buffer, so we need a copy.
|
||||
char *args = (char *) malloc(len + 1);
|
||||
jenv->GetStringUTFRegion(jargs, 0, len, args);
|
||||
args[len] = '\0';
|
||||
ElfLoader::Singleton.ExpectShutdown(false);
|
||||
GeckoStart(jenv, args, &sAppData);
|
||||
ElfLoader::Singleton.ExpectShutdown(true);
|
||||
free(args);
|
||||
size_t stringCount = jenv->GetArrayLength(jargs);
|
||||
|
||||
if (length) {
|
||||
*length = stringCount;
|
||||
}
|
||||
|
||||
if (!stringCount) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
char** argv = new char*[stringCount + 1];
|
||||
|
||||
argv[stringCount] = nullptr;
|
||||
|
||||
for (size_t ix = 0; ix < stringCount; ix++) {
|
||||
jstring string = (jstring) (jenv->GetObjectArrayElement(jargs, ix));
|
||||
const char* rawString = jenv->GetStringUTFChars(string, nullptr);
|
||||
const int strLength = jenv->GetStringUTFLength(string);
|
||||
argv[ix] = strndup(rawString, strLength);
|
||||
jenv->ReleaseStringUTFChars(string, rawString);
|
||||
jenv->DeleteLocalRef(string);
|
||||
}
|
||||
|
||||
return argv;
|
||||
}
|
||||
|
||||
static void
|
||||
FreeArgv(char** argv, int argc)
|
||||
{
|
||||
for (int ix=0; ix < argc; ix++) {
|
||||
// String was allocated with strndup, so need to use free to deallocate.
|
||||
free(argv[ix]);
|
||||
}
|
||||
delete[](argv);
|
||||
}
|
||||
|
||||
typedef void (*GeckoStart_t)(JNIEnv*, char**, int, const nsXREAppData*);
|
||||
typedef int GeckoProcessType;
|
||||
|
||||
extern "C" NS_EXPORT void MOZ_JNICALL
|
||||
Java_org_mozilla_gecko_mozglue_GeckoLoader_nativeRun(JNIEnv *jenv, jclass jc, jobjectArray jargs, int crashFd, int ipcFd)
|
||||
{
|
||||
int argc = 0;
|
||||
char** argv = CreateArgvFromObjectArray(jenv, jargs, &argc);
|
||||
|
||||
if (ipcFd < 0) {
|
||||
GeckoStart_t GeckoStart;
|
||||
xul_dlsym("GeckoStart", &GeckoStart);
|
||||
|
||||
if (GeckoStart == nullptr) {
|
||||
FreeArgv(argv, argc);
|
||||
return;
|
||||
}
|
||||
|
||||
ElfLoader::Singleton.ExpectShutdown(false);
|
||||
GeckoStart(jenv, argv, argc, &sAppData);
|
||||
ElfLoader::Singleton.ExpectShutdown(true);
|
||||
} else {
|
||||
void (*fXRE_SetAndroidChildFds)(int, int);
|
||||
xul_dlsym("XRE_SetAndroidChildFds", &fXRE_SetAndroidChildFds);
|
||||
|
||||
void (*fXRE_SetProcessType)(char*);
|
||||
xul_dlsym("XRE_SetProcessType", &fXRE_SetProcessType);
|
||||
|
||||
mozglueresult (*fXRE_InitChildProcess)(int, char**, void*);
|
||||
xul_dlsym("XRE_InitChildProcess", &fXRE_InitChildProcess);
|
||||
|
||||
fXRE_SetAndroidChildFds(crashFd, ipcFd);
|
||||
fXRE_SetProcessType(argv[argc - 1]);
|
||||
|
||||
XREChildData childData;
|
||||
fXRE_InitChildProcess(argc - 1, argv, &childData);
|
||||
}
|
||||
|
||||
FreeArgv(argv, argc);
|
||||
}
|
||||
|
||||
extern "C" NS_EXPORT mozglueresult
|
||||
ChildProcessInit(int argc, char* argv[])
|
||||
{
|
||||
|
@ -22,7 +22,7 @@
|
||||
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, MOZ_APP_NAME, args)
|
||||
|
||||
extern "C" NS_EXPORT void
|
||||
GeckoStart(JNIEnv* env, char* data, const nsXREAppData* appData)
|
||||
GeckoStart(JNIEnv* env, char** argv, int argc, const nsXREAppData* appData)
|
||||
{
|
||||
mozilla::jni::SetGeckoThreadEnv(env);
|
||||
|
||||
@ -35,20 +35,12 @@ GeckoStart(JNIEnv* env, char* data, const nsXREAppData* appData)
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!data) {
|
||||
if (!argv) {
|
||||
LOG("Failed to get arguments for GeckoStart\n");
|
||||
return;
|
||||
}
|
||||
|
||||
nsTArray<char *> targs;
|
||||
char *arg = strtok(data, " ");
|
||||
while (arg) {
|
||||
targs.AppendElement(arg);
|
||||
arg = strtok(nullptr, " ");
|
||||
}
|
||||
targs.AppendElement(static_cast<char *>(nullptr));
|
||||
|
||||
int result = XRE_main(targs.Length() - 1, targs.Elements(), appData, 0);
|
||||
int result = XRE_main(argc, argv, appData, 0);
|
||||
|
||||
if (result)
|
||||
LOG("XRE_main returned %d", result);
|
||||
|
@ -50,6 +50,9 @@
|
||||
#include "base/message_loop.h"
|
||||
#include "base/process_util.h"
|
||||
#include "chrome/common/child_process.h"
|
||||
#if defined(MOZ_WIDGET_ANDROID)
|
||||
#include "chrome/common/ipc_channel.h"
|
||||
#endif // defined(MOZ_WIDGET_ANDROID)
|
||||
|
||||
#include "mozilla/ipc/BrowserProcessSubThread.h"
|
||||
#include "mozilla/ipc/GeckoChildProcessHost.h"
|
||||
@ -222,6 +225,17 @@ GeckoProcessType sChildProcessType = GeckoProcessType_Default;
|
||||
} // namespace startup
|
||||
} // namespace mozilla
|
||||
|
||||
#if defined(MOZ_WIDGET_ANDROID)
|
||||
void
|
||||
XRE_SetAndroidChildFds (int crashFd, int ipcFd)
|
||||
{
|
||||
#if defined(MOZ_CRASHREPORTER)
|
||||
CrashReporter::SetNotificationPipeForChild(crashFd);
|
||||
#endif // defined(MOZ_CRASHREPORTER)
|
||||
IPC::Channel::SetClientChannelFd(ipcFd);
|
||||
}
|
||||
#endif // defined(MOZ_WIDGET_ANDROID)
|
||||
|
||||
void
|
||||
XRE_SetProcessType(const char* aProcessTypeString)
|
||||
{
|
||||
|
@ -423,6 +423,11 @@ static_assert(MOZ_ARRAY_LENGTH(kGeckoProcessTypeString) ==
|
||||
XRE_API(const char*,
|
||||
XRE_ChildProcessTypeToString, (GeckoProcessType aProcessType))
|
||||
|
||||
#if defined(MOZ_WIDGET_ANDROID)
|
||||
XRE_API(void,
|
||||
XRE_SetAndroidChildFds, (int crashFd, int ipcFd))
|
||||
#endif // defined(MOZ_WIDGET_ANDROID)
|
||||
|
||||
XRE_API(void,
|
||||
XRE_SetProcessType, (const char* aProcessTypeString))
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user