diff --git a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java index e61fa93217b5..5fa16673481e 100644 --- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java +++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java @@ -1220,8 +1220,8 @@ public abstract class GeckoApp final String args = intent.getStringExtra("args"); sAlreadyLoaded = true; - GeckoThread.init(/* profile */ null, args, action, - /* debugging */ ACTION_DEBUG.equals(action)); + GeckoThread.initMainProcess(/* profile */ null, args, + /* debugging */ ACTION_DEBUG.equals(action)); // Speculatively pre-fetch the profile in the background. ThreadUtils.postToBackgroundThread(new Runnable() { diff --git a/mobile/android/base/java/org/mozilla/gecko/GeckoService.java b/mobile/android/base/java/org/mozilla/gecko/GeckoService.java index 44c92c062981..05c1f6992c31 100644 --- a/mobile/android/base/java/org/mozilla/gecko/GeckoService.java +++ b/mobile/android/base/java/org/mozilla/gecko/GeckoService.java @@ -150,7 +150,8 @@ public class GeckoService extends Service { throw new IllegalArgumentException("Intent must specify profile."); } - if (!GeckoThread.initWithProfile(profileName, profileDir != null ? new File(profileDir) : null)) { + if (!GeckoThread.initMainProcessWithProfile( + profileName, profileDir != null ? new File(profileDir) : null)) { Log.w(LOGTAG, "Ignoring due to profile mismatch: " + profileName + " [" + profileDir + ']'); diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoProfile.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoProfile.java index 70140a250cf7..b54df2d76bb4 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoProfile.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoProfile.java @@ -83,7 +83,6 @@ public final class GeckoProfile { private final String mName; private final File mMozillaDir; - private final Context mApplicationContext; private Object mData; @@ -320,7 +319,6 @@ public final class GeckoProfile { throw new IllegalArgumentException("Custom profile must have a directory"); } - mApplicationContext = context.getApplicationContext(); mName = profileName; mMozillaDir = GeckoProfileDirectories.getMozillaDirectory(context); diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java index 5d5e5e9a2abb..90b47815b74b 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java @@ -125,52 +125,64 @@ public class GeckoThread extends Thread { } }; - private static GeckoThread sGeckoThread; + private static final GeckoThread INSTANCE = new GeckoThread(); @WrapForJNI private static final ClassLoader clsLoader = GeckoThread.class.getClassLoader(); @WrapForJNI private static MessageQueue msgQueue; + private boolean mInitialized; + private String[] mArgs; + + // Main process parameters private GeckoProfile mProfile; + private String mExtraArgs; + private boolean mDebugging; - private final String mArgs; - private final String mAction; - private final boolean mDebugging; - - private String[] mChildProcessArgs; + // Child process parameters 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; - + GeckoThread() { setName("Gecko"); } - public static boolean init(GeckoProfile profile, String args, String action, boolean debugging) { - ThreadUtils.assertOnUiThread(); - if (isState(State.INITIAL) && sGeckoThread == null) { - sGeckoThread = new GeckoThread(profile, args, action, debugging); - return true; - } - return false; + private boolean isChildProcess() { + return mIPCFileDescriptor != -1; } - 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; + private synchronized boolean init(final GeckoProfile profile, final String[] args, + final String extraArgs, final boolean debugging, + final int crashFd, final int ipcFd) { + ThreadUtils.assertOnUiThread(); + + if (mInitialized) { + return false; } - return false; + + mProfile = profile; + mArgs = args; + mExtraArgs = extraArgs; + mDebugging = debugging; + mCrashFileDescriptor = crashFd; + mIPCFileDescriptor = ipcFd; + + mInitialized = true; + notifyAll(); + return true; + } + + public static boolean initMainProcess(final GeckoProfile profile, final String extraArgs, + final boolean debugging) { + return INSTANCE.init(profile, /* args */ null, extraArgs, debugging, + /* crashFd */ -1, /* ipcFd */ -1); + } + + public static boolean initChildProcess(final String[] args, final int crashFd, + final int ipcFd) { + return INSTANCE.init(/* profile */ null, args, /* extraArgs */ null, + /* debugging */ false, crashFd, ipcFd); } private static boolean canUseProfile(final Context context, final GeckoProfile profile, @@ -203,7 +215,8 @@ public class GeckoThread extends Thread { profileName, profileDir); } - public static boolean initWithProfile(final String profileName, final File profileDir) { + public static boolean initMainProcessWithProfile(final String profileName, + final File profileDir) { if (profileName == null) { throw new IllegalArgumentException("Null profile name"); } @@ -222,14 +235,15 @@ public class GeckoThread extends Thread { } // We haven't initialized yet; okay to initialize now. - return init(GeckoProfile.get(context, profileName, profileDir), - /* args */ null, /* action */ null, /* debugging */ false); + return initMainProcess(GeckoProfile.get(context, profileName, profileDir), + /* args */ null, /* debugging */ false); } public static boolean launch() { ThreadUtils.assertOnUiThread(); + if (checkAndSetState(State.INITIAL, State.LAUNCHED)) { - sGeckoThread.start(); + INSTANCE.start(); return true; } return false; @@ -397,7 +411,7 @@ public class GeckoThread extends Thread { GeckoLoader.loadGeckoLibs(context, resourcePath); } - private static String initGeckoEnvironment() { + private static void initGeckoEnvironment() { final Context context = GeckoAppShell.getApplicationContext(); GeckoLoader.loadMozGlue(context); setState(State.MOZGLUE_READY); @@ -437,48 +451,41 @@ public class GeckoThread extends Thread { } setState(State.LIBS_READY); - return resourcePath; } - private void addCustomProfileArg(String args, ArrayList list) { - // Make sure a profile exists. - final GeckoProfile profile = getProfile(); - profile.getDir(); // call the lazy initializer - - 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); - } - } - - // 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) { - // argv[0] is the program name, which for us is the package name. + private String[] getMainProcessArgs() { final Context context = GeckoAppShell.getApplicationContext(); final ArrayList args = new ArrayList(); + + // argv[0] is the program name, which for us is the package name. args.add(context.getPackageName()); args.add("-greomni"); - args.add(apkPath); + args.add(context.getPackageResourcePath()); - addCustomProfileArg(mArgs, args); + final GeckoProfile profile = getProfile(); + if (profile.isCustomProfile()) { + args.add("-profile"); + args.add(profile.getDir().getAbsolutePath()); + } else { + profile.getDir(); // Make sure the profile dir exists. + args.add("-P"); + args.add(profile.getName()); + } + + if (mExtraArgs != null) { + final StringTokenizer st = new StringTokenizer(mExtraArgs); + while (st.hasMoreTokens()) { + final String token = st.nextToken(); + if ("-P".equals(token) || "-profile".equals(token)) { + // Skip -P and -profile arguments because we added them above. + if (st.hasMoreTokens()) { + st.nextToken(); + } + continue; + } + args.add(token); + } + } // In un-official builds, we want to load Javascript resources fresh // with each build. In official builds, the startup cache is purged by @@ -495,20 +502,20 @@ public class GeckoThread extends Thread { } public static GeckoProfile getActiveProfile() { - if (sGeckoThread == null) { - return null; - } - final GeckoProfile profile = sGeckoThread.mProfile; - if (profile != null) { - return profile; - } - return sGeckoThread.getProfile(); + return INSTANCE.getProfile(); } public synchronized GeckoProfile getProfile() { + if (!mInitialized) { + return null; + } + if (isChildProcess()) { + throw new UnsupportedOperationException( + "Cannot access profile from child process"); + } if (mProfile == null) { final Context context = GeckoAppShell.getApplicationContext(); - mProfile = GeckoProfile.initFromArgs(context, mArgs); + mProfile = GeckoProfile.initFromArgs(context, mExtraArgs); } return mProfile; } @@ -536,20 +543,7 @@ public class GeckoThread extends Thread { }; Looper.myQueue().addIdleHandler(idleHandler); - if (mDebugging) { - try { - Thread.sleep(5 * 1000 /* 5 seconds */); - } catch (final InterruptedException e) { - } - } - - final String[] args; - if (mChildProcessArgs != null) { - initGeckoEnvironment(); - args = mChildProcessArgs; - } else { - args = getGeckoArgs(initGeckoEnvironment()); - } + initGeckoEnvironment(); // This can only happen after the call to initGeckoEnvironment // above, because otherwise the JNI code hasn't been loaded yet. @@ -559,11 +553,30 @@ public class GeckoThread extends Thread { } }); + // Wait until initialization before calling Gecko entry point. + synchronized (this) { + while (!mInitialized) { + try { + wait(); + } catch (final InterruptedException e) { + } + } + } + + final String[] args = isChildProcess() ? mArgs : getMainProcessArgs(); + + if (mDebugging) { + try { + Thread.sleep(5 * 1000 /* 5 seconds */); + } catch (final InterruptedException e) { + } + } + Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - runGecko"); final GeckoAppShell.GeckoInterface gi = GeckoAppShell.getGeckoInterface(); if (gi == null || !gi.isOfficial()) { - Log.i(LOGTAG, "RunGecko - args = " + args); + Log.i(LOGTAG, "RunGecko - args = " + TextUtils.join(" ", args)); } // And go. diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoServiceChildProcess.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoServiceChildProcess.java index 42971a15ae5f..3b98cf71cf42 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoServiceChildProcess.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoServiceChildProcess.java @@ -74,7 +74,7 @@ public class GeckoServiceChildProcess extends Service { public void run() { GeckoAppShell.ensureCrashHandling(); GeckoAppShell.setApplicationContext(getApplicationContext()); - if (GeckoThread.initChildProcess(null, args, crashReporterFd, ipcFd, false)) { + if (GeckoThread.initChildProcess(args, crashReporterFd, ipcFd)) { GeckoThread.launch(); } }