summaryrefslogtreecommitdiffstats
path: root/dom/system/gonk/RadioInterfaceLayer.js
diff options
context:
space:
mode:
Diffstat (limited to 'dom/system/gonk/RadioInterfaceLayer.js')
-rw-r--r--dom/system/gonk/RadioInterfaceLayer.js1324
1 files changed, 1324 insertions, 0 deletions
diff --git a/dom/system/gonk/RadioInterfaceLayer.js b/dom/system/gonk/RadioInterfaceLayer.js
new file mode 100644
index 000000000..f5885db5d
--- /dev/null
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -0,0 +1,1324 @@
+/* Copyright 2012 Mozilla Foundation and Mozilla contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+"use strict";
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/Sntp.jsm");
+Cu.import("resource://gre/modules/systemlibs.js");
+Cu.import("resource://gre/modules/Promise.jsm");
+Cu.import("resource://gre/modules/FileUtils.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "RIL", function () {
+ let obj = {};
+ Cu.import("resource://gre/modules/ril_consts.js", obj);
+ return obj;
+});
+
+// Ril quirk to always turn the radio off for the client without SIM card
+// except hw default client.
+var RILQUIRKS_RADIO_OFF_WO_CARD =
+ libcutils.property_get("ro.moz.ril.radio_off_wo_card", "false") == "true";
+
+const RADIOINTERFACELAYER_CID =
+ Components.ID("{2d831c8d-6017-435b-a80c-e5d422810cea}");
+const RADIOINTERFACE_CID =
+ Components.ID("{6a7c91f0-a2b3-4193-8562-8969296c0b54}");
+
+const NS_XPCOM_SHUTDOWN_OBSERVER_ID = "xpcom-shutdown";
+const kNetworkConnStateChangedTopic = "network-connection-state-changed";
+const kMozSettingsChangedObserverTopic = "mozsettings-changed";
+const kSysMsgListenerReadyObserverTopic = "system-message-listener-ready";
+const kSysClockChangeObserverTopic = "system-clock-change";
+const kScreenStateChangedTopic = "screen-state-changed";
+
+const kSettingsClockAutoUpdateEnabled = "time.clock.automatic-update.enabled";
+const kSettingsClockAutoUpdateAvailable = "time.clock.automatic-update.available";
+const kSettingsTimezoneAutoUpdateEnabled = "time.timezone.automatic-update.enabled";
+const kSettingsTimezoneAutoUpdateAvailable = "time.timezone.automatic-update.available";
+
+const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
+
+const kPrefRilNumRadioInterfaces = "ril.numRadioInterfaces";
+const kPrefRilDebuggingEnabled = "ril.debugging.enabled";
+
+const RADIO_POWER_OFF_TIMEOUT = 30000;
+const HW_DEFAULT_CLIENT_ID = 0;
+
+const NETWORK_TYPE_WIFI = Ci.nsINetworkInfo.NETWORK_TYPE_WIFI;
+const NETWORK_TYPE_MOBILE = Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE;
+
+// set to true in ril_consts.js to see debug messages
+var DEBUG = RIL.DEBUG_RIL;
+
+function updateDebugFlag() {
+ // Read debug setting from pref
+ let debugPref;
+ try {
+ debugPref = Services.prefs.getBoolPref(kPrefRilDebuggingEnabled);
+ } catch (e) {
+ debugPref = false;
+ }
+ DEBUG = RIL.DEBUG_RIL || debugPref;
+}
+updateDebugFlag();
+
+function debug(s) {
+ dump("-*- RadioInterfaceLayer: " + s + "\n");
+}
+
+XPCOMUtils.defineLazyServiceGetter(this, "gIccService",
+ "@mozilla.org/icc/gonkiccservice;1",
+ "nsIGonkIccService");
+
+XPCOMUtils.defineLazyServiceGetter(this, "gMobileMessageService",
+ "@mozilla.org/mobilemessage/mobilemessageservice;1",
+ "nsIMobileMessageService");
+
+XPCOMUtils.defineLazyServiceGetter(this, "gSmsService",
+ "@mozilla.org/sms/gonksmsservice;1",
+ "nsIGonkSmsService");
+
+XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
+ "@mozilla.org/parentprocessmessagemanager;1",
+ "nsIMessageBroadcaster");
+
+XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService",
+ "@mozilla.org/settingsService;1",
+ "nsISettingsService");
+
+XPCOMUtils.defineLazyServiceGetter(this, "gNetworkManager",
+ "@mozilla.org/network/manager;1",
+ "nsINetworkManager");
+
+XPCOMUtils.defineLazyServiceGetter(this, "gTimeService",
+ "@mozilla.org/time/timeservice;1",
+ "nsITimeService");
+
+XPCOMUtils.defineLazyServiceGetter(this, "gSystemWorkerManager",
+ "@mozilla.org/telephony/system-worker-manager;1",
+ "nsISystemWorkerManager");
+
+XPCOMUtils.defineLazyServiceGetter(this, "gTelephonyService",
+ "@mozilla.org/telephony/telephonyservice;1",
+ "nsIGonkTelephonyService");
+
+XPCOMUtils.defineLazyServiceGetter(this, "gMobileConnectionService",
+ "@mozilla.org/mobileconnection/mobileconnectionservice;1",
+ "nsIGonkMobileConnectionService");
+
+XPCOMUtils.defineLazyServiceGetter(this, "gCellBroadcastService",
+ "@mozilla.org/cellbroadcast/cellbroadcastservice;1",
+ "nsIGonkCellBroadcastService");
+
+XPCOMUtils.defineLazyServiceGetter(this, "gDataCallManager",
+ "@mozilla.org/datacall/manager;1",
+ "nsIDataCallManager");
+
+XPCOMUtils.defineLazyServiceGetter(this, "gDataCallInterfaceService",
+ "@mozilla.org/datacall/interfaceservice;1",
+ "nsIGonkDataCallInterfaceService");
+
+XPCOMUtils.defineLazyServiceGetter(this, "gStkCmdFactory",
+ "@mozilla.org/icc/stkcmdfactory;1",
+ "nsIStkCmdFactory");
+
+XPCOMUtils.defineLazyGetter(this, "gRadioEnabledController", function() {
+ let _ril = null;
+ let _pendingMessages = []; // For queueing "setRadioEnabled" message.
+ let _isProcessingPending = false;
+ let _timer = null;
+ let _request = null;
+ let _deactivatingDeferred = {};
+ let _initializedCardState = {};
+ let _allCardStateInitialized = !RILQUIRKS_RADIO_OFF_WO_CARD;
+
+ return {
+ init: function(ril) {
+ _ril = ril;
+ },
+
+ receiveCardState: function(clientId) {
+ if (_allCardStateInitialized) {
+ return;
+ }
+
+ if (DEBUG) debug("RadioControl: receive cardState from " + clientId);
+ _initializedCardState[clientId] = true;
+ if (Object.keys(_initializedCardState).length == _ril.numRadioInterfaces) {
+ _allCardStateInitialized = true;
+ this._startProcessingPending();
+ }
+ },
+
+ setRadioEnabled: function(clientId, data, callback) {
+ if (DEBUG) debug("setRadioEnabled: " + clientId + ": " + JSON.stringify(data));
+ let message = {
+ clientId: clientId,
+ data: data,
+ callback: callback
+ };
+ _pendingMessages.push(message);
+ this._startProcessingPending();
+ },
+
+ notifyRadioStateChanged: function(clientId, radioState) {
+ gMobileConnectionService.notifyRadioStateChanged(clientId, radioState);
+ },
+
+ _startProcessingPending: function() {
+ if (!_isProcessingPending) {
+ if (DEBUG) debug("RadioControl: start dequeue");
+ _isProcessingPending = true;
+ this._processNextMessage();
+ }
+ },
+
+ _processNextMessage: function() {
+ if (_pendingMessages.length === 0 || !_allCardStateInitialized) {
+ if (DEBUG) debug("RadioControl: stop dequeue");
+ _isProcessingPending = false;
+ return;
+ }
+
+ let msg = _pendingMessages.shift();
+ this._handleMessage(msg);
+ },
+
+ _getNumCards: function() {
+ let numCards = 0;
+ for (let i = 0, N = _ril.numRadioInterfaces; i < N; ++i) {
+ if (_ril.getRadioInterface(i).isCardPresent()) {
+ numCards++;
+ }
+ }
+ return numCards;
+ },
+
+ _isRadioAbleToEnableAtClient: function(clientId, numCards) {
+ if (!RILQUIRKS_RADIO_OFF_WO_CARD) {
+ return true;
+ }
+
+ // We could only turn on the radio for clientId if
+ // 1. a SIM card is presented or
+ // 2. it is the default clientId and there is no any SIM card at any client.
+
+ if (_ril.getRadioInterface(clientId).isCardPresent()) {
+ return true;
+ }
+
+ numCards = numCards == null ? this._getNumCards() : numCards;
+ if (clientId === HW_DEFAULT_CLIENT_ID && numCards === 0) {
+ return true;
+ }
+
+ return false;
+ },
+
+ _handleMessage: function(message) {
+ if (DEBUG) debug("RadioControl: handleMessage: " + JSON.stringify(message));
+ let clientId = message.clientId || 0;
+ let connection =
+ gMobileConnectionService.getItemByServiceId(clientId);
+ let radioState = connection && connection.radioState;
+
+ if (message.data.enabled) {
+ if (this._isRadioAbleToEnableAtClient(clientId)) {
+ this._setRadioEnabledInternal(message);
+ } else {
+ // Not really do it but respond success.
+ message.callback(message.data);
+ }
+
+ this._processNextMessage();
+ } else {
+ _request = this._setRadioEnabledInternal.bind(this, message);
+
+ // In 2G network, modem takes 35+ seconds to process deactivate data
+ // call request if device has active voice call (please see bug 964974
+ // for more details). Therefore we should hangup all active voice calls
+ // first. And considering some DSDS architecture, toggling one radio may
+ // toggle both, so we send hangUpAll to all clients.
+ let hangUpCallback = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsITelephonyCallback]),
+ notifySuccess: function() {},
+ notifyError: function() {}
+ };
+
+ gTelephonyService.enumerateCalls({
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsITelephonyListener]),
+ enumerateCallState: function(aInfo) {
+ gTelephonyService.hangUpCall(aInfo.clientId, aInfo.callIndex,
+ hangUpCallback);
+ },
+ enumerateCallStateComplete: function() {}
+ });
+
+ // In some DSDS architecture with only one modem, toggling one radio may
+ // toggle both. Therefore, for safely turning off, we should first
+ // explicitly deactivate all data calls from all clients.
+ this._deactivateDataCalls().then(() => {
+ if (DEBUG) debug("RadioControl: deactivation done");
+ this._executeRequest();
+ });
+
+ this._createTimer();
+ }
+ },
+
+ _setRadioEnabledInternal: function(message) {
+ let clientId = message.clientId || 0;
+ let enabled = message.data.enabled || false;
+ let radioInterface = _ril.getRadioInterface(clientId);
+
+ radioInterface.workerMessenger.send("setRadioEnabled", message.data,
+ (function(response) {
+ if (response.errorMsg) {
+ // If request fails, set current radio state to unknown, since we will
+ // handle it in |mobileConnectionService|.
+ this.notifyRadioStateChanged(clientId,
+ Ci.nsIMobileConnection.MOBILE_RADIO_STATE_UNKNOWN);
+ }
+ return message.callback(response);
+ }).bind(this));
+ },
+
+ _deactivateDataCalls: function() {
+ if (DEBUG) debug("RadioControl: deactivating data calls...");
+ _deactivatingDeferred = {};
+
+ let promise = Promise.resolve();
+ for (let i = 0, N = _ril.numRadioInterfaces; i < N; ++i) {
+ promise = promise.then(this._deactivateDataCallsForClient(i));
+ }
+
+ return promise;
+ },
+
+ _deactivateDataCallsForClient: function(clientId) {
+ return function() {
+ let deferred = _deactivatingDeferred[clientId] = Promise.defer();
+ let dataCallHandler = gDataCallManager.getDataCallHandler(clientId);
+
+ dataCallHandler.deactivateDataCalls(function() {
+ deferred.resolve();
+ });
+
+ return deferred.promise;
+ };
+ },
+
+ _createTimer: function() {
+ if (!_timer) {
+ _timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ }
+ _timer.initWithCallback(this._executeRequest.bind(this),
+ RADIO_POWER_OFF_TIMEOUT,
+ Ci.nsITimer.TYPE_ONE_SHOT);
+ },
+
+ _cancelTimer: function() {
+ if (_timer) {
+ _timer.cancel();
+ }
+ },
+
+ _executeRequest: function() {
+ if (typeof _request === "function") {
+ if (DEBUG) debug("RadioControl: executeRequest");
+ this._cancelTimer();
+ _request();
+ _request = null;
+ }
+ this._processNextMessage();
+ },
+ };
+});
+
+// Initialize shared preference "ril.numRadioInterfaces" according to system
+// property.
+try {
+ Services.prefs.setIntPref(kPrefRilNumRadioInterfaces, (function() {
+ // When Gonk property "ro.moz.ril.numclients" is not set, return 1; if
+ // explicitly set to any number larger-equal than 0, return num; else, return
+ // 1 for compatibility.
+ try {
+ let numString = libcutils.property_get("ro.moz.ril.numclients", "1");
+ let num = parseInt(numString, 10);
+ if (num >= 0) {
+ return num;
+ }
+ } catch (e) {}
+
+ return 1;
+ })());
+} catch (e) {}
+
+function DataCall(aAttributes) {
+ for (let key in aAttributes) {
+ if (key === "pdpType") {
+ // Convert pdp type into constant int value.
+ this[key] = RIL.RIL_DATACALL_PDP_TYPES.indexOf(aAttributes[key]);
+ continue;
+ }
+
+ this[key] = aAttributes[key];
+ }
+}
+DataCall.prototype = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIDataCall]),
+
+ failCause: Ci.nsIDataCallInterface.DATACALL_FAIL_NONE,
+ suggestedRetryTime: -1,
+ cid: -1,
+ active: -1,
+ pdpType: -1,
+ ifname: null,
+ addreses: null,
+ dnses: null,
+ gateways: null,
+ pcscf: null,
+ mtu: -1
+};
+
+function RadioInterfaceLayer() {
+ let workerMessenger = new WorkerMessenger();
+ workerMessenger.init();
+ this.setWorkerDebugFlag = workerMessenger.setDebugFlag.bind(workerMessenger);
+
+ let numIfaces = this.numRadioInterfaces;
+ if (DEBUG) debug(numIfaces + " interfaces");
+ this.radioInterfaces = [];
+ for (let clientId = 0; clientId < numIfaces; clientId++) {
+ this.radioInterfaces.push(new RadioInterface(clientId, workerMessenger));
+ }
+
+ Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
+ Services.prefs.addObserver(kPrefRilDebuggingEnabled, this, false);
+
+ gRadioEnabledController.init(this);
+}
+RadioInterfaceLayer.prototype = {
+
+ classID: RADIOINTERFACELAYER_CID,
+ classInfo: XPCOMUtils.generateCI({classID: RADIOINTERFACELAYER_CID,
+ classDescription: "RadioInterfaceLayer",
+ interfaces: [Ci.nsIRadioInterfaceLayer]}),
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIRadioInterfaceLayer,
+ Ci.nsIObserver]),
+
+ /**
+ * nsIObserver interface methods.
+ */
+
+ observe: function(subject, topic, data) {
+ switch (topic) {
+ case NS_XPCOM_SHUTDOWN_OBSERVER_ID:
+ for (let radioInterface of this.radioInterfaces) {
+ radioInterface.shutdown();
+ }
+ this.radioInterfaces = null;
+ Services.obs.removeObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
+ break;
+
+ case NS_PREFBRANCH_PREFCHANGE_TOPIC_ID:
+ if (data === kPrefRilDebuggingEnabled) {
+ updateDebugFlag();
+ this.setWorkerDebugFlag(DEBUG);
+ }
+ break;
+ }
+ },
+
+ /**
+ * nsIRadioInterfaceLayer interface methods.
+ */
+
+ getRadioInterface: function(clientId) {
+ return this.radioInterfaces[clientId];
+ },
+
+ setMicrophoneMuted: function(muted) {
+ for (let clientId = 0; clientId < this.numRadioInterfaces; clientId++) {
+ let radioInterface = this.radioInterfaces[clientId];
+ radioInterface.workerMessenger.send("setMute", { muted: muted });
+ }
+ }
+};
+
+XPCOMUtils.defineLazyGetter(RadioInterfaceLayer.prototype,
+ "numRadioInterfaces", function() {
+ try {
+ return Services.prefs.getIntPref(kPrefRilNumRadioInterfaces);
+ } catch(e) {}
+
+ return 1;
+});
+
+function WorkerMessenger() {
+ // Initial owning attributes.
+ this.radioInterfaces = [];
+ this.tokenCallbackMap = {};
+
+ this.worker = new ChromeWorker("resource://gre/modules/ril_worker.js");
+ this.worker.onerror = this.onerror.bind(this);
+ this.worker.onmessage = this.onmessage.bind(this);
+}
+WorkerMessenger.prototype = {
+ radioInterfaces: null,
+ worker: null,
+
+ // This gets incremented each time we send out a message.
+ token: 1,
+
+ // Maps tokens we send out with messages to the message callback.
+ tokenCallbackMap: null,
+
+ init: function() {
+ let options = {
+ debug: DEBUG,
+ quirks: {
+ callstateExtraUint32:
+ libcutils.property_get("ro.moz.ril.callstate_extra_int", "false") === "true",
+ requestUseDialEmergencyCall:
+ libcutils.property_get("ro.moz.ril.dial_emergency_call", "false") === "true",
+ simAppStateExtraFields:
+ libcutils.property_get("ro.moz.ril.simstate_extra_field", "false") === "true",
+ extraUint2ndCall:
+ libcutils.property_get("ro.moz.ril.extra_int_2nd_call", "false") === "true",
+ haveQueryIccLockRetryCount:
+ libcutils.property_get("ro.moz.ril.query_icc_count", "false") === "true",
+ sendStkProfileDownload:
+ libcutils.property_get("ro.moz.ril.send_stk_profile_dl", "false") === "true",
+ smscAddressFormat:
+ libcutils.property_get("ro.moz.ril.smsc_address_format", "text"),
+ dataRegistrationOnDemand:
+ libcutils.property_get("ro.moz.ril.data_reg_on_demand", "false") === "true",
+ subscriptionControl:
+ libcutils.property_get("ro.moz.ril.subscription_control", "false") === "true",
+ signalExtraInt:
+ libcutils.property_get("ro.moz.ril.signal_extra_int", "false") === "true",
+ availableNetworkExtraStr:
+ libcutils.property_get("ro.moz.ril.avlbl_nw_extra_str", "false") === "true",
+ }
+ };
+
+ this.send(null, "setInitialOptions", options);
+ },
+
+ setDebugFlag: function(aDebug) {
+ let options = { debug: aDebug };
+ this.send(null, "setDebugFlag", options);
+ },
+
+ debug: function(aClientId, aMessage) {
+ // We use the same debug subject with RadioInterface's here.
+ dump("-*- RadioInterface[" + aClientId + "]: " + aMessage + "\n");
+ },
+
+ onerror: function(event) {
+ if (DEBUG) {
+ this.debug("X", "Got an error: " + event.filename + ":" +
+ event.lineno + ": " + event.message + "\n");
+ }
+ event.preventDefault();
+ },
+
+ /**
+ * Process the incoming message from the RIL worker.
+ */
+ onmessage: function(event) {
+ let message = event.data;
+ let clientId = message.rilMessageClientId;
+ if (clientId === null) {
+ return;
+ }
+
+ if (DEBUG) {
+ this.debug(clientId, "Received message from worker: " + JSON.stringify(message));
+ }
+
+ let token = message.rilMessageToken;
+ if (token == null) {
+ // That's an unsolicited message. Pass to RadioInterface directly.
+ let radioInterface = this.radioInterfaces[clientId];
+ radioInterface.handleUnsolicitedWorkerMessage(message);
+ return;
+ }
+
+ let callback = this.tokenCallbackMap[message.rilMessageToken];
+ if (!callback) {
+ if (DEBUG) this.debug(clientId, "Ignore orphan token: " + message.rilMessageToken);
+ return;
+ }
+
+ let keep = false;
+ try {
+ keep = callback(message);
+ } catch(e) {
+ if (DEBUG) this.debug(clientId, "callback throws an exception: " + e);
+ }
+
+ if (!keep) {
+ delete this.tokenCallbackMap[message.rilMessageToken];
+ }
+ },
+
+ registerClient: function(aClientId, aRadioInterface) {
+ if (DEBUG) this.debug(aClientId, "Starting RIL Worker");
+
+ // Keep a reference so that we can dispatch unsolicited messages to it.
+ this.radioInterfaces[aClientId] = aRadioInterface;
+
+ this.send(null, "registerClient", { clientId: aClientId });
+ gSystemWorkerManager.registerRilWorker(aClientId, this.worker);
+ },
+
+ /**
+ * Send arbitrary message to worker.
+ *
+ * @param rilMessageType
+ * A text message type.
+ * @param message [optional]
+ * An optional message object to send.
+ * @param callback [optional]
+ * An optional callback function which is called when worker replies
+ * with an message containing a "rilMessageToken" attribute of the
+ * same value we passed. This callback function accepts only one
+ * parameter -- the reply from worker. It also returns a boolean
+ * value true to keep current token-callback mapping and wait for
+ * another worker reply, or false to remove the mapping.
+ */
+ send: function(clientId, rilMessageType, message, callback) {
+ message = message || {};
+
+ message.rilMessageClientId = clientId;
+ message.rilMessageToken = this.token;
+ this.token++;
+
+ if (callback) {
+ // Only create the map if callback is provided. For sending a request
+ // and intentionally leaving the callback undefined, that reply will
+ // be dropped in |this.onmessage| because of that orphan token.
+ //
+ // For sending a request that never replied at all, we're fine with this
+ // because no callback shall be passed and we leave nothing to be cleaned
+ // up later.
+ this.tokenCallbackMap[message.rilMessageToken] = callback;
+ }
+
+ message.rilMessageType = rilMessageType;
+ this.worker.postMessage(message);
+ }
+};
+
+function RadioInterface(aClientId, aWorkerMessenger) {
+ this.clientId = aClientId;
+ this.workerMessenger = {
+ send: aWorkerMessenger.send.bind(aWorkerMessenger, aClientId)
+ };
+ aWorkerMessenger.registerClient(aClientId, this);
+
+ this.operatorInfo = {};
+
+ let lock = gSettingsService.createLock();
+
+ // Read the "time.clock.automatic-update.enabled" setting to see if
+ // we need to adjust the system clock time by NITZ or SNTP.
+ lock.get(kSettingsClockAutoUpdateEnabled, this);
+
+ // Read the "time.timezone.automatic-update.enabled" setting to see if
+ // we need to adjust the system timezone by NITZ.
+ lock.get(kSettingsTimezoneAutoUpdateEnabled, this);
+
+ // Set "time.clock.automatic-update.available" to false when starting up.
+ this.setClockAutoUpdateAvailable(false);
+
+ // Set "time.timezone.automatic-update.available" to false when starting up.
+ this.setTimezoneAutoUpdateAvailable(false);
+
+ Services.obs.addObserver(this, kMozSettingsChangedObserverTopic, false);
+ Services.obs.addObserver(this, kSysClockChangeObserverTopic, false);
+ Services.obs.addObserver(this, kScreenStateChangedTopic, false);
+
+ Services.obs.addObserver(this, kNetworkConnStateChangedTopic, false);
+
+ this._sntp = new Sntp(this.setClockBySntp.bind(this),
+ Services.prefs.getIntPref("network.sntp.maxRetryCount"),
+ Services.prefs.getIntPref("network.sntp.refreshPeriod"),
+ Services.prefs.getIntPref("network.sntp.timeout"),
+ Services.prefs.getCharPref("network.sntp.pools").split(";"),
+ Services.prefs.getIntPref("network.sntp.port"));
+}
+
+RadioInterface.prototype = {
+
+ classID: RADIOINTERFACE_CID,
+ classInfo: XPCOMUtils.generateCI({classID: RADIOINTERFACE_CID,
+ classDescription: "RadioInterface",
+ interfaces: [Ci.nsIRadioInterface]}),
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIRadioInterface,
+ Ci.nsIObserver,
+ Ci.nsISettingsServiceCallback]),
+
+ // A private wrapped WorkerMessenger instance.
+ workerMessenger: null,
+
+ debug: function(s) {
+ dump("-*- RadioInterface[" + this.clientId + "]: " + s + "\n");
+ },
+
+ shutdown: function() {
+ Services.obs.removeObserver(this, kMozSettingsChangedObserverTopic);
+ Services.obs.removeObserver(this, kSysClockChangeObserverTopic);
+ Services.obs.removeObserver(this, kScreenStateChangedTopic);
+ Services.obs.removeObserver(this, kNetworkConnStateChangedTopic);
+ },
+
+ isCardPresent: function() {
+ let icc = gIccService.getIccByServiceId(this.clientId);
+ let cardState = icc ? icc.cardState : Ci.nsIIcc.CARD_STATE_UNKNOWN;
+ return cardState !== Ci.nsIIcc.CARD_STATE_UNDETECTED &&
+ cardState !== Ci.nsIIcc.CARD_STATE_UNKNOWN;
+ },
+
+ handleUnsolicitedWorkerMessage: function(message) {
+ switch (message.rilMessageType) {
+ case "callRing":
+ gTelephonyService.notifyCallRing();
+ break;
+ case "currentCalls":
+ gTelephonyService.notifyCurrentCalls(this.clientId, message.calls);
+ break;
+ case "cdmaCallWaiting":
+ gTelephonyService.notifyCdmaCallWaiting(this.clientId,
+ message.waitingCall);
+ break;
+ case "suppSvcNotification":
+ gTelephonyService.notifySupplementaryService(this.clientId,
+ message.number,
+ message.notification);
+ break;
+ case "ussdreceived":
+ gTelephonyService.notifyUssdReceived(this.clientId, message.message,
+ message.sessionEnded);
+ break;
+ case "datacalllistchanged":
+ let dataCalls = message.datacalls.map(dataCall => new DataCall(dataCall));
+ gDataCallInterfaceService.notifyDataCallListChanged(this.clientId,
+ dataCalls.length,
+ dataCalls);
+ break;
+ case "emergencyCbModeChange":
+ gMobileConnectionService.notifyEmergencyCallbackModeChanged(this.clientId,
+ message.active,
+ message.timeoutMs);
+ break;
+ case "networkinfochanged":
+ gMobileConnectionService.notifyNetworkInfoChanged(this.clientId,
+ message);
+ break;
+ case "networkselectionmodechange":
+ gMobileConnectionService.notifyNetworkSelectModeChanged(this.clientId,
+ message.mode);
+ break;
+ case "voiceregistrationstatechange":
+ gMobileConnectionService.notifyVoiceInfoChanged(this.clientId, message);
+ break;
+ case "dataregistrationstatechange":
+ gMobileConnectionService.notifyDataInfoChanged(this.clientId, message);
+ break;
+ case "signalstrengthchange":
+ gMobileConnectionService.notifySignalStrengthChanged(this.clientId,
+ message);
+ break;
+ case "operatorchange":
+ gMobileConnectionService.notifyOperatorChanged(this.clientId, message);
+ break;
+ case "otastatuschange":
+ gMobileConnectionService.notifyOtaStatusChanged(this.clientId, message.status);
+ break;
+ case "deviceidentitieschange":
+ gMobileConnectionService.notifyDeviceIdentitiesChanged(this.clientId,
+ message.deviceIdentities.imei,
+ message.deviceIdentities.imeisv,
+ message.deviceIdentities.esn,
+ message.deviceIdentities.meid);
+ break;
+ case "radiostatechange":
+ // gRadioEnabledController should know the radio state for each client,
+ // so notify gRadioEnabledController here.
+ gRadioEnabledController.notifyRadioStateChanged(this.clientId,
+ message.radioState);
+ break;
+ case "cardstatechange":
+ gIccService.notifyCardStateChanged(this.clientId,
+ message.cardState);
+ gRadioEnabledController.receiveCardState(this.clientId);
+ break;
+ case "sms-received":
+ this.handleSmsReceived(message);
+ break;
+ case "cellbroadcast-received":
+ this.handleCellbroadcastMessageReceived(message);
+ break;
+ case "nitzTime":
+ this.handleNitzTime(message);
+ break;
+ case "iccinfochange":
+ gIccService.notifyIccInfoChanged(this.clientId,
+ message.iccid ? message : null);
+ break;
+ case "iccimsi":
+ gIccService.notifyImsiChanged(this.clientId, message.imsi);
+ break;
+ case "iccmbdn":
+ this.handleIccMbdn(message);
+ break;
+ case "iccmwis":
+ this.handleIccMwis(message.mwi);
+ break;
+ case "stkcommand":
+ gIccService.notifyStkCommand(this.clientId,
+ gStkCmdFactory.createCommand(message));
+ break;
+ case "stksessionend":
+ gIccService.notifyStkSessionEnd(this.clientId);
+ break;
+ case "cdma-info-rec-received":
+ this.handleCdmaInformationRecords(message.records);
+ break;
+ default:
+ throw new Error("Don't know about this message type: " +
+ message.rilMessageType);
+ }
+ },
+
+ setDataRegistration: function(attach) {
+ let deferred = Promise.defer();
+ this.workerMessenger.send("setDataRegistration",
+ {attach: attach},
+ (function(response) {
+ // Always resolve to proceed with the following steps.
+ deferred.resolve(response.errorMsg ? response.errorMsg : null);
+ }).bind(this));
+
+ return deferred.promise;
+ },
+
+ /**
+ * TODO: Bug 911713 - B2G NetworkManager: Move policy control logic to
+ * NetworkManager
+ */
+ updateRILNetworkInterface: function() {
+ let connHandler = gDataCallManager.getDataCallHandler(this.clientId);
+ connHandler.updateRILNetworkInterface();
+ },
+
+ /**
+ * handle received SMS.
+ */
+ handleSmsReceived: function(aMessage) {
+ let header = aMessage.header;
+ // Concatenation Info:
+ // - segmentRef: a modulo 256 counter indicating the reference number for a
+ // particular concatenated short message. '0' is a valid number.
+ // - The concatenation info will not be available in |header| if
+ // segmentSeq or segmentMaxSeq is 0.
+ // See 3GPP TS 23.040, 9.2.3.24.1 Concatenated Short Messages.
+ let segmentRef = (header && header.segmentRef !== undefined)
+ ? header.segmentRef : 1;
+ let segmentSeq = header && header.segmentSeq || 1;
+ let segmentMaxSeq = header && header.segmentMaxSeq || 1;
+ // Application Ports:
+ // The port number ranges from 0 to 49151.
+ // see 3GPP TS 23.040, 9.2.3.24.3/4 Application Port Addressing.
+ let originatorPort = (header && header.originatorPort !== undefined)
+ ? header.originatorPort
+ : Ci.nsIGonkSmsService.SMS_APPLICATION_PORT_INVALID;
+ let destinationPort = (header && header.destinationPort !== undefined)
+ ? header.destinationPort
+ : Ci.nsIGonkSmsService.SMS_APPLICATION_PORT_INVALID;
+ // MWI info:
+ let mwiPresent = (aMessage.mwi)? true : false;
+ let mwiDiscard = (mwiPresent)? aMessage.mwi.discard: false;
+ let mwiMsgCount = (mwiPresent)? aMessage.mwi.msgCount: 0;
+ let mwiActive = (mwiPresent)? aMessage.mwi.active: false;
+ // CDMA related attributes:
+ let cdmaMessageType = aMessage.messageType || 0;
+ let cdmaTeleservice = aMessage.teleservice || 0;
+ let cdmaServiceCategory = aMessage.serviceCategory || 0;
+
+ gSmsService
+ .notifyMessageReceived(this.clientId,
+ aMessage.SMSC || null,
+ aMessage.sentTimestamp,
+ aMessage.sender,
+ aMessage.pid,
+ aMessage.encoding,
+ RIL.GECKO_SMS_MESSAGE_CLASSES
+ .indexOf(aMessage.messageClass),
+ aMessage.language || null,
+ segmentRef,
+ segmentSeq,
+ segmentMaxSeq,
+ originatorPort,
+ destinationPort,
+ mwiPresent,
+ mwiDiscard,
+ mwiMsgCount,
+ mwiActive,
+ cdmaMessageType,
+ cdmaTeleservice,
+ cdmaServiceCategory,
+ aMessage.body || null,
+ aMessage.data || [],
+ (aMessage.data) ? aMessage.data.length : 0);
+ },
+
+ /**
+ * Set the setting value of "time.clock.automatic-update.available".
+ */
+ setClockAutoUpdateAvailable: function(value) {
+ gSettingsService.createLock().set(kSettingsClockAutoUpdateAvailable, value, null);
+ },
+
+ /**
+ * Set the setting value of "time.timezone.automatic-update.available".
+ */
+ setTimezoneAutoUpdateAvailable: function(value) {
+ gSettingsService.createLock().set(kSettingsTimezoneAutoUpdateAvailable, value, null);
+ },
+
+ /**
+ * Set the system clock by NITZ.
+ */
+ setClockByNitz: function(message) {
+ // To set the system clock time. Note that there could be a time diff
+ // between when the NITZ was received and when the time is actually set.
+ gTimeService.set(
+ message.networkTimeInMS + (Date.now() - message.receiveTimeInMS));
+ },
+
+ /**
+ * Set the system time zone by NITZ.
+ */
+ setTimezoneByNitz: function(message) {
+ // To set the sytem timezone. Note that we need to convert the time zone
+ // value to a UTC repesentation string in the format of "UTC(+/-)hh:mm".
+ // Ex, time zone -480 is "UTC+08:00"; time zone 630 is "UTC-10:30".
+ //
+ // We can unapply the DST correction if we want the raw time zone offset:
+ // message.networkTimeZoneInMinutes -= message.networkDSTInMinutes;
+ if (message.networkTimeZoneInMinutes != (new Date()).getTimezoneOffset()) {
+ let absTimeZoneInMinutes = Math.abs(message.networkTimeZoneInMinutes);
+ let timeZoneStr = "UTC";
+ timeZoneStr += (message.networkTimeZoneInMinutes > 0 ? "-" : "+");
+ timeZoneStr += ("0" + Math.floor(absTimeZoneInMinutes / 60)).slice(-2);
+ timeZoneStr += ":";
+ timeZoneStr += ("0" + absTimeZoneInMinutes % 60).slice(-2);
+ gSettingsService.createLock().set("time.timezone", timeZoneStr, null);
+ }
+ },
+
+ /**
+ * Handle the NITZ message.
+ */
+ handleNitzTime: function(message) {
+ // Got the NITZ info received from the ril_worker.
+ this.setClockAutoUpdateAvailable(true);
+ this.setTimezoneAutoUpdateAvailable(true);
+
+ // Cache the latest NITZ message whenever receiving it.
+ this._lastNitzMessage = message;
+
+ // Set the received NITZ clock if the setting is enabled.
+ if (this._clockAutoUpdateEnabled) {
+ this.setClockByNitz(message);
+ }
+ // Set the received NITZ timezone if the setting is enabled.
+ if (this._timezoneAutoUpdateEnabled) {
+ this.setTimezoneByNitz(message);
+ }
+ },
+
+ /**
+ * Set the system clock by SNTP.
+ */
+ setClockBySntp: function(offset) {
+ // Got the SNTP info.
+ this.setClockAutoUpdateAvailable(true);
+ if (!this._clockAutoUpdateEnabled) {
+ return;
+ }
+ if (this._lastNitzMessage) {
+ if (DEBUG) debug("SNTP: NITZ available, discard SNTP");
+ return;
+ }
+ gTimeService.set(Date.now() + offset);
+ },
+
+ handleIccMbdn: function(message) {
+ let service = Cc["@mozilla.org/voicemail/voicemailservice;1"]
+ .getService(Ci.nsIGonkVoicemailService);
+ service.notifyInfoChanged(this.clientId, message.number, message.alphaId);
+ },
+
+ handleIccMwis: function(mwi) {
+ let service = Cc["@mozilla.org/voicemail/voicemailservice;1"]
+ .getService(Ci.nsIGonkVoicemailService);
+ // Note: returnNumber and returnMessage is not available from UICC.
+ service.notifyStatusChanged(this.clientId, mwi.active, mwi.msgCount,
+ null, null);
+ },
+
+ _convertCbGsmGeographicalScope: function(aGeographicalScope) {
+ return (aGeographicalScope != null)
+ ? aGeographicalScope
+ : Ci.nsICellBroadcastService.GSM_GEOGRAPHICAL_SCOPE_INVALID;
+ },
+
+ _convertCbMessageClass: function(aMessageClass) {
+ let index = RIL.GECKO_SMS_MESSAGE_CLASSES.indexOf(aMessageClass);
+ return (index != -1)
+ ? index
+ : Ci.nsICellBroadcastService.GSM_MESSAGE_CLASS_NORMAL;
+ },
+
+ _convertCbEtwsWarningType: function(aWarningType) {
+ return (aWarningType != null)
+ ? aWarningType
+ : Ci.nsICellBroadcastService.GSM_ETWS_WARNING_INVALID;
+ },
+
+ handleCellbroadcastMessageReceived: function(aMessage) {
+ let etwsInfo = aMessage.etws;
+ let hasEtwsInfo = etwsInfo != null;
+ let serviceCategory = (aMessage.serviceCategory)
+ ? aMessage.serviceCategory
+ : Ci.nsICellBroadcastService.CDMA_SERVICE_CATEGORY_INVALID;
+
+ gCellBroadcastService
+ .notifyMessageReceived(this.clientId,
+ this._convertCbGsmGeographicalScope(aMessage.geographicalScope),
+ aMessage.messageCode,
+ aMessage.messageId,
+ aMessage.language,
+ aMessage.fullBody,
+ this._convertCbMessageClass(aMessage.messageClass),
+ Date.now(),
+ serviceCategory,
+ hasEtwsInfo,
+ (hasEtwsInfo)
+ ? this._convertCbEtwsWarningType(etwsInfo.warningType)
+ : Ci.nsICellBroadcastService.GSM_ETWS_WARNING_INVALID,
+ hasEtwsInfo ? etwsInfo.emergencyUserAlert : false,
+ hasEtwsInfo ? etwsInfo.popup : false);
+ },
+
+ handleCdmaInformationRecords: function(aRecords) {
+ if (DEBUG) this.debug("cdma-info-rec-received: " + JSON.stringify(aRecords));
+
+ let clientId = this.clientId;
+
+ aRecords.forEach(function(aRecord) {
+ if (aRecord.display) {
+ gMobileConnectionService
+ .notifyCdmaInfoRecDisplay(clientId, aRecord.display);
+ return;
+ }
+
+ if (aRecord.calledNumber) {
+ gMobileConnectionService
+ .notifyCdmaInfoRecCalledPartyNumber(clientId,
+ aRecord.calledNumber.type,
+ aRecord.calledNumber.plan,
+ aRecord.calledNumber.number,
+ aRecord.calledNumber.pi,
+ aRecord.calledNumber.si);
+ return;
+ }
+
+ if (aRecord.callingNumber) {
+ gMobileConnectionService
+ .notifyCdmaInfoRecCallingPartyNumber(clientId,
+ aRecord.callingNumber.type,
+ aRecord.callingNumber.plan,
+ aRecord.callingNumber.number,
+ aRecord.callingNumber.pi,
+ aRecord.callingNumber.si);
+ return;
+ }
+
+ if (aRecord.connectedNumber) {
+ gMobileConnectionService
+ .notifyCdmaInfoRecConnectedPartyNumber(clientId,
+ aRecord.connectedNumber.type,
+ aRecord.connectedNumber.plan,
+ aRecord.connectedNumber.number,
+ aRecord.connectedNumber.pi,
+ aRecord.connectedNumber.si);
+ return;
+ }
+
+ if (aRecord.signal) {
+ gMobileConnectionService
+ .notifyCdmaInfoRecSignal(clientId,
+ aRecord.signal.type,
+ aRecord.signal.alertPitch,
+ aRecord.signal.signal);
+ return;
+ }
+
+ if (aRecord.redirect) {
+ gMobileConnectionService
+ .notifyCdmaInfoRecRedirectingNumber(clientId,
+ aRecord.redirect.type,
+ aRecord.redirect.plan,
+ aRecord.redirect.number,
+ aRecord.redirect.pi,
+ aRecord.redirect.si,
+ aRecord.redirect.reason);
+ return;
+ }
+
+ if (aRecord.lineControl) {
+ gMobileConnectionService
+ .notifyCdmaInfoRecLineControl(clientId,
+ aRecord.lineControl.polarityIncluded,
+ aRecord.lineControl.toggle,
+ aRecord.lineControl.reverse,
+ aRecord.lineControl.powerDenial);
+ return;
+ }
+
+ if (aRecord.clirCause) {
+ gMobileConnectionService
+ .notifyCdmaInfoRecClir(clientId,
+ aRecord.clirCause);
+ return;
+ }
+
+ if (aRecord.audioControl) {
+ gMobileConnectionService
+ .notifyCdmaInfoRecAudioControl(clientId,
+ aRecord.audioControl.upLink,
+ aRecord.audioControl.downLink);
+ return;
+ }
+ });
+ },
+
+ // nsIObserver
+
+ observe: function(subject, topic, data) {
+ switch (topic) {
+ case kMozSettingsChangedObserverTopic:
+ if ("wrappedJSObject" in subject) {
+ subject = subject.wrappedJSObject;
+ }
+ this.handleSettingsChange(subject.key, subject.value, subject.isInternalChange);
+ break;
+ case kSysClockChangeObserverTopic:
+ let offset = parseInt(data, 10);
+ if (this._lastNitzMessage) {
+ this._lastNitzMessage.receiveTimeInMS += offset;
+ }
+ this._sntp.updateOffset(offset);
+ break;
+ case kNetworkConnStateChangedTopic:
+ let networkInfo = subject.QueryInterface(Ci.nsINetworkInfo);
+ if (networkInfo.state != Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED) {
+ return;
+ }
+
+ // SNTP can only update when we have mobile or Wifi connections.
+ if (networkInfo.type != NETWORK_TYPE_WIFI &&
+ networkInfo.type != NETWORK_TYPE_MOBILE) {
+ return;
+ }
+
+ // If the network comes from RIL, make sure the RIL service is matched.
+ if (subject instanceof Ci.nsIRilNetworkInfo) {
+ networkInfo = subject.QueryInterface(Ci.nsIRilNetworkInfo);
+ if (networkInfo.serviceId != this.clientId) {
+ return;
+ }
+ }
+
+ // SNTP won't update unless the SNTP is already expired.
+ if (this._sntp.isExpired()) {
+ this._sntp.request();
+ }
+ break;
+ case kScreenStateChangedTopic:
+ this.workerMessenger.send("setScreenState", { on: (data === "on") });
+ break;
+ }
+ },
+
+ // Flag to determine whether to update system clock automatically. It
+ // corresponds to the "time.clock.automatic-update.enabled" setting.
+ _clockAutoUpdateEnabled: null,
+
+ // Flag to determine whether to update system timezone automatically. It
+ // corresponds to the "time.clock.automatic-update.enabled" setting.
+ _timezoneAutoUpdateEnabled: null,
+
+ // Remember the last NITZ message so that we can set the time based on
+ // the network immediately when users enable network-based time.
+ _lastNitzMessage: null,
+
+ // Object that handles SNTP.
+ _sntp: null,
+
+ // Cell Broadcast settings values.
+ _cellBroadcastSearchList: null,
+
+ handleSettingsChange: function(aName, aResult, aIsInternalSetting) {
+ // Don't allow any content processes to modify the setting
+ // "time.clock.automatic-update.available" except for the chrome process.
+ if (aName === kSettingsClockAutoUpdateAvailable &&
+ !aIsInternalSetting) {
+ let isClockAutoUpdateAvailable = this._lastNitzMessage !== null ||
+ this._sntp.isAvailable();
+ if (aResult !== isClockAutoUpdateAvailable) {
+ if (DEBUG) {
+ debug("Content processes cannot modify 'time.clock.automatic-update.available'. Restore!");
+ }
+ // Restore the setting to the current value.
+ this.setClockAutoUpdateAvailable(isClockAutoUpdateAvailable);
+ }
+ }
+
+ // Don't allow any content processes to modify the setting
+ // "time.timezone.automatic-update.available" except for the chrome
+ // process.
+ if (aName === kSettingsTimezoneAutoUpdateAvailable &&
+ !aIsInternalSetting) {
+ let isTimezoneAutoUpdateAvailable = this._lastNitzMessage !== null;
+ if (aResult !== isTimezoneAutoUpdateAvailable) {
+ if (DEBUG) {
+ this.debug("Content processes cannot modify 'time.timezone.automatic-update.available'. Restore!");
+ }
+ // Restore the setting to the current value.
+ this.setTimezoneAutoUpdateAvailable(isTimezoneAutoUpdateAvailable);
+ }
+ }
+
+ this.handle(aName, aResult);
+ },
+
+ // nsISettingsServiceCallback
+ handle: function(aName, aResult) {
+ switch(aName) {
+ case kSettingsClockAutoUpdateEnabled:
+ this._clockAutoUpdateEnabled = aResult;
+ if (!this._clockAutoUpdateEnabled) {
+ break;
+ }
+
+ // Set the latest cached NITZ time if it's available.
+ if (this._lastNitzMessage) {
+ this.setClockByNitz(this._lastNitzMessage);
+ } else if (gNetworkManager.activeNetworkInfo &&
+ gNetworkManager.activeNetworkInfo.state ==
+ Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED) {
+ // Set the latest cached SNTP time if it's available.
+ if (!this._sntp.isExpired()) {
+ this.setClockBySntp(this._sntp.getOffset());
+ } else {
+ // Or refresh the SNTP.
+ this._sntp.request();
+ }
+ } else {
+ // Set a sane minimum time.
+ let buildTime = libcutils.property_get("ro.build.date.utc", "0") * 1000;
+ let file = FileUtils.File("/system/b2g/b2g");
+ if (file.lastModifiedTime > buildTime) {
+ buildTime = file.lastModifiedTime;
+ }
+ if (buildTime > Date.now()) {
+ gTimeService.set(buildTime);
+ }
+ }
+ break;
+ case kSettingsTimezoneAutoUpdateEnabled:
+ this._timezoneAutoUpdateEnabled = aResult;
+
+ if (this._timezoneAutoUpdateEnabled) {
+ // Apply the latest cached NITZ for timezone if it's available.
+ if (this._timezoneAutoUpdateEnabled && this._lastNitzMessage) {
+ this.setTimezoneByNitz(this._lastNitzMessage);
+ }
+ }
+ break;
+ }
+ },
+
+ handleError: function(aErrorMessage) {
+ if (DEBUG) {
+ this.debug("There was an error while reading RIL settings.");
+ }
+ },
+
+ // nsIRadioInterface
+
+ // TODO: Bug 928861 - B2G NetworkManager: Provide a more generic function
+ // for connecting
+ setupDataCallByType: function(networkType) {
+ let connHandler = gDataCallManager.getDataCallHandler(this.clientId);
+ connHandler.setupDataCallByType(networkType);
+ },
+
+ // TODO: Bug 928861 - B2G NetworkManager: Provide a more generic function
+ // for connecting
+ deactivateDataCallByType: function(networkType) {
+ let connHandler = gDataCallManager.getDataCallHandler(this.clientId);
+ connHandler.deactivateDataCallByType(networkType);
+ },
+
+ // TODO: Bug 904514 - [meta] NetworkManager enhancement
+ getDataCallStateByType: function(networkType) {
+ let connHandler = gDataCallManager.getDataCallHandler(this.clientId);
+ return connHandler.getDataCallStateByType(networkType);
+ },
+
+ sendWorkerMessage: function(rilMessageType, message, callback) {
+ // Special handler for setRadioEnabled.
+ if (rilMessageType === "setRadioEnabled") {
+ // Forward it to gRadioEnabledController.
+ gRadioEnabledController.setRadioEnabled(this.clientId, message,
+ callback.handleResponse);
+ return;
+ }
+
+ if (callback) {
+ this.workerMessenger.send(rilMessageType, message, function(response) {
+ return callback.handleResponse(response);
+ });
+ } else {
+ this.workerMessenger.send(rilMessageType, message);
+ }
+ },
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([RadioInterfaceLayer]);