mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1214338 - Implement Android GCM-based PushService protocol. r=rnewman r=kitcambridge
MozReview-Commit-ID: 1KV7CZBuosx
This commit is contained in:
parent
6a26062006
commit
99ade81c39
@ -10,19 +10,26 @@ const Ci = Components.interfaces;
|
|||||||
const Cu = Components.utils;
|
const Cu = Components.utils;
|
||||||
const Cr = Components.results;
|
const Cr = Components.results;
|
||||||
|
|
||||||
const {PushDB} = Cu.import("resource://gre/modules/PushDB.jsm");
|
Cu.import("resource://gre/modules/AppConstants.jsm");
|
||||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
||||||
Cu.import("resource://gre/modules/Services.jsm");
|
|
||||||
Cu.import("resource://gre/modules/Timer.jsm");
|
|
||||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||||
Cu.import("resource://gre/modules/Promise.jsm");
|
Cu.import("resource://gre/modules/Promise.jsm");
|
||||||
|
Cu.import("resource://gre/modules/Services.jsm");
|
||||||
|
Cu.import("resource://gre/modules/Timer.jsm");
|
||||||
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
|
|
||||||
const {PushServiceWebSocket} = Cu.import("resource://gre/modules/PushServiceWebSocket.jsm");
|
|
||||||
const {PushServiceHttp2} = Cu.import("resource://gre/modules/PushServiceHttp2.jsm");
|
|
||||||
const {PushCrypto} = Cu.import("resource://gre/modules/PushCrypto.jsm");
|
const {PushCrypto} = Cu.import("resource://gre/modules/PushCrypto.jsm");
|
||||||
|
const {PushDB} = Cu.import("resource://gre/modules/PushDB.jsm");
|
||||||
|
|
||||||
// Currently supported protocols: WebSocket.
|
const CONNECTION_PROTOCOLS = (function() {
|
||||||
const CONNECTION_PROTOCOLS = [PushServiceWebSocket, PushServiceHttp2];
|
if ('android' != AppConstants.MOZ_WIDGET_TOOLKIT) {
|
||||||
|
const {PushServiceWebSocket} = Cu.import("resource://gre/modules/PushServiceWebSocket.jsm");
|
||||||
|
const {PushServiceHttp2} = Cu.import("resource://gre/modules/PushServiceHttp2.jsm");
|
||||||
|
return [PushServiceWebSocket, PushServiceHttp2];
|
||||||
|
} else {
|
||||||
|
const {PushServiceAndroidGCM} = Cu.import("resource://gre/modules/PushServiceAndroidGCM.jsm");
|
||||||
|
return [PushServiceAndroidGCM];
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "AlarmService",
|
XPCOMUtils.defineLazyModuleGetter(this, "AlarmService",
|
||||||
"resource://gre/modules/AlarmService.jsm");
|
"resource://gre/modules/AlarmService.jsm");
|
||||||
|
255
dom/push/PushServiceAndroidGCM.jsm
Normal file
255
dom/push/PushServiceAndroidGCM.jsm
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
/* jshint moz: true, esnext: true */
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const Cc = Components.classes;
|
||||||
|
const Ci = Components.interfaces;
|
||||||
|
const Cu = Components.utils;
|
||||||
|
const Cr = Components.results;
|
||||||
|
|
||||||
|
const {PushDB} = Cu.import("resource://gre/modules/PushDB.jsm");
|
||||||
|
const {PushRecord} = Cu.import("resource://gre/modules/PushRecord.jsm");
|
||||||
|
Cu.import("resource://gre/modules/Messaging.jsm"); /*global: Services */
|
||||||
|
Cu.import("resource://gre/modules/Services.jsm"); /*global: Services */
|
||||||
|
Cu.import("resource://gre/modules/Preferences.jsm"); /*global: Preferences */
|
||||||
|
Cu.import("resource://gre/modules/Promise.jsm"); /*global: Promise */
|
||||||
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); /*global: XPCOMUtils */
|
||||||
|
|
||||||
|
const Log = Cu.import("resource://gre/modules/AndroidLog.jsm", {}).AndroidLog.bind("Push");
|
||||||
|
|
||||||
|
const {
|
||||||
|
PushCrypto,
|
||||||
|
base64UrlDecode,
|
||||||
|
concatArray,
|
||||||
|
getCryptoParams,
|
||||||
|
} = Cu.import("resource://gre/modules/PushCrypto.jsm");
|
||||||
|
|
||||||
|
this.EXPORTED_SYMBOLS = ["PushServiceAndroidGCM"];
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyGetter(this, "console", () => {
|
||||||
|
let {ConsoleAPI} = Cu.import("resource://gre/modules/Console.jsm", {});
|
||||||
|
return new ConsoleAPI({
|
||||||
|
dump: Log.i,
|
||||||
|
maxLogLevelPref: "dom.push.loglevel",
|
||||||
|
prefix: "PushServiceAndroidGCM",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const kPUSHANDROIDGCMDB_DB_NAME = "pushAndroidGCM";
|
||||||
|
const kPUSHANDROIDGCMDB_DB_VERSION = 5; // Change this if the IndexedDB format changes
|
||||||
|
const kPUSHANDROIDGCMDB_STORE_NAME = "pushAndroidGCM";
|
||||||
|
|
||||||
|
const prefs = new Preferences("dom.push.");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The implementation of WebPush push backed by Android's GCM
|
||||||
|
* delivery.
|
||||||
|
*/
|
||||||
|
this.PushServiceAndroidGCM = {
|
||||||
|
_mainPushService: null,
|
||||||
|
_serverURI: null,
|
||||||
|
|
||||||
|
newPushDB: function() {
|
||||||
|
return new PushDB(kPUSHANDROIDGCMDB_DB_NAME,
|
||||||
|
kPUSHANDROIDGCMDB_DB_VERSION,
|
||||||
|
kPUSHANDROIDGCMDB_STORE_NAME,
|
||||||
|
"channelID",
|
||||||
|
PushRecordAndroidGCM);
|
||||||
|
},
|
||||||
|
|
||||||
|
serviceType: function() {
|
||||||
|
return "AndroidGCM";
|
||||||
|
},
|
||||||
|
|
||||||
|
validServerURI: function(serverURI) {
|
||||||
|
if (!serverURI) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serverURI.scheme == "https") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (prefs.get("debug") && serverURI.scheme == "http") {
|
||||||
|
// Accept HTTP endpoints when debugging.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
console.info("Unsupported Android GCM dom.push.serverURL scheme", serverURI.scheme);
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
observe: function(subject, topic, data) {
|
||||||
|
if (topic == "nsPref:changed") {
|
||||||
|
if (data == "dom.push.debug") {
|
||||||
|
// Reconfigure.
|
||||||
|
let debug = !!prefs.get("debug");
|
||||||
|
console.info("Debug parameter changed; updating configuration with new debug", debug);
|
||||||
|
this._configure(this._serverURI, debug);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (topic == "PushServiceAndroidGCM:ReceivedPushMessage") {
|
||||||
|
// TODO: Use Messaging.jsm for this.
|
||||||
|
if (this._mainPushService == null) {
|
||||||
|
// Shouldn't ever happen, but let's be careful.
|
||||||
|
console.error("No main PushService! Dropping message.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!data) {
|
||||||
|
console.error("No data from Java! Dropping message.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data = JSON.parse(data);
|
||||||
|
console.debug("ReceivedPushMessage with data", data);
|
||||||
|
|
||||||
|
// Default is no data (and no encryption).
|
||||||
|
let message = null;
|
||||||
|
let cryptoParams = null;
|
||||||
|
|
||||||
|
if (data.message && data.enc && (data.enckey || data.cryptokey)) {
|
||||||
|
let headers = {
|
||||||
|
encryption_key: data.enckey,
|
||||||
|
crypto_key: data.cryptokey,
|
||||||
|
encryption: data.enc,
|
||||||
|
};
|
||||||
|
cryptoParams = getCryptoParams(headers);
|
||||||
|
// Ciphertext is (urlsafe) Base 64 encoded.
|
||||||
|
message = base64UrlDecode(data.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.debug("Delivering message to main PushService:", message, cryptoParams);
|
||||||
|
this._mainPushService.receivedPushMessage(
|
||||||
|
data.channelID, message, cryptoParams, (record) => {
|
||||||
|
// Always update the stored record.
|
||||||
|
return record;
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_configure: function(serverURL, debug) {
|
||||||
|
return Messaging.sendRequestForResult({
|
||||||
|
type: "PushServiceAndroidGCM:Configure",
|
||||||
|
endpoint: serverURL.spec,
|
||||||
|
debug: debug,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
init: function(options, mainPushService, serverURL) {
|
||||||
|
console.debug("init()");
|
||||||
|
this._mainPushService = mainPushService;
|
||||||
|
this._serverURI = serverURL;
|
||||||
|
|
||||||
|
prefs.observe("debug", this);
|
||||||
|
Services.obs.addObserver(this, "PushServiceAndroidGCM:ReceivedPushMessage", false);
|
||||||
|
|
||||||
|
return this._configure(serverURL, !!prefs.get("debug"));
|
||||||
|
},
|
||||||
|
|
||||||
|
uninit: function() {
|
||||||
|
console.debug("uninit()");
|
||||||
|
this._mainPushService = null;
|
||||||
|
Services.obs.removeObserver(this, "PushServiceAndroidGCM:ReceivedPushMessage");
|
||||||
|
prefs.ignore("debug", this);
|
||||||
|
},
|
||||||
|
|
||||||
|
onAlarmFired: function() {
|
||||||
|
// No action required.
|
||||||
|
},
|
||||||
|
|
||||||
|
connect: function(records) {
|
||||||
|
console.debug("connect:", records);
|
||||||
|
// It's possible for the registration or subscriptions backing the
|
||||||
|
// PushService to not be registered with the underlying AndroidPushService.
|
||||||
|
// Expire those that are unrecognized.
|
||||||
|
return Messaging.sendRequestForResult({
|
||||||
|
type: "PushServiceAndroidGCM:DumpSubscriptions",
|
||||||
|
})
|
||||||
|
.then(subscriptions => {
|
||||||
|
console.debug("connect:", subscriptions);
|
||||||
|
// subscriptions maps chid => subscription data.
|
||||||
|
return Promise.all(records.map(record => {
|
||||||
|
if (subscriptions.hasOwnProperty(record.keyID)) {
|
||||||
|
console.debug("connect:", "hasOwnProperty", record.keyID);
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
console.debug("connect:", "!hasOwnProperty", record.keyID);
|
||||||
|
// Subscription is known to PushService.jsm but not to AndroidPushService. Drop it.
|
||||||
|
return this._mainPushService.dropRegistrationAndNotifyApp(record.keyID)
|
||||||
|
.catch(error => {
|
||||||
|
console.error("connect: Error dropping registration", record.keyID, error);
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
isConnected: function() {
|
||||||
|
return this._mainPushService != null;
|
||||||
|
},
|
||||||
|
|
||||||
|
disconnect: function() {
|
||||||
|
console.debug("disconnect");
|
||||||
|
},
|
||||||
|
|
||||||
|
request: function(action, record) {
|
||||||
|
switch (action) {
|
||||||
|
case "register":
|
||||||
|
console.debug("register:", record);
|
||||||
|
return this._register(record);
|
||||||
|
case "unregister":
|
||||||
|
console.debug("unregister: ", record);
|
||||||
|
return this._unregister(record);
|
||||||
|
default:
|
||||||
|
console.debug("Ignoring unrecognized request action:", action);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_register: function(record) {
|
||||||
|
let ctime = Date.now();
|
||||||
|
// Caller handles errors.
|
||||||
|
return Messaging.sendRequestForResult({
|
||||||
|
type: "PushServiceAndroidGCM:SubscribeChannel",
|
||||||
|
}).then(data => {
|
||||||
|
console.debug("Got data:", data);
|
||||||
|
return PushCrypto.generateKeys()
|
||||||
|
.then(exportedKeys =>
|
||||||
|
new PushRecordAndroidGCM({
|
||||||
|
// Straight from autopush.
|
||||||
|
channelID: data.channelID,
|
||||||
|
pushEndpoint: data.endpoint,
|
||||||
|
// Common to all PushRecord implementations.
|
||||||
|
scope: record.scope,
|
||||||
|
originAttributes: record.originAttributes,
|
||||||
|
ctime: ctime,
|
||||||
|
// Cryptography!
|
||||||
|
p256dhPublicKey: exportedKeys[0],
|
||||||
|
p256dhPrivateKey: exportedKeys[1],
|
||||||
|
authenticationSecret: PushCrypto.generateAuthenticationSecret(),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_unregister: function(record) {
|
||||||
|
return Messaging.sendRequestForResult({
|
||||||
|
type: "PushServiceAndroidGCM:UnsubscribeChannel",
|
||||||
|
channelID: record.keyID,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function PushRecordAndroidGCM(record) {
|
||||||
|
PushRecord.call(this, record);
|
||||||
|
this.channelID = record.channelID;
|
||||||
|
}
|
||||||
|
|
||||||
|
PushRecordAndroidGCM.prototype = Object.create(PushRecord.prototype, {
|
||||||
|
keyID: {
|
||||||
|
get() {
|
||||||
|
return this.channelID;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
@ -14,10 +14,20 @@ EXTRA_JS_MODULES += [
|
|||||||
'PushDB.jsm',
|
'PushDB.jsm',
|
||||||
'PushRecord.jsm',
|
'PushRecord.jsm',
|
||||||
'PushService.jsm',
|
'PushService.jsm',
|
||||||
'PushServiceHttp2.jsm',
|
|
||||||
'PushServiceWebSocket.jsm',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if CONFIG['MOZ_BUILD_APP'] != 'mobile/android':
|
||||||
|
# Everything but Fennec.
|
||||||
|
EXTRA_JS_MODULES += [
|
||||||
|
'PushServiceHttp2.jsm',
|
||||||
|
'PushServiceWebSocket.jsm',
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
# Fennec only.
|
||||||
|
EXTRA_JS_MODULES += [
|
||||||
|
'PushServiceAndroidGCM.jsm',
|
||||||
|
]
|
||||||
|
|
||||||
MOCHITEST_MANIFESTS += [
|
MOCHITEST_MANIFESTS += [
|
||||||
'test/mochitest.ini',
|
'test/mochitest.ini',
|
||||||
]
|
]
|
||||||
|
@ -175,6 +175,7 @@ public class GeckoApplication extends Application
|
|||||||
// network access, so is naturally asynchronous. This, of course, races against Gecko page load of
|
// network access, so is naturally asynchronous. This, of course, races against Gecko page load of
|
||||||
// content requiring GCM-backed services, like Web Push. There's nothing to be done here.
|
// content requiring GCM-backed services, like Web Push. There's nothing to be done here.
|
||||||
PushService.createInstance(context);
|
PushService.createInstance(context);
|
||||||
|
PushService.registerGeckoEventListener();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
PushService.getInstance().onStartup();
|
PushService.getInstance().onStartup();
|
||||||
|
@ -34,13 +34,23 @@ import java.util.Map;
|
|||||||
* <p/>
|
* <p/>
|
||||||
* It's worth noting that we allow the DOM push API in restricted profiles.
|
* It's worth noting that we allow the DOM push API in restricted profiles.
|
||||||
*/
|
*/
|
||||||
public class PushService {
|
public class PushService implements BundleEventListener {
|
||||||
private static final String LOG_TAG = "GeckoPushService";
|
private static final String LOG_TAG = "GeckoPushService";
|
||||||
|
|
||||||
public static final String SERVICE_WEBPUSH = "webpush";
|
public static final String SERVICE_WEBPUSH = "webpush";
|
||||||
|
|
||||||
private static PushService sInstance;
|
private static PushService sInstance;
|
||||||
|
|
||||||
|
private static final String[] GECKO_EVENTS = new String[]{
|
||||||
|
"PushServiceAndroidGCM:Configure",
|
||||||
|
"PushServiceAndroidGCM:DumpRegistration",
|
||||||
|
"PushServiceAndroidGCM:DumpSubscriptions",
|
||||||
|
"PushServiceAndroidGCM:RegisterUserAgent",
|
||||||
|
"PushServiceAndroidGCM:UnregisterUserAgent",
|
||||||
|
"PushServiceAndroidGCM:SubscribeChannel",
|
||||||
|
"PushServiceAndroidGCM:UnsubscribeChannel",
|
||||||
|
};
|
||||||
|
|
||||||
public static synchronized PushService getInstance() {
|
public static synchronized PushService getInstance() {
|
||||||
if (sInstance == null) {
|
if (sInstance == null) {
|
||||||
throw new IllegalStateException("PushService not yet created!");
|
throw new IllegalStateException("PushService not yet created!");
|
||||||
@ -87,7 +97,7 @@ public class PushService {
|
|||||||
try {
|
try {
|
||||||
pushManager.startup(System.currentTimeMillis());
|
pushManager.startup(System.currentTimeMillis());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(LOG_TAG, "Got exception during startup; ignoring.", e);
|
Log.e(LOG_TAG, "Got exception during refresh; ignoring.", e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -119,11 +129,169 @@ public class PushService {
|
|||||||
Log.i(LOG_TAG, "Message directed to service: " + subscription.service);
|
Log.i(LOG_TAG, "Message directed to service: " + subscription.service);
|
||||||
|
|
||||||
if (SERVICE_WEBPUSH.equals(subscription.service)) {
|
if (SERVICE_WEBPUSH.equals(subscription.service)) {
|
||||||
// Nothing yet.
|
if (subscription.serviceData == null) {
|
||||||
Log.i(LOG_TAG, "Message directed to unimplemented service; ignoring: " + subscription.service);
|
Log.e(LOG_TAG, "No serviceData found for chid: " + chid + "; ignoring dom/push message.");
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String profileName = subscription.serviceData.optString("profileName", null);
|
||||||
|
final String profilePath = subscription.serviceData.optString("profilePath", null);
|
||||||
|
if (profileName == null || profilePath == null) {
|
||||||
|
Log.e(LOG_TAG, "Corrupt serviceData found for chid: " + chid + "; ignoring dom/push message.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!GeckoThread.isRunning()) {
|
||||||
|
Log.w(LOG_TAG, "dom/push message received but no Gecko thread is running; ignoring message.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final GeckoAppShell.GeckoInterface geckoInterface = GeckoAppShell.getGeckoInterface();
|
||||||
|
if (geckoInterface == null) {
|
||||||
|
Log.w(LOG_TAG, "dom/push message received but no Gecko interface is registered; ignoring message.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final GeckoProfile profile = geckoInterface.getProfile();
|
||||||
|
if (profile == null || !profileName.equals(profile.getName()) || !profilePath.equals(profile.getDir().getAbsolutePath())) {
|
||||||
|
Log.w(LOG_TAG, "dom/push message received but Gecko is running with the wrong profile name or path; ignoring message.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DELIVERANCE!
|
||||||
|
final JSONObject data = new JSONObject();
|
||||||
|
try {
|
||||||
|
data.put("channelID", chid);
|
||||||
|
data.put("enc", bundle.getString("enc"));
|
||||||
|
// Only one of cryptokey (newer) and enckey (deprecated) should be set, but the
|
||||||
|
// Gecko handler will verify this.
|
||||||
|
data.put("cryptokey", bundle.getString("cryptokey"));
|
||||||
|
data.put("enckey", bundle.getString("enckey"));
|
||||||
|
data.put("message", bundle.getString("body"));
|
||||||
|
} catch (JSONException e) {
|
||||||
|
Log.e(LOG_TAG, "Got exception delivering dom/push message to Gecko!", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.i(LOG_TAG, "Delivering dom/push message to Gecko!");
|
||||||
|
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("PushServiceAndroidGCM:ReceivedPushMessage", data.toString()));
|
||||||
} else {
|
} else {
|
||||||
Log.e(LOG_TAG, "Message directed to unknown service; dropping: " + subscription.service);
|
Log.e(LOG_TAG, "Message directed to unknown service; dropping: " + subscription.service);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void registerGeckoEventListener() {
|
||||||
|
Log.d(LOG_TAG, "Registered Gecko event listener.");
|
||||||
|
EventDispatcher.getInstance().registerBackgroundThreadListener(getInstance(), GECKO_EVENTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void unregisterGeckoEventListener() {
|
||||||
|
Log.d(LOG_TAG, "Unregistered Gecko event listener.");
|
||||||
|
EventDispatcher.getInstance().unregisterBackgroundThreadListener(getInstance(), GECKO_EVENTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(final String event, final Bundle message, final EventCallback callback) {
|
||||||
|
Log.i(LOG_TAG, "Handling event: " + event);
|
||||||
|
ThreadUtils.assertOnBackgroundThread();
|
||||||
|
|
||||||
|
// We're invoked in response to a Gecko message on a background thread. We should always
|
||||||
|
// be able to safely retrieve the current Gecko profile.
|
||||||
|
final GeckoProfile geckoProfile = GeckoProfile.get(GeckoAppShell.getApplicationContext());
|
||||||
|
|
||||||
|
if (callback == null) {
|
||||||
|
Log.e(LOG_TAG, "callback must not be null in " + event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ("PushServiceAndroidGCM:Configure".equals(event)) {
|
||||||
|
final String endpoint = message.getString("endpoint");
|
||||||
|
if (endpoint == null) {
|
||||||
|
Log.e(LOG_TAG, "endpoint must not be null in " + event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final boolean debug = message.getBoolean("debug", false);
|
||||||
|
pushManager.configure(geckoProfile.getName(), endpoint, debug, System.currentTimeMillis()); // For side effects.
|
||||||
|
callback.sendSuccess(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ("PushServiceAndroidGCM:DumpRegistration".equals(event)) {
|
||||||
|
callback.sendError("Not yet implemented!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ("PushServiceAndroidGCM:DumpSubscriptions".equals(event)) {
|
||||||
|
try {
|
||||||
|
final Map<String, PushSubscription> result = pushManager.allSubscriptionsForProfile(geckoProfile.getName());
|
||||||
|
|
||||||
|
final JSONObject json = new JSONObject();
|
||||||
|
for (Map.Entry<String, PushSubscription> entry : result.entrySet()) {
|
||||||
|
json.put(entry.getKey(), entry.getValue().toJSONObject());
|
||||||
|
}
|
||||||
|
callback.sendSuccess(json);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
callback.sendError("Got exception handling message [" + event + "]: " + e.toString());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ("PushServiceAndroidGCM:RegisterUserAgent".equals(event)) {
|
||||||
|
try {
|
||||||
|
pushManager.registerUserAgent(geckoProfile.getName(), System.currentTimeMillis()); // For side-effects.
|
||||||
|
callback.sendSuccess(null);
|
||||||
|
} catch (PushManager.ProfileNeedsConfigurationException | AutopushClientException | PushClient.LocalException | IOException e) {
|
||||||
|
Log.e(LOG_TAG, "Got exception in " + event, e);
|
||||||
|
callback.sendError("Got exception handling message [" + event + "]: " + e.toString());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ("PushServiceAndroidGCM:UnregisterUserAgent".equals(event)) {
|
||||||
|
callback.sendError("Not yet implemented!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ("PushServiceAndroidGCM:SubscribeChannel".equals(event)) {
|
||||||
|
final String service = SERVICE_WEBPUSH;
|
||||||
|
final JSONObject serviceData;
|
||||||
|
try {
|
||||||
|
serviceData = new JSONObject();
|
||||||
|
serviceData.put("profileName", geckoProfile.getName());
|
||||||
|
serviceData.put("profilePath", geckoProfile.getDir().getAbsolutePath());
|
||||||
|
} catch (JSONException e) {
|
||||||
|
Log.e(LOG_TAG, "Got exception in " + event, e);
|
||||||
|
callback.sendError("Got exception handling message [" + event + "]: " + e.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final PushSubscription subscription;
|
||||||
|
try {
|
||||||
|
subscription = pushManager.subscribeChannel(geckoProfile.getName(), service, serviceData, System.currentTimeMillis());
|
||||||
|
} catch (PushManager.ProfileNeedsConfigurationException | AutopushClientException | PushClient.LocalException | IOException e) {
|
||||||
|
Log.e(LOG_TAG, "Got exception in " + event, e);
|
||||||
|
callback.sendError("Got exception handling message [" + event + "]: " + e.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final JSONObject json = new JSONObject();
|
||||||
|
try {
|
||||||
|
json.put("channelID", subscription.chid);
|
||||||
|
json.put("endpoint", subscription.webpushEndpoint);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
Log.e(LOG_TAG, "Got exception in " + event, e);
|
||||||
|
callback.sendError("Got exception handling message [" + event + "]: " + e.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callback.sendSuccess(json);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ("PushServiceAndroidGCM:UnsubscribeChannel".equals(event)) {
|
||||||
|
callback.sendError("Not yet implemented!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (GcmTokenClient.NeedsGooglePlayServicesException e) {
|
||||||
|
// TODO: improve this. Can we find a point where the user is *definitely* interacting
|
||||||
|
// with the WebPush? Perhaps we can show a dialog when interacting with the Push
|
||||||
|
// permissions, and then be more aggressive showing this notification when we have
|
||||||
|
// registrations and subscriptions that can't be advanced.
|
||||||
|
callback.sendError("To handle event [" + event + "], user interaction is needed to enable Google Play Services.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user