diff options
Diffstat (limited to 'mobile/android/base/java/org/mozilla/gecko/GeckoService.java')
-rw-r--r-- | mobile/android/base/java/org/mozilla/gecko/GeckoService.java | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/mobile/android/base/java/org/mozilla/gecko/GeckoService.java b/mobile/android/base/java/org/mozilla/gecko/GeckoService.java new file mode 100644 index 000000000..3a99fd2a1 --- /dev/null +++ b/mobile/android/base/java/org/mozilla/gecko/GeckoService.java @@ -0,0 +1,236 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; 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; + +import android.app.AlarmManager; +import android.app.Service; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.os.IBinder; +import android.os.Handler; +import android.os.Looper; +import android.util.Log; + +import java.io.File; + +import org.mozilla.gecko.util.NativeEventListener; +import org.mozilla.gecko.util.NativeJSObject; +import org.mozilla.gecko.util.EventCallback; + +public class GeckoService extends Service { + + private static final String LOGTAG = "GeckoService"; + private static final boolean DEBUG = false; + + private static final String INTENT_PROFILE_NAME = "org.mozilla.gecko.intent.PROFILE_NAME"; + private static final String INTENT_PROFILE_DIR = "org.mozilla.gecko.intent.PROFILE_DIR"; + + private static final String INTENT_ACTION_UPDATE_ADDONS = "update-addons"; + private static final String INTENT_ACTION_CREATE_SERVICES = "create-services"; + + private static final String INTENT_SERVICE_CATEGORY = "category"; + private static final String INTENT_SERVICE_DATA = "data"; + + private static class EventListener implements NativeEventListener { + @Override // NativeEventListener + public void handleMessage(final String event, + final NativeJSObject message, + final EventCallback callback) { + final Context context = GeckoAppShell.getApplicationContext(); + switch (event) { + case "Gecko:ScheduleRun": + if (DEBUG) { + Log.d(LOGTAG, "Scheduling " + message.getString("action") + + " @ " + message.getInt("interval") + "ms"); + } + + final Intent intent = getIntentForAction(context, message.getString("action")); + final PendingIntent pendingIntent = PendingIntent.getService( + context, /* requestCode */ 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); + + final AlarmManager am = (AlarmManager) + context.getSystemService(Context.ALARM_SERVICE); + // Cancel any previous alarm and schedule a new one. + am.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, + message.getInt("trigger"), + message.getInt("interval"), + pendingIntent); + break; + + default: + throw new UnsupportedOperationException(event); + } + } + } + + private static final EventListener EVENT_LISTENER = new EventListener(); + + public static void register() { + if (DEBUG) { + Log.d(LOGTAG, "Registered listener"); + } + EventDispatcher.getInstance().registerGeckoThreadListener(EVENT_LISTENER, + "Gecko:ScheduleRun"); + } + + public static void unregister() { + if (DEBUG) { + Log.d(LOGTAG, "Unregistered listener"); + } + EventDispatcher.getInstance().unregisterGeckoThreadListener(EVENT_LISTENER, + "Gecko:ScheduleRun"); + } + + @Override // Service + public void onCreate() { + GeckoAppShell.ensureCrashHandling(); + GeckoThread.onResume(); + super.onCreate(); + + if (DEBUG) { + Log.d(LOGTAG, "Created"); + } + } + + @Override // Service + public void onDestroy() { + GeckoThread.onPause(); + + // We want to block here if we can, so we don't get killed when Gecko is in the + // middle of handling onPause(). + if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) { + GeckoThread.waitOnGecko(); + } + + if (DEBUG) { + Log.d(LOGTAG, "Destroyed"); + } + super.onDestroy(); + } + + private static Intent getIntentForAction(final Context context, final String action) { + final Intent intent = new Intent(action, /* uri */ null, context, GeckoService.class); + final GeckoProfile profile = GeckoThread.getActiveProfile(); + if (profile != null) { + setIntentProfile(intent, profile.getName(), profile.getDir().getAbsolutePath()); + } + return intent; + } + + public static Intent getIntentToCreateServices(final Context context, final String category, final String data) { + final Intent intent = getIntentForAction(context, INTENT_ACTION_CREATE_SERVICES); + intent.putExtra(INTENT_SERVICE_CATEGORY, category); + intent.putExtra(INTENT_SERVICE_DATA, data); + return intent; + } + + public static Intent getIntentToCreateServices(final Context context, final String category) { + return getIntentToCreateServices(context, category, /* data */ null); + } + + public static void setIntentProfile(final Intent intent, final String profileName, + final String profileDir) { + intent.putExtra(INTENT_PROFILE_NAME, profileName); + intent.putExtra(INTENT_PROFILE_DIR, profileDir); + } + + private int handleIntent(final Intent intent, final int startId) { + if (DEBUG) { + Log.d(LOGTAG, "Handling " + intent.getAction()); + } + + final String profileName = intent.getStringExtra(INTENT_PROFILE_NAME); + final String profileDir = intent.getStringExtra(INTENT_PROFILE_DIR); + + if (profileName == null) { + throw new IllegalArgumentException("Intent must specify profile."); + } + + if (!GeckoThread.initWithProfile(profileName != null ? profileName : "", + profileDir != null ? new File(profileDir) : null)) { + Log.w(LOGTAG, "Ignoring due to profile mismatch: " + + profileName + " [" + profileDir + ']'); + + final GeckoProfile profile = GeckoThread.getActiveProfile(); + if (profile != null) { + Log.w(LOGTAG, "Current profile is " + profile.getName() + + " [" + profile.getDir().getAbsolutePath() + ']'); + } + stopSelf(startId); + return Service.START_NOT_STICKY; + } + + GeckoThread.launch(); + + switch (intent.getAction()) { + case INTENT_ACTION_UPDATE_ADDONS: + // Run the add-on update service. Because the service is automatically invoked + // when loading Gecko, we don't have to do anything else here. + break; + + case INTENT_ACTION_CREATE_SERVICES: + final String category = intent.getStringExtra(INTENT_SERVICE_CATEGORY); + final String data = intent.getStringExtra(INTENT_SERVICE_DATA); + + if (category == null) { + break; + } + GeckoThread.createServices(category, data); + break; + + default: + Log.w(LOGTAG, "Unknown request: " + intent); + } + + stopSelf(startId); + return Service.START_NOT_STICKY; + } + + @Override // Service + public int onStartCommand(final Intent intent, final int flags, final int startId) { + if (intent == null) { + return Service.START_NOT_STICKY; + } + try { + return handleIntent(intent, startId); + } catch (final Throwable e) { + Log.e(LOGTAG, "Cannot handle intent: " + intent, e); + return Service.START_NOT_STICKY; + } + } + + @Override // Service + public IBinder onBind(final Intent intent) { + return null; + } + + public static void startGecko(final GeckoProfile profile, final String args, final Context context) { + if (GeckoThread.isLaunched()) { + if (DEBUG) { + Log.v(LOGTAG, "already launched"); + } + return; + } + + Handler handler = new Handler(Looper.getMainLooper()); + handler.post(new Runnable() { + @Override + public void run() { + GeckoAppShell.ensureCrashHandling(); + GeckoAppShell.setApplicationContext(context); + GeckoThread.onResume(); + + GeckoThread.init(profile, args, null, false); + GeckoThread.launch(); + + if (DEBUG) { + Log.v(LOGTAG, "warmed up (launched)"); + } + } + }); + } +} |