dom/system/gonk/RadioInterfaceLayer.js

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* Copyright 2012 Mozilla Foundation and Mozilla contributors
michael@0 2 *
michael@0 3 * Licensed under the Apache License, Version 2.0 (the "License");
michael@0 4 * you may not use this file except in compliance with the License.
michael@0 5 * You may obtain a copy of the License at
michael@0 6 *
michael@0 7 * http://www.apache.org/licenses/LICENSE-2.0
michael@0 8 *
michael@0 9 * Unless required by applicable law or agreed to in writing, software
michael@0 10 * distributed under the License is distributed on an "AS IS" BASIS,
michael@0 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
michael@0 12 * See the License for the specific language governing permissions and
michael@0 13 * limitations under the License.
michael@0 14 */
michael@0 15
michael@0 16 "use strict";
michael@0 17
michael@0 18 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
michael@0 19
michael@0 20 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
michael@0 21 Cu.import("resource://gre/modules/Services.jsm");
michael@0 22 Cu.import("resource://gre/modules/Sntp.jsm");
michael@0 23 Cu.import("resource://gre/modules/systemlibs.js");
michael@0 24 Cu.import("resource://gre/modules/Promise.jsm");
michael@0 25 Cu.import("resource://gre/modules/FileUtils.jsm");
michael@0 26
michael@0 27 var RIL = {};
michael@0 28 Cu.import("resource://gre/modules/ril_consts.js", RIL);
michael@0 29
michael@0 30 // set to true in ril_consts.js to see debug messages
michael@0 31 var DEBUG = RIL.DEBUG_RIL;
michael@0 32
michael@0 33 // Read debug setting from pref
michael@0 34 let debugPref = false;
michael@0 35 try {
michael@0 36 debugPref = Services.prefs.getBoolPref("ril.debugging.enabled");
michael@0 37 } catch(e) {
michael@0 38 debugPref = false;
michael@0 39 }
michael@0 40 DEBUG = RIL.DEBUG_RIL || debugPref;
michael@0 41
michael@0 42 function debug(s) {
michael@0 43 dump("-*- RadioInterfaceLayer: " + s + "\n");
michael@0 44 }
michael@0 45
michael@0 46 // Ril quirk to attach data registration on demand.
michael@0 47 let RILQUIRKS_DATA_REGISTRATION_ON_DEMAND =
michael@0 48 libcutils.property_get("ro.moz.ril.data_reg_on_demand", "false") == "true";
michael@0 49
michael@0 50 // Ril quirk to always turn the radio off for the client without SIM card
michael@0 51 // except hw default client.
michael@0 52 let RILQUIRKS_RADIO_OFF_WO_CARD =
michael@0 53 libcutils.property_get("ro.moz.ril.radio_off_wo_card", "false") == "true";
michael@0 54
michael@0 55 // Ril quirk to enable IPv6 protocol/roaming protocol in APN settings.
michael@0 56 let RILQUIRKS_HAVE_IPV6 =
michael@0 57 libcutils.property_get("ro.moz.ril.ipv6", "false") == "true";
michael@0 58
michael@0 59 const RADIOINTERFACELAYER_CID =
michael@0 60 Components.ID("{2d831c8d-6017-435b-a80c-e5d422810cea}");
michael@0 61 const RADIOINTERFACE_CID =
michael@0 62 Components.ID("{6a7c91f0-a2b3-4193-8562-8969296c0b54}");
michael@0 63 const RILNETWORKINTERFACE_CID =
michael@0 64 Components.ID("{3bdd52a9-3965-4130-b569-0ac5afed045e}");
michael@0 65 const GSMICCINFO_CID =
michael@0 66 Components.ID("{d90c4261-a99d-47bc-8b05-b057bb7e8f8a}");
michael@0 67 const CDMAICCINFO_CID =
michael@0 68 Components.ID("{39ba3c08-aacc-46d0-8c04-9b619c387061}");
michael@0 69
michael@0 70 const NS_XPCOM_SHUTDOWN_OBSERVER_ID = "xpcom-shutdown";
michael@0 71 const kNetworkInterfaceStateChangedTopic = "network-interface-state-changed";
michael@0 72 const kNetworkConnStateChangedTopic = "network-connection-state-changed";
michael@0 73 const kNetworkActiveChangedTopic = "network-active-changed";
michael@0 74 const kSmsReceivedObserverTopic = "sms-received";
michael@0 75 const kSilentSmsReceivedObserverTopic = "silent-sms-received";
michael@0 76 const kSmsSendingObserverTopic = "sms-sending";
michael@0 77 const kSmsSentObserverTopic = "sms-sent";
michael@0 78 const kSmsFailedObserverTopic = "sms-failed";
michael@0 79 const kSmsDeliverySuccessObserverTopic = "sms-delivery-success";
michael@0 80 const kSmsDeliveryErrorObserverTopic = "sms-delivery-error";
michael@0 81 const kMozSettingsChangedObserverTopic = "mozsettings-changed";
michael@0 82 const kSysMsgListenerReadyObserverTopic = "system-message-listener-ready";
michael@0 83 const kSysClockChangeObserverTopic = "system-clock-change";
michael@0 84 const kScreenStateChangedTopic = "screen-state-changed";
michael@0 85
michael@0 86 const kSettingsCellBroadcastSearchList = "ril.cellbroadcast.searchlist";
michael@0 87 const kSettingsClockAutoUpdateEnabled = "time.clock.automatic-update.enabled";
michael@0 88 const kSettingsClockAutoUpdateAvailable = "time.clock.automatic-update.available";
michael@0 89 const kSettingsTimezoneAutoUpdateEnabled = "time.timezone.automatic-update.enabled";
michael@0 90 const kSettingsTimezoneAutoUpdateAvailable = "time.timezone.automatic-update.available";
michael@0 91
michael@0 92 const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
michael@0 93
michael@0 94 const kPrefCellBroadcastDisabled = "ril.cellbroadcast.disabled";
michael@0 95 const kPrefClirModePreference = "ril.clirMode";
michael@0 96 const kPrefRilNumRadioInterfaces = "ril.numRadioInterfaces";
michael@0 97
michael@0 98 const DOM_MOBILE_MESSAGE_DELIVERY_RECEIVED = "received";
michael@0 99 const DOM_MOBILE_MESSAGE_DELIVERY_SENDING = "sending";
michael@0 100 const DOM_MOBILE_MESSAGE_DELIVERY_SENT = "sent";
michael@0 101 const DOM_MOBILE_MESSAGE_DELIVERY_ERROR = "error";
michael@0 102
michael@0 103 const RADIO_POWER_OFF_TIMEOUT = 30000;
michael@0 104 const SMS_HANDLED_WAKELOCK_TIMEOUT = 5000;
michael@0 105 const HW_DEFAULT_CLIENT_ID = 0;
michael@0 106
michael@0 107 const RIL_IPC_MOBILECONNECTION_MSG_NAMES = [
michael@0 108 "RIL:GetRilContext",
michael@0 109 "RIL:GetAvailableNetworks",
michael@0 110 "RIL:SelectNetwork",
michael@0 111 "RIL:SelectNetworkAuto",
michael@0 112 "RIL:SetPreferredNetworkType",
michael@0 113 "RIL:GetPreferredNetworkType",
michael@0 114 "RIL:SendMMI",
michael@0 115 "RIL:CancelMMI",
michael@0 116 "RIL:RegisterMobileConnectionMsg",
michael@0 117 "RIL:SetCallForwardingOptions",
michael@0 118 "RIL:GetCallForwardingOptions",
michael@0 119 "RIL:SetCallBarringOptions",
michael@0 120 "RIL:GetCallBarringOptions",
michael@0 121 "RIL:ChangeCallBarringPassword",
michael@0 122 "RIL:SetCallWaitingOptions",
michael@0 123 "RIL:GetCallWaitingOptions",
michael@0 124 "RIL:SetCallingLineIdRestriction",
michael@0 125 "RIL:GetCallingLineIdRestriction",
michael@0 126 "RIL:SetRoamingPreference",
michael@0 127 "RIL:GetRoamingPreference",
michael@0 128 "RIL:ExitEmergencyCbMode",
michael@0 129 "RIL:SetRadioEnabled",
michael@0 130 "RIL:SetVoicePrivacyMode",
michael@0 131 "RIL:GetVoicePrivacyMode",
michael@0 132 "RIL:GetSupportedNetworkTypes"
michael@0 133 ];
michael@0 134
michael@0 135 const RIL_IPC_MOBILENETWORK_MSG_NAMES = [
michael@0 136 "RIL:GetLastKnownNetwork",
michael@0 137 "RIL:GetLastKnownHomeNetwork"
michael@0 138 ];
michael@0 139
michael@0 140 const RIL_IPC_ICCMANAGER_MSG_NAMES = [
michael@0 141 "RIL:SendStkResponse",
michael@0 142 "RIL:SendStkMenuSelection",
michael@0 143 "RIL:SendStkTimerExpiration",
michael@0 144 "RIL:SendStkEventDownload",
michael@0 145 "RIL:GetCardLockState",
michael@0 146 "RIL:UnlockCardLock",
michael@0 147 "RIL:SetCardLock",
michael@0 148 "RIL:GetCardLockRetryCount",
michael@0 149 "RIL:IccOpenChannel",
michael@0 150 "RIL:IccExchangeAPDU",
michael@0 151 "RIL:IccCloseChannel",
michael@0 152 "RIL:ReadIccContacts",
michael@0 153 "RIL:UpdateIccContact",
michael@0 154 "RIL:RegisterIccMsg",
michael@0 155 "RIL:MatchMvno"
michael@0 156 ];
michael@0 157
michael@0 158 const RIL_IPC_VOICEMAIL_MSG_NAMES = [
michael@0 159 "RIL:RegisterVoicemailMsg",
michael@0 160 "RIL:GetVoicemailInfo"
michael@0 161 ];
michael@0 162
michael@0 163 const RIL_IPC_CELLBROADCAST_MSG_NAMES = [
michael@0 164 "RIL:RegisterCellBroadcastMsg"
michael@0 165 ];
michael@0 166
michael@0 167 XPCOMUtils.defineLazyServiceGetter(this, "gPowerManagerService",
michael@0 168 "@mozilla.org/power/powermanagerservice;1",
michael@0 169 "nsIPowerManagerService");
michael@0 170
michael@0 171 XPCOMUtils.defineLazyServiceGetter(this, "gMobileMessageService",
michael@0 172 "@mozilla.org/mobilemessage/mobilemessageservice;1",
michael@0 173 "nsIMobileMessageService");
michael@0 174
michael@0 175 XPCOMUtils.defineLazyServiceGetter(this, "gSmsService",
michael@0 176 "@mozilla.org/sms/smsservice;1",
michael@0 177 "nsISmsService");
michael@0 178
michael@0 179 XPCOMUtils.defineLazyServiceGetter(this, "gMobileMessageDatabaseService",
michael@0 180 "@mozilla.org/mobilemessage/rilmobilemessagedatabaseservice;1",
michael@0 181 "nsIRilMobileMessageDatabaseService");
michael@0 182
michael@0 183 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
michael@0 184 "@mozilla.org/parentprocessmessagemanager;1",
michael@0 185 "nsIMessageBroadcaster");
michael@0 186
michael@0 187 XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService",
michael@0 188 "@mozilla.org/settingsService;1",
michael@0 189 "nsISettingsService");
michael@0 190
michael@0 191 XPCOMUtils.defineLazyServiceGetter(this, "gSystemMessenger",
michael@0 192 "@mozilla.org/system-message-internal;1",
michael@0 193 "nsISystemMessagesInternal");
michael@0 194
michael@0 195 XPCOMUtils.defineLazyServiceGetter(this, "gNetworkManager",
michael@0 196 "@mozilla.org/network/manager;1",
michael@0 197 "nsINetworkManager");
michael@0 198
michael@0 199 XPCOMUtils.defineLazyServiceGetter(this, "gTimeService",
michael@0 200 "@mozilla.org/time/timeservice;1",
michael@0 201 "nsITimeService");
michael@0 202
michael@0 203 XPCOMUtils.defineLazyServiceGetter(this, "gSystemWorkerManager",
michael@0 204 "@mozilla.org/telephony/system-worker-manager;1",
michael@0 205 "nsISystemWorkerManager");
michael@0 206
michael@0 207 XPCOMUtils.defineLazyServiceGetter(this, "gTelephonyProvider",
michael@0 208 "@mozilla.org/telephony/telephonyprovider;1",
michael@0 209 "nsIGonkTelephonyProvider");
michael@0 210
michael@0 211 XPCOMUtils.defineLazyGetter(this, "WAP", function() {
michael@0 212 let wap = {};
michael@0 213 Cu.import("resource://gre/modules/WapPushManager.js", wap);
michael@0 214 return wap;
michael@0 215 });
michael@0 216
michael@0 217 XPCOMUtils.defineLazyGetter(this, "PhoneNumberUtils", function() {
michael@0 218 let ns = {};
michael@0 219 Cu.import("resource://gre/modules/PhoneNumberUtils.jsm", ns);
michael@0 220 return ns.PhoneNumberUtils;
michael@0 221 });
michael@0 222
michael@0 223 XPCOMUtils.defineLazyGetter(this, "gMessageManager", function() {
michael@0 224 return {
michael@0 225 QueryInterface: XPCOMUtils.generateQI([Ci.nsIMessageListener,
michael@0 226 Ci.nsIObserver]),
michael@0 227
michael@0 228 ril: null,
michael@0 229
michael@0 230 // Manage message targets in terms of topic. Only the authorized and
michael@0 231 // registered contents can receive related messages.
michael@0 232 targetsByTopic: {},
michael@0 233 topics: [],
michael@0 234
michael@0 235 targetMessageQueue: [],
michael@0 236 ready: false,
michael@0 237
michael@0 238 init: function(ril) {
michael@0 239 this.ril = ril;
michael@0 240
michael@0 241 Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
michael@0 242 Services.obs.addObserver(this, kSysMsgListenerReadyObserverTopic, false);
michael@0 243 this._registerMessageListeners();
michael@0 244 },
michael@0 245
michael@0 246 _shutdown: function() {
michael@0 247 this.ril = null;
michael@0 248
michael@0 249 Services.obs.removeObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
michael@0 250 this._unregisterMessageListeners();
michael@0 251 },
michael@0 252
michael@0 253 _registerMessageListeners: function() {
michael@0 254 ppmm.addMessageListener("child-process-shutdown", this);
michael@0 255 for (let msgname of RIL_IPC_MOBILECONNECTION_MSG_NAMES) {
michael@0 256 ppmm.addMessageListener(msgname, this);
michael@0 257 }
michael@0 258 for (let msgname of RIL_IPC_MOBILENETWORK_MSG_NAMES) {
michael@0 259 ppmm.addMessageListener(msgname, this);
michael@0 260 }
michael@0 261 for (let msgName of RIL_IPC_ICCMANAGER_MSG_NAMES) {
michael@0 262 ppmm.addMessageListener(msgName, this);
michael@0 263 }
michael@0 264 for (let msgname of RIL_IPC_VOICEMAIL_MSG_NAMES) {
michael@0 265 ppmm.addMessageListener(msgname, this);
michael@0 266 }
michael@0 267 for (let msgname of RIL_IPC_CELLBROADCAST_MSG_NAMES) {
michael@0 268 ppmm.addMessageListener(msgname, this);
michael@0 269 }
michael@0 270 },
michael@0 271
michael@0 272 _unregisterMessageListeners: function() {
michael@0 273 ppmm.removeMessageListener("child-process-shutdown", this);
michael@0 274 for (let msgname of RIL_IPC_MOBILECONNECTION_MSG_NAMES) {
michael@0 275 ppmm.removeMessageListener(msgname, this);
michael@0 276 }
michael@0 277 for (let msgname of RIL_IPC_MOBILENETWORK_MSG_NAMES) {
michael@0 278 ppmm.removeMessageListener(msgname, this);
michael@0 279 }
michael@0 280 for (let msgName of RIL_IPC_ICCMANAGER_MSG_NAMES) {
michael@0 281 ppmm.removeMessageListener(msgName, this);
michael@0 282 }
michael@0 283 for (let msgname of RIL_IPC_VOICEMAIL_MSG_NAMES) {
michael@0 284 ppmm.removeMessageListener(msgname, this);
michael@0 285 }
michael@0 286 for (let msgname of RIL_IPC_CELLBROADCAST_MSG_NAMES) {
michael@0 287 ppmm.removeMessageListener(msgname, this);
michael@0 288 }
michael@0 289 ppmm = null;
michael@0 290 },
michael@0 291
michael@0 292 _registerMessageTarget: function(topic, target) {
michael@0 293 let targets = this.targetsByTopic[topic];
michael@0 294 if (!targets) {
michael@0 295 targets = this.targetsByTopic[topic] = [];
michael@0 296 let list = this.topics;
michael@0 297 if (list.indexOf(topic) == -1) {
michael@0 298 list.push(topic);
michael@0 299 }
michael@0 300 }
michael@0 301
michael@0 302 if (targets.indexOf(target) != -1) {
michael@0 303 if (DEBUG) debug("Already registered this target!");
michael@0 304 return;
michael@0 305 }
michael@0 306
michael@0 307 targets.push(target);
michael@0 308 if (DEBUG) debug("Registered " + topic + " target: " + target);
michael@0 309 },
michael@0 310
michael@0 311 _unregisterMessageTarget: function(topic, target) {
michael@0 312 if (topic == null) {
michael@0 313 // Unregister the target for every topic when no topic is specified.
michael@0 314 for (let type of this.topics) {
michael@0 315 this._unregisterMessageTarget(type, target);
michael@0 316 }
michael@0 317 return;
michael@0 318 }
michael@0 319
michael@0 320 // Unregister the target for a specified topic.
michael@0 321 let targets = this.targetsByTopic[topic];
michael@0 322 if (!targets) {
michael@0 323 return;
michael@0 324 }
michael@0 325
michael@0 326 let index = targets.indexOf(target);
michael@0 327 if (index != -1) {
michael@0 328 targets.splice(index, 1);
michael@0 329 if (DEBUG) debug("Unregistered " + topic + " target: " + target);
michael@0 330 }
michael@0 331 },
michael@0 332
michael@0 333 _enqueueTargetMessage: function(topic, message, options) {
michael@0 334 let msg = { topic : topic,
michael@0 335 message : message,
michael@0 336 options : options };
michael@0 337 // Remove previous queued message with the same message type and client Id
michael@0 338 // , only one message per (message type + client Id) is allowed in queue.
michael@0 339 let messageQueue = this.targetMessageQueue;
michael@0 340 for(let i = 0; i < messageQueue.length; i++) {
michael@0 341 if (messageQueue[i].message === message &&
michael@0 342 messageQueue[i].options.clientId === options.clientId) {
michael@0 343 messageQueue.splice(i, 1);
michael@0 344 break;
michael@0 345 }
michael@0 346 }
michael@0 347
michael@0 348 messageQueue.push(msg);
michael@0 349 },
michael@0 350
michael@0 351 _sendTargetMessage: function(topic, message, options) {
michael@0 352 if (!this.ready) {
michael@0 353 this._enqueueTargetMessage(topic, message, options);
michael@0 354 return;
michael@0 355 }
michael@0 356
michael@0 357 let targets = this.targetsByTopic[topic];
michael@0 358 if (!targets) {
michael@0 359 return;
michael@0 360 }
michael@0 361
michael@0 362 for (let target of targets) {
michael@0 363 target.sendAsyncMessage(message, options);
michael@0 364 }
michael@0 365 },
michael@0 366
michael@0 367 _resendQueuedTargetMessage: function() {
michael@0 368 this.ready = true;
michael@0 369
michael@0 370 // Here uses this._sendTargetMessage() to resend message, which will
michael@0 371 // enqueue message if listener is not ready.
michael@0 372 // So only resend after listener is ready, or it will cause infinate loop and
michael@0 373 // hang the system.
michael@0 374
michael@0 375 // Dequeue and resend messages.
michael@0 376 for each (let msg in this.targetMessageQueue) {
michael@0 377 this._sendTargetMessage(msg.topic, msg.message, msg.options);
michael@0 378 }
michael@0 379 this.targetMessageQueue = null;
michael@0 380 },
michael@0 381
michael@0 382 /**
michael@0 383 * nsIMessageListener interface methods.
michael@0 384 */
michael@0 385
michael@0 386 receiveMessage: function(msg) {
michael@0 387 if (DEBUG) debug("Received '" + msg.name + "' message from content process");
michael@0 388 if (msg.name == "child-process-shutdown") {
michael@0 389 // By the time we receive child-process-shutdown, the child process has
michael@0 390 // already forgotten its permissions so we need to unregister the target
michael@0 391 // for every permission.
michael@0 392 this._unregisterMessageTarget(null, msg.target);
michael@0 393 return null;
michael@0 394 }
michael@0 395
michael@0 396 if (RIL_IPC_MOBILECONNECTION_MSG_NAMES.indexOf(msg.name) != -1) {
michael@0 397 if (!msg.target.assertPermission("mobileconnection")) {
michael@0 398 if (DEBUG) {
michael@0 399 debug("MobileConnection message " + msg.name +
michael@0 400 " from a content process with no 'mobileconnection' privileges.");
michael@0 401 }
michael@0 402 return null;
michael@0 403 }
michael@0 404 } else if (RIL_IPC_MOBILENETWORK_MSG_NAMES.indexOf(msg.name) != -1) {
michael@0 405 if (!msg.target.assertPermission("mobilenetwork")) {
michael@0 406 if (DEBUG) {
michael@0 407 debug("MobileNetwork message " + msg.name +
michael@0 408 " from a content process with no 'mobilenetwork' privileges.");
michael@0 409 }
michael@0 410 return null;
michael@0 411 }
michael@0 412 } else if (RIL_IPC_ICCMANAGER_MSG_NAMES.indexOf(msg.name) != -1) {
michael@0 413 if (!msg.target.assertPermission("mobileconnection")) {
michael@0 414 if (DEBUG) {
michael@0 415 debug("IccManager message " + msg.name +
michael@0 416 " from a content process with no 'mobileconnection' privileges.");
michael@0 417 }
michael@0 418 return null;
michael@0 419 }
michael@0 420 } else if (RIL_IPC_VOICEMAIL_MSG_NAMES.indexOf(msg.name) != -1) {
michael@0 421 if (!msg.target.assertPermission("voicemail")) {
michael@0 422 if (DEBUG) {
michael@0 423 debug("Voicemail message " + msg.name +
michael@0 424 " from a content process with no 'voicemail' privileges.");
michael@0 425 }
michael@0 426 return null;
michael@0 427 }
michael@0 428 } else if (RIL_IPC_CELLBROADCAST_MSG_NAMES.indexOf(msg.name) != -1) {
michael@0 429 if (!msg.target.assertPermission("cellbroadcast")) {
michael@0 430 if (DEBUG) {
michael@0 431 debug("Cell Broadcast message " + msg.name +
michael@0 432 " from a content process with no 'cellbroadcast' privileges.");
michael@0 433 }
michael@0 434 return null;
michael@0 435 }
michael@0 436 } else {
michael@0 437 if (DEBUG) debug("Ignoring unknown message type: " + msg.name);
michael@0 438 return null;
michael@0 439 }
michael@0 440
michael@0 441 switch (msg.name) {
michael@0 442 case "RIL:RegisterMobileConnectionMsg":
michael@0 443 this._registerMessageTarget("mobileconnection", msg.target);
michael@0 444 return null;
michael@0 445 case "RIL:RegisterIccMsg":
michael@0 446 this._registerMessageTarget("icc", msg.target);
michael@0 447 return null;
michael@0 448 case "RIL:RegisterVoicemailMsg":
michael@0 449 this._registerMessageTarget("voicemail", msg.target);
michael@0 450 return null;
michael@0 451 case "RIL:RegisterCellBroadcastMsg":
michael@0 452 this._registerMessageTarget("cellbroadcast", msg.target);
michael@0 453 return null;
michael@0 454 }
michael@0 455
michael@0 456 let clientId = msg.json.clientId || 0;
michael@0 457 let radioInterface = this.ril.getRadioInterface(clientId);
michael@0 458 if (!radioInterface) {
michael@0 459 if (DEBUG) debug("No such radio interface: " + clientId);
michael@0 460 return null;
michael@0 461 }
michael@0 462
michael@0 463 if (msg.name === "RIL:SetRadioEnabled") {
michael@0 464 // Special handler for SetRadioEnabled.
michael@0 465 return gRadioEnabledController.receiveMessage(msg);
michael@0 466 }
michael@0 467
michael@0 468 return radioInterface.receiveMessage(msg);
michael@0 469 },
michael@0 470
michael@0 471 /**
michael@0 472 * nsIObserver interface methods.
michael@0 473 */
michael@0 474
michael@0 475 observe: function(subject, topic, data) {
michael@0 476 switch (topic) {
michael@0 477 case kSysMsgListenerReadyObserverTopic:
michael@0 478 Services.obs.removeObserver(this, kSysMsgListenerReadyObserverTopic);
michael@0 479 this._resendQueuedTargetMessage();
michael@0 480 break;
michael@0 481 case NS_XPCOM_SHUTDOWN_OBSERVER_ID:
michael@0 482 this._shutdown();
michael@0 483 break;
michael@0 484 }
michael@0 485 },
michael@0 486
michael@0 487 sendMobileConnectionMessage: function(message, clientId, data) {
michael@0 488 this._sendTargetMessage("mobileconnection", message, {
michael@0 489 clientId: clientId,
michael@0 490 data: data
michael@0 491 });
michael@0 492 },
michael@0 493
michael@0 494 sendVoicemailMessage: function(message, clientId, data) {
michael@0 495 this._sendTargetMessage("voicemail", message, {
michael@0 496 clientId: clientId,
michael@0 497 data: data
michael@0 498 });
michael@0 499 },
michael@0 500
michael@0 501 sendCellBroadcastMessage: function(message, clientId, data) {
michael@0 502 this._sendTargetMessage("cellbroadcast", message, {
michael@0 503 clientId: clientId,
michael@0 504 data: data
michael@0 505 });
michael@0 506 },
michael@0 507
michael@0 508 sendIccMessage: function(message, clientId, data) {
michael@0 509 this._sendTargetMessage("icc", message, {
michael@0 510 clientId: clientId,
michael@0 511 data: data
michael@0 512 });
michael@0 513 }
michael@0 514 };
michael@0 515 });
michael@0 516
michael@0 517 XPCOMUtils.defineLazyGetter(this, "gRadioEnabledController", function() {
michael@0 518 let _ril = null;
michael@0 519 let _pendingMessages = []; // For queueing "RIL =SetRadioEnabled" messages.
michael@0 520 let _isProcessingPending = false;
michael@0 521 let _timer = null;
michael@0 522 let _request = null;
michael@0 523 let _deactivatingDeferred = {};
michael@0 524 let _initializedCardState = {};
michael@0 525 let _allCardStateInitialized = !RILQUIRKS_RADIO_OFF_WO_CARD;
michael@0 526
michael@0 527 return {
michael@0 528 init: function(ril) {
michael@0 529 _ril = ril;
michael@0 530 },
michael@0 531
michael@0 532 receiveCardState: function(clientId) {
michael@0 533 if (_allCardStateInitialized) {
michael@0 534 return;
michael@0 535 }
michael@0 536
michael@0 537 if (DEBUG) debug("RadioControl: receive cardState from " + clientId);
michael@0 538 _initializedCardState[clientId] = true;
michael@0 539 if (Object.keys(_initializedCardState).length == _ril.numRadioInterfaces) {
michael@0 540 _allCardStateInitialized = true;
michael@0 541 this._startProcessingPending();
michael@0 542 }
michael@0 543 },
michael@0 544
michael@0 545 receiveMessage: function(msg) {
michael@0 546 if (DEBUG) debug("RadioControl: receiveMessage: " + JSON.stringify(msg));
michael@0 547 _pendingMessages.push(msg);
michael@0 548 this._startProcessingPending();
michael@0 549 },
michael@0 550
michael@0 551 isDeactivatingDataCalls: function() {
michael@0 552 return _request !== null;
michael@0 553 },
michael@0 554
michael@0 555 finishDeactivatingDataCalls: function(clientId) {
michael@0 556 if (DEBUG) debug("RadioControl: finishDeactivatingDataCalls: " + clientId);
michael@0 557 let deferred = _deactivatingDeferred[clientId];
michael@0 558 if (deferred) {
michael@0 559 deferred.resolve();
michael@0 560 }
michael@0 561 },
michael@0 562
michael@0 563 _startProcessingPending: function() {
michael@0 564 if (!_isProcessingPending) {
michael@0 565 if (DEBUG) debug("RadioControl: start dequeue");
michael@0 566 _isProcessingPending = true;
michael@0 567 this._processNextMessage();
michael@0 568 }
michael@0 569 },
michael@0 570
michael@0 571 _processNextMessage: function() {
michael@0 572 if (_pendingMessages.length === 0 || !_allCardStateInitialized) {
michael@0 573 if (DEBUG) debug("RadioControl: stop dequeue");
michael@0 574 _isProcessingPending = false;
michael@0 575 return;
michael@0 576 }
michael@0 577
michael@0 578 let msg = _pendingMessages.shift();
michael@0 579 this._handleMessage(msg);
michael@0 580 },
michael@0 581
michael@0 582 _getNumCards: function() {
michael@0 583 let numCards = 0;
michael@0 584 for (let i = 0, N = _ril.numRadioInterfaces; i < N; ++i) {
michael@0 585 if (this._isCardPresentAtClient(i)) {
michael@0 586 numCards++;
michael@0 587 }
michael@0 588 }
michael@0 589 return numCards;
michael@0 590 },
michael@0 591
michael@0 592 _isCardPresentAtClient: function(clientId) {
michael@0 593 let cardState = _ril.getRadioInterface(clientId).rilContext.cardState;
michael@0 594 return cardState !== RIL.GECKO_CARDSTATE_UNDETECTED &&
michael@0 595 cardState !== RIL.GECKO_CARDSTATE_UNKNOWN;
michael@0 596 },
michael@0 597
michael@0 598 _isRadioAbleToEnableAtClient: function(clientId, numCards) {
michael@0 599 if (!RILQUIRKS_RADIO_OFF_WO_CARD) {
michael@0 600 return true;
michael@0 601 }
michael@0 602
michael@0 603 // We could only turn on the radio for clientId if
michael@0 604 // 1. a SIM card is presented or
michael@0 605 // 2. it is the default clientId and there is no any SIM card at any client.
michael@0 606
michael@0 607 if (this._isCardPresentAtClient(clientId)) {
michael@0 608 return true;
michael@0 609 }
michael@0 610
michael@0 611 numCards = numCards == null ? this._getNumCards() : numCards;
michael@0 612 if (clientId === HW_DEFAULT_CLIENT_ID && numCards === 0) {
michael@0 613 return true;
michael@0 614 }
michael@0 615
michael@0 616 return false;
michael@0 617 },
michael@0 618
michael@0 619 _handleMessage: function(msg) {
michael@0 620 if (DEBUG) debug("RadioControl: handleMessage: " + JSON.stringify(msg));
michael@0 621 let clientId = msg.json.clientId || 0;
michael@0 622 let radioInterface = _ril.getRadioInterface(clientId);
michael@0 623
michael@0 624 if (!radioInterface.isValidStateForSetRadioEnabled()) {
michael@0 625 radioInterface.setRadioEnabledResponse(msg.target, msg.json.data,
michael@0 626 "InvalidStateError");
michael@0 627 this._processNextMessage();
michael@0 628 return;
michael@0 629 }
michael@0 630
michael@0 631 if (radioInterface.isDummyForSetRadioEnabled(msg.json.data)) {
michael@0 632 radioInterface.setRadioEnabledResponse(msg.target, msg.json.data);
michael@0 633 this._processNextMessage();
michael@0 634 return;
michael@0 635 }
michael@0 636
michael@0 637 if (msg.json.data.enabled) {
michael@0 638 if (this._isRadioAbleToEnableAtClient(clientId)) {
michael@0 639 radioInterface.receiveMessage(msg);
michael@0 640 } else {
michael@0 641 // Not really do it but respond success.
michael@0 642 radioInterface.setRadioEnabledResponse(msg.target, msg.json.data);
michael@0 643 }
michael@0 644
michael@0 645 this._processNextMessage();
michael@0 646 } else {
michael@0 647 _request = function() {
michael@0 648 radioInterface.receiveMessage(msg);
michael@0 649 };
michael@0 650
michael@0 651 // In 2G network, modem takes 35+ seconds to process deactivate data
michael@0 652 // call request if device has active voice call (please see bug 964974
michael@0 653 // for more details). Therefore we should hangup all active voice calls
michael@0 654 // first. And considering some DSDS architecture, toggling one radio may
michael@0 655 // toggle both, so we send hangUpAll to all clients.
michael@0 656 for (let i = 0, N = _ril.numRadioInterfaces; i < N; ++i) {
michael@0 657 let iface = _ril.getRadioInterface(i);
michael@0 658 iface.workerMessenger.send("hangUpAll");
michael@0 659 }
michael@0 660
michael@0 661 // In some DSDS architecture with only one modem, toggling one radio may
michael@0 662 // toggle both. Therefore, for safely turning off, we should first
michael@0 663 // explicitly deactivate all data calls from all clients.
michael@0 664 this._deactivateDataCalls().then(() => {
michael@0 665 if (DEBUG) debug("RadioControl: deactivation done");
michael@0 666 this._executeRequest();
michael@0 667 });
michael@0 668
michael@0 669 this._createTimer();
michael@0 670 }
michael@0 671 },
michael@0 672
michael@0 673 _deactivateDataCalls: function() {
michael@0 674 if (DEBUG) debug("RadioControl: deactivating data calls...");
michael@0 675 _deactivatingDeferred = {};
michael@0 676
michael@0 677 let promise = Promise.resolve();
michael@0 678 for (let i = 0, N = _ril.numRadioInterfaces; i < N; ++i) {
michael@0 679 promise = promise.then(this._deactivateDataCallsForClient(i));
michael@0 680 }
michael@0 681
michael@0 682 return promise;
michael@0 683 },
michael@0 684
michael@0 685 _deactivateDataCallsForClient: function(clientId) {
michael@0 686 return function() {
michael@0 687 let deferred = _deactivatingDeferred[clientId] = Promise.defer();
michael@0 688 let dataConnectionHandler = gDataConnectionManager.getConnectionHandler(clientId);
michael@0 689 dataConnectionHandler.deactivateDataCalls();
michael@0 690 return deferred.promise;
michael@0 691 };
michael@0 692 },
michael@0 693
michael@0 694 _createTimer: function() {
michael@0 695 if (!_timer) {
michael@0 696 _timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
michael@0 697 }
michael@0 698 _timer.initWithCallback(this._executeRequest.bind(this),
michael@0 699 RADIO_POWER_OFF_TIMEOUT,
michael@0 700 Ci.nsITimer.TYPE_ONE_SHOT);
michael@0 701 },
michael@0 702
michael@0 703 _cancelTimer: function() {
michael@0 704 if (_timer) {
michael@0 705 _timer.cancel();
michael@0 706 }
michael@0 707 },
michael@0 708
michael@0 709 _executeRequest: function() {
michael@0 710 if (typeof _request === "function") {
michael@0 711 if (DEBUG) debug("RadioControl: executeRequest");
michael@0 712 this._cancelTimer();
michael@0 713 _request();
michael@0 714 _request = null;
michael@0 715 }
michael@0 716 this._processNextMessage();
michael@0 717 },
michael@0 718 };
michael@0 719 });
michael@0 720
michael@0 721 XPCOMUtils.defineLazyGetter(this, "gDataConnectionManager", function () {
michael@0 722 return {
michael@0 723 QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
michael@0 724 Ci.nsISettingsServiceCallback]),
michael@0 725
michael@0 726 _connectionHandlers: null,
michael@0 727
michael@0 728 // Flag to determine the data state to start with when we boot up. It
michael@0 729 // corresponds to the 'ril.data.enabled' setting from the UI.
michael@0 730 _dataEnabled: false,
michael@0 731
michael@0 732 // Flag to record the default client id for data call. It corresponds to
michael@0 733 // the 'ril.data.defaultServiceId' setting from the UI.
michael@0 734 _dataDefaultClientId: -1,
michael@0 735
michael@0 736 // Flag to record the current default client id for data call.
michael@0 737 // It differs from _dataDefaultClientId in that it is set only when
michael@0 738 // the switch of client id process is done.
michael@0 739 _currentDataClientId: -1,
michael@0 740
michael@0 741 // Pending function to execute when we are notified that another data call has
michael@0 742 // been disconnected.
michael@0 743 _pendingDataCallRequest: null,
michael@0 744
michael@0 745 debug: function(s) {
michael@0 746 dump("-*- DataConnectionManager: " + s + "\n");
michael@0 747 },
michael@0 748
michael@0 749 init: function(ril) {
michael@0 750 if (!ril) {
michael@0 751 return;
michael@0 752 }
michael@0 753
michael@0 754 this._connectionHandlers = [];
michael@0 755 for (let clientId = 0; clientId < ril.numRadioInterfaces; clientId++) {
michael@0 756 let radioInterface = ril.getRadioInterface(clientId);
michael@0 757 this._connectionHandlers.push(
michael@0 758 new DataConnectionHandler(clientId, radioInterface));
michael@0 759 }
michael@0 760
michael@0 761 let lock = gSettingsService.createLock();
michael@0 762 // Read the APN data from the settings DB.
michael@0 763 lock.get("ril.data.apnSettings", this);
michael@0 764 // Read the data enabled setting from DB.
michael@0 765 lock.get("ril.data.enabled", this);
michael@0 766 lock.get("ril.data.roaming_enabled", this);
michael@0 767 // Read the default client id for data call.
michael@0 768 lock.get("ril.data.defaultServiceId", this);
michael@0 769
michael@0 770 Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
michael@0 771 Services.obs.addObserver(this, kMozSettingsChangedObserverTopic, false);
michael@0 772 Services.obs.addObserver(this, kNetworkInterfaceStateChangedTopic, false);
michael@0 773 },
michael@0 774
michael@0 775 getConnectionHandler: function(clientId) {
michael@0 776 return this._connectionHandlers[clientId];
michael@0 777 },
michael@0 778
michael@0 779 _handleDataClientIdChange: function(newDefault) {
michael@0 780 if (this._dataDefaultClientId === newDefault) {
michael@0 781 return;
michael@0 782 }
michael@0 783 this._dataDefaultClientId = newDefault;
michael@0 784
michael@0 785 if (this._currentDataClientId == -1) {
michael@0 786 // This is to handle boot up stage.
michael@0 787 this._currentDataClientId = this._dataDefaultClientId;
michael@0 788 let connHandler = this._connectionHandlers[this._currentDataClientId];
michael@0 789 let radioInterface = connHandler.radioInterface;
michael@0 790 if (RILQUIRKS_DATA_REGISTRATION_ON_DEMAND) {
michael@0 791 radioInterface.setDataRegistration(true);
michael@0 792 }
michael@0 793 if (this._dataEnabled) {
michael@0 794 let settings = connHandler.dataCallSettings;
michael@0 795 settings.oldEnabled = settings.enabled;
michael@0 796 settings.enabled = true;
michael@0 797 connHandler.updateRILNetworkInterface();
michael@0 798 }
michael@0 799 return;
michael@0 800 }
michael@0 801
michael@0 802 let oldConnHandler = this._connectionHandlers[this._currentDataClientId];
michael@0 803 let oldIface = oldConnHandler.radioInterface;
michael@0 804 let oldSettings = oldConnHandler.dataCallSettings;
michael@0 805 let newConnHandler = this._connectionHandlers[this._dataDefaultClientId];
michael@0 806 let newIface = newConnHandler.radioInterface;
michael@0 807 let newSettings = newConnHandler.dataCallSettings;
michael@0 808
michael@0 809 if (!this._dataEnabled) {
michael@0 810 if (RILQUIRKS_DATA_REGISTRATION_ON_DEMAND) {
michael@0 811 oldIface.setDataRegistration(false);
michael@0 812 newIface.setDataRegistration(true);
michael@0 813 }
michael@0 814 this._currentDataClientId = this._dataDefaultClientId;
michael@0 815 return;
michael@0 816 }
michael@0 817
michael@0 818 oldSettings.oldEnabled = oldSettings.enabled;
michael@0 819 oldSettings.enabled = false;
michael@0 820
michael@0 821 if (oldConnHandler.anyDataConnected()) {
michael@0 822 this._pendingDataCallRequest = function () {
michael@0 823 if (DEBUG) {
michael@0 824 this.debug("Executing pending data call request.");
michael@0 825 }
michael@0 826 if (RILQUIRKS_DATA_REGISTRATION_ON_DEMAND) {
michael@0 827 newIface.setDataRegistration(true);
michael@0 828 }
michael@0 829 newSettings.oldEnabled = newSettings.enabled;
michael@0 830 newSettings.enabled = this._dataEnabled;
michael@0 831
michael@0 832 this._currentDataClientId = this._dataDefaultClientId;
michael@0 833 newConnHandler.updateRILNetworkInterface();
michael@0 834 };
michael@0 835
michael@0 836 if (DEBUG) {
michael@0 837 this.debug("_handleDataClientIdChange: existing data call(s) active" +
michael@0 838 ", wait for them to get disconnected.");
michael@0 839 }
michael@0 840 oldConnHandler.deactivateDataCalls();
michael@0 841 return;
michael@0 842 }
michael@0 843
michael@0 844 newSettings.oldEnabled = newSettings.enabled;
michael@0 845 newSettings.enabled = true;
michael@0 846
michael@0 847 this._currentDataClientId = this._dataDefaultClientId;
michael@0 848 if (RILQUIRKS_DATA_REGISTRATION_ON_DEMAND) {
michael@0 849 oldIface.setDataRegistration(false);
michael@0 850 newIface.setDataRegistration(true);
michael@0 851 }
michael@0 852 newConnHandler.updateRILNetworkInterface();
michael@0 853 },
michael@0 854
michael@0 855 _shutdown: function() {
michael@0 856 for (let handler of this._connectionHandlers) {
michael@0 857 handler.shutdown();
michael@0 858 }
michael@0 859 this._connectionHandlers = null;
michael@0 860 Services.obs.removeObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
michael@0 861 Services.obs.removeObserver(this, kMozSettingsChangedObserverTopic);
michael@0 862 Services.obs.removeObserver(this, kNetworkInterfaceStateChangedTopic);
michael@0 863 },
michael@0 864
michael@0 865 /**
michael@0 866 * nsISettingsServiceCallback
michael@0 867 */
michael@0 868 handle: function(name, result) {
michael@0 869 switch(name) {
michael@0 870 case "ril.data.apnSettings":
michael@0 871 if (DEBUG) {
michael@0 872 this.debug("'ril.data.apnSettings' is now " +
michael@0 873 JSON.stringify(result));
michael@0 874 }
michael@0 875 if (!result) {
michael@0 876 break;
michael@0 877 }
michael@0 878 for (let clientId in this._connectionHandlers) {
michael@0 879 let handler = this._connectionHandlers[clientId];
michael@0 880 let apnSetting = result[clientId];
michael@0 881 if (handler && apnSetting) {
michael@0 882 handler.updateApnSettings(apnSetting);
michael@0 883 handler.updateRILNetworkInterface();
michael@0 884 }
michael@0 885 }
michael@0 886 break;
michael@0 887 case "ril.data.enabled":
michael@0 888 if (DEBUG) {
michael@0 889 this.debug("'ril.data.enabled' is now " + result);
michael@0 890 }
michael@0 891 if (this._dataEnabled === result) {
michael@0 892 break;
michael@0 893 }
michael@0 894 this._dataEnabled = result;
michael@0 895
michael@0 896 if (DEBUG) {
michael@0 897 this.debug("Default id for data call: " + this._dataDefaultClientId);
michael@0 898 }
michael@0 899 if (this._dataDefaultClientId === -1) {
michael@0 900 // We haven't got the default id for data from db.
michael@0 901 break;
michael@0 902 }
michael@0 903
michael@0 904 let connHandler = this._connectionHandlers[this._dataDefaultClientId];
michael@0 905 let settings = connHandler.dataCallSettings;
michael@0 906 settings.oldEnabled = settings.enabled;
michael@0 907 settings.enabled = result;
michael@0 908 connHandler.updateRILNetworkInterface();
michael@0 909 break;
michael@0 910 case "ril.data.roaming_enabled":
michael@0 911 if (DEBUG) {
michael@0 912 this.debug("'ril.data.roaming_enabled' is now " + result);
michael@0 913 this.debug("Default id for data call: " + this._dataDefaultClientId);
michael@0 914 }
michael@0 915 for (let clientId = 0; clientId < this._connectionHandlers.length; clientId++) {
michael@0 916 let connHandler = this._connectionHandlers[clientId];
michael@0 917 let settings = connHandler.dataCallSettings;
michael@0 918 settings.roamingEnabled = Array.isArray(result) ? result[clientId] : result;
michael@0 919 }
michael@0 920 if (this._dataDefaultClientId === -1) {
michael@0 921 // We haven't got the default id for data from db.
michael@0 922 break;
michael@0 923 }
michael@0 924 this._connectionHandlers[this._dataDefaultClientId].updateRILNetworkInterface();
michael@0 925 break;
michael@0 926 case "ril.data.defaultServiceId":
michael@0 927 result = result || 0;
michael@0 928 if (DEBUG) {
michael@0 929 this.debug("'ril.data.defaultServiceId' is now " + result);
michael@0 930 }
michael@0 931 this._handleDataClientIdChange(result);
michael@0 932 break;
michael@0 933 }
michael@0 934 },
michael@0 935
michael@0 936 handleError: function(errorMessage) {
michael@0 937 if (DEBUG) {
michael@0 938 this.debug("There was an error while reading RIL settings.");
michael@0 939 }
michael@0 940 },
michael@0 941
michael@0 942 /**
michael@0 943 * nsIObserver interface methods.
michael@0 944 */
michael@0 945 observe: function(subject, topic, data) {
michael@0 946 switch (topic) {
michael@0 947 case kMozSettingsChangedObserverTopic:
michael@0 948 let setting = JSON.parse(data);
michael@0 949 this.handle(setting.key, setting.value);
michael@0 950 break;
michael@0 951 case kNetworkInterfaceStateChangedTopic:
michael@0 952 let network = subject.QueryInterface(Ci.nsINetworkInterface);
michael@0 953 // DSDS: setup pending data connection when switching the default id
michael@0 954 // for data call. We can not use network.type to tell if it's
michael@0 955 // NETWORK_TYPE_MOBILE, since the type is removed from
michael@0 956 // RILNetworkInterface.connectedTypes on disconnect().
michael@0 957 if (network.state == Ci.nsINetworkInterface.NETWORK_STATE_UNKNOWN) {
michael@0 958 let connHandler = this._connectionHandlers[this._currentDataClientId];
michael@0 959 let radioInterface = connHandler.radioInterface;
michael@0 960 if (connHandler.allDataDisconnected() &&
michael@0 961 typeof this._pendingDataCallRequest === "function") {
michael@0 962 if (RILQUIRKS_DATA_REGISTRATION_ON_DEMAND) {
michael@0 963 radioInterface.setDataRegistration(false);
michael@0 964 }
michael@0 965 if (DEBUG) {
michael@0 966 this.debug("All data calls disconnected, setup pending data call.");
michael@0 967 }
michael@0 968 this._pendingDataCallRequest();
michael@0 969 this._pendingDataCallRequest = null;
michael@0 970 }
michael@0 971 }
michael@0 972 break;
michael@0 973 case NS_XPCOM_SHUTDOWN_OBSERVER_ID:
michael@0 974 this._shutdown();
michael@0 975 break;
michael@0 976 }
michael@0 977 },
michael@0 978 };
michael@0 979 });
michael@0 980
michael@0 981 // Initialize shared preference "ril.numRadioInterfaces" according to system
michael@0 982 // property.
michael@0 983 try {
michael@0 984 Services.prefs.setIntPref(kPrefRilNumRadioInterfaces, (function() {
michael@0 985 // When Gonk property "ro.moz.ril.numclients" is not set, return 1; if
michael@0 986 // explicitly set to any number larger-equal than 0, return num; else, return
michael@0 987 // 1 for compatibility.
michael@0 988 try {
michael@0 989 let numString = libcutils.property_get("ro.moz.ril.numclients", "1");
michael@0 990 let num = parseInt(numString, 10);
michael@0 991 if (num >= 0) {
michael@0 992 return num;
michael@0 993 }
michael@0 994 } catch (e) {}
michael@0 995
michael@0 996 return 1;
michael@0 997 })());
michael@0 998 } catch (e) {}
michael@0 999
michael@0 1000 function IccInfo() {}
michael@0 1001 IccInfo.prototype = {
michael@0 1002 iccType: null,
michael@0 1003 iccid: null,
michael@0 1004 mcc: null,
michael@0 1005 mnc: null,
michael@0 1006 spn: null,
michael@0 1007 isDisplayNetworkNameRequired: null,
michael@0 1008 isDisplaySpnRequired: null
michael@0 1009 };
michael@0 1010
michael@0 1011 function GsmIccInfo() {}
michael@0 1012 GsmIccInfo.prototype = {
michael@0 1013 __proto__: IccInfo.prototype,
michael@0 1014 QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMMozGsmIccInfo]),
michael@0 1015 classID: GSMICCINFO_CID,
michael@0 1016 classInfo: XPCOMUtils.generateCI({
michael@0 1017 classID: GSMICCINFO_CID,
michael@0 1018 classDescription: "MozGsmIccInfo",
michael@0 1019 flags: Ci.nsIClassInfo.DOM_OBJECT,
michael@0 1020 interfaces: [Ci.nsIDOMMozGsmIccInfo]
michael@0 1021 }),
michael@0 1022
michael@0 1023 // nsIDOMMozGsmIccInfo
michael@0 1024
michael@0 1025 msisdn: null
michael@0 1026 };
michael@0 1027
michael@0 1028 function CdmaIccInfo() {}
michael@0 1029 CdmaIccInfo.prototype = {
michael@0 1030 __proto__: IccInfo.prototype,
michael@0 1031 QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMMozCdmaIccInfo]),
michael@0 1032 classID: CDMAICCINFO_CID,
michael@0 1033 classInfo: XPCOMUtils.generateCI({
michael@0 1034 classID: CDMAICCINFO_CID,
michael@0 1035 classDescription: "MozCdmaIccInfo",
michael@0 1036 flags: Ci.nsIClassInfo.DOM_OBJECT,
michael@0 1037 interfaces: [Ci.nsIDOMMozCdmaIccInfo]
michael@0 1038 }),
michael@0 1039
michael@0 1040 // nsIDOMMozCdmaIccInfo
michael@0 1041
michael@0 1042 mdn: null,
michael@0 1043 prlVersion: 0
michael@0 1044 };
michael@0 1045
michael@0 1046 function DataConnectionHandler(clientId, radioInterface) {
michael@0 1047 // Initial owning attributes.
michael@0 1048 this.clientId = clientId;
michael@0 1049 this.radioInterface = radioInterface;
michael@0 1050 this.dataCallSettings = {
michael@0 1051 oldEnabled: false,
michael@0 1052 enabled: false,
michael@0 1053 roamingEnabled: false
michael@0 1054 };
michael@0 1055 this._dataCallbacks = [];
michael@0 1056
michael@0 1057 // This matrix is used to keep all the APN settings.
michael@0 1058 // - |byApn| object makes it easier to get the corresponding APN setting
michael@0 1059 // via a given set of APN, user name and password.
michael@0 1060 // - |byType| object makes it easier to get the corresponding APN setting
michael@0 1061 // via a given APN type.
michael@0 1062 this.apnSettings = {
michael@0 1063 byType: {},
michael@0 1064 byApn: {}
michael@0 1065 };
michael@0 1066 }
michael@0 1067 DataConnectionHandler.prototype = {
michael@0 1068 clientId: 0,
michael@0 1069 radioInterface: null,
michael@0 1070 // Data calls setting.
michael@0 1071 dataCallSettings: null,
michael@0 1072 apnSettings: null,
michael@0 1073
michael@0 1074 // Apn settings to be setup after data call are cleared.
michael@0 1075 _pendingApnSettings: null,
michael@0 1076
michael@0 1077 debug: function(s) {
michael@0 1078 dump("-*- DataConnectionHandler[" + this.clientId + "]: " + s + "\n");
michael@0 1079 },
michael@0 1080
michael@0 1081 shutdown: function() {
michael@0 1082 // Shutdown all RIL network interfaces
michael@0 1083 for (let [, apnSetting] of Iterator(this.apnSettings.byApn)) {
michael@0 1084 if (apnSetting.iface) {
michael@0 1085 apnSetting.iface.shutdown();
michael@0 1086 }
michael@0 1087 }
michael@0 1088 this.clientId = null;
michael@0 1089 this.radioInterface = null;
michael@0 1090 },
michael@0 1091
michael@0 1092 /**
michael@0 1093 * Check if we get all necessary APN data.
michael@0 1094 */
michael@0 1095 _validateApnSetting: function(apnSetting) {
michael@0 1096 return (apnSetting &&
michael@0 1097 apnSetting.apn &&
michael@0 1098 apnSetting.types &&
michael@0 1099 apnSetting.types.length);
michael@0 1100 },
michael@0 1101
michael@0 1102 _deliverDataCallCallback: function(name, args) {
michael@0 1103 // We need to worry about callback registration state mutations during the
michael@0 1104 // callback firing. The behaviour we want is to *not* call any callbacks
michael@0 1105 // that are added during the firing and to *not* call any callbacks that are
michael@0 1106 // removed during the firing. To address this, we make a copy of the
michael@0 1107 // callback list before dispatching and then double-check that each callback
michael@0 1108 // is still registered before calling it.
michael@0 1109 let callbacks = this._dataCallbacks.slice();
michael@0 1110 for (let callback of callbacks) {
michael@0 1111 if (this._dataCallbacks.indexOf(callback) == -1) {
michael@0 1112 continue;
michael@0 1113 }
michael@0 1114 try {
michael@0 1115 let handler = callback[name];
michael@0 1116 if (typeof handler !== "function") {
michael@0 1117 throw new Error("No handler for " + name);
michael@0 1118 }
michael@0 1119 handler.apply(callback, args);
michael@0 1120 } catch (e) {
michael@0 1121 if (DEBUG) {
michael@0 1122 this.debug("callback handler for " + name + " threw an exception: " + e);
michael@0 1123 }
michael@0 1124 }
michael@0 1125 }
michael@0 1126 },
michael@0 1127
michael@0 1128 /**
michael@0 1129 * This function will do the following steps:
michael@0 1130 * 1. Clear the cached APN settings in the RIL.
michael@0 1131 * 2. Combine APN, user name, and password as the key of |byApn| object to
michael@0 1132 * refer to the corresponding APN setting.
michael@0 1133 * 3. Use APN type as the index of |byType| object to refer to the
michael@0 1134 * corresponding APN setting.
michael@0 1135 * 4. Create RilNetworkInterface for each APN setting created at step 2.
michael@0 1136 */
michael@0 1137 _setupApnSettings: function(newApnSettings) {
michael@0 1138 if (!newApnSettings) {
michael@0 1139 return;
michael@0 1140 }
michael@0 1141 if (DEBUG) this.debug("setupApnSettings: " + JSON.stringify(newApnSettings));
michael@0 1142
michael@0 1143 // Unregister anything from iface and delete it.
michael@0 1144 for (let [, apnSetting] in Iterator(this.apnSettings.byApn)) {
michael@0 1145 if (apnSetting.iface.name in gNetworkManager.networkInterfaces) {
michael@0 1146 gNetworkManager.unregisterNetworkInterface(apnSetting.iface);
michael@0 1147 }
michael@0 1148 this.unregisterDataCallCallback(apnSetting.iface);
michael@0 1149 delete apnSetting.iface;
michael@0 1150 }
michael@0 1151 this.apnSettings.byApn = {};
michael@0 1152 this.apnSettings.byType = {};
michael@0 1153
michael@0 1154 // Cache the APN settings by APNs and by types in the RIL.
michael@0 1155 for (let inputApnSetting of newApnSettings) {
michael@0 1156 if (!this._validateApnSetting(inputApnSetting)) {
michael@0 1157 continue;
michael@0 1158 }
michael@0 1159
michael@0 1160 // Combine APN, user name, and password as the key of |byApn| object to
michael@0 1161 // refer to the corresponding APN setting.
michael@0 1162 let apnKey = inputApnSetting.apn +
michael@0 1163 (inputApnSetting.user || "") +
michael@0 1164 (inputApnSetting.password || "");
michael@0 1165
michael@0 1166 if (!this.apnSettings.byApn[apnKey]) {
michael@0 1167 this.apnSettings.byApn[apnKey] = inputApnSetting;
michael@0 1168 } else {
michael@0 1169 this.apnSettings.byApn[apnKey].types =
michael@0 1170 this.apnSettings.byApn[apnKey].types.concat(inputApnSetting.types);
michael@0 1171 }
michael@0 1172
michael@0 1173 // Use APN type as the index of |byType| object to refer to the
michael@0 1174 // corresponding APN setting.
michael@0 1175 for (let type of inputApnSetting.types) {
michael@0 1176 this.apnSettings.byType[type] = this.apnSettings.byApn[apnKey];
michael@0 1177 }
michael@0 1178 }
michael@0 1179
michael@0 1180 // Create RilNetworkInterface for each APN setting that just cached.
michael@0 1181 for (let [, apnSetting] in Iterator(this.apnSettings.byApn)) {
michael@0 1182 apnSetting.iface = new RILNetworkInterface(this, apnSetting);
michael@0 1183 }
michael@0 1184 },
michael@0 1185
michael@0 1186 /**
michael@0 1187 * Check if all data is disconnected.
michael@0 1188 */
michael@0 1189 allDataDisconnected: function() {
michael@0 1190 for (let [, apnSetting] of Iterator(this.apnSettings.byApn)) {
michael@0 1191 let iface = apnSetting.iface;
michael@0 1192 if (iface && iface.state != RIL.GECKO_NETWORK_STATE_UNKNOWN &&
michael@0 1193 iface.state != RIL.GECKO_NETWORK_STATE_DISCONNECTED) {
michael@0 1194 return false;
michael@0 1195 }
michael@0 1196 }
michael@0 1197 return true;
michael@0 1198 },
michael@0 1199
michael@0 1200 /**
michael@0 1201 * Check if there is any activated data connection.
michael@0 1202 */
michael@0 1203 anyDataConnected: function() {
michael@0 1204 for (let [, apnSetting] of Iterator(this.apnSettings.byApn)) {
michael@0 1205 let iface = apnSetting.iface;
michael@0 1206 if (iface && iface.state == RIL.GECKO_NETWORK_STATE_CONNECTED) {
michael@0 1207 return true;
michael@0 1208 }
michael@0 1209 }
michael@0 1210 return false;
michael@0 1211 },
michael@0 1212
michael@0 1213 updateApnSettings: function(newApnSettings) {
michael@0 1214 if (!newApnSettings) {
michael@0 1215 return;
michael@0 1216 }
michael@0 1217 if (this._pendingApnSettings) {
michael@0 1218 // Change of apn settings in process, just update to the newest.
michael@0 1219 this._pengingApnSettings = newApnSettings;
michael@0 1220 return;
michael@0 1221 }
michael@0 1222
michael@0 1223 let isDeactivatingDataCalls = false;
michael@0 1224 // Clear the cached APN settings in the RIL.
michael@0 1225 for (let [, apnSetting] of Iterator(this.apnSettings.byApn)) {
michael@0 1226 // Clear all existing connections based on APN types.
michael@0 1227 for (let type of apnSetting.types) {
michael@0 1228 if (this.getDataCallStateByType(type) ==
michael@0 1229 RIL.GECKO_NETWORK_STATE_CONNECTED) {
michael@0 1230 this.deactivateDataCallByType(type);
michael@0 1231 isDeactivatingDataCalls = true;
michael@0 1232 }
michael@0 1233 }
michael@0 1234 }
michael@0 1235 if (isDeactivatingDataCalls) {
michael@0 1236 // Defer apn settings setup until all data calls are cleared.
michael@0 1237 this._pendingApnSettings = newApnSettings;
michael@0 1238 return;
michael@0 1239 }
michael@0 1240 this._setupApnSettings(newApnSettings);
michael@0 1241 },
michael@0 1242
michael@0 1243 updateRILNetworkInterface: function() {
michael@0 1244 let apnSetting = this.apnSettings.byType.default;
michael@0 1245 if (!this._validateApnSetting(apnSetting)) {
michael@0 1246 if (DEBUG) {
michael@0 1247 this.debug("We haven't gotten completely the APN data.");
michael@0 1248 }
michael@0 1249 return;
michael@0 1250 }
michael@0 1251
michael@0 1252 // This check avoids data call connection if the radio is not ready
michael@0 1253 // yet after toggling off airplane mode.
michael@0 1254 let rilContext = this.radioInterface.rilContext;
michael@0 1255 if (rilContext.radioState != RIL.GECKO_RADIOSTATE_READY) {
michael@0 1256 if (DEBUG) {
michael@0 1257 this.debug("RIL is not ready for data connection: radio's not ready");
michael@0 1258 }
michael@0 1259 return;
michael@0 1260 }
michael@0 1261
michael@0 1262 // We only watch at "ril.data.enabled" flag changes for connecting or
michael@0 1263 // disconnecting the data call. If the value of "ril.data.enabled" is
michael@0 1264 // true and any of the remaining flags change the setting application
michael@0 1265 // should turn this flag to false and then to true in order to reload
michael@0 1266 // the new values and reconnect the data call.
michael@0 1267 if (this.dataCallSettings.oldEnabled === this.dataCallSettings.enabled) {
michael@0 1268 if (DEBUG) {
michael@0 1269 this.debug("No changes for ril.data.enabled flag. Nothing to do.");
michael@0 1270 }
michael@0 1271 return;
michael@0 1272 }
michael@0 1273
michael@0 1274 let defaultDataCallState = this.getDataCallStateByType("default");
michael@0 1275 if (defaultDataCallState == RIL.GECKO_NETWORK_STATE_CONNECTING ||
michael@0 1276 defaultDataCallState == RIL.GECKO_NETWORK_STATE_DISCONNECTING) {
michael@0 1277 if (DEBUG) {
michael@0 1278 this.debug("Nothing to do during connecting/disconnecting in progress.");
michael@0 1279 }
michael@0 1280 return;
michael@0 1281 }
michael@0 1282
michael@0 1283 let dataInfo = rilContext.data;
michael@0 1284 let isRegistered =
michael@0 1285 dataInfo.state == RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED;
michael@0 1286 let haveDataConnection =
michael@0 1287 dataInfo.type != RIL.GECKO_MOBILE_CONNECTION_STATE_UNKNOWN;
michael@0 1288 if (!isRegistered || !haveDataConnection) {
michael@0 1289 if (DEBUG) {
michael@0 1290 this.debug("RIL is not ready for data connection: Phone's not " +
michael@0 1291 "registered or doesn't have data connection.");
michael@0 1292 }
michael@0 1293 return;
michael@0 1294 }
michael@0 1295 let wifi_active = false;
michael@0 1296 if (gNetworkManager.active &&
michael@0 1297 gNetworkManager.active.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
michael@0 1298 wifi_active = true;
michael@0 1299 }
michael@0 1300
michael@0 1301 let defaultDataCallConnected = defaultDataCallState ==
michael@0 1302 RIL.GECKO_NETWORK_STATE_CONNECTED;
michael@0 1303 if (defaultDataCallConnected &&
michael@0 1304 (!this.dataCallSettings.enabled ||
michael@0 1305 (dataInfo.roaming && !this.dataCallSettings.roamingEnabled))) {
michael@0 1306 if (DEBUG) {
michael@0 1307 this.debug("Data call settings: disconnect data call.");
michael@0 1308 }
michael@0 1309 this.deactivateDataCallByType("default");
michael@0 1310 return;
michael@0 1311 }
michael@0 1312
michael@0 1313 if (defaultDataCallConnected && wifi_active) {
michael@0 1314 if (DEBUG) {
michael@0 1315 this.debug("Disconnect data call when Wifi is connected.");
michael@0 1316 }
michael@0 1317 this.deactivateDataCallByType("default");
michael@0 1318 return;
michael@0 1319 }
michael@0 1320
michael@0 1321 if (!this.dataCallSettings.enabled || defaultDataCallConnected) {
michael@0 1322 if (DEBUG) {
michael@0 1323 this.debug("Data call settings: nothing to do.");
michael@0 1324 }
michael@0 1325 return;
michael@0 1326 }
michael@0 1327 if (dataInfo.roaming && !this.dataCallSettings.roamingEnabled) {
michael@0 1328 if (DEBUG) {
michael@0 1329 this.debug("We're roaming, but data roaming is disabled.");
michael@0 1330 }
michael@0 1331 return;
michael@0 1332 }
michael@0 1333 if (wifi_active) {
michael@0 1334 if (DEBUG) {
michael@0 1335 this.debug("Don't connect data call when Wifi is connected.");
michael@0 1336 }
michael@0 1337 return;
michael@0 1338 }
michael@0 1339 if (this._pendingApnSettings) {
michael@0 1340 if (DEBUG) this.debug("We're changing apn settings, ignore any changes.");
michael@0 1341 return;
michael@0 1342 }
michael@0 1343
michael@0 1344 let detailedRadioState = rilContext.detailedRadioState;
michael@0 1345 if (gRadioEnabledController.isDeactivatingDataCalls() ||
michael@0 1346 detailedRadioState == RIL.GECKO_DETAILED_RADIOSTATE_ENABLING ||
michael@0 1347 detailedRadioState == RIL.GECKO_DETAILED_RADIOSTATE_DISABLING) {
michael@0 1348 // We're changing the radio power currently, ignore any changes.
michael@0 1349 return;
michael@0 1350 }
michael@0 1351
michael@0 1352 if (DEBUG) {
michael@0 1353 this.debug("Data call settings: connect data call.");
michael@0 1354 }
michael@0 1355 this.setupDataCallByType("default");
michael@0 1356 },
michael@0 1357
michael@0 1358 getDataCallStateByType: function(apnType) {
michael@0 1359 let apnSetting = this.apnSettings.byType[apnType];
michael@0 1360 if (!apnSetting) {
michael@0 1361 return RIL.GECKO_NETWORK_STATE_UNKNOWN;
michael@0 1362 }
michael@0 1363 if (!apnSetting.iface.inConnectedTypes(apnType)) {
michael@0 1364 return RIL.GECKO_NETWORK_STATE_DISCONNECTED;
michael@0 1365 }
michael@0 1366 return apnSetting.iface.state;
michael@0 1367 },
michael@0 1368
michael@0 1369 setupDataCallByType: function(apnType) {
michael@0 1370 if (DEBUG) {
michael@0 1371 this.debug("setupDataCallByType: " + apnType);
michael@0 1372 }
michael@0 1373 let apnSetting = this.apnSettings.byType[apnType];
michael@0 1374 if (!apnSetting) {
michael@0 1375 if (DEBUG) {
michael@0 1376 this.debug("No apn setting for type: " + apnType);
michael@0 1377 }
michael@0 1378 return;
michael@0 1379 }
michael@0 1380
michael@0 1381 let dataInfo = this.radioInterface.rilContext.data;
michael@0 1382 if (dataInfo.state != RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED ||
michael@0 1383 dataInfo.type == RIL.GECKO_MOBILE_CONNECTION_STATE_UNKNOWN) {
michael@0 1384 return;
michael@0 1385 }
michael@0 1386
michael@0 1387 apnSetting.iface.connect(apnType);
michael@0 1388 // We just call connect() function, so this interface should be in
michael@0 1389 // connecting state. If this interface is already in connected state, we
michael@0 1390 // are sure that this interface have successfully established connection
michael@0 1391 // for other data call types before we call connect() function for current
michael@0 1392 // data call type. In this circumstance, we have to directly update the
michael@0 1393 // necessary data call and interface information to RILContentHelper
michael@0 1394 // and network manager for current data call type.
michael@0 1395 if (apnSetting.iface.connected) {
michael@0 1396 // Update the interface status via-registration if the interface has
michael@0 1397 // already been registered in the network manager.
michael@0 1398 if (apnSetting.iface.name in gNetworkManager.networkInterfaces) {
michael@0 1399 gNetworkManager.unregisterNetworkInterface(apnSetting.iface);
michael@0 1400 }
michael@0 1401 gNetworkManager.registerNetworkInterface(apnSetting.iface);
michael@0 1402
michael@0 1403 Services.obs.notifyObservers(apnSetting.iface,
michael@0 1404 kNetworkInterfaceStateChangedTopic,
michael@0 1405 null);
michael@0 1406 }
michael@0 1407 },
michael@0 1408
michael@0 1409 deactivateDataCallByType: function(apnType) {
michael@0 1410 if (DEBUG) {
michael@0 1411 this.debug("deactivateDataCallByType: " + apnType);
michael@0 1412 }
michael@0 1413 let apnSetting = this.apnSettings.byType[apnType];
michael@0 1414 if (!apnSetting) {
michael@0 1415 if (DEBUG) {
michael@0 1416 this.debug("No apn setting for type: " + apnType);
michael@0 1417 }
michael@0 1418 return;
michael@0 1419 }
michael@0 1420
michael@0 1421 apnSetting.iface.disconnect(apnType);
michael@0 1422 // We just call disconnect() function, so this interface should be in
michael@0 1423 // disconnecting state. If this interface is still in connected state, we
michael@0 1424 // are sure that other data call types still need this connection of this
michael@0 1425 // interface. In this circumstance, we have to directly update the
michael@0 1426 // necessary data call and interface information to RILContentHelper
michael@0 1427 // and network manager for current data call type.
michael@0 1428 if (apnSetting.iface.connectedTypes.length && apnSetting.iface.connected) {
michael@0 1429 // Update the interface status via-registration if the interface has
michael@0 1430 // already been registered in the network manager.
michael@0 1431 if (apnSetting.iface.name in gNetworkManager.networkInterfaces) {
michael@0 1432 gNetworkManager.unregisterNetworkInterface(apnSetting.iface);
michael@0 1433 }
michael@0 1434 gNetworkManager.registerNetworkInterface(apnSetting.iface);
michael@0 1435
michael@0 1436 Services.obs.notifyObservers(apnSetting.iface,
michael@0 1437 kNetworkInterfaceStateChangedTopic,
michael@0 1438 null);
michael@0 1439 }
michael@0 1440 },
michael@0 1441
michael@0 1442 deactivateDataCalls: function() {
michael@0 1443 let dataDisconnecting = false;
michael@0 1444 for (let [, apnSetting] of Iterator(this.apnSettings.byApn)) {
michael@0 1445 for (let type of apnSetting.types) {
michael@0 1446 if (this.getDataCallStateByType(type) ==
michael@0 1447 RIL.GECKO_NETWORK_STATE_CONNECTED) {
michael@0 1448 this.deactivateDataCallByType(type);
michael@0 1449 dataDisconnecting = true;
michael@0 1450 }
michael@0 1451 }
michael@0 1452 }
michael@0 1453
michael@0 1454 // No data calls exist. It's safe to proceed the pending radio power off
michael@0 1455 // request.
michael@0 1456 if (gRadioEnabledController.isDeactivatingDataCalls() && !dataDisconnecting) {
michael@0 1457 gRadioEnabledController.finishDeactivatingDataCalls(this.clientId);
michael@0 1458 }
michael@0 1459 },
michael@0 1460
michael@0 1461 registerDataCallCallback: function(callback) {
michael@0 1462 if (this._dataCallbacks.indexOf(callback) != -1) {
michael@0 1463 throw new Error("Already registered this callback: " + callback);
michael@0 1464 }
michael@0 1465 this._dataCallbacks.push(callback);
michael@0 1466 if (DEBUG) {
michael@0 1467 this.debug("Registering callback: " + callback);
michael@0 1468 }
michael@0 1469 },
michael@0 1470
michael@0 1471 unregisterDataCallCallback: function(callback) {
michael@0 1472 let index = this._dataCallbacks.indexOf(callback);
michael@0 1473 if (index != -1) {
michael@0 1474 this._dataCallbacks.splice(index, 1);
michael@0 1475 if (DEBUG) {
michael@0 1476 this.debug("Unregistering callback: " + callback);
michael@0 1477 }
michael@0 1478 }
michael@0 1479 },
michael@0 1480
michael@0 1481 /**
michael@0 1482 * Handle data errors.
michael@0 1483 */
michael@0 1484 handleDataCallError: function(message) {
michael@0 1485 // Notify data call error only for data APN
michael@0 1486 let apnSetting = this.apnSettings && this.apnSettings.byType.default;
michael@0 1487 if (apnSetting) {
michael@0 1488 if (message.apn == apnSetting.apn &&
michael@0 1489 apnSetting.iface.inConnectedTypes("default")) {
michael@0 1490 gMessageManager.sendMobileConnectionMessage("RIL:DataError",
michael@0 1491 this.clientId, message);
michael@0 1492 }
michael@0 1493 }
michael@0 1494
michael@0 1495 this._deliverDataCallCallback("dataCallError", [message]);
michael@0 1496 },
michael@0 1497
michael@0 1498 /**
michael@0 1499 * Handle data call state changes.
michael@0 1500 */
michael@0 1501 handleDataCallState: function(datacall) {
michael@0 1502 this._deliverDataCallCallback("dataCallStateChanged", [datacall]);
michael@0 1503
michael@0 1504 // Process pending radio power off request after all data calls
michael@0 1505 // are disconnected.
michael@0 1506 if (datacall.state == RIL.GECKO_NETWORK_STATE_UNKNOWN &&
michael@0 1507 this.allDataDisconnected()) {
michael@0 1508 if (gRadioEnabledController.isDeactivatingDataCalls()) {
michael@0 1509 if (DEBUG) {
michael@0 1510 this.debug("All data connections are disconnected.");
michael@0 1511 }
michael@0 1512 gRadioEnabledController.finishDeactivatingDataCalls(this.clientId);
michael@0 1513 }
michael@0 1514
michael@0 1515 if (this._pendingApnSettings) {
michael@0 1516 if (DEBUG) {
michael@0 1517 this.debug("Setup pending apn settings.");
michael@0 1518 }
michael@0 1519 this._setupApnSettings(this._pendingApnSettings);
michael@0 1520 this._pendingApnSettings = null;
michael@0 1521 this.updateRILNetworkInterface();
michael@0 1522 }
michael@0 1523 }
michael@0 1524 },
michael@0 1525 };
michael@0 1526
michael@0 1527 function RadioInterfaceLayer() {
michael@0 1528 let workerMessenger = new WorkerMessenger();
michael@0 1529 workerMessenger.init();
michael@0 1530
michael@0 1531 let numIfaces = this.numRadioInterfaces;
michael@0 1532 if (DEBUG) debug(numIfaces + " interfaces");
michael@0 1533 this.radioInterfaces = [];
michael@0 1534 for (let clientId = 0; clientId < numIfaces; clientId++) {
michael@0 1535 this.radioInterfaces.push(new RadioInterface(clientId, workerMessenger));
michael@0 1536 }
michael@0 1537
michael@0 1538 Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
michael@0 1539
michael@0 1540 gMessageManager.init(this);
michael@0 1541 gRadioEnabledController.init(this);
michael@0 1542 gDataConnectionManager.init(this);
michael@0 1543 }
michael@0 1544 RadioInterfaceLayer.prototype = {
michael@0 1545
michael@0 1546 classID: RADIOINTERFACELAYER_CID,
michael@0 1547 classInfo: XPCOMUtils.generateCI({classID: RADIOINTERFACELAYER_CID,
michael@0 1548 classDescription: "RadioInterfaceLayer",
michael@0 1549 interfaces: [Ci.nsIRadioInterfaceLayer]}),
michael@0 1550
michael@0 1551 QueryInterface: XPCOMUtils.generateQI([Ci.nsIRadioInterfaceLayer,
michael@0 1552 Ci.nsIObserver]),
michael@0 1553
michael@0 1554 /**
michael@0 1555 * nsIObserver interface methods.
michael@0 1556 */
michael@0 1557
michael@0 1558 observe: function(subject, topic, data) {
michael@0 1559 switch (topic) {
michael@0 1560 case NS_XPCOM_SHUTDOWN_OBSERVER_ID:
michael@0 1561 for (let radioInterface of this.radioInterfaces) {
michael@0 1562 radioInterface.shutdown();
michael@0 1563 }
michael@0 1564 this.radioInterfaces = null;
michael@0 1565 Services.obs.removeObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
michael@0 1566 break;
michael@0 1567 }
michael@0 1568 },
michael@0 1569
michael@0 1570 /**
michael@0 1571 * nsIRadioInterfaceLayer interface methods.
michael@0 1572 */
michael@0 1573
michael@0 1574 getRadioInterface: function(clientId) {
michael@0 1575 return this.radioInterfaces[clientId];
michael@0 1576 },
michael@0 1577
michael@0 1578 setMicrophoneMuted: function(muted) {
michael@0 1579 for (let clientId = 0; clientId < this.numRadioInterfaces; clientId++) {
michael@0 1580 let radioInterface = this.radioInterfaces[clientId];
michael@0 1581 radioInterface.workerMessenger.send("setMute", { muted: muted });
michael@0 1582 }
michael@0 1583 }
michael@0 1584 };
michael@0 1585
michael@0 1586 XPCOMUtils.defineLazyGetter(RadioInterfaceLayer.prototype,
michael@0 1587 "numRadioInterfaces", function() {
michael@0 1588 try {
michael@0 1589 return Services.prefs.getIntPref(kPrefRilNumRadioInterfaces);
michael@0 1590 } catch(e) {}
michael@0 1591
michael@0 1592 return 1;
michael@0 1593 });
michael@0 1594
michael@0 1595 function WorkerMessenger() {
michael@0 1596 // Initial owning attributes.
michael@0 1597 this.radioInterfaces = [];
michael@0 1598 this.tokenCallbackMap = {};
michael@0 1599
michael@0 1600 this.worker = new ChromeWorker("resource://gre/modules/ril_worker.js");
michael@0 1601 this.worker.onerror = this.onerror.bind(this);
michael@0 1602 this.worker.onmessage = this.onmessage.bind(this);
michael@0 1603 }
michael@0 1604 WorkerMessenger.prototype = {
michael@0 1605 radioInterfaces: null,
michael@0 1606 worker: null,
michael@0 1607
michael@0 1608 // This gets incremented each time we send out a message.
michael@0 1609 token: 1,
michael@0 1610
michael@0 1611 // Maps tokens we send out with messages to the message callback.
michael@0 1612 tokenCallbackMap: null,
michael@0 1613
michael@0 1614 init: function() {
michael@0 1615 let options = {
michael@0 1616 debug: DEBUG,
michael@0 1617 cellBroadcastDisabled: false,
michael@0 1618 clirMode: RIL.CLIR_DEFAULT,
michael@0 1619 quirks: {
michael@0 1620 callstateExtraUint32:
michael@0 1621 libcutils.property_get("ro.moz.ril.callstate_extra_int", "false") === "true",
michael@0 1622 v5Legacy:
michael@0 1623 libcutils.property_get("ro.moz.ril.v5_legacy", "true") === "true",
michael@0 1624 requestUseDialEmergencyCall:
michael@0 1625 libcutils.property_get("ro.moz.ril.dial_emergency_call", "false") === "true",
michael@0 1626 simAppStateExtraFields:
michael@0 1627 libcutils.property_get("ro.moz.ril.simstate_extra_field", "false") === "true",
michael@0 1628 extraUint2ndCall:
michael@0 1629 libcutils.property_get("ro.moz.ril.extra_int_2nd_call", "false") == "true",
michael@0 1630 haveQueryIccLockRetryCount:
michael@0 1631 libcutils.property_get("ro.moz.ril.query_icc_count", "false") == "true",
michael@0 1632 sendStkProfileDownload:
michael@0 1633 libcutils.property_get("ro.moz.ril.send_stk_profile_dl", "false") == "true",
michael@0 1634 dataRegistrationOnDemand:
michael@0 1635 libcutils.property_get("ro.moz.ril.data_reg_on_demand", "false") == "true"
michael@0 1636 },
michael@0 1637 rilEmergencyNumbers: libcutils.property_get("ril.ecclist") ||
michael@0 1638 libcutils.property_get("ro.ril.ecclist")
michael@0 1639 };
michael@0 1640
michael@0 1641 try {
michael@0 1642 options.cellBroadcastDisabled =
michael@0 1643 Services.prefs.getBoolPref(kPrefCellBroadcastDisabled);
michael@0 1644 } catch(e) {}
michael@0 1645
michael@0 1646 try {
michael@0 1647 options.clirMode = Services.prefs.getIntPref(kPrefClirModePreference);
michael@0 1648 } catch(e) {}
michael@0 1649
michael@0 1650 this.send(null, "setInitialOptions", options);
michael@0 1651 },
michael@0 1652
michael@0 1653 debug: function(aClientId, aMessage) {
michael@0 1654 // We use the same debug subject with RadioInterface's here.
michael@0 1655 dump("-*- RadioInterface[" + aClientId + "]: " + aMessage + "\n");
michael@0 1656 },
michael@0 1657
michael@0 1658 onerror: function(event) {
michael@0 1659 if (DEBUG) {
michael@0 1660 this.debug("X", "Got an error: " + event.filename + ":" +
michael@0 1661 event.lineno + ": " + event.message + "\n");
michael@0 1662 }
michael@0 1663 event.preventDefault();
michael@0 1664 },
michael@0 1665
michael@0 1666 /**
michael@0 1667 * Process the incoming message from the RIL worker.
michael@0 1668 */
michael@0 1669 onmessage: function(event) {
michael@0 1670 let message = event.data;
michael@0 1671 let clientId = message.rilMessageClientId;
michael@0 1672 if (clientId === null) {
michael@0 1673 return;
michael@0 1674 }
michael@0 1675
michael@0 1676 if (DEBUG) {
michael@0 1677 this.debug(clientId, "Received message from worker: " + JSON.stringify(message));
michael@0 1678 }
michael@0 1679
michael@0 1680 let token = message.rilMessageToken;
michael@0 1681 if (token == null) {
michael@0 1682 // That's an unsolicited message. Pass to RadioInterface directly.
michael@0 1683 let radioInterface = this.radioInterfaces[clientId];
michael@0 1684 radioInterface.handleUnsolicitedWorkerMessage(message);
michael@0 1685 return;
michael@0 1686 }
michael@0 1687
michael@0 1688 let callback = this.tokenCallbackMap[message.rilMessageToken];
michael@0 1689 if (!callback) {
michael@0 1690 if (DEBUG) this.debug(clientId, "Ignore orphan token: " + message.rilMessageToken);
michael@0 1691 return;
michael@0 1692 }
michael@0 1693
michael@0 1694 let keep = false;
michael@0 1695 try {
michael@0 1696 keep = callback(message);
michael@0 1697 } catch(e) {
michael@0 1698 if (DEBUG) this.debug(clientId, "callback throws an exception: " + e);
michael@0 1699 }
michael@0 1700
michael@0 1701 if (!keep) {
michael@0 1702 delete this.tokenCallbackMap[message.rilMessageToken];
michael@0 1703 }
michael@0 1704 },
michael@0 1705
michael@0 1706 registerClient: function(aClientId, aRadioInterface) {
michael@0 1707 if (DEBUG) this.debug(aClientId, "Starting RIL Worker");
michael@0 1708
michael@0 1709 // Keep a reference so that we can dispatch unsolicited messages to it.
michael@0 1710 this.radioInterfaces[aClientId] = aRadioInterface;
michael@0 1711
michael@0 1712 this.send(null, "registerClient", { clientId: aClientId });
michael@0 1713 gSystemWorkerManager.registerRilWorker(aClientId, this.worker);
michael@0 1714 },
michael@0 1715
michael@0 1716 /**
michael@0 1717 * Send arbitrary message to worker.
michael@0 1718 *
michael@0 1719 * @param rilMessageType
michael@0 1720 * A text message type.
michael@0 1721 * @param message [optional]
michael@0 1722 * An optional message object to send.
michael@0 1723 * @param callback [optional]
michael@0 1724 * An optional callback function which is called when worker replies
michael@0 1725 * with an message containing a "rilMessageToken" attribute of the
michael@0 1726 * same value we passed. This callback function accepts only one
michael@0 1727 * parameter -- the reply from worker. It also returns a boolean
michael@0 1728 * value true to keep current token-callback mapping and wait for
michael@0 1729 * another worker reply, or false to remove the mapping.
michael@0 1730 */
michael@0 1731 send: function(clientId, rilMessageType, message, callback) {
michael@0 1732 message = message || {};
michael@0 1733
michael@0 1734 message.rilMessageClientId = clientId;
michael@0 1735 message.rilMessageToken = this.token;
michael@0 1736 this.token++;
michael@0 1737
michael@0 1738 if (callback) {
michael@0 1739 // Only create the map if callback is provided. For sending a request
michael@0 1740 // and intentionally leaving the callback undefined, that reply will
michael@0 1741 // be dropped in |this.onmessage| because of that orphan token.
michael@0 1742 //
michael@0 1743 // For sending a request that never replied at all, we're fine with this
michael@0 1744 // because no callback shall be passed and we leave nothing to be cleaned
michael@0 1745 // up later.
michael@0 1746 this.tokenCallbackMap[message.rilMessageToken] = callback;
michael@0 1747 }
michael@0 1748
michael@0 1749 message.rilMessageType = rilMessageType;
michael@0 1750 this.worker.postMessage(message);
michael@0 1751 },
michael@0 1752
michael@0 1753 /**
michael@0 1754 * Send message to worker and return worker reply to RILContentHelper.
michael@0 1755 *
michael@0 1756 * @param msg
michael@0 1757 * A message object from ppmm.
michael@0 1758 * @param rilMessageType
michael@0 1759 * A text string for worker message type.
michael@0 1760 * @param ipcType [optinal]
michael@0 1761 * A text string for ipc message type. "msg.name" if omitted.
michael@0 1762 *
michael@0 1763 * @TODO: Bug 815526 - deprecate RILContentHelper.
michael@0 1764 */
michael@0 1765 sendWithIPCMessage: function(clientId, msg, rilMessageType, ipcType) {
michael@0 1766 this.send(clientId, rilMessageType, msg.json.data, (function(reply) {
michael@0 1767 ipcType = ipcType || msg.name;
michael@0 1768 msg.target.sendAsyncMessage(ipcType, {
michael@0 1769 clientId: clientId,
michael@0 1770 data: reply
michael@0 1771 });
michael@0 1772 return false;
michael@0 1773 }).bind(this));
michael@0 1774 }
michael@0 1775 };
michael@0 1776
michael@0 1777 function RadioInterface(aClientId, aWorkerMessenger) {
michael@0 1778 this.clientId = aClientId;
michael@0 1779 this.workerMessenger = {
michael@0 1780 send: aWorkerMessenger.send.bind(aWorkerMessenger, aClientId),
michael@0 1781 sendWithIPCMessage:
michael@0 1782 aWorkerMessenger.sendWithIPCMessage.bind(aWorkerMessenger, aClientId),
michael@0 1783 };
michael@0 1784 aWorkerMessenger.registerClient(aClientId, this);
michael@0 1785
michael@0 1786 this.supportedNetworkTypes = this.getSupportedNetworkTypes();
michael@0 1787
michael@0 1788 this.rilContext = {
michael@0 1789 radioState: RIL.GECKO_RADIOSTATE_UNAVAILABLE,
michael@0 1790 detailedRadioState: null,
michael@0 1791 cardState: RIL.GECKO_CARDSTATE_UNKNOWN,
michael@0 1792 networkSelectionMode: RIL.GECKO_NETWORK_SELECTION_UNKNOWN,
michael@0 1793 iccInfo: null,
michael@0 1794 imsi: null,
michael@0 1795
michael@0 1796 // These objects implement the nsIDOMMozMobileConnectionInfo interface,
michael@0 1797 // although the actual implementation lives in the content process. So are
michael@0 1798 // the child attributes `network` and `cell`, which implement
michael@0 1799 // nsIDOMMozMobileNetworkInfo and nsIDOMMozMobileCellInfo respectively.
michael@0 1800 voice: {connected: false,
michael@0 1801 emergencyCallsOnly: false,
michael@0 1802 roaming: false,
michael@0 1803 network: null,
michael@0 1804 cell: null,
michael@0 1805 type: null,
michael@0 1806 signalStrength: null,
michael@0 1807 relSignalStrength: null},
michael@0 1808 data: {connected: false,
michael@0 1809 emergencyCallsOnly: false,
michael@0 1810 roaming: false,
michael@0 1811 network: null,
michael@0 1812 cell: null,
michael@0 1813 type: null,
michael@0 1814 signalStrength: null,
michael@0 1815 relSignalStrength: null},
michael@0 1816 };
michael@0 1817
michael@0 1818 this.voicemailInfo = {
michael@0 1819 number: null,
michael@0 1820 displayName: null
michael@0 1821 };
michael@0 1822
michael@0 1823 this.operatorInfo = {};
michael@0 1824
michael@0 1825 let lock = gSettingsService.createLock();
michael@0 1826
michael@0 1827 // Read the "time.clock.automatic-update.enabled" setting to see if
michael@0 1828 // we need to adjust the system clock time by NITZ or SNTP.
michael@0 1829 lock.get(kSettingsClockAutoUpdateEnabled, this);
michael@0 1830
michael@0 1831 // Read the "time.timezone.automatic-update.enabled" setting to see if
michael@0 1832 // we need to adjust the system timezone by NITZ.
michael@0 1833 lock.get(kSettingsTimezoneAutoUpdateEnabled, this);
michael@0 1834
michael@0 1835 // Set "time.clock.automatic-update.available" to false when starting up.
michael@0 1836 this.setClockAutoUpdateAvailable(false);
michael@0 1837
michael@0 1838 // Set "time.timezone.automatic-update.available" to false when starting up.
michael@0 1839 this.setTimezoneAutoUpdateAvailable(false);
michael@0 1840
michael@0 1841 // Read the Cell Broadcast Search List setting, string of integers or integer
michael@0 1842 // ranges separated by comma, to set listening channels.
michael@0 1843 lock.get(kSettingsCellBroadcastSearchList, this);
michael@0 1844
michael@0 1845 Services.obs.addObserver(this, kMozSettingsChangedObserverTopic, false);
michael@0 1846 Services.obs.addObserver(this, kSysClockChangeObserverTopic, false);
michael@0 1847 Services.obs.addObserver(this, kScreenStateChangedTopic, false);
michael@0 1848
michael@0 1849 Services.obs.addObserver(this, kNetworkConnStateChangedTopic, false);
michael@0 1850 Services.obs.addObserver(this, kNetworkActiveChangedTopic, false);
michael@0 1851 Services.prefs.addObserver(kPrefCellBroadcastDisabled, this, false);
michael@0 1852
michael@0 1853 this.portAddressedSmsApps = {};
michael@0 1854 this.portAddressedSmsApps[WAP.WDP_PORT_PUSH] = this.handleSmsWdpPortPush.bind(this);
michael@0 1855
michael@0 1856 this._receivedSmsSegmentsMap = {};
michael@0 1857
michael@0 1858 this._sntp = new Sntp(this.setClockBySntp.bind(this),
michael@0 1859 Services.prefs.getIntPref("network.sntp.maxRetryCount"),
michael@0 1860 Services.prefs.getIntPref("network.sntp.refreshPeriod"),
michael@0 1861 Services.prefs.getIntPref("network.sntp.timeout"),
michael@0 1862 Services.prefs.getCharPref("network.sntp.pools").split(";"),
michael@0 1863 Services.prefs.getIntPref("network.sntp.port"));
michael@0 1864 }
michael@0 1865
michael@0 1866 RadioInterface.prototype = {
michael@0 1867
michael@0 1868 classID: RADIOINTERFACE_CID,
michael@0 1869 classInfo: XPCOMUtils.generateCI({classID: RADIOINTERFACE_CID,
michael@0 1870 classDescription: "RadioInterface",
michael@0 1871 interfaces: [Ci.nsIRadioInterface]}),
michael@0 1872
michael@0 1873 QueryInterface: XPCOMUtils.generateQI([Ci.nsIRadioInterface,
michael@0 1874 Ci.nsIObserver,
michael@0 1875 Ci.nsISettingsServiceCallback]),
michael@0 1876
michael@0 1877 // A private wrapped WorkerMessenger instance.
michael@0 1878 workerMessenger: null,
michael@0 1879
michael@0 1880 debug: function(s) {
michael@0 1881 dump("-*- RadioInterface[" + this.clientId + "]: " + s + "\n");
michael@0 1882 },
michael@0 1883
michael@0 1884 shutdown: function() {
michael@0 1885 // Release the CPU wake lock for handling the received SMS.
michael@0 1886 this._releaseSmsHandledWakeLock();
michael@0 1887
michael@0 1888 Services.obs.removeObserver(this, kMozSettingsChangedObserverTopic);
michael@0 1889 Services.obs.removeObserver(this, kSysClockChangeObserverTopic);
michael@0 1890 Services.obs.removeObserver(this, kScreenStateChangedTopic);
michael@0 1891 Services.obs.removeObserver(this, kNetworkConnStateChangedTopic);
michael@0 1892 Services.obs.removeObserver(this, kNetworkActiveChangedTopic);
michael@0 1893 },
michael@0 1894
michael@0 1895 /**
michael@0 1896 * A utility function to copy objects. The srcInfo may contain
michael@0 1897 * "rilMessageType", should ignore it.
michael@0 1898 */
michael@0 1899 updateInfo: function(srcInfo, destInfo) {
michael@0 1900 for (let key in srcInfo) {
michael@0 1901 if (key === "rilMessageType") {
michael@0 1902 continue;
michael@0 1903 }
michael@0 1904 destInfo[key] = srcInfo[key];
michael@0 1905 }
michael@0 1906 },
michael@0 1907
michael@0 1908 /**
michael@0 1909 * A utility function to compare objects. The srcInfo may contain
michael@0 1910 * "rilMessageType", should ignore it.
michael@0 1911 */
michael@0 1912 isInfoChanged: function(srcInfo, destInfo) {
michael@0 1913 if (!destInfo) {
michael@0 1914 return true;
michael@0 1915 }
michael@0 1916
michael@0 1917 for (let key in srcInfo) {
michael@0 1918 if (key === "rilMessageType") {
michael@0 1919 continue;
michael@0 1920 }
michael@0 1921 if (srcInfo[key] !== destInfo[key]) {
michael@0 1922 return true;
michael@0 1923 }
michael@0 1924 }
michael@0 1925
michael@0 1926 return false;
michael@0 1927 },
michael@0 1928
michael@0 1929 /**
michael@0 1930 * A utility function to get supportedNetworkTypes from system property
michael@0 1931 */
michael@0 1932 getSupportedNetworkTypes: function() {
michael@0 1933 let key = "ro.moz.ril." + this.clientId + ".network_types";
michael@0 1934 let supportedNetworkTypes = libcutils.property_get(key, "").split(",");
michael@0 1935 for (let type of supportedNetworkTypes) {
michael@0 1936 // If the value in system property is not valid, use the default one which
michael@0 1937 // is defined in ril_consts.js.
michael@0 1938 if (RIL.GECKO_SUPPORTED_NETWORK_TYPES.indexOf(type) < 0) {
michael@0 1939 if (DEBUG) this.debug("Unknown network type: " + type);
michael@0 1940 supportedNetworkTypes =
michael@0 1941 RIL.GECKO_SUPPORTED_NETWORK_TYPES_DEFAULT.split(",");
michael@0 1942 break;
michael@0 1943 }
michael@0 1944 }
michael@0 1945 if (DEBUG) this.debug("Supported Network Types: " + supportedNetworkTypes);
michael@0 1946 return supportedNetworkTypes;
michael@0 1947 },
michael@0 1948
michael@0 1949 /**
michael@0 1950 * Process a message from the content process.
michael@0 1951 */
michael@0 1952 receiveMessage: function(msg) {
michael@0 1953 switch (msg.name) {
michael@0 1954 case "RIL:GetRilContext":
michael@0 1955 // This message is sync.
michael@0 1956 return this.rilContext;
michael@0 1957 case "RIL:GetLastKnownNetwork":
michael@0 1958 // This message is sync.
michael@0 1959 return this._lastKnownNetwork;
michael@0 1960 case "RIL:GetLastKnownHomeNetwork":
michael@0 1961 // This message is sync.
michael@0 1962 return this._lastKnownHomeNetwork;
michael@0 1963 case "RIL:GetAvailableNetworks":
michael@0 1964 this.workerMessenger.sendWithIPCMessage(msg, "getAvailableNetworks");
michael@0 1965 break;
michael@0 1966 case "RIL:SelectNetwork":
michael@0 1967 this.workerMessenger.sendWithIPCMessage(msg, "selectNetwork");
michael@0 1968 break;
michael@0 1969 case "RIL:SelectNetworkAuto":
michael@0 1970 this.workerMessenger.sendWithIPCMessage(msg, "selectNetworkAuto");
michael@0 1971 break;
michael@0 1972 case "RIL:SetPreferredNetworkType":
michael@0 1973 this.setPreferredNetworkType(msg.target, msg.json.data);
michael@0 1974 break;
michael@0 1975 case "RIL:GetPreferredNetworkType":
michael@0 1976 this.getPreferredNetworkType(msg.target, msg.json.data);
michael@0 1977 break;
michael@0 1978 case "RIL:GetCardLockState":
michael@0 1979 this.workerMessenger.sendWithIPCMessage(msg, "iccGetCardLockState",
michael@0 1980 "RIL:CardLockResult");
michael@0 1981 break;
michael@0 1982 case "RIL:UnlockCardLock":
michael@0 1983 this.workerMessenger.sendWithIPCMessage(msg, "iccUnlockCardLock",
michael@0 1984 "RIL:CardLockResult");
michael@0 1985 break;
michael@0 1986 case "RIL:SetCardLock":
michael@0 1987 this.workerMessenger.sendWithIPCMessage(msg, "iccSetCardLock",
michael@0 1988 "RIL:CardLockResult");
michael@0 1989 break;
michael@0 1990 case "RIL:GetCardLockRetryCount":
michael@0 1991 this.workerMessenger.sendWithIPCMessage(msg, "iccGetCardLockRetryCount",
michael@0 1992 "RIL:CardLockRetryCount");
michael@0 1993 break;
michael@0 1994 case "RIL:SendMMI":
michael@0 1995 this.sendMMI(msg.target, msg.json.data);
michael@0 1996 break;
michael@0 1997 case "RIL:CancelMMI":
michael@0 1998 this.workerMessenger.sendWithIPCMessage(msg, "cancelUSSD");
michael@0 1999 break;
michael@0 2000 case "RIL:SendStkResponse":
michael@0 2001 this.workerMessenger.send("sendStkTerminalResponse", msg.json.data);
michael@0 2002 break;
michael@0 2003 case "RIL:SendStkMenuSelection":
michael@0 2004 this.workerMessenger.send("sendStkMenuSelection", msg.json.data);
michael@0 2005 break;
michael@0 2006 case "RIL:SendStkTimerExpiration":
michael@0 2007 this.workerMessenger.send("sendStkTimerExpiration", msg.json.data);
michael@0 2008 break;
michael@0 2009 case "RIL:SendStkEventDownload":
michael@0 2010 this.workerMessenger.send("sendStkEventDownload", msg.json.data);
michael@0 2011 break;
michael@0 2012 case "RIL:IccOpenChannel":
michael@0 2013 this.workerMessenger.sendWithIPCMessage(msg, "iccOpenChannel");
michael@0 2014 break;
michael@0 2015 case "RIL:IccCloseChannel":
michael@0 2016 this.workerMessenger.sendWithIPCMessage(msg, "iccCloseChannel");
michael@0 2017 break;
michael@0 2018 case "RIL:IccExchangeAPDU":
michael@0 2019 this.workerMessenger.sendWithIPCMessage(msg, "iccExchangeAPDU");
michael@0 2020 break;
michael@0 2021 case "RIL:ReadIccContacts":
michael@0 2022 this.workerMessenger.sendWithIPCMessage(msg, "readICCContacts");
michael@0 2023 break;
michael@0 2024 case "RIL:UpdateIccContact":
michael@0 2025 this.workerMessenger.sendWithIPCMessage(msg, "updateICCContact");
michael@0 2026 break;
michael@0 2027 case "RIL:MatchMvno":
michael@0 2028 this.matchMvno(msg.target, msg.json.data);
michael@0 2029 break;
michael@0 2030 case "RIL:SetCallForwardingOptions":
michael@0 2031 this.setCallForwardingOptions(msg.target, msg.json.data);
michael@0 2032 break;
michael@0 2033 case "RIL:GetCallForwardingOptions":
michael@0 2034 this.workerMessenger.sendWithIPCMessage(msg, "queryCallForwardStatus");
michael@0 2035 break;
michael@0 2036 case "RIL:SetCallBarringOptions":
michael@0 2037 this.workerMessenger.sendWithIPCMessage(msg, "setCallBarring");
michael@0 2038 break;
michael@0 2039 case "RIL:GetCallBarringOptions":
michael@0 2040 this.workerMessenger.sendWithIPCMessage(msg, "queryCallBarringStatus");
michael@0 2041 break;
michael@0 2042 case "RIL:ChangeCallBarringPassword":
michael@0 2043 this.workerMessenger.sendWithIPCMessage(msg, "changeCallBarringPassword");
michael@0 2044 break;
michael@0 2045 case "RIL:SetCallWaitingOptions":
michael@0 2046 this.workerMessenger.sendWithIPCMessage(msg, "setCallWaiting");
michael@0 2047 break;
michael@0 2048 case "RIL:GetCallWaitingOptions":
michael@0 2049 this.workerMessenger.sendWithIPCMessage(msg, "queryCallWaiting");
michael@0 2050 break;
michael@0 2051 case "RIL:SetCallingLineIdRestriction":
michael@0 2052 this.setCallingLineIdRestriction(msg.target, msg.json.data);
michael@0 2053 break;
michael@0 2054 case "RIL:GetCallingLineIdRestriction":
michael@0 2055 this.workerMessenger.sendWithIPCMessage(msg, "getCLIR");
michael@0 2056 break;
michael@0 2057 case "RIL:ExitEmergencyCbMode":
michael@0 2058 this.workerMessenger.sendWithIPCMessage(msg, "exitEmergencyCbMode");
michael@0 2059 break;
michael@0 2060 case "RIL:SetRadioEnabled":
michael@0 2061 this.setRadioEnabled(msg.target, msg.json.data);
michael@0 2062 break;
michael@0 2063 case "RIL:GetVoicemailInfo":
michael@0 2064 // This message is sync.
michael@0 2065 return this.voicemailInfo;
michael@0 2066 case "RIL:SetRoamingPreference":
michael@0 2067 this.workerMessenger.sendWithIPCMessage(msg, "setRoamingPreference");
michael@0 2068 break;
michael@0 2069 case "RIL:GetRoamingPreference":
michael@0 2070 this.workerMessenger.sendWithIPCMessage(msg, "queryRoamingPreference");
michael@0 2071 break;
michael@0 2072 case "RIL:SetVoicePrivacyMode":
michael@0 2073 this.workerMessenger.sendWithIPCMessage(msg, "setVoicePrivacyMode");
michael@0 2074 break;
michael@0 2075 case "RIL:GetVoicePrivacyMode":
michael@0 2076 this.workerMessenger.sendWithIPCMessage(msg, "queryVoicePrivacyMode");
michael@0 2077 break;
michael@0 2078 case "RIL:GetSupportedNetworkTypes":
michael@0 2079 // This message is sync.
michael@0 2080 return this.supportedNetworkTypes;
michael@0 2081 }
michael@0 2082 return null;
michael@0 2083 },
michael@0 2084
michael@0 2085 handleUnsolicitedWorkerMessage: function(message) {
michael@0 2086 let connHandler = gDataConnectionManager.getConnectionHandler(this.clientId);
michael@0 2087 switch (message.rilMessageType) {
michael@0 2088 case "callRing":
michael@0 2089 gTelephonyProvider.notifyCallRing();
michael@0 2090 break;
michael@0 2091 case "callStateChange":
michael@0 2092 gTelephonyProvider.notifyCallStateChanged(this.clientId, message.call);
michael@0 2093 break;
michael@0 2094 case "callDisconnected":
michael@0 2095 gTelephonyProvider.notifyCallDisconnected(this.clientId, message.call);
michael@0 2096 break;
michael@0 2097 case "conferenceCallStateChanged":
michael@0 2098 gTelephonyProvider.notifyConferenceCallStateChanged(message.state);
michael@0 2099 break;
michael@0 2100 case "cdmaCallWaiting":
michael@0 2101 gTelephonyProvider.notifyCdmaCallWaiting(this.clientId, message.number);
michael@0 2102 break;
michael@0 2103 case "suppSvcNotification":
michael@0 2104 gTelephonyProvider.notifySupplementaryService(this.clientId,
michael@0 2105 message.callIndex,
michael@0 2106 message.notification);
michael@0 2107 break;
michael@0 2108 case "datacallerror":
michael@0 2109 connHandler.handleDataCallError(message);
michael@0 2110 break;
michael@0 2111 case "datacallstatechange":
michael@0 2112 let addresses = [];
michael@0 2113 for (let i = 0; i < message.addresses.length; i++) {
michael@0 2114 let [address, prefixLength] = message.addresses[i].split("/");
michael@0 2115 // From AOSP hardware/ril/include/telephony/ril.h, that address prefix
michael@0 2116 // is said to be OPTIONAL, but we never met such case before.
michael@0 2117 addresses.push({
michael@0 2118 address: address,
michael@0 2119 prefixLength: prefixLength ? parseInt(prefixLength, 10) : 0
michael@0 2120 });
michael@0 2121 }
michael@0 2122 message.addresses = addresses;
michael@0 2123 connHandler.handleDataCallState(message);
michael@0 2124 break;
michael@0 2125 case "emergencyCbModeChange":
michael@0 2126 this.handleEmergencyCbModeChange(message);
michael@0 2127 break;
michael@0 2128 case "networkinfochanged":
michael@0 2129 this.updateNetworkInfo(message);
michael@0 2130 break;
michael@0 2131 case "networkselectionmodechange":
michael@0 2132 this.updateNetworkSelectionMode(message);
michael@0 2133 break;
michael@0 2134 case "voiceregistrationstatechange":
michael@0 2135 this.updateVoiceConnection(message);
michael@0 2136 break;
michael@0 2137 case "dataregistrationstatechange":
michael@0 2138 this.updateDataConnection(message);
michael@0 2139 break;
michael@0 2140 case "signalstrengthchange":
michael@0 2141 this.handleSignalStrengthChange(message);
michael@0 2142 break;
michael@0 2143 case "operatorchange":
michael@0 2144 this.handleOperatorChange(message);
michael@0 2145 break;
michael@0 2146 case "otastatuschange":
michael@0 2147 this.handleOtaStatus(message);
michael@0 2148 break;
michael@0 2149 case "radiostatechange":
michael@0 2150 this.handleRadioStateChange(message);
michael@0 2151 break;
michael@0 2152 case "cardstatechange":
michael@0 2153 this.rilContext.cardState = message.cardState;
michael@0 2154 gRadioEnabledController.receiveCardState(this.clientId);
michael@0 2155 gMessageManager.sendIccMessage("RIL:CardStateChanged",
michael@0 2156 this.clientId, message);
michael@0 2157 break;
michael@0 2158 case "sms-received":
michael@0 2159 this.handleSmsMultipart(message);
michael@0 2160 break;
michael@0 2161 case "cellbroadcast-received":
michael@0 2162 message.timestamp = Date.now();
michael@0 2163 gMessageManager.sendCellBroadcastMessage("RIL:CellBroadcastReceived",
michael@0 2164 this.clientId, message);
michael@0 2165 break;
michael@0 2166 case "nitzTime":
michael@0 2167 this.handleNitzTime(message);
michael@0 2168 break;
michael@0 2169 case "iccinfochange":
michael@0 2170 this.handleIccInfoChange(message);
michael@0 2171 break;
michael@0 2172 case "iccimsi":
michael@0 2173 this.rilContext.imsi = message.imsi;
michael@0 2174 break;
michael@0 2175 case "iccmbdn":
michael@0 2176 this.handleIccMbdn(message);
michael@0 2177 break;
michael@0 2178 case "iccmwis":
michael@0 2179 gMessageManager.sendVoicemailMessage("RIL:VoicemailNotification",
michael@0 2180 this.clientId, message.mwi);
michael@0 2181 break;
michael@0 2182 case "USSDReceived":
michael@0 2183 if (DEBUG) this.debug("USSDReceived " + JSON.stringify(message));
michael@0 2184 this.handleUSSDReceived(message);
michael@0 2185 break;
michael@0 2186 case "stkcommand":
michael@0 2187 this.handleStkProactiveCommand(message);
michael@0 2188 break;
michael@0 2189 case "stksessionend":
michael@0 2190 gMessageManager.sendIccMessage("RIL:StkSessionEnd", this.clientId, null);
michael@0 2191 break;
michael@0 2192 case "exitEmergencyCbMode":
michael@0 2193 this.handleExitEmergencyCbMode(message);
michael@0 2194 break;
michael@0 2195 case "cdma-info-rec-received":
michael@0 2196 if (DEBUG) this.debug("cdma-info-rec-received: " + JSON.stringify(message));
michael@0 2197 gSystemMessenger.broadcastMessage("cdma-info-rec-received", message);
michael@0 2198 break;
michael@0 2199 default:
michael@0 2200 throw new Error("Don't know about this message type: " +
michael@0 2201 message.rilMessageType);
michael@0 2202 }
michael@0 2203 },
michael@0 2204
michael@0 2205 /**
michael@0 2206 * Get phone number from iccInfo.
michael@0 2207 *
michael@0 2208 * If the icc card is gsm card, the phone number is in msisdn.
michael@0 2209 * @see nsIDOMMozGsmIccInfo
michael@0 2210 *
michael@0 2211 * Otherwise, the phone number is in mdn.
michael@0 2212 * @see nsIDOMMozCdmaIccInfo
michael@0 2213 */
michael@0 2214 getPhoneNumber: function() {
michael@0 2215 let iccInfo = this.rilContext.iccInfo;
michael@0 2216
michael@0 2217 if (!iccInfo) {
michael@0 2218 return null;
michael@0 2219 }
michael@0 2220
michael@0 2221 // After moving SMS code out of RadioInterfaceLayer, we could use
michael@0 2222 // |iccInfo instanceof Ci.nsIDOMMozGsmIccInfo| here.
michael@0 2223 // TODO: Bug 873351 - B2G SMS: move SMS code out of RadioInterfaceLayer to
michael@0 2224 // SmsService
michael@0 2225 let number = (iccInfo instanceof GsmIccInfo) ? iccInfo.msisdn : iccInfo.mdn;
michael@0 2226
michael@0 2227 // Workaround an xpconnect issue with undefined string objects.
michael@0 2228 // See bug 808220
michael@0 2229 if (number === undefined || number === "undefined") {
michael@0 2230 return null;
michael@0 2231 }
michael@0 2232
michael@0 2233 return number;
michael@0 2234 },
michael@0 2235
michael@0 2236 /**
michael@0 2237 * A utility function to get the ICC ID of the SIM card (if installed).
michael@0 2238 */
michael@0 2239 getIccId: function() {
michael@0 2240 let iccInfo = this.rilContext.iccInfo;
michael@0 2241
michael@0 2242 if (!iccInfo) {
michael@0 2243 return null;
michael@0 2244 }
michael@0 2245
michael@0 2246 let iccId = iccInfo.iccid;
michael@0 2247
michael@0 2248 // Workaround an xpconnect issue with undefined string objects.
michael@0 2249 // See bug 808220
michael@0 2250 if (iccId === undefined || iccId === "undefined") {
michael@0 2251 return null;
michael@0 2252 }
michael@0 2253
michael@0 2254 return iccId;
michael@0 2255 },
michael@0 2256
michael@0 2257 // Matches the mvnoData pattern with imsi. Characters 'x' and 'X' are skipped
michael@0 2258 // and not compared. E.g., if the mvnoData passed is '310260x10xxxxxx',
michael@0 2259 // then the function returns true only if imsi has the same first 6 digits,
michael@0 2260 // 8th and 9th digit.
michael@0 2261 isImsiMatches: function(mvnoData) {
michael@0 2262 let imsi = this.rilContext.imsi;
michael@0 2263
michael@0 2264 // This should not be an error, but a mismatch.
michael@0 2265 if (mvnoData.length > imsi.length) {
michael@0 2266 return false;
michael@0 2267 }
michael@0 2268
michael@0 2269 for (let i = 0; i < mvnoData.length; i++) {
michael@0 2270 let c = mvnoData[i];
michael@0 2271 if ((c !== 'x') && (c !== 'X') && (c !== imsi[i])) {
michael@0 2272 return false;
michael@0 2273 }
michael@0 2274 }
michael@0 2275 return true;
michael@0 2276 },
michael@0 2277
michael@0 2278 matchMvno: function(target, message) {
michael@0 2279 if (DEBUG) this.debug("matchMvno: " + JSON.stringify(message));
michael@0 2280
michael@0 2281 if (!message || !message.mvnoType || !message.mvnoData) {
michael@0 2282 message.errorMsg = RIL.GECKO_ERROR_INVALID_PARAMETER;
michael@0 2283 }
michael@0 2284 // Currently we only support imsi matching.
michael@0 2285 if (message.mvnoType != "imsi") {
michael@0 2286 message.errorMsg = RIL.GECKO_ERROR_MODE_NOT_SUPPORTED;
michael@0 2287 }
michael@0 2288 // Fire error if mvnoType is imsi but imsi is not available.
michael@0 2289 if (!this.rilContext.imsi) {
michael@0 2290 message.errorMsg = RIL.GECKO_ERROR_GENERIC_FAILURE;
michael@0 2291 }
michael@0 2292
michael@0 2293 if (!message.errorMsg) {
michael@0 2294 message.result = this.isImsiMatches(message.mvnoData);
michael@0 2295 }
michael@0 2296
michael@0 2297 target.sendAsyncMessage("RIL:MatchMvno", {
michael@0 2298 clientId: this.clientId,
michael@0 2299 data: message
michael@0 2300 });
michael@0 2301 },
michael@0 2302
michael@0 2303 updateNetworkInfo: function(message) {
michael@0 2304 let voiceMessage = message[RIL.NETWORK_INFO_VOICE_REGISTRATION_STATE];
michael@0 2305 let dataMessage = message[RIL.NETWORK_INFO_DATA_REGISTRATION_STATE];
michael@0 2306 let operatorMessage = message[RIL.NETWORK_INFO_OPERATOR];
michael@0 2307 let selectionMessage = message[RIL.NETWORK_INFO_NETWORK_SELECTION_MODE];
michael@0 2308 let signalMessage = message[RIL.NETWORK_INFO_SIGNAL];
michael@0 2309
michael@0 2310 // Batch the *InfoChanged messages together
michael@0 2311 if (voiceMessage) {
michael@0 2312 this.updateVoiceConnection(voiceMessage, true);
michael@0 2313 }
michael@0 2314
michael@0 2315 if (dataMessage) {
michael@0 2316 this.updateDataConnection(dataMessage, true);
michael@0 2317 }
michael@0 2318
michael@0 2319 if (operatorMessage) {
michael@0 2320 this.handleOperatorChange(operatorMessage, true);
michael@0 2321 }
michael@0 2322
michael@0 2323 if (signalMessage) {
michael@0 2324 this.handleSignalStrengthChange(signalMessage, true);
michael@0 2325 }
michael@0 2326
michael@0 2327 let voice = this.rilContext.voice;
michael@0 2328 let data = this.rilContext.data;
michael@0 2329
michael@0 2330 this.checkRoamingBetweenOperators(voice);
michael@0 2331 this.checkRoamingBetweenOperators(data);
michael@0 2332
michael@0 2333 if (voiceMessage || operatorMessage || signalMessage) {
michael@0 2334 gMessageManager.sendMobileConnectionMessage("RIL:VoiceInfoChanged",
michael@0 2335 this.clientId, voice);
michael@0 2336 }
michael@0 2337 if (dataMessage || operatorMessage || signalMessage) {
michael@0 2338 gMessageManager.sendMobileConnectionMessage("RIL:DataInfoChanged",
michael@0 2339 this.clientId, data);
michael@0 2340 }
michael@0 2341
michael@0 2342 if (selectionMessage) {
michael@0 2343 this.updateNetworkSelectionMode(selectionMessage);
michael@0 2344 }
michael@0 2345 },
michael@0 2346
michael@0 2347 /**
michael@0 2348 * Fix the roaming. RIL can report roaming in some case it is not
michael@0 2349 * really the case. See bug 787967
michael@0 2350 *
michael@0 2351 * @param registration The voiceMessage or dataMessage from which the
michael@0 2352 * roaming state will be changed (maybe, if needed).
michael@0 2353 */
michael@0 2354 checkRoamingBetweenOperators: function(registration) {
michael@0 2355 let iccInfo = this.rilContext.iccInfo;
michael@0 2356 let operator = registration.network;
michael@0 2357 let state = registration.state;
michael@0 2358
michael@0 2359 if (!iccInfo || !operator ||
michael@0 2360 state != RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED) {
michael@0 2361 return;
michael@0 2362 }
michael@0 2363
michael@0 2364 let spn = iccInfo.spn && iccInfo.spn.toLowerCase();
michael@0 2365 let longName = operator.longName && operator.longName.toLowerCase();
michael@0 2366 let shortName = operator.shortName && operator.shortName.toLowerCase();
michael@0 2367
michael@0 2368 let equalsLongName = longName && (spn == longName);
michael@0 2369 let equalsShortName = shortName && (spn == shortName);
michael@0 2370 let equalsMcc = iccInfo.mcc == operator.mcc;
michael@0 2371
michael@0 2372 registration.roaming = registration.roaming &&
michael@0 2373 !(equalsMcc && (equalsLongName || equalsShortName));
michael@0 2374 },
michael@0 2375
michael@0 2376 /**
michael@0 2377 * Handle data connection changes.
michael@0 2378 *
michael@0 2379 * @param newInfo The new voice connection information.
michael@0 2380 * @param batch When batch is true, the RIL:VoiceInfoChanged message will
michael@0 2381 * not be sent.
michael@0 2382 */
michael@0 2383 updateVoiceConnection: function(newInfo, batch) {
michael@0 2384 let voiceInfo = this.rilContext.voice;
michael@0 2385 voiceInfo.state = newInfo.state;
michael@0 2386 voiceInfo.connected = newInfo.connected;
michael@0 2387 voiceInfo.roaming = newInfo.roaming;
michael@0 2388 voiceInfo.emergencyCallsOnly = newInfo.emergencyCallsOnly;
michael@0 2389 voiceInfo.type = newInfo.type;
michael@0 2390
michael@0 2391 // Make sure we also reset the operator and signal strength information
michael@0 2392 // if we drop off the network.
michael@0 2393 if (newInfo.state !== RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED) {
michael@0 2394 voiceInfo.cell = null;
michael@0 2395 voiceInfo.network = null;
michael@0 2396 voiceInfo.signalStrength = null;
michael@0 2397 voiceInfo.relSignalStrength = null;
michael@0 2398 } else {
michael@0 2399 voiceInfo.cell = newInfo.cell;
michael@0 2400 voiceInfo.network = this.operatorInfo;
michael@0 2401 }
michael@0 2402
michael@0 2403 if (!batch) {
michael@0 2404 gMessageManager.sendMobileConnectionMessage("RIL:VoiceInfoChanged",
michael@0 2405 this.clientId, voiceInfo);
michael@0 2406 }
michael@0 2407 },
michael@0 2408
michael@0 2409 /**
michael@0 2410 * Handle the data connection's state has changed.
michael@0 2411 *
michael@0 2412 * @param newInfo The new data connection information.
michael@0 2413 * @param batch When batch is true, the RIL:DataInfoChanged message will
michael@0 2414 * not be sent.
michael@0 2415 */
michael@0 2416 updateDataConnection: function(newInfo, batch) {
michael@0 2417 let dataInfo = this.rilContext.data;
michael@0 2418 dataInfo.state = newInfo.state;
michael@0 2419 dataInfo.roaming = newInfo.roaming;
michael@0 2420 dataInfo.emergencyCallsOnly = newInfo.emergencyCallsOnly;
michael@0 2421 dataInfo.type = newInfo.type;
michael@0 2422 // For the data connection, the `connected` flag indicates whether
michael@0 2423 // there's an active data call.
michael@0 2424 dataInfo.connected = false;
michael@0 2425 if (gNetworkManager.active &&
michael@0 2426 gNetworkManager.active.type ===
michael@0 2427 Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE &&
michael@0 2428 gNetworkManager.active.serviceId === this.clientId) {
michael@0 2429 dataInfo.connected = true;
michael@0 2430 }
michael@0 2431
michael@0 2432 // Make sure we also reset the operator and signal strength information
michael@0 2433 // if we drop off the network.
michael@0 2434 if (newInfo.state !== RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED) {
michael@0 2435 dataInfo.cell = null;
michael@0 2436 dataInfo.network = null;
michael@0 2437 dataInfo.signalStrength = null;
michael@0 2438 dataInfo.relSignalStrength = null;
michael@0 2439 } else {
michael@0 2440 dataInfo.cell = newInfo.cell;
michael@0 2441 dataInfo.network = this.operatorInfo;
michael@0 2442 }
michael@0 2443
michael@0 2444 if (!batch) {
michael@0 2445 gMessageManager.sendMobileConnectionMessage("RIL:DataInfoChanged",
michael@0 2446 this.clientId, dataInfo);
michael@0 2447 }
michael@0 2448
michael@0 2449 let connHandler = gDataConnectionManager.getConnectionHandler(this.clientId);
michael@0 2450 connHandler.updateRILNetworkInterface();
michael@0 2451 },
michael@0 2452
michael@0 2453 getPreferredNetworkType: function(target, message) {
michael@0 2454 this.workerMessenger.send("getPreferredNetworkType", message, (function(response) {
michael@0 2455 if (response.success) {
michael@0 2456 response.type = RIL.RIL_PREFERRED_NETWORK_TYPE_TO_GECKO[response.networkType];
michael@0 2457 }
michael@0 2458
michael@0 2459 target.sendAsyncMessage("RIL:GetPreferredNetworkType", {
michael@0 2460 clientId: this.clientId,
michael@0 2461 data: response
michael@0 2462 });
michael@0 2463 return false;
michael@0 2464 }).bind(this));
michael@0 2465 },
michael@0 2466
michael@0 2467 setPreferredNetworkType: function(target, message) {
michael@0 2468 let networkType = RIL.RIL_PREFERRED_NETWORK_TYPE_TO_GECKO.indexOf(message.type);
michael@0 2469 if (networkType < 0) {
michael@0 2470 message.errorMsg = RIL.GECKO_ERROR_INVALID_PARAMETER;
michael@0 2471 target.sendAsyncMessage("RIL:SetPreferredNetworkType", {
michael@0 2472 clientId: this.clientId,
michael@0 2473 data: message
michael@0 2474 });
michael@0 2475 return false;
michael@0 2476 }
michael@0 2477 message.networkType = networkType;
michael@0 2478
michael@0 2479 this.workerMessenger.send("setPreferredNetworkType", message, (function(response) {
michael@0 2480 target.sendAsyncMessage("RIL:SetPreferredNetworkType", {
michael@0 2481 clientId: this.clientId,
michael@0 2482 data: response
michael@0 2483 });
michael@0 2484 return false;
michael@0 2485 }).bind(this));
michael@0 2486 },
michael@0 2487
michael@0 2488 setCellBroadcastSearchList: function(newSearchList) {
michael@0 2489 if ((newSearchList == this._cellBroadcastSearchList) ||
michael@0 2490 (newSearchList && this._cellBroadcastSearchList &&
michael@0 2491 newSearchList.gsm == this._cellBroadcastSearchList.gsm &&
michael@0 2492 newSearchList.cdma == this._cellBroadcastSearchList.cdma)) {
michael@0 2493 return;
michael@0 2494 }
michael@0 2495
michael@0 2496 this.workerMessenger.send("setCellBroadcastSearchList",
michael@0 2497 { searchList: newSearchList },
michael@0 2498 (function callback(response) {
michael@0 2499 if (!response.success) {
michael@0 2500 let lock = gSettingsService.createLock();
michael@0 2501 lock.set(kSettingsCellBroadcastSearchList,
michael@0 2502 this._cellBroadcastSearchList, null);
michael@0 2503 } else {
michael@0 2504 this._cellBroadcastSearchList = response.searchList;
michael@0 2505 }
michael@0 2506
michael@0 2507 return false;
michael@0 2508 }).bind(this));
michael@0 2509 },
michael@0 2510
michael@0 2511 /**
michael@0 2512 * Handle signal strength changes.
michael@0 2513 *
michael@0 2514 * @param message The new signal strength.
michael@0 2515 * @param batch When batch is true, the RIL:VoiceInfoChanged and
michael@0 2516 * RIL:DataInfoChanged message will not be sent.
michael@0 2517 */
michael@0 2518 handleSignalStrengthChange: function(message, batch) {
michael@0 2519 let voiceInfo = this.rilContext.voice;
michael@0 2520 // If the voice is not registered, need not to update signal information.
michael@0 2521 if (voiceInfo.state === RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED &&
michael@0 2522 this.isInfoChanged(message.voice, voiceInfo)) {
michael@0 2523 this.updateInfo(message.voice, voiceInfo);
michael@0 2524 if (!batch) {
michael@0 2525 gMessageManager.sendMobileConnectionMessage("RIL:VoiceInfoChanged",
michael@0 2526 this.clientId, voiceInfo);
michael@0 2527 }
michael@0 2528 }
michael@0 2529
michael@0 2530 let dataInfo = this.rilContext.data;
michael@0 2531 // If the data is not registered, need not to update signal information.
michael@0 2532 if (dataInfo.state === RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED &&
michael@0 2533 this.isInfoChanged(message.data, dataInfo)) {
michael@0 2534 this.updateInfo(message.data, dataInfo);
michael@0 2535 if (!batch) {
michael@0 2536 gMessageManager.sendMobileConnectionMessage("RIL:DataInfoChanged",
michael@0 2537 this.clientId, dataInfo);
michael@0 2538 }
michael@0 2539 }
michael@0 2540 },
michael@0 2541
michael@0 2542 /**
michael@0 2543 * Handle operator information changes.
michael@0 2544 *
michael@0 2545 * @param message The new operator information.
michael@0 2546 * @param batch When batch is true, the RIL:VoiceInfoChanged and
michael@0 2547 * RIL:DataInfoChanged message will not be sent.
michael@0 2548 */
michael@0 2549 handleOperatorChange: function(message, batch) {
michael@0 2550 let operatorInfo = this.operatorInfo;
michael@0 2551 let voice = this.rilContext.voice;
michael@0 2552 let data = this.rilContext.data;
michael@0 2553
michael@0 2554 if (this.isInfoChanged(message, operatorInfo)) {
michael@0 2555 this.updateInfo(message, operatorInfo);
michael@0 2556
michael@0 2557 // Update lastKnownNetwork
michael@0 2558 if (message.mcc && message.mnc) {
michael@0 2559 this._lastKnownNetwork = message.mcc + "-" + message.mnc;
michael@0 2560 }
michael@0 2561
michael@0 2562 // If the voice is unregistered, no need to send RIL:VoiceInfoChanged.
michael@0 2563 if (voice.network && !batch) {
michael@0 2564 gMessageManager.sendMobileConnectionMessage("RIL:VoiceInfoChanged",
michael@0 2565 this.clientId, voice);
michael@0 2566 }
michael@0 2567
michael@0 2568 // If the data is unregistered, no need to send RIL:DataInfoChanged.
michael@0 2569 if (data.network && !batch) {
michael@0 2570 gMessageManager.sendMobileConnectionMessage("RIL:DataInfoChanged",
michael@0 2571 this.clientId, data);
michael@0 2572 }
michael@0 2573 }
michael@0 2574 },
michael@0 2575
michael@0 2576 handleOtaStatus: function(message) {
michael@0 2577 if (message.status < 0 ||
michael@0 2578 RIL.CDMA_OTA_PROVISION_STATUS_TO_GECKO.length <= message.status) {
michael@0 2579 return;
michael@0 2580 }
michael@0 2581
michael@0 2582 let status = RIL.CDMA_OTA_PROVISION_STATUS_TO_GECKO[message.status];
michael@0 2583
michael@0 2584 gMessageManager.sendMobileConnectionMessage("RIL:OtaStatusChanged",
michael@0 2585 this.clientId, status);
michael@0 2586 },
michael@0 2587
michael@0 2588 _convertRadioState: function(state) {
michael@0 2589 switch (state) {
michael@0 2590 case RIL.GECKO_RADIOSTATE_OFF:
michael@0 2591 return RIL.GECKO_DETAILED_RADIOSTATE_DISABLED;
michael@0 2592 case RIL.GECKO_RADIOSTATE_READY:
michael@0 2593 return RIL.GECKO_DETAILED_RADIOSTATE_ENABLED;
michael@0 2594 default:
michael@0 2595 return RIL.GECKO_DETAILED_RADIOSTATE_UNKNOWN;
michael@0 2596 }
michael@0 2597 },
michael@0 2598
michael@0 2599 handleRadioStateChange: function(message) {
michael@0 2600 let newState = message.radioState;
michael@0 2601 if (this.rilContext.radioState == newState) {
michael@0 2602 return;
michael@0 2603 }
michael@0 2604 this.rilContext.radioState = newState;
michael@0 2605 this.handleDetailedRadioStateChanged(this._convertRadioState(newState));
michael@0 2606
michael@0 2607 //TODO Should we notify this change as a card state change?
michael@0 2608 },
michael@0 2609
michael@0 2610 handleDetailedRadioStateChanged: function(state) {
michael@0 2611 if (this.rilContext.detailedRadioState == state) {
michael@0 2612 return;
michael@0 2613 }
michael@0 2614 this.rilContext.detailedRadioState = state;
michael@0 2615 gMessageManager.sendMobileConnectionMessage("RIL:RadioStateChanged",
michael@0 2616 this.clientId, state);
michael@0 2617 },
michael@0 2618
michael@0 2619 setDataRegistration: function(attach) {
michael@0 2620 this.workerMessenger.send("setDataRegistration", {attach: attach});
michael@0 2621 },
michael@0 2622
michael@0 2623 /**
michael@0 2624 * TODO: Bug 911713 - B2G NetworkManager: Move policy control logic to
michael@0 2625 * NetworkManager
michael@0 2626 */
michael@0 2627 updateRILNetworkInterface: function() {
michael@0 2628 let connHandler = gDataConnectionManager.getConnectionHandler(this.clientId);
michael@0 2629 connHandler.updateRILNetworkInterface();
michael@0 2630 },
michael@0 2631
michael@0 2632 /**
michael@0 2633 * Update network selection mode
michael@0 2634 */
michael@0 2635 updateNetworkSelectionMode: function(message) {
michael@0 2636 if (DEBUG) this.debug("updateNetworkSelectionMode: " + JSON.stringify(message));
michael@0 2637 this.rilContext.networkSelectionMode = message.mode;
michael@0 2638 gMessageManager.sendMobileConnectionMessage("RIL:NetworkSelectionModeChanged",
michael@0 2639 this.clientId, message);
michael@0 2640 },
michael@0 2641
michael@0 2642 /**
michael@0 2643 * Handle emergency callback mode change.
michael@0 2644 */
michael@0 2645 handleEmergencyCbModeChange: function(message) {
michael@0 2646 if (DEBUG) this.debug("handleEmergencyCbModeChange: " + JSON.stringify(message));
michael@0 2647 gMessageManager.sendMobileConnectionMessage("RIL:EmergencyCbModeChanged",
michael@0 2648 this.clientId, message);
michael@0 2649 },
michael@0 2650
michael@0 2651 /**
michael@0 2652 * Handle WDP port push PDU. Constructor WDP bearer information and deliver
michael@0 2653 * to WapPushManager.
michael@0 2654 *
michael@0 2655 * @param message
michael@0 2656 * A SMS message.
michael@0 2657 */
michael@0 2658 handleSmsWdpPortPush: function(message) {
michael@0 2659 if (message.encoding != RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
michael@0 2660 if (DEBUG) {
michael@0 2661 this.debug("Got port addressed SMS but not encoded in 8-bit alphabet." +
michael@0 2662 " Drop!");
michael@0 2663 }
michael@0 2664 return;
michael@0 2665 }
michael@0 2666
michael@0 2667 let options = {
michael@0 2668 bearer: WAP.WDP_BEARER_GSM_SMS_GSM_MSISDN,
michael@0 2669 sourceAddress: message.sender,
michael@0 2670 sourcePort: message.originatorPort,
michael@0 2671 destinationAddress: this.rilContext.iccInfo.msisdn,
michael@0 2672 destinationPort: message.destinationPort,
michael@0 2673 serviceId: this.clientId
michael@0 2674 };
michael@0 2675 WAP.WapPushManager.receiveWdpPDU(message.fullData, message.fullData.length,
michael@0 2676 0, options);
michael@0 2677 },
michael@0 2678
michael@0 2679 /**
michael@0 2680 * A helper to broadcast the system message to launch registered apps
michael@0 2681 * like Costcontrol, Notification and Message app... etc.
michael@0 2682 *
michael@0 2683 * @param aName
michael@0 2684 * The system message name.
michael@0 2685 * @param aDomMessage
michael@0 2686 * The nsIDOMMozSmsMessage object.
michael@0 2687 */
michael@0 2688 broadcastSmsSystemMessage: function(aName, aDomMessage) {
michael@0 2689 if (DEBUG) this.debug("Broadcasting the SMS system message: " + aName);
michael@0 2690
michael@0 2691 // Sadly we cannot directly broadcast the aDomMessage object
michael@0 2692 // because the system message mechamism will rewrap the object
michael@0 2693 // based on the content window, which needs to know the properties.
michael@0 2694 gSystemMessenger.broadcastMessage(aName, {
michael@0 2695 iccId: aDomMessage.iccId,
michael@0 2696 type: aDomMessage.type,
michael@0 2697 id: aDomMessage.id,
michael@0 2698 threadId: aDomMessage.threadId,
michael@0 2699 delivery: aDomMessage.delivery,
michael@0 2700 deliveryStatus: aDomMessage.deliveryStatus,
michael@0 2701 sender: aDomMessage.sender,
michael@0 2702 receiver: aDomMessage.receiver,
michael@0 2703 body: aDomMessage.body,
michael@0 2704 messageClass: aDomMessage.messageClass,
michael@0 2705 timestamp: aDomMessage.timestamp,
michael@0 2706 sentTimestamp: aDomMessage.sentTimestamp,
michael@0 2707 deliveryTimestamp: aDomMessage.deliveryTimestamp,
michael@0 2708 read: aDomMessage.read
michael@0 2709 });
michael@0 2710 },
michael@0 2711
michael@0 2712 // The following attributes/functions are used for acquiring/releasing the
michael@0 2713 // CPU wake lock when the RIL handles the received SMS. Note that we need
michael@0 2714 // a timer to bound the lock's life cycle to avoid exhausting the battery.
michael@0 2715 _smsHandledWakeLock: null,
michael@0 2716 _smsHandledWakeLockTimer: null,
michael@0 2717
michael@0 2718 _acquireSmsHandledWakeLock: function() {
michael@0 2719 if (!this._smsHandledWakeLock) {
michael@0 2720 if (DEBUG) this.debug("Acquiring a CPU wake lock for handling SMS.");
michael@0 2721 this._smsHandledWakeLock = gPowerManagerService.newWakeLock("cpu");
michael@0 2722 }
michael@0 2723 if (!this._smsHandledWakeLockTimer) {
michael@0 2724 if (DEBUG) this.debug("Creating a timer for releasing the CPU wake lock.");
michael@0 2725 this._smsHandledWakeLockTimer =
michael@0 2726 Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
michael@0 2727 }
michael@0 2728 if (DEBUG) this.debug("Setting the timer for releasing the CPU wake lock.");
michael@0 2729 this._smsHandledWakeLockTimer
michael@0 2730 .initWithCallback(this._releaseSmsHandledWakeLock.bind(this),
michael@0 2731 SMS_HANDLED_WAKELOCK_TIMEOUT,
michael@0 2732 Ci.nsITimer.TYPE_ONE_SHOT);
michael@0 2733 },
michael@0 2734
michael@0 2735 _releaseSmsHandledWakeLock: function() {
michael@0 2736 if (DEBUG) this.debug("Releasing the CPU wake lock for handling SMS.");
michael@0 2737 if (this._smsHandledWakeLockTimer) {
michael@0 2738 this._smsHandledWakeLockTimer.cancel();
michael@0 2739 }
michael@0 2740 if (this._smsHandledWakeLock) {
michael@0 2741 this._smsHandledWakeLock.unlock();
michael@0 2742 this._smsHandledWakeLock = null;
michael@0 2743 }
michael@0 2744 },
michael@0 2745
michael@0 2746 /**
michael@0 2747 * Hash map for received multipart sms fragments. Messages are hashed with
michael@0 2748 * its sender address and concatenation reference number. Three additional
michael@0 2749 * attributes `segmentMaxSeq`, `receivedSegments`, `segments` are inserted.
michael@0 2750 */
michael@0 2751 _receivedSmsSegmentsMap: null,
michael@0 2752
michael@0 2753 /**
michael@0 2754 * Helper for processing received multipart SMS.
michael@0 2755 *
michael@0 2756 * @return null for handled segments, and an object containing full message
michael@0 2757 * body/data once all segments are received.
michael@0 2758 */
michael@0 2759 _processReceivedSmsSegment: function(aSegment) {
michael@0 2760
michael@0 2761 // Directly replace full message body for single SMS.
michael@0 2762 if (!(aSegment.segmentMaxSeq && (aSegment.segmentMaxSeq > 1))) {
michael@0 2763 if (aSegment.encoding == RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
michael@0 2764 aSegment.fullData = aSegment.data;
michael@0 2765 } else {
michael@0 2766 aSegment.fullBody = aSegment.body;
michael@0 2767 }
michael@0 2768 return aSegment;
michael@0 2769 }
michael@0 2770
michael@0 2771 // Handle Concatenation for Class 0 SMS
michael@0 2772 let hash = aSegment.sender + ":" +
michael@0 2773 aSegment.segmentRef + ":" +
michael@0 2774 aSegment.segmentMaxSeq;
michael@0 2775 let seq = aSegment.segmentSeq;
michael@0 2776
michael@0 2777 let options = this._receivedSmsSegmentsMap[hash];
michael@0 2778 if (!options) {
michael@0 2779 options = aSegment;
michael@0 2780 this._receivedSmsSegmentsMap[hash] = options;
michael@0 2781
michael@0 2782 options.receivedSegments = 0;
michael@0 2783 options.segments = [];
michael@0 2784 } else if (options.segments[seq]) {
michael@0 2785 // Duplicated segment?
michael@0 2786 if (DEBUG) {
michael@0 2787 this.debug("Got duplicated segment no." + seq +
michael@0 2788 " of a multipart SMS: " + JSON.stringify(aSegment));
michael@0 2789 }
michael@0 2790 return null;
michael@0 2791 }
michael@0 2792
michael@0 2793 if (options.receivedSegments > 0) {
michael@0 2794 // Update received timestamp.
michael@0 2795 options.timestamp = aSegment.timestamp;
michael@0 2796 }
michael@0 2797
michael@0 2798 if (options.encoding == RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
michael@0 2799 options.segments[seq] = aSegment.data;
michael@0 2800 } else {
michael@0 2801 options.segments[seq] = aSegment.body;
michael@0 2802 }
michael@0 2803 options.receivedSegments++;
michael@0 2804
michael@0 2805 // The port information is only available in 1st segment for CDMA WAP Push.
michael@0 2806 // If the segments of a WAP Push are not received in sequence
michael@0 2807 // (e.g., SMS with seq == 1 is not the 1st segment received by the device),
michael@0 2808 // we have to retrieve the port information from 1st segment and
michael@0 2809 // save it into the cached options.
michael@0 2810 if (aSegment.teleservice === RIL.PDU_CDMA_MSG_TELESERIVCIE_ID_WAP
michael@0 2811 && seq === 1) {
michael@0 2812 if (!options.originatorPort && aSegment.originatorPort) {
michael@0 2813 options.originatorPort = aSegment.originatorPort;
michael@0 2814 }
michael@0 2815
michael@0 2816 if (!options.destinationPort && aSegment.destinationPort) {
michael@0 2817 options.destinationPort = aSegment.destinationPort;
michael@0 2818 }
michael@0 2819 }
michael@0 2820
michael@0 2821 if (options.receivedSegments < options.segmentMaxSeq) {
michael@0 2822 if (DEBUG) {
michael@0 2823 this.debug("Got segment no." + seq + " of a multipart SMS: " +
michael@0 2824 JSON.stringify(options));
michael@0 2825 }
michael@0 2826 return null;
michael@0 2827 }
michael@0 2828
michael@0 2829 // Remove from map
michael@0 2830 delete this._receivedSmsSegmentsMap[hash];
michael@0 2831
michael@0 2832 // Rebuild full body
michael@0 2833 if (options.encoding == RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
michael@0 2834 // Uint8Array doesn't have `concat`, so we have to merge all segements
michael@0 2835 // by hand.
michael@0 2836 let fullDataLen = 0;
michael@0 2837 for (let i = 1; i <= options.segmentMaxSeq; i++) {
michael@0 2838 fullDataLen += options.segments[i].length;
michael@0 2839 }
michael@0 2840
michael@0 2841 options.fullData = new Uint8Array(fullDataLen);
michael@0 2842 for (let d= 0, i = 1; i <= options.segmentMaxSeq; i++) {
michael@0 2843 let data = options.segments[i];
michael@0 2844 for (let j = 0; j < data.length; j++) {
michael@0 2845 options.fullData[d++] = data[j];
michael@0 2846 }
michael@0 2847 }
michael@0 2848 } else {
michael@0 2849 options.fullBody = options.segments.join("");
michael@0 2850 }
michael@0 2851
michael@0 2852 // Remove handy fields after completing the concatenation.
michael@0 2853 delete options.receivedSegments;
michael@0 2854 delete options.segments;
michael@0 2855
michael@0 2856 if (DEBUG) {
michael@0 2857 this.debug("Got full multipart SMS: " + JSON.stringify(options));
michael@0 2858 }
michael@0 2859
michael@0 2860 return options;
michael@0 2861 },
michael@0 2862
michael@0 2863 /**
michael@0 2864 * Helper to create Savable SmsSegment.
michael@0 2865 */
michael@0 2866 _createSavableSmsSegment: function(aMessage) {
michael@0 2867 // We precisely define what data fields to be stored into
michael@0 2868 // DB here for better data migration.
michael@0 2869 let segment = {};
michael@0 2870 segment.messageType = aMessage.messageType;
michael@0 2871 segment.teleservice = aMessage.teleservice;
michael@0 2872 segment.SMSC = aMessage.SMSC;
michael@0 2873 segment.sentTimestamp = aMessage.sentTimestamp;
michael@0 2874 segment.timestamp = Date.now();
michael@0 2875 segment.sender = aMessage.sender;
michael@0 2876 segment.pid = aMessage.pid;
michael@0 2877 segment.encoding = aMessage.encoding;
michael@0 2878 segment.messageClass = aMessage.messageClass;
michael@0 2879 segment.iccId = this.getIccId();
michael@0 2880 if (aMessage.header) {
michael@0 2881 segment.segmentRef = aMessage.header.segmentRef;
michael@0 2882 segment.segmentSeq = aMessage.header.segmentSeq;
michael@0 2883 segment.segmentMaxSeq = aMessage.header.segmentMaxSeq;
michael@0 2884 segment.originatorPort = aMessage.header.originatorPort;
michael@0 2885 segment.destinationPort = aMessage.header.destinationPort;
michael@0 2886 }
michael@0 2887 segment.mwiPresent = (aMessage.mwi)? true: false;
michael@0 2888 segment.mwiDiscard = (segment.mwiPresent)? aMessage.mwi.discard: false;
michael@0 2889 segment.mwiMsgCount = (segment.mwiPresent)? aMessage.mwi.msgCount: 0;
michael@0 2890 segment.mwiActive = (segment.mwiPresent)? aMessage.mwi.active: false;
michael@0 2891 segment.serviceCategory = aMessage.serviceCategory;
michael@0 2892 segment.language = aMessage.language;
michael@0 2893 segment.data = aMessage.data;
michael@0 2894 segment.body = aMessage.body;
michael@0 2895
michael@0 2896 return segment;
michael@0 2897 },
michael@0 2898
michael@0 2899 /**
michael@0 2900 * Helper to purge complete message.
michael@0 2901 *
michael@0 2902 * We remove unnessary fields defined in _createSavableSmsSegment() after
michael@0 2903 * completing the concatenation.
michael@0 2904 */
michael@0 2905 _purgeCompleteSmsMessage: function(aMessage) {
michael@0 2906 // Purge concatenation info
michael@0 2907 delete aMessage.segmentRef;
michael@0 2908 delete aMessage.segmentSeq;
michael@0 2909 delete aMessage.segmentMaxSeq;
michael@0 2910
michael@0 2911 // Purge partial message body
michael@0 2912 delete aMessage.data;
michael@0 2913 delete aMessage.body;
michael@0 2914 },
michael@0 2915
michael@0 2916 /**
michael@0 2917 * handle concatenation of received SMS.
michael@0 2918 */
michael@0 2919 handleSmsMultipart: function(aMessage) {
michael@0 2920 if (DEBUG) this.debug("handleSmsMultipart: " + JSON.stringify(aMessage));
michael@0 2921
michael@0 2922 this._acquireSmsHandledWakeLock();
michael@0 2923
michael@0 2924 let segment = this._createSavableSmsSegment(aMessage);
michael@0 2925
michael@0 2926 let isMultipart = (segment.segmentMaxSeq && (segment.segmentMaxSeq > 1));
michael@0 2927 let messageClass = segment.messageClass;
michael@0 2928
michael@0 2929 let handleReceivedAndAck = function(aRvOfIncompleteMsg, aCompleteMessage) {
michael@0 2930 if (aCompleteMessage) {
michael@0 2931 this._purgeCompleteSmsMessage(aCompleteMessage);
michael@0 2932 if (this.handleSmsReceived(aCompleteMessage)) {
michael@0 2933 this.sendAckSms(Cr.NS_OK, aCompleteMessage);
michael@0 2934 }
michael@0 2935 // else Ack will be sent after further process in handleSmsReceived.
michael@0 2936 } else {
michael@0 2937 this.sendAckSms(aRvOfIncompleteMsg, segment);
michael@0 2938 }
michael@0 2939 }.bind(this);
michael@0 2940
michael@0 2941 // No need to access SmsSegmentStore for Class 0 SMS and Single SMS.
michael@0 2942 if (!isMultipart ||
michael@0 2943 (messageClass == RIL.GECKO_SMS_MESSAGE_CLASSES[RIL.PDU_DCS_MSG_CLASS_0])) {
michael@0 2944 // `When a mobile terminated message is class 0 and the MS has the
michael@0 2945 // capability of displaying short messages, the MS shall display the
michael@0 2946 // message immediately and send an acknowledgement to the SC when the
michael@0 2947 // message has successfully reached the MS irrespective of whether
michael@0 2948 // there is memory available in the (U)SIM or ME. The message shall
michael@0 2949 // not be automatically stored in the (U)SIM or ME.`
michael@0 2950 // ~ 3GPP 23.038 clause 4
michael@0 2951
michael@0 2952 handleReceivedAndAck(Cr.NS_OK, // ACK OK For Incomplete Class 0
michael@0 2953 this._processReceivedSmsSegment(segment));
michael@0 2954 } else {
michael@0 2955 gMobileMessageDatabaseService
michael@0 2956 .saveSmsSegment(segment, function notifyResult(aRv, aCompleteMessage) {
michael@0 2957 handleReceivedAndAck(aRv, // Ack according to the result after saving
michael@0 2958 aCompleteMessage);
michael@0 2959 });
michael@0 2960 }
michael@0 2961 },
michael@0 2962
michael@0 2963 portAddressedSmsApps: null,
michael@0 2964 handleSmsReceived: function(message) {
michael@0 2965 if (DEBUG) this.debug("handleSmsReceived: " + JSON.stringify(message));
michael@0 2966
michael@0 2967 if (message.messageType == RIL.PDU_CDMA_MSG_TYPE_BROADCAST) {
michael@0 2968 gMessageManager.sendCellBroadcastMessage("RIL:CellBroadcastReceived",
michael@0 2969 this.clientId, message);
michael@0 2970 return true;
michael@0 2971 }
michael@0 2972
michael@0 2973 // Dispatch to registered handler if application port addressing is
michael@0 2974 // available. Note that the destination port can possibly be zero when
michael@0 2975 // representing a UDP/TCP port.
michael@0 2976 if (message.destinationPort != null) {
michael@0 2977 let handler = this.portAddressedSmsApps[message.destinationPort];
michael@0 2978 if (handler) {
michael@0 2979 handler(message);
michael@0 2980 }
michael@0 2981 return true;
michael@0 2982 }
michael@0 2983
michael@0 2984 if (message.encoding == RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
michael@0 2985 // Don't know how to handle binary data yet.
michael@0 2986 return true;
michael@0 2987 }
michael@0 2988
michael@0 2989 message.type = "sms";
michael@0 2990 message.sender = message.sender || null;
michael@0 2991 message.receiver = this.getPhoneNumber();
michael@0 2992 message.body = message.fullBody = message.fullBody || null;
michael@0 2993
michael@0 2994 if (gSmsService.isSilentNumber(message.sender)) {
michael@0 2995 message.id = -1;
michael@0 2996 message.threadId = 0;
michael@0 2997 message.delivery = DOM_MOBILE_MESSAGE_DELIVERY_RECEIVED;
michael@0 2998 message.deliveryStatus = RIL.GECKO_SMS_DELIVERY_STATUS_SUCCESS;
michael@0 2999 message.read = false;
michael@0 3000
michael@0 3001 let domMessage =
michael@0 3002 gMobileMessageService.createSmsMessage(message.id,
michael@0 3003 message.threadId,
michael@0 3004 message.iccId,
michael@0 3005 message.delivery,
michael@0 3006 message.deliveryStatus,
michael@0 3007 message.sender,
michael@0 3008 message.receiver,
michael@0 3009 message.body,
michael@0 3010 message.messageClass,
michael@0 3011 message.timestamp,
michael@0 3012 message.sentTimestamp,
michael@0 3013 0,
michael@0 3014 message.read);
michael@0 3015
michael@0 3016 Services.obs.notifyObservers(domMessage,
michael@0 3017 kSilentSmsReceivedObserverTopic,
michael@0 3018 null);
michael@0 3019 return true;
michael@0 3020 }
michael@0 3021
michael@0 3022 if (message.mwiPresent) {
michael@0 3023 let mwi = {
michael@0 3024 discard: message.mwiDiscard,
michael@0 3025 msgCount: message.mwiMsgCount,
michael@0 3026 active: message.mwiActive
michael@0 3027 };
michael@0 3028 this.workerMessenger.send("updateMwis", { mwi: mwi });
michael@0 3029
michael@0 3030 mwi.returnNumber = message.sender;
michael@0 3031 mwi.returnMessage = message.fullBody;
michael@0 3032 gMessageManager.sendVoicemailMessage("RIL:VoicemailNotification",
michael@0 3033 this.clientId, mwi);
michael@0 3034
michael@0 3035 // Dicarded MWI comes without text body.
michael@0 3036 // Hence, we discard it here after notifying the MWI status.
michael@0 3037 if (message.mwiDiscard) {
michael@0 3038 return true;
michael@0 3039 }
michael@0 3040 }
michael@0 3041
michael@0 3042 let notifyReceived = function notifyReceived(rv, domMessage) {
michael@0 3043 let success = Components.isSuccessCode(rv);
michael@0 3044
michael@0 3045 this.sendAckSms(rv, message);
michael@0 3046
michael@0 3047 if (!success) {
michael@0 3048 // At this point we could send a message to content to notify the user
michael@0 3049 // that storing an incoming SMS failed, most likely due to a full disk.
michael@0 3050 if (DEBUG) {
michael@0 3051 this.debug("Could not store SMS, error code " + rv);
michael@0 3052 }
michael@0 3053 return;
michael@0 3054 }
michael@0 3055
michael@0 3056 this.broadcastSmsSystemMessage(kSmsReceivedObserverTopic, domMessage);
michael@0 3057 Services.obs.notifyObservers(domMessage, kSmsReceivedObserverTopic, null);
michael@0 3058 }.bind(this);
michael@0 3059
michael@0 3060 if (message.messageClass != RIL.GECKO_SMS_MESSAGE_CLASSES[RIL.PDU_DCS_MSG_CLASS_0]) {
michael@0 3061 gMobileMessageDatabaseService.saveReceivedMessage(message,
michael@0 3062 notifyReceived);
michael@0 3063 } else {
michael@0 3064 message.id = -1;
michael@0 3065 message.threadId = 0;
michael@0 3066 message.delivery = DOM_MOBILE_MESSAGE_DELIVERY_RECEIVED;
michael@0 3067 message.deliveryStatus = RIL.GECKO_SMS_DELIVERY_STATUS_SUCCESS;
michael@0 3068 message.read = false;
michael@0 3069
michael@0 3070 let domMessage =
michael@0 3071 gMobileMessageService.createSmsMessage(message.id,
michael@0 3072 message.threadId,
michael@0 3073 message.iccId,
michael@0 3074 message.delivery,
michael@0 3075 message.deliveryStatus,
michael@0 3076 message.sender,
michael@0 3077 message.receiver,
michael@0 3078 message.body,
michael@0 3079 message.messageClass,
michael@0 3080 message.timestamp,
michael@0 3081 message.sentTimestamp,
michael@0 3082 0,
michael@0 3083 message.read);
michael@0 3084
michael@0 3085 notifyReceived(Cr.NS_OK, domMessage);
michael@0 3086 }
michael@0 3087
michael@0 3088 // SMS ACK will be sent in notifyReceived. Return false here.
michael@0 3089 return false;
michael@0 3090 },
michael@0 3091
michael@0 3092 /**
michael@0 3093 * Handle ACK response of received SMS.
michael@0 3094 */
michael@0 3095 sendAckSms: function(aRv, aMessage) {
michael@0 3096 if (aMessage.messageClass === RIL.GECKO_SMS_MESSAGE_CLASSES[RIL.PDU_DCS_MSG_CLASS_2]) {
michael@0 3097 return;
michael@0 3098 }
michael@0 3099
michael@0 3100 let result = RIL.PDU_FCS_OK;
michael@0 3101 if (!Components.isSuccessCode(aRv)) {
michael@0 3102 if (DEBUG) this.debug("Failed to handle received sms: " + aRv);
michael@0 3103 result = (aRv === Cr.NS_ERROR_FILE_NO_DEVICE_SPACE)
michael@0 3104 ? RIL.PDU_FCS_MEMORY_CAPACITY_EXCEEDED
michael@0 3105 : RIL.PDU_FCS_UNSPECIFIED;
michael@0 3106 }
michael@0 3107
michael@0 3108 this.workerMessenger.send("ackSMS", { result: result });
michael@0 3109
michael@0 3110 },
michael@0 3111
michael@0 3112 /**
michael@0 3113 * Set the setting value of "time.clock.automatic-update.available".
michael@0 3114 */
michael@0 3115 setClockAutoUpdateAvailable: function(value) {
michael@0 3116 gSettingsService.createLock().set(kSettingsClockAutoUpdateAvailable, value, null,
michael@0 3117 "fromInternalSetting");
michael@0 3118 },
michael@0 3119
michael@0 3120 /**
michael@0 3121 * Set the setting value of "time.timezone.automatic-update.available".
michael@0 3122 */
michael@0 3123 setTimezoneAutoUpdateAvailable: function(value) {
michael@0 3124 gSettingsService.createLock().set(kSettingsTimezoneAutoUpdateAvailable, value, null,
michael@0 3125 "fromInternalSetting");
michael@0 3126 },
michael@0 3127
michael@0 3128 /**
michael@0 3129 * Set the system clock by NITZ.
michael@0 3130 */
michael@0 3131 setClockByNitz: function(message) {
michael@0 3132 // To set the system clock time. Note that there could be a time diff
michael@0 3133 // between when the NITZ was received and when the time is actually set.
michael@0 3134 gTimeService.set(
michael@0 3135 message.networkTimeInMS + (Date.now() - message.receiveTimeInMS));
michael@0 3136 },
michael@0 3137
michael@0 3138 /**
michael@0 3139 * Set the system time zone by NITZ.
michael@0 3140 */
michael@0 3141 setTimezoneByNitz: function(message) {
michael@0 3142 // To set the sytem timezone. Note that we need to convert the time zone
michael@0 3143 // value to a UTC repesentation string in the format of "UTC(+/-)hh:mm".
michael@0 3144 // Ex, time zone -480 is "UTC+08:00"; time zone 630 is "UTC-10:30".
michael@0 3145 //
michael@0 3146 // We can unapply the DST correction if we want the raw time zone offset:
michael@0 3147 // message.networkTimeZoneInMinutes -= message.networkDSTInMinutes;
michael@0 3148 if (message.networkTimeZoneInMinutes != (new Date()).getTimezoneOffset()) {
michael@0 3149 let absTimeZoneInMinutes = Math.abs(message.networkTimeZoneInMinutes);
michael@0 3150 let timeZoneStr = "UTC";
michael@0 3151 timeZoneStr += (message.networkTimeZoneInMinutes > 0 ? "-" : "+");
michael@0 3152 timeZoneStr += ("0" + Math.floor(absTimeZoneInMinutes / 60)).slice(-2);
michael@0 3153 timeZoneStr += ":";
michael@0 3154 timeZoneStr += ("0" + absTimeZoneInMinutes % 60).slice(-2);
michael@0 3155 gSettingsService.createLock().set("time.timezone", timeZoneStr, null);
michael@0 3156 }
michael@0 3157 },
michael@0 3158
michael@0 3159 /**
michael@0 3160 * Handle the NITZ message.
michael@0 3161 */
michael@0 3162 handleNitzTime: function(message) {
michael@0 3163 // Got the NITZ info received from the ril_worker.
michael@0 3164 this.setClockAutoUpdateAvailable(true);
michael@0 3165 this.setTimezoneAutoUpdateAvailable(true);
michael@0 3166
michael@0 3167 // Cache the latest NITZ message whenever receiving it.
michael@0 3168 this._lastNitzMessage = message;
michael@0 3169
michael@0 3170 // Set the received NITZ clock if the setting is enabled.
michael@0 3171 if (this._clockAutoUpdateEnabled) {
michael@0 3172 this.setClockByNitz(message);
michael@0 3173 }
michael@0 3174 // Set the received NITZ timezone if the setting is enabled.
michael@0 3175 if (this._timezoneAutoUpdateEnabled) {
michael@0 3176 this.setTimezoneByNitz(message);
michael@0 3177 }
michael@0 3178 },
michael@0 3179
michael@0 3180 /**
michael@0 3181 * Set the system clock by SNTP.
michael@0 3182 */
michael@0 3183 setClockBySntp: function(offset) {
michael@0 3184 // Got the SNTP info.
michael@0 3185 this.setClockAutoUpdateAvailable(true);
michael@0 3186 if (!this._clockAutoUpdateEnabled) {
michael@0 3187 return;
michael@0 3188 }
michael@0 3189 if (this._lastNitzMessage) {
michael@0 3190 if (DEBUG) debug("SNTP: NITZ available, discard SNTP");
michael@0 3191 return;
michael@0 3192 }
michael@0 3193 gTimeService.set(Date.now() + offset);
michael@0 3194 },
michael@0 3195
michael@0 3196 handleIccMbdn: function(message) {
michael@0 3197 let voicemailInfo = this.voicemailInfo;
michael@0 3198
michael@0 3199 voicemailInfo.number = message.number;
michael@0 3200 voicemailInfo.displayName = message.alphaId;
michael@0 3201
michael@0 3202 gMessageManager.sendVoicemailMessage("RIL:VoicemailInfoChanged",
michael@0 3203 this.clientId, voicemailInfo);
michael@0 3204 },
michael@0 3205
michael@0 3206 handleIccInfoChange: function(message) {
michael@0 3207 let oldSpn = this.rilContext.iccInfo ? this.rilContext.iccInfo.spn : null;
michael@0 3208
michael@0 3209 if (!message || !message.iccType) {
michael@0 3210 // Card is not detected, clear iccInfo to null.
michael@0 3211 this.rilContext.iccInfo = null;
michael@0 3212 } else {
michael@0 3213 if (!this.rilContext.iccInfo) {
michael@0 3214 if (message.iccType === "ruim" || message.iccType === "csim") {
michael@0 3215 this.rilContext.iccInfo = new CdmaIccInfo();
michael@0 3216 } else {
michael@0 3217 this.rilContext.iccInfo = new GsmIccInfo();
michael@0 3218 }
michael@0 3219 }
michael@0 3220
michael@0 3221 if (!this.isInfoChanged(message, this.rilContext.iccInfo)) {
michael@0 3222 return;
michael@0 3223 }
michael@0 3224
michael@0 3225 this.updateInfo(message, this.rilContext.iccInfo);
michael@0 3226 }
michael@0 3227
michael@0 3228 // RIL:IccInfoChanged corresponds to a DOM event that gets fired only
michael@0 3229 // when iccInfo has changed.
michael@0 3230 gMessageManager.sendIccMessage("RIL:IccInfoChanged",
michael@0 3231 this.clientId,
michael@0 3232 message.iccType ? message : null);
michael@0 3233
michael@0 3234 // Update lastKnownSimMcc.
michael@0 3235 if (message.mcc) {
michael@0 3236 try {
michael@0 3237 Services.prefs.setCharPref("ril.lastKnownSimMcc",
michael@0 3238 message.mcc.toString());
michael@0 3239 } catch (e) {}
michael@0 3240 }
michael@0 3241
michael@0 3242 // Update lastKnownHomeNetwork.
michael@0 3243 if (message.mcc && message.mnc) {
michael@0 3244 this._lastKnownHomeNetwork = message.mcc + "-" + message.mnc;
michael@0 3245 }
michael@0 3246
michael@0 3247 // If spn becomes available, we should check roaming again.
michael@0 3248 if (!oldSpn && message.spn) {
michael@0 3249 let voice = this.rilContext.voice;
michael@0 3250 let data = this.rilContext.data;
michael@0 3251 let voiceRoaming = voice.roaming;
michael@0 3252 let dataRoaming = data.roaming;
michael@0 3253 this.checkRoamingBetweenOperators(voice);
michael@0 3254 this.checkRoamingBetweenOperators(data);
michael@0 3255 if (voiceRoaming != voice.roaming) {
michael@0 3256 gMessageManager.sendMobileConnectionMessage("RIL:VoiceInfoChanged",
michael@0 3257 this.clientId, voice);
michael@0 3258 }
michael@0 3259 if (dataRoaming != data.roaming) {
michael@0 3260 gMessageManager.sendMobileConnectionMessage("RIL:DataInfoChanged",
michael@0 3261 this.clientId, data);
michael@0 3262 }
michael@0 3263 }
michael@0 3264 },
michael@0 3265
michael@0 3266 handleUSSDReceived: function(ussd) {
michael@0 3267 if (DEBUG) this.debug("handleUSSDReceived " + JSON.stringify(ussd));
michael@0 3268 gSystemMessenger.broadcastMessage("ussd-received", ussd);
michael@0 3269 gMessageManager.sendMobileConnectionMessage("RIL:USSDReceived",
michael@0 3270 this.clientId, ussd);
michael@0 3271 },
michael@0 3272
michael@0 3273 handleStkProactiveCommand: function(message) {
michael@0 3274 if (DEBUG) this.debug("handleStkProactiveCommand " + JSON.stringify(message));
michael@0 3275 let iccId = this.rilContext.iccInfo && this.rilContext.iccInfo.iccid;
michael@0 3276 if (iccId) {
michael@0 3277 gSystemMessenger.broadcastMessage("icc-stkcommand",
michael@0 3278 {iccId: iccId,
michael@0 3279 command: message});
michael@0 3280 }
michael@0 3281 gMessageManager.sendIccMessage("RIL:StkCommand", this.clientId, message);
michael@0 3282 },
michael@0 3283
michael@0 3284 handleExitEmergencyCbMode: function(message) {
michael@0 3285 if (DEBUG) this.debug("handleExitEmergencyCbMode: " + JSON.stringify(message));
michael@0 3286 gMessageManager.sendRequestResults("RIL:ExitEmergencyCbMode", message);
michael@0 3287 },
michael@0 3288
michael@0 3289 // nsIObserver
michael@0 3290
michael@0 3291 observe: function(subject, topic, data) {
michael@0 3292 switch (topic) {
michael@0 3293 case kMozSettingsChangedObserverTopic:
michael@0 3294 let setting = JSON.parse(data);
michael@0 3295 this.handleSettingsChange(setting.key, setting.value, setting.message);
michael@0 3296 break;
michael@0 3297 case NS_PREFBRANCH_PREFCHANGE_TOPIC_ID:
michael@0 3298 if (data === kPrefCellBroadcastDisabled) {
michael@0 3299 let value = false;
michael@0 3300 try {
michael@0 3301 value = Services.prefs.getBoolPref(kPrefCellBroadcastDisabled);
michael@0 3302 } catch(e) {}
michael@0 3303 this.workerMessenger.send("setCellBroadcastDisabled",
michael@0 3304 { disabled: value });
michael@0 3305 }
michael@0 3306 break;
michael@0 3307 case kSysClockChangeObserverTopic:
michael@0 3308 let offset = parseInt(data, 10);
michael@0 3309 if (this._lastNitzMessage) {
michael@0 3310 this._lastNitzMessage.receiveTimeInMS += offset;
michael@0 3311 }
michael@0 3312 this._sntp.updateOffset(offset);
michael@0 3313 break;
michael@0 3314 case kNetworkConnStateChangedTopic:
michael@0 3315 let network = subject.QueryInterface(Ci.nsINetworkInterface);
michael@0 3316 if (network.state != Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED) {
michael@0 3317 return;
michael@0 3318 }
michael@0 3319
michael@0 3320 // SNTP can only update when we have mobile or Wifi connections.
michael@0 3321 if (network.type != Ci.nsINetworkInterface.NETWORK_TYPE_WIFI &&
michael@0 3322 network.type != Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE) {
michael@0 3323 return;
michael@0 3324 }
michael@0 3325
michael@0 3326 // If the network comes from RIL, make sure the RIL service is matched.
michael@0 3327 if (subject instanceof Ci.nsIRilNetworkInterface) {
michael@0 3328 network = subject.QueryInterface(Ci.nsIRilNetworkInterface);
michael@0 3329 if (network.serviceId != this.clientId) {
michael@0 3330 return;
michael@0 3331 }
michael@0 3332 }
michael@0 3333
michael@0 3334 // SNTP won't update unless the SNTP is already expired.
michael@0 3335 if (this._sntp.isExpired()) {
michael@0 3336 this._sntp.request();
michael@0 3337 }
michael@0 3338 break;
michael@0 3339 case kNetworkActiveChangedTopic:
michael@0 3340 let dataInfo = this.rilContext.data;
michael@0 3341 let connected = false;
michael@0 3342 if (gNetworkManager.active &&
michael@0 3343 gNetworkManager.active.type ===
michael@0 3344 Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE &&
michael@0 3345 gNetworkManager.active.serviceId === this.clientId) {
michael@0 3346 connected = true;
michael@0 3347 }
michael@0 3348 if (dataInfo.connected !== connected) {
michael@0 3349 dataInfo.connected = connected;
michael@0 3350 gMessageManager.sendMobileConnectionMessage("RIL:DataInfoChanged",
michael@0 3351 this.clientId, dataInfo);
michael@0 3352 }
michael@0 3353 break;
michael@0 3354 case kScreenStateChangedTopic:
michael@0 3355 this.workerMessenger.send("setScreenState", { on: (data === "on") });
michael@0 3356 break;
michael@0 3357 }
michael@0 3358 },
michael@0 3359
michael@0 3360 supportedNetworkTypes: null,
michael@0 3361
michael@0 3362 // Flag to determine whether to update system clock automatically. It
michael@0 3363 // corresponds to the "time.clock.automatic-update.enabled" setting.
michael@0 3364 _clockAutoUpdateEnabled: null,
michael@0 3365
michael@0 3366 // Flag to determine whether to update system timezone automatically. It
michael@0 3367 // corresponds to the "time.clock.automatic-update.enabled" setting.
michael@0 3368 _timezoneAutoUpdateEnabled: null,
michael@0 3369
michael@0 3370 // Remember the last NITZ message so that we can set the time based on
michael@0 3371 // the network immediately when users enable network-based time.
michael@0 3372 _lastNitzMessage: null,
michael@0 3373
michael@0 3374 // Object that handles SNTP.
michael@0 3375 _sntp: null,
michael@0 3376
michael@0 3377 // Cell Broadcast settings values.
michael@0 3378 _cellBroadcastSearchList: null,
michael@0 3379
michael@0 3380 // Operator's mcc-mnc.
michael@0 3381 _lastKnownNetwork: null,
michael@0 3382
michael@0 3383 // ICC's mcc-mnc.
michael@0 3384 _lastKnownHomeNetwork: null,
michael@0 3385
michael@0 3386 handleSettingsChange: function(aName, aResult, aMessage) {
michael@0 3387 // Don't allow any content processes to modify the setting
michael@0 3388 // "time.clock.automatic-update.available" except for the chrome process.
michael@0 3389 if (aName === kSettingsClockAutoUpdateAvailable &&
michael@0 3390 aMessage !== "fromInternalSetting") {
michael@0 3391 let isClockAutoUpdateAvailable = this._lastNitzMessage !== null ||
michael@0 3392 this._sntp.isAvailable();
michael@0 3393 if (aResult !== isClockAutoUpdateAvailable) {
michael@0 3394 if (DEBUG) {
michael@0 3395 debug("Content processes cannot modify 'time.clock.automatic-update.available'. Restore!");
michael@0 3396 }
michael@0 3397 // Restore the setting to the current value.
michael@0 3398 this.setClockAutoUpdateAvailable(isClockAutoUpdateAvailable);
michael@0 3399 }
michael@0 3400 }
michael@0 3401
michael@0 3402 // Don't allow any content processes to modify the setting
michael@0 3403 // "time.timezone.automatic-update.available" except for the chrome
michael@0 3404 // process.
michael@0 3405 if (aName === kSettingsTimezoneAutoUpdateAvailable &&
michael@0 3406 aMessage !== "fromInternalSetting") {
michael@0 3407 let isTimezoneAutoUpdateAvailable = this._lastNitzMessage !== null;
michael@0 3408 if (aResult !== isTimezoneAutoUpdateAvailable) {
michael@0 3409 if (DEBUG) {
michael@0 3410 this.debug("Content processes cannot modify 'time.timezone.automatic-update.available'. Restore!");
michael@0 3411 }
michael@0 3412 // Restore the setting to the current value.
michael@0 3413 this.setTimezoneAutoUpdateAvailable(isTimezoneAutoUpdateAvailable);
michael@0 3414 }
michael@0 3415 }
michael@0 3416
michael@0 3417 this.handle(aName, aResult);
michael@0 3418 },
michael@0 3419
michael@0 3420 // nsISettingsServiceCallback
michael@0 3421 handle: function(aName, aResult) {
michael@0 3422 switch(aName) {
michael@0 3423 case kSettingsClockAutoUpdateEnabled:
michael@0 3424 this._clockAutoUpdateEnabled = aResult;
michael@0 3425 if (!this._clockAutoUpdateEnabled) {
michael@0 3426 break;
michael@0 3427 }
michael@0 3428
michael@0 3429 // Set the latest cached NITZ time if it's available.
michael@0 3430 if (this._lastNitzMessage) {
michael@0 3431 this.setClockByNitz(this._lastNitzMessage);
michael@0 3432 } else if (gNetworkManager.active && gNetworkManager.active.state ==
michael@0 3433 Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED) {
michael@0 3434 // Set the latest cached SNTP time if it's available.
michael@0 3435 if (!this._sntp.isExpired()) {
michael@0 3436 this.setClockBySntp(this._sntp.getOffset());
michael@0 3437 } else {
michael@0 3438 // Or refresh the SNTP.
michael@0 3439 this._sntp.request();
michael@0 3440 }
michael@0 3441 } else {
michael@0 3442 // Set a sane minimum time.
michael@0 3443 let buildTime = libcutils.property_get("ro.build.date.utc", "0") * 1000;
michael@0 3444 let file = FileUtils.File("/system/b2g/b2g");
michael@0 3445 if (file.lastModifiedTime > buildTime) {
michael@0 3446 buildTime = file.lastModifiedTime;
michael@0 3447 }
michael@0 3448 if (buildTime > Date.now()) {
michael@0 3449 gTimeService.set(buildTime);
michael@0 3450 }
michael@0 3451 }
michael@0 3452 break;
michael@0 3453 case kSettingsTimezoneAutoUpdateEnabled:
michael@0 3454 this._timezoneAutoUpdateEnabled = aResult;
michael@0 3455
michael@0 3456 if (this._timezoneAutoUpdateEnabled) {
michael@0 3457 // Apply the latest cached NITZ for timezone if it's available.
michael@0 3458 if (this._timezoneAutoUpdateEnabled && this._lastNitzMessage) {
michael@0 3459 this.setTimezoneByNitz(this._lastNitzMessage);
michael@0 3460 }
michael@0 3461 }
michael@0 3462 break;
michael@0 3463 case kSettingsCellBroadcastSearchList:
michael@0 3464 if (DEBUG) {
michael@0 3465 this.debug("'" + kSettingsCellBroadcastSearchList +
michael@0 3466 "' is now " + JSON.stringify(aResult));
michael@0 3467 }
michael@0 3468 // TODO: Set searchlist for Multi-SIM. See Bug 921326.
michael@0 3469 let result = Array.isArray(aResult) ? aResult[0] : aResult;
michael@0 3470 this.setCellBroadcastSearchList(result);
michael@0 3471 break;
michael@0 3472 }
michael@0 3473 },
michael@0 3474
michael@0 3475 handleError: function(aErrorMessage) {
michael@0 3476 if (DEBUG) {
michael@0 3477 this.debug("There was an error while reading RIL settings.");
michael@0 3478 }
michael@0 3479 },
michael@0 3480
michael@0 3481 // nsIRadioInterface
michael@0 3482
michael@0 3483 rilContext: null,
michael@0 3484
michael@0 3485 // Handle phone functions of nsIRILContentHelper
michael@0 3486
michael@0 3487 _sendCfStateChanged: function(message) {
michael@0 3488 gMessageManager.sendMobileConnectionMessage("RIL:CfStateChanged",
michael@0 3489 this.clientId, message);
michael@0 3490 },
michael@0 3491
michael@0 3492 _updateCallingLineIdRestrictionPref: function(mode) {
michael@0 3493 try {
michael@0 3494 Services.prefs.setIntPref(kPrefClirModePreference, mode);
michael@0 3495 Services.prefs.savePrefFile(null);
michael@0 3496 if (DEBUG) {
michael@0 3497 this.debug(kPrefClirModePreference + " pref is now " + mode);
michael@0 3498 }
michael@0 3499 } catch (e) {}
michael@0 3500 },
michael@0 3501
michael@0 3502 sendMMI: function(target, message) {
michael@0 3503 if (DEBUG) this.debug("SendMMI " + JSON.stringify(message));
michael@0 3504 this.workerMessenger.send("sendMMI", message, (function(response) {
michael@0 3505 if (response.isSetCallForward) {
michael@0 3506 this._sendCfStateChanged(response);
michael@0 3507 } else if (response.isSetCLIR && response.success) {
michael@0 3508 this._updateCallingLineIdRestrictionPref(response.clirMode);
michael@0 3509 }
michael@0 3510
michael@0 3511 target.sendAsyncMessage("RIL:SendMMI", {
michael@0 3512 clientId: this.clientId,
michael@0 3513 data: response
michael@0 3514 });
michael@0 3515 return false;
michael@0 3516 }).bind(this));
michael@0 3517 },
michael@0 3518
michael@0 3519 setCallForwardingOptions: function(target, message) {
michael@0 3520 if (DEBUG) this.debug("setCallForwardingOptions: " + JSON.stringify(message));
michael@0 3521 message.serviceClass = RIL.ICC_SERVICE_CLASS_VOICE;
michael@0 3522 this.workerMessenger.send("setCallForward", message, (function(response) {
michael@0 3523 this._sendCfStateChanged(response);
michael@0 3524 target.sendAsyncMessage("RIL:SetCallForwardingOptions", {
michael@0 3525 clientId: this.clientId,
michael@0 3526 data: response
michael@0 3527 });
michael@0 3528 return false;
michael@0 3529 }).bind(this));
michael@0 3530 },
michael@0 3531
michael@0 3532 setCallingLineIdRestriction: function(target, message) {
michael@0 3533 if (DEBUG) {
michael@0 3534 this.debug("setCallingLineIdRestriction: " + JSON.stringify(message));
michael@0 3535 }
michael@0 3536 this.workerMessenger.send("setCLIR", message, (function(response) {
michael@0 3537 if (response.success) {
michael@0 3538 this._updateCallingLineIdRestrictionPref(response.clirMode);
michael@0 3539 }
michael@0 3540 target.sendAsyncMessage("RIL:SetCallingLineIdRestriction", {
michael@0 3541 clientId: this.clientId,
michael@0 3542 data: response
michael@0 3543 });
michael@0 3544 return false;
michael@0 3545 }).bind(this));
michael@0 3546 },
michael@0 3547
michael@0 3548 isValidStateForSetRadioEnabled: function() {
michael@0 3549 let state = this.rilContext.detailedRadioState;
michael@0 3550 return state == RIL.GECKO_DETAILED_RADIOSTATE_ENABLED ||
michael@0 3551 state == RIL.GECKO_DETAILED_RADIOSTATE_DISABLED;
michael@0 3552 },
michael@0 3553
michael@0 3554 isDummyForSetRadioEnabled: function(message) {
michael@0 3555 let state = this.rilContext.detailedRadioState;
michael@0 3556 return (state == RIL.GECKO_DETAILED_RADIOSTATE_ENABLED && message.enabled) ||
michael@0 3557 (state == RIL.GECKO_DETAILED_RADIOSTATE_DISABLED && !message.enabled);
michael@0 3558 },
michael@0 3559
michael@0 3560 setRadioEnabledResponse: function(target, message, errorMsg) {
michael@0 3561 if (errorMsg) {
michael@0 3562 message.errorMsg = errorMsg;
michael@0 3563 }
michael@0 3564
michael@0 3565 target.sendAsyncMessage("RIL:SetRadioEnabled", {
michael@0 3566 clientId: this.clientId,
michael@0 3567 data: message
michael@0 3568 });
michael@0 3569 },
michael@0 3570
michael@0 3571 setRadioEnabled: function(target, message) {
michael@0 3572 if (DEBUG) {
michael@0 3573 this.debug("setRadioEnabled: " + JSON.stringify(message));
michael@0 3574 }
michael@0 3575
michael@0 3576 if (!this.isValidStateForSetRadioEnabled()) {
michael@0 3577 this.setRadioEnabledResponse(target, message, "InvalidStateError");
michael@0 3578 return;
michael@0 3579 }
michael@0 3580
michael@0 3581 if (this.isDummyForSetRadioEnabled(message)) {
michael@0 3582 this.setRadioEnabledResponse(target, message);
michael@0 3583 return;
michael@0 3584 }
michael@0 3585
michael@0 3586 let callback = (function(response) {
michael@0 3587 if (response.errorMsg) {
michael@0 3588 // Request fails. Rollback to the original radiostate.
michael@0 3589 let state = message.enabled ? RIL.GECKO_DETAILED_RADIOSTATE_DISABLED
michael@0 3590 : RIL.GECKO_DETAILED_RADIOSTATE_ENABLED;
michael@0 3591 this.handleDetailedRadioStateChanged(state);
michael@0 3592 }
michael@0 3593 this.setRadioEnabledResponse(target, response);
michael@0 3594 return false;
michael@0 3595 }).bind(this);
michael@0 3596
michael@0 3597 this.setRadioEnabledInternal(message, callback);
michael@0 3598 },
michael@0 3599
michael@0 3600 setRadioEnabledInternal: function(message, callback) {
michael@0 3601 let state = message.enabled ? RIL.GECKO_DETAILED_RADIOSTATE_ENABLING
michael@0 3602 : RIL.GECKO_DETAILED_RADIOSTATE_DISABLING;
michael@0 3603 this.handleDetailedRadioStateChanged(state);
michael@0 3604 this.workerMessenger.send("setRadioEnabled", message, callback);
michael@0 3605 },
michael@0 3606
michael@0 3607 /**
michael@0 3608 * List of tuples of national language identifier pairs.
michael@0 3609 *
michael@0 3610 * TODO: Support static/runtime settings, see bug 733331.
michael@0 3611 */
michael@0 3612 enabledGsmTableTuples: [
michael@0 3613 [RIL.PDU_NL_IDENTIFIER_DEFAULT, RIL.PDU_NL_IDENTIFIER_DEFAULT],
michael@0 3614 ],
michael@0 3615
michael@0 3616 /**
michael@0 3617 * Use 16-bit reference number for concatenated outgoint messages.
michael@0 3618 *
michael@0 3619 * TODO: Support static/runtime settings, see bug 733331.
michael@0 3620 */
michael@0 3621 segmentRef16Bit: false,
michael@0 3622
michael@0 3623 /**
michael@0 3624 * Get valid SMS concatenation reference number.
michael@0 3625 */
michael@0 3626 _segmentRef: 0,
michael@0 3627 get nextSegmentRef() {
michael@0 3628 let ref = this._segmentRef++;
michael@0 3629
michael@0 3630 this._segmentRef %= (this.segmentRef16Bit ? 65535 : 255);
michael@0 3631
michael@0 3632 // 0 is not a valid SMS concatenation reference number.
michael@0 3633 return ref + 1;
michael@0 3634 },
michael@0 3635
michael@0 3636 /**
michael@0 3637 * Calculate encoded length using specified locking/single shift table
michael@0 3638 *
michael@0 3639 * @param message
michael@0 3640 * message string to be encoded.
michael@0 3641 * @param langTable
michael@0 3642 * locking shift table string.
michael@0 3643 * @param langShiftTable
michael@0 3644 * single shift table string.
michael@0 3645 * @param strict7BitEncoding
michael@0 3646 * Optional. Enable Latin characters replacement with corresponding
michael@0 3647 * ones in GSM SMS 7-bit default alphabet.
michael@0 3648 *
michael@0 3649 * @return encoded length in septets.
michael@0 3650 *
michael@0 3651 * @note that the algorithm used in this function must match exactly with
michael@0 3652 * GsmPDUHelper#writeStringAsSeptets.
michael@0 3653 */
michael@0 3654 _countGsm7BitSeptets: function(message, langTable, langShiftTable, strict7BitEncoding) {
michael@0 3655 let length = 0;
michael@0 3656 for (let msgIndex = 0; msgIndex < message.length; msgIndex++) {
michael@0 3657 let c = message.charAt(msgIndex);
michael@0 3658 if (strict7BitEncoding) {
michael@0 3659 c = RIL.GSM_SMS_STRICT_7BIT_CHARMAP[c] || c;
michael@0 3660 }
michael@0 3661
michael@0 3662 let septet = langTable.indexOf(c);
michael@0 3663
michael@0 3664 // According to 3GPP TS 23.038, section 6.1.1 General notes, "The
michael@0 3665 // characters marked '1)' are not used but are displayed as a space."
michael@0 3666 if (septet == RIL.PDU_NL_EXTENDED_ESCAPE) {
michael@0 3667 continue;
michael@0 3668 }
michael@0 3669
michael@0 3670 if (septet >= 0) {
michael@0 3671 length++;
michael@0 3672 continue;
michael@0 3673 }
michael@0 3674
michael@0 3675 septet = langShiftTable.indexOf(c);
michael@0 3676 if (septet < 0) {
michael@0 3677 if (!strict7BitEncoding) {
michael@0 3678 return -1;
michael@0 3679 }
michael@0 3680
michael@0 3681 // Bug 816082, when strict7BitEncoding is enabled, we should replace
michael@0 3682 // characters that can't be encoded with GSM 7-Bit alphabets with '*'.
michael@0 3683 c = "*";
michael@0 3684 if (langTable.indexOf(c) >= 0) {
michael@0 3685 length++;
michael@0 3686 } else if (langShiftTable.indexOf(c) >= 0) {
michael@0 3687 length += 2;
michael@0 3688 } else {
michael@0 3689 // We can't even encode a '*' character with current configuration.
michael@0 3690 return -1;
michael@0 3691 }
michael@0 3692
michael@0 3693 continue;
michael@0 3694 }
michael@0 3695
michael@0 3696 // According to 3GPP TS 23.038 B.2, "This code represents a control
michael@0 3697 // character and therefore must not be used for language specific
michael@0 3698 // characters."
michael@0 3699 if (septet == RIL.PDU_NL_RESERVED_CONTROL) {
michael@0 3700 continue;
michael@0 3701 }
michael@0 3702
michael@0 3703 // The character is not found in locking shfit table, but could be
michael@0 3704 // encoded as <escape><char> with single shift table. Note that it's
michael@0 3705 // still possible for septet to has the value of PDU_NL_EXTENDED_ESCAPE,
michael@0 3706 // but we can display it as a space in this case as said in previous
michael@0 3707 // comment.
michael@0 3708 length += 2;
michael@0 3709 }
michael@0 3710
michael@0 3711 return length;
michael@0 3712 },
michael@0 3713
michael@0 3714 /**
michael@0 3715 * Calculate user data length of specified message string encoded in GSM 7Bit
michael@0 3716 * alphabets.
michael@0 3717 *
michael@0 3718 * @param message
michael@0 3719 * a message string to be encoded.
michael@0 3720 * @param strict7BitEncoding
michael@0 3721 * Optional. Enable Latin characters replacement with corresponding
michael@0 3722 * ones in GSM SMS 7-bit default alphabet.
michael@0 3723 *
michael@0 3724 * @return null or an options object with attributes `dcs`,
michael@0 3725 * `userDataHeaderLength`, `encodedFullBodyLength`, `langIndex`,
michael@0 3726 * `langShiftIndex`, `segmentMaxSeq` set.
michael@0 3727 *
michael@0 3728 * @see #_calculateUserDataLength().
michael@0 3729 */
michael@0 3730 _calculateUserDataLength7Bit: function(message, strict7BitEncoding) {
michael@0 3731 let options = null;
michael@0 3732 let minUserDataSeptets = Number.MAX_VALUE;
michael@0 3733 for (let i = 0; i < this.enabledGsmTableTuples.length; i++) {
michael@0 3734 let [langIndex, langShiftIndex] = this.enabledGsmTableTuples[i];
michael@0 3735
michael@0 3736 const langTable = RIL.PDU_NL_LOCKING_SHIFT_TABLES[langIndex];
michael@0 3737 const langShiftTable = RIL.PDU_NL_SINGLE_SHIFT_TABLES[langShiftIndex];
michael@0 3738
michael@0 3739 let bodySeptets = this._countGsm7BitSeptets(message,
michael@0 3740 langTable,
michael@0 3741 langShiftTable,
michael@0 3742 strict7BitEncoding);
michael@0 3743 if (bodySeptets < 0) {
michael@0 3744 continue;
michael@0 3745 }
michael@0 3746
michael@0 3747 let headerLen = 0;
michael@0 3748 if (langIndex != RIL.PDU_NL_IDENTIFIER_DEFAULT) {
michael@0 3749 headerLen += 3; // IEI + len + langIndex
michael@0 3750 }
michael@0 3751 if (langShiftIndex != RIL.PDU_NL_IDENTIFIER_DEFAULT) {
michael@0 3752 headerLen += 3; // IEI + len + langShiftIndex
michael@0 3753 }
michael@0 3754
michael@0 3755 // Calculate full user data length, note the extra byte is for header len
michael@0 3756 let headerSeptets = Math.ceil((headerLen ? headerLen + 1 : 0) * 8 / 7);
michael@0 3757 let segmentSeptets = RIL.PDU_MAX_USER_DATA_7BIT;
michael@0 3758 if ((bodySeptets + headerSeptets) > segmentSeptets) {
michael@0 3759 headerLen += this.segmentRef16Bit ? 6 : 5;
michael@0 3760 headerSeptets = Math.ceil((headerLen + 1) * 8 / 7);
michael@0 3761 segmentSeptets -= headerSeptets;
michael@0 3762 }
michael@0 3763
michael@0 3764 let segments = Math.ceil(bodySeptets / segmentSeptets);
michael@0 3765 let userDataSeptets = bodySeptets + headerSeptets * segments;
michael@0 3766 if (userDataSeptets >= minUserDataSeptets) {
michael@0 3767 continue;
michael@0 3768 }
michael@0 3769
michael@0 3770 minUserDataSeptets = userDataSeptets;
michael@0 3771
michael@0 3772 options = {
michael@0 3773 dcs: RIL.PDU_DCS_MSG_CODING_7BITS_ALPHABET,
michael@0 3774 encodedFullBodyLength: bodySeptets,
michael@0 3775 userDataHeaderLength: headerLen,
michael@0 3776 langIndex: langIndex,
michael@0 3777 langShiftIndex: langShiftIndex,
michael@0 3778 segmentMaxSeq: segments,
michael@0 3779 segmentChars: segmentSeptets,
michael@0 3780 };
michael@0 3781 }
michael@0 3782
michael@0 3783 return options;
michael@0 3784 },
michael@0 3785
michael@0 3786 /**
michael@0 3787 * Calculate user data length of specified message string encoded in UCS2.
michael@0 3788 *
michael@0 3789 * @param message
michael@0 3790 * a message string to be encoded.
michael@0 3791 *
michael@0 3792 * @return an options object with attributes `dcs`, `userDataHeaderLength`,
michael@0 3793 * `encodedFullBodyLength`, `segmentMaxSeq` set.
michael@0 3794 *
michael@0 3795 * @see #_calculateUserDataLength().
michael@0 3796 */
michael@0 3797 _calculateUserDataLengthUCS2: function(message) {
michael@0 3798 let bodyChars = message.length;
michael@0 3799 let headerLen = 0;
michael@0 3800 let headerChars = Math.ceil((headerLen ? headerLen + 1 : 0) / 2);
michael@0 3801 let segmentChars = RIL.PDU_MAX_USER_DATA_UCS2;
michael@0 3802 if ((bodyChars + headerChars) > segmentChars) {
michael@0 3803 headerLen += this.segmentRef16Bit ? 6 : 5;
michael@0 3804 headerChars = Math.ceil((headerLen + 1) / 2);
michael@0 3805 segmentChars -= headerChars;
michael@0 3806 }
michael@0 3807
michael@0 3808 let segments = Math.ceil(bodyChars / segmentChars);
michael@0 3809
michael@0 3810 return {
michael@0 3811 dcs: RIL.PDU_DCS_MSG_CODING_16BITS_ALPHABET,
michael@0 3812 encodedFullBodyLength: bodyChars * 2,
michael@0 3813 userDataHeaderLength: headerLen,
michael@0 3814 segmentMaxSeq: segments,
michael@0 3815 segmentChars: segmentChars,
michael@0 3816 };
michael@0 3817 },
michael@0 3818
michael@0 3819 /**
michael@0 3820 * Calculate user data length and its encoding.
michael@0 3821 *
michael@0 3822 * @param message
michael@0 3823 * a message string to be encoded.
michael@0 3824 * @param strict7BitEncoding
michael@0 3825 * Optional. Enable Latin characters replacement with corresponding
michael@0 3826 * ones in GSM SMS 7-bit default alphabet.
michael@0 3827 *
michael@0 3828 * @return an options object with some or all of following attributes set:
michael@0 3829 *
michael@0 3830 * @param dcs
michael@0 3831 * Data coding scheme. One of the PDU_DCS_MSG_CODING_*BITS_ALPHABET
michael@0 3832 * constants.
michael@0 3833 * @param userDataHeaderLength
michael@0 3834 * Length of embedded user data header, in bytes. The whole header
michael@0 3835 * size will be userDataHeaderLength + 1; 0 for no header.
michael@0 3836 * @param encodedFullBodyLength
michael@0 3837 * Length of the message body when encoded with the given DCS. For
michael@0 3838 * UCS2, in bytes; for 7-bit, in septets.
michael@0 3839 * @param langIndex
michael@0 3840 * Table index used for normal 7-bit encoded character lookup.
michael@0 3841 * @param langShiftIndex
michael@0 3842 * Table index used for escaped 7-bit encoded character lookup.
michael@0 3843 * @param segmentMaxSeq
michael@0 3844 * Max sequence number of a multi-part messages, or 1 for single one.
michael@0 3845 * This number might not be accurate for a multi-part message until
michael@0 3846 * it's processed by #_fragmentText() again.
michael@0 3847 */
michael@0 3848 _calculateUserDataLength: function(message, strict7BitEncoding) {
michael@0 3849 let options = this._calculateUserDataLength7Bit(message, strict7BitEncoding);
michael@0 3850 if (!options) {
michael@0 3851 options = this._calculateUserDataLengthUCS2(message);
michael@0 3852 }
michael@0 3853
michael@0 3854 if (DEBUG) this.debug("_calculateUserDataLength: " + JSON.stringify(options));
michael@0 3855 return options;
michael@0 3856 },
michael@0 3857
michael@0 3858 /**
michael@0 3859 * Fragment GSM 7-Bit encodable string for transmission.
michael@0 3860 *
michael@0 3861 * @param text
michael@0 3862 * text string to be fragmented.
michael@0 3863 * @param langTable
michael@0 3864 * locking shift table string.
michael@0 3865 * @param langShiftTable
michael@0 3866 * single shift table string.
michael@0 3867 * @param segmentSeptets
michael@0 3868 * Number of available spetets per segment.
michael@0 3869 * @param strict7BitEncoding
michael@0 3870 * Optional. Enable Latin characters replacement with corresponding
michael@0 3871 * ones in GSM SMS 7-bit default alphabet.
michael@0 3872 *
michael@0 3873 * @return an array of objects. See #_fragmentText() for detailed definition.
michael@0 3874 */
michael@0 3875 _fragmentText7Bit: function(text, langTable, langShiftTable, segmentSeptets, strict7BitEncoding) {
michael@0 3876 let ret = [];
michael@0 3877 let body = "", len = 0;
michael@0 3878 // If the message is empty, we only push the empty message to ret.
michael@0 3879 if (text.length === 0) {
michael@0 3880 ret.push({
michael@0 3881 body: text,
michael@0 3882 encodedBodyLength: text.length,
michael@0 3883 });
michael@0 3884 return ret;
michael@0 3885 }
michael@0 3886
michael@0 3887 for (let i = 0, inc = 0; i < text.length; i++) {
michael@0 3888 let c = text.charAt(i);
michael@0 3889 if (strict7BitEncoding) {
michael@0 3890 c = RIL.GSM_SMS_STRICT_7BIT_CHARMAP[c] || c;
michael@0 3891 }
michael@0 3892
michael@0 3893 let septet = langTable.indexOf(c);
michael@0 3894 if (septet == RIL.PDU_NL_EXTENDED_ESCAPE) {
michael@0 3895 continue;
michael@0 3896 }
michael@0 3897
michael@0 3898 if (septet >= 0) {
michael@0 3899 inc = 1;
michael@0 3900 } else {
michael@0 3901 septet = langShiftTable.indexOf(c);
michael@0 3902 if (septet == RIL.PDU_NL_RESERVED_CONTROL) {
michael@0 3903 continue;
michael@0 3904 }
michael@0 3905
michael@0 3906 inc = 2;
michael@0 3907 if (septet < 0) {
michael@0 3908 if (!strict7BitEncoding) {
michael@0 3909 throw new Error("Given text cannot be encoded with GSM 7-bit Alphabet!");
michael@0 3910 }
michael@0 3911
michael@0 3912 // Bug 816082, when strict7BitEncoding is enabled, we should replace
michael@0 3913 // characters that can't be encoded with GSM 7-Bit alphabets with '*'.
michael@0 3914 c = "*";
michael@0 3915 if (langTable.indexOf(c) >= 0) {
michael@0 3916 inc = 1;
michael@0 3917 }
michael@0 3918 }
michael@0 3919 }
michael@0 3920
michael@0 3921 if ((len + inc) > segmentSeptets) {
michael@0 3922 ret.push({
michael@0 3923 body: body,
michael@0 3924 encodedBodyLength: len,
michael@0 3925 });
michael@0 3926 body = c;
michael@0 3927 len = inc;
michael@0 3928 } else {
michael@0 3929 body += c;
michael@0 3930 len += inc;
michael@0 3931 }
michael@0 3932 }
michael@0 3933
michael@0 3934 if (len) {
michael@0 3935 ret.push({
michael@0 3936 body: body,
michael@0 3937 encodedBodyLength: len,
michael@0 3938 });
michael@0 3939 }
michael@0 3940
michael@0 3941 return ret;
michael@0 3942 },
michael@0 3943
michael@0 3944 /**
michael@0 3945 * Fragment UCS2 encodable string for transmission.
michael@0 3946 *
michael@0 3947 * @param text
michael@0 3948 * text string to be fragmented.
michael@0 3949 * @param segmentChars
michael@0 3950 * Number of available characters per segment.
michael@0 3951 *
michael@0 3952 * @return an array of objects. See #_fragmentText() for detailed definition.
michael@0 3953 */
michael@0 3954 _fragmentTextUCS2: function(text, segmentChars) {
michael@0 3955 let ret = [];
michael@0 3956 // If the message is empty, we only push the empty message to ret.
michael@0 3957 if (text.length === 0) {
michael@0 3958 ret.push({
michael@0 3959 body: text,
michael@0 3960 encodedBodyLength: text.length,
michael@0 3961 });
michael@0 3962 return ret;
michael@0 3963 }
michael@0 3964
michael@0 3965 for (let offset = 0; offset < text.length; offset += segmentChars) {
michael@0 3966 let str = text.substr(offset, segmentChars);
michael@0 3967 ret.push({
michael@0 3968 body: str,
michael@0 3969 encodedBodyLength: str.length * 2,
michael@0 3970 });
michael@0 3971 }
michael@0 3972
michael@0 3973 return ret;
michael@0 3974 },
michael@0 3975
michael@0 3976 /**
michael@0 3977 * Fragment string for transmission.
michael@0 3978 *
michael@0 3979 * Fragment input text string into an array of objects that contains
michael@0 3980 * attributes `body`, substring for this segment, `encodedBodyLength`,
michael@0 3981 * length of the encoded segment body in septets.
michael@0 3982 *
michael@0 3983 * @param text
michael@0 3984 * Text string to be fragmented.
michael@0 3985 * @param options
michael@0 3986 * Optional pre-calculated option object. The output array will be
michael@0 3987 * stored at options.segments if there are multiple segments.
michael@0 3988 * @param strict7BitEncoding
michael@0 3989 * Optional. Enable Latin characters replacement with corresponding
michael@0 3990 * ones in GSM SMS 7-bit default alphabet.
michael@0 3991 *
michael@0 3992 * @return Populated options object.
michael@0 3993 */
michael@0 3994 _fragmentText: function(text, options, strict7BitEncoding) {
michael@0 3995 if (!options) {
michael@0 3996 options = this._calculateUserDataLength(text, strict7BitEncoding);
michael@0 3997 }
michael@0 3998
michael@0 3999 if (options.dcs == RIL.PDU_DCS_MSG_CODING_7BITS_ALPHABET) {
michael@0 4000 const langTable = RIL.PDU_NL_LOCKING_SHIFT_TABLES[options.langIndex];
michael@0 4001 const langShiftTable = RIL.PDU_NL_SINGLE_SHIFT_TABLES[options.langShiftIndex];
michael@0 4002 options.segments = this._fragmentText7Bit(text,
michael@0 4003 langTable, langShiftTable,
michael@0 4004 options.segmentChars,
michael@0 4005 strict7BitEncoding);
michael@0 4006 } else {
michael@0 4007 options.segments = this._fragmentTextUCS2(text,
michael@0 4008 options.segmentChars);
michael@0 4009 }
michael@0 4010
michael@0 4011 // Re-sync options.segmentMaxSeq with actual length of returning array.
michael@0 4012 options.segmentMaxSeq = options.segments.length;
michael@0 4013
michael@0 4014 return options;
michael@0 4015 },
michael@0 4016
michael@0 4017 getSegmentInfoForText: function(text, request) {
michael@0 4018 let strict7BitEncoding;
michael@0 4019 try {
michael@0 4020 strict7BitEncoding = Services.prefs.getBoolPref("dom.sms.strict7BitEncoding");
michael@0 4021 } catch (e) {
michael@0 4022 strict7BitEncoding = false;
michael@0 4023 }
michael@0 4024
michael@0 4025 let options = this._fragmentText(text, null, strict7BitEncoding);
michael@0 4026 let charsInLastSegment;
michael@0 4027 if (options.segmentMaxSeq) {
michael@0 4028 let lastSegment = options.segments[options.segmentMaxSeq - 1];
michael@0 4029 charsInLastSegment = lastSegment.encodedBodyLength;
michael@0 4030 if (options.dcs == RIL.PDU_DCS_MSG_CODING_16BITS_ALPHABET) {
michael@0 4031 // In UCS2 encoding, encodedBodyLength is in octets.
michael@0 4032 charsInLastSegment /= 2;
michael@0 4033 }
michael@0 4034 } else {
michael@0 4035 charsInLastSegment = 0;
michael@0 4036 }
michael@0 4037
michael@0 4038 let result = gMobileMessageService
michael@0 4039 .createSmsSegmentInfo(options.segmentMaxSeq,
michael@0 4040 options.segmentChars,
michael@0 4041 options.segmentChars - charsInLastSegment);
michael@0 4042 request.notifySegmentInfoForTextGot(result);
michael@0 4043 },
michael@0 4044
michael@0 4045 getSmscAddress: function(request) {
michael@0 4046 this.workerMessenger.send("getSmscAddress",
michael@0 4047 null,
michael@0 4048 (function(response) {
michael@0 4049 if (!response.errorMsg) {
michael@0 4050 request.notifyGetSmscAddress(response.smscAddress);
michael@0 4051 } else {
michael@0 4052 request.notifyGetSmscAddressFailed(response.errorMsg);
michael@0 4053 }
michael@0 4054 }).bind(this));
michael@0 4055 },
michael@0 4056
michael@0 4057 sendSMS: function(number, message, silent, request) {
michael@0 4058 let strict7BitEncoding;
michael@0 4059 try {
michael@0 4060 strict7BitEncoding = Services.prefs.getBoolPref("dom.sms.strict7BitEncoding");
michael@0 4061 } catch (e) {
michael@0 4062 strict7BitEncoding = false;
michael@0 4063 }
michael@0 4064
michael@0 4065 let options = this._fragmentText(message, null, strict7BitEncoding);
michael@0 4066 options.number = PhoneNumberUtils.normalize(number);
michael@0 4067 let requestStatusReport;
michael@0 4068 try {
michael@0 4069 requestStatusReport =
michael@0 4070 Services.prefs.getBoolPref("dom.sms.requestStatusReport");
michael@0 4071 } catch (e) {
michael@0 4072 requestStatusReport = true;
michael@0 4073 }
michael@0 4074 options.requestStatusReport = requestStatusReport && !silent;
michael@0 4075 if (options.segmentMaxSeq > 1) {
michael@0 4076 options.segmentRef16Bit = this.segmentRef16Bit;
michael@0 4077 options.segmentRef = this.nextSegmentRef;
michael@0 4078 }
michael@0 4079
michael@0 4080 let notifyResult = (function notifyResult(rv, domMessage) {
michael@0 4081 if (!Components.isSuccessCode(rv)) {
michael@0 4082 if (DEBUG) this.debug("Error! Fail to save sending message! rv = " + rv);
michael@0 4083 request.notifySendMessageFailed(
michael@0 4084 gMobileMessageDatabaseService.translateCrErrorToMessageCallbackError(rv));
michael@0 4085 Services.obs.notifyObservers(domMessage, kSmsFailedObserverTopic, null);
michael@0 4086 return;
michael@0 4087 }
michael@0 4088
michael@0 4089 if (!silent) {
michael@0 4090 Services.obs.notifyObservers(domMessage, kSmsSendingObserverTopic, null);
michael@0 4091 }
michael@0 4092
michael@0 4093 // If the radio is disabled or the SIM card is not ready, just directly
michael@0 4094 // return with the corresponding error code.
michael@0 4095 let errorCode;
michael@0 4096 if (!PhoneNumberUtils.isPlainPhoneNumber(options.number)) {
michael@0 4097 if (DEBUG) this.debug("Error! Address is invalid when sending SMS: " +
michael@0 4098 options.number);
michael@0 4099 errorCode = Ci.nsIMobileMessageCallback.INVALID_ADDRESS_ERROR;
michael@0 4100 } else if (this.rilContext.detailedRadioState ==
michael@0 4101 RIL.GECKO_DETAILED_RADIOSTATE_DISABLED) {
michael@0 4102 if (DEBUG) this.debug("Error! Radio is disabled when sending SMS.");
michael@0 4103 errorCode = Ci.nsIMobileMessageCallback.RADIO_DISABLED_ERROR;
michael@0 4104 } else if (this.rilContext.cardState != "ready") {
michael@0 4105 if (DEBUG) this.debug("Error! SIM card is not ready when sending SMS.");
michael@0 4106 errorCode = Ci.nsIMobileMessageCallback.NO_SIM_CARD_ERROR;
michael@0 4107 }
michael@0 4108 if (errorCode) {
michael@0 4109 if (silent) {
michael@0 4110 request.notifySendMessageFailed(errorCode);
michael@0 4111 return;
michael@0 4112 }
michael@0 4113
michael@0 4114 gMobileMessageDatabaseService
michael@0 4115 .setMessageDeliveryByMessageId(domMessage.id,
michael@0 4116 null,
michael@0 4117 DOM_MOBILE_MESSAGE_DELIVERY_ERROR,
michael@0 4118 RIL.GECKO_SMS_DELIVERY_STATUS_ERROR,
michael@0 4119 null,
michael@0 4120 function notifyResult(rv, domMessage) {
michael@0 4121 // TODO bug 832140 handle !Components.isSuccessCode(rv)
michael@0 4122 request.notifySendMessageFailed(errorCode);
michael@0 4123 Services.obs.notifyObservers(domMessage, kSmsFailedObserverTopic, null);
michael@0 4124 });
michael@0 4125 return;
michael@0 4126 }
michael@0 4127
michael@0 4128 // Keep current SMS message info for sent/delivered notifications
michael@0 4129 let context = {
michael@0 4130 request: request,
michael@0 4131 sms: domMessage,
michael@0 4132 requestStatusReport: options.requestStatusReport,
michael@0 4133 silent: silent
michael@0 4134 };
michael@0 4135
michael@0 4136 // This is the entry point starting to send SMS.
michael@0 4137 this.workerMessenger.send("sendSMS", options,
michael@0 4138 (function(context, response) {
michael@0 4139 if (response.errorMsg) {
michael@0 4140 // Failed to send SMS out.
michael@0 4141 let error = Ci.nsIMobileMessageCallback.UNKNOWN_ERROR;
michael@0 4142 switch (response.errorMsg) {
michael@0 4143 case RIL.ERROR_RADIO_NOT_AVAILABLE:
michael@0 4144 error = Ci.nsIMobileMessageCallback.NO_SIGNAL_ERROR;
michael@0 4145 break;
michael@0 4146 case RIL.ERROR_FDN_CHECK_FAILURE:
michael@0 4147 error = Ci.nsIMobileMessageCallback.FDN_CHECK_ERROR;
michael@0 4148 break;
michael@0 4149 }
michael@0 4150
michael@0 4151 if (context.silent) {
michael@0 4152 context.request.notifySendMessageFailed(error);
michael@0 4153 return false;
michael@0 4154 }
michael@0 4155
michael@0 4156 gMobileMessageDatabaseService
michael@0 4157 .setMessageDeliveryByMessageId(context.sms.id,
michael@0 4158 null,
michael@0 4159 DOM_MOBILE_MESSAGE_DELIVERY_ERROR,
michael@0 4160 RIL.GECKO_SMS_DELIVERY_STATUS_ERROR,
michael@0 4161 null,
michael@0 4162 function notifyResult(rv, domMessage) {
michael@0 4163 // TODO bug 832140 handle !Components.isSuccessCode(rv)
michael@0 4164 context.request.notifySendMessageFailed(error);
michael@0 4165 Services.obs.notifyObservers(domMessage, kSmsFailedObserverTopic, null);
michael@0 4166 });
michael@0 4167 return false;
michael@0 4168 } // End of send failure.
michael@0 4169
michael@0 4170 if (response.deliveryStatus) {
michael@0 4171 // Message delivery.
michael@0 4172 gMobileMessageDatabaseService
michael@0 4173 .setMessageDeliveryByMessageId(context.sms.id,
michael@0 4174 null,
michael@0 4175 context.sms.delivery,
michael@0 4176 response.deliveryStatus,
michael@0 4177 null,
michael@0 4178 (function notifyResult(rv, domMessage) {
michael@0 4179 // TODO bug 832140 handle !Components.isSuccessCode(rv)
michael@0 4180
michael@0 4181 let topic = (response.deliveryStatus ==
michael@0 4182 RIL.GECKO_SMS_DELIVERY_STATUS_SUCCESS)
michael@0 4183 ? kSmsDeliverySuccessObserverTopic
michael@0 4184 : kSmsDeliveryErrorObserverTopic;
michael@0 4185
michael@0 4186 // Broadcasting a "sms-delivery-success" system message to open apps.
michael@0 4187 if (topic == kSmsDeliverySuccessObserverTopic) {
michael@0 4188 this.broadcastSmsSystemMessage(topic, domMessage);
michael@0 4189 }
michael@0 4190
michael@0 4191 // Notifying observers the delivery status is updated.
michael@0 4192 Services.obs.notifyObservers(domMessage, topic, null);
michael@0 4193 }).bind(this));
michael@0 4194
michael@0 4195 // Send transaction has ended completely.
michael@0 4196 return false;
michael@0 4197 } // End of message delivery.
michael@0 4198
michael@0 4199 // Message sent.
michael@0 4200 if (context.silent) {
michael@0 4201 // There is no way to modify nsIDOMMozSmsMessage attributes as they
michael@0 4202 // are read only so we just create a new sms instance to send along
michael@0 4203 // with the notification.
michael@0 4204 let sms = context.sms;
michael@0 4205 context.request.notifyMessageSent(
michael@0 4206 gMobileMessageService.createSmsMessage(sms.id,
michael@0 4207 sms.threadId,
michael@0 4208 sms.iccId,
michael@0 4209 DOM_MOBILE_MESSAGE_DELIVERY_SENT,
michael@0 4210 sms.deliveryStatus,
michael@0 4211 sms.sender,
michael@0 4212 sms.receiver,
michael@0 4213 sms.body,
michael@0 4214 sms.messageClass,
michael@0 4215 sms.timestamp,
michael@0 4216 Date.now(),
michael@0 4217 0,
michael@0 4218 sms.read));
michael@0 4219 // We don't wait for SMS-DELIVER-REPORT for silent one.
michael@0 4220 return false;
michael@0 4221 }
michael@0 4222
michael@0 4223 gMobileMessageDatabaseService
michael@0 4224 .setMessageDeliveryByMessageId(context.sms.id,
michael@0 4225 null,
michael@0 4226 DOM_MOBILE_MESSAGE_DELIVERY_SENT,
michael@0 4227 context.sms.deliveryStatus,
michael@0 4228 null,
michael@0 4229 (function notifyResult(rv, domMessage) {
michael@0 4230 // TODO bug 832140 handle !Components.isSuccessCode(rv)
michael@0 4231
michael@0 4232 if (context.requestStatusReport) {
michael@0 4233 context.sms = domMessage;
michael@0 4234 }
michael@0 4235
michael@0 4236 this.broadcastSmsSystemMessage(kSmsSentObserverTopic, domMessage);
michael@0 4237 context.request.notifyMessageSent(domMessage);
michael@0 4238 Services.obs.notifyObservers(domMessage, kSmsSentObserverTopic, null);
michael@0 4239 }).bind(this));
michael@0 4240
michael@0 4241 // Only keep current context if we have requested for delivery report.
michael@0 4242 return context.requestStatusReport;
michael@0 4243 }).bind(this, context)); // End of |workerMessenger.send| callback.
michael@0 4244 }).bind(this); // End of DB saveSendingMessage callback.
michael@0 4245
michael@0 4246 let sendingMessage = {
michael@0 4247 type: "sms",
michael@0 4248 sender: this.getPhoneNumber(),
michael@0 4249 receiver: number,
michael@0 4250 body: message,
michael@0 4251 deliveryStatusRequested: options.requestStatusReport,
michael@0 4252 timestamp: Date.now(),
michael@0 4253 iccId: this.getIccId()
michael@0 4254 };
michael@0 4255
michael@0 4256 if (silent) {
michael@0 4257 let delivery = DOM_MOBILE_MESSAGE_DELIVERY_SENDING;
michael@0 4258 let deliveryStatus = RIL.GECKO_SMS_DELIVERY_STATUS_PENDING;
michael@0 4259 let domMessage =
michael@0 4260 gMobileMessageService.createSmsMessage(-1, // id
michael@0 4261 0, // threadId
michael@0 4262 sendingMessage.iccId,
michael@0 4263 delivery,
michael@0 4264 deliveryStatus,
michael@0 4265 sendingMessage.sender,
michael@0 4266 sendingMessage.receiver,
michael@0 4267 sendingMessage.body,
michael@0 4268 "normal", // message class
michael@0 4269 sendingMessage.timestamp,
michael@0 4270 0,
michael@0 4271 0,
michael@0 4272 false);
michael@0 4273 notifyResult(Cr.NS_OK, domMessage);
michael@0 4274 return;
michael@0 4275 }
michael@0 4276
michael@0 4277 let id = gMobileMessageDatabaseService.saveSendingMessage(
michael@0 4278 sendingMessage, notifyResult);
michael@0 4279 },
michael@0 4280
michael@0 4281 // TODO: Bug 928861 - B2G NetworkManager: Provide a more generic function
michael@0 4282 // for connecting
michael@0 4283 setupDataCallByType: function(apntype) {
michael@0 4284 let connHandler = gDataConnectionManager.getConnectionHandler(this.clientId);
michael@0 4285 connHandler.setupDataCallByType(apntype);
michael@0 4286 },
michael@0 4287
michael@0 4288 // TODO: Bug 928861 - B2G NetworkManager: Provide a more generic function
michael@0 4289 // for connecting
michael@0 4290 deactivateDataCallByType: function(apntype) {
michael@0 4291 let connHandler = gDataConnectionManager.getConnectionHandler(this.clientId);
michael@0 4292 connHandler.deactivateDataCallByType(apntype);
michael@0 4293 },
michael@0 4294
michael@0 4295 // TODO: Bug 904514 - [meta] NetworkManager enhancement
michael@0 4296 getDataCallStateByType: function(apntype) {
michael@0 4297 let connHandler = gDataConnectionManager.getConnectionHandler(this.clientId);
michael@0 4298 return connHandler.getDataCallStateByType(apntype);
michael@0 4299 },
michael@0 4300
michael@0 4301 setupDataCall: function(radioTech, apn, user, passwd, chappap, pdptype) {
michael@0 4302 this.workerMessenger.send("setupDataCall", { radioTech: radioTech,
michael@0 4303 apn: apn,
michael@0 4304 user: user,
michael@0 4305 passwd: passwd,
michael@0 4306 chappap: chappap,
michael@0 4307 pdptype: pdptype });
michael@0 4308 },
michael@0 4309
michael@0 4310 deactivateDataCall: function(cid, reason) {
michael@0 4311 this.workerMessenger.send("deactivateDataCall", { cid: cid,
michael@0 4312 reason: reason });
michael@0 4313 },
michael@0 4314
michael@0 4315 sendWorkerMessage: function(rilMessageType, message, callback) {
michael@0 4316 if (callback) {
michael@0 4317 this.workerMessenger.send(rilMessageType, message, function(response) {
michael@0 4318 return callback.handleResponse(response);
michael@0 4319 });
michael@0 4320 } else {
michael@0 4321 this.workerMessenger.send(rilMessageType, message);
michael@0 4322 }
michael@0 4323 }
michael@0 4324 };
michael@0 4325
michael@0 4326 function RILNetworkInterface(dataConnectionHandler, apnSetting) {
michael@0 4327 this.dataConnectionHandler = dataConnectionHandler;
michael@0 4328 this.apnSetting = apnSetting;
michael@0 4329 this.connectedTypes = [];
michael@0 4330
michael@0 4331 this.ips = [];
michael@0 4332 this.prefixLengths = [];
michael@0 4333 this.dnses = [];
michael@0 4334 this.gateways = [];
michael@0 4335 }
michael@0 4336
michael@0 4337 RILNetworkInterface.prototype = {
michael@0 4338 classID: RILNETWORKINTERFACE_CID,
michael@0 4339 classInfo: XPCOMUtils.generateCI({classID: RILNETWORKINTERFACE_CID,
michael@0 4340 classDescription: "RILNetworkInterface",
michael@0 4341 interfaces: [Ci.nsINetworkInterface,
michael@0 4342 Ci.nsIRilNetworkInterface]}),
michael@0 4343 QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkInterface,
michael@0 4344 Ci.nsIRilNetworkInterface]),
michael@0 4345
michael@0 4346 // nsINetworkInterface
michael@0 4347
michael@0 4348 NETWORK_STATE_UNKNOWN: Ci.nsINetworkInterface.NETWORK_STATE_UNKNOWN,
michael@0 4349 NETWORK_STATE_CONNECTING: Ci.nsINetworkInterface.CONNECTING,
michael@0 4350 NETWORK_STATE_CONNECTED: Ci.nsINetworkInterface.CONNECTED,
michael@0 4351 NETWORK_STATE_DISCONNECTING: Ci.nsINetworkInterface.DISCONNECTING,
michael@0 4352 NETWORK_STATE_DISCONNECTED: Ci.nsINetworkInterface.DISCONNECTED,
michael@0 4353
michael@0 4354 NETWORK_TYPE_WIFI: Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
michael@0 4355 NETWORK_TYPE_MOBILE: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE,
michael@0 4356 NETWORK_TYPE_MOBILE_MMS: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_MMS,
michael@0 4357 NETWORK_TYPE_MOBILE_SUPL: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_SUPL,
michael@0 4358 NETWORK_TYPE_MOBILE_IMS: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_IMS,
michael@0 4359 NETWORK_TYPE_MOBILE_DUN: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN,
michael@0 4360 // The network manager should only need to add the host route for "other"
michael@0 4361 // types, which is the same handling method as the supl type. So let the
michael@0 4362 // definition of other types to be the same as the one of supl type.
michael@0 4363 NETWORK_TYPE_MOBILE_OTHERS: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_SUPL,
michael@0 4364
michael@0 4365 /**
michael@0 4366 * Standard values for the APN connection retry process
michael@0 4367 * Retry funcion: time(secs) = A * numer_of_retries^2 + B
michael@0 4368 */
michael@0 4369 NETWORK_APNRETRY_FACTOR: 8,
michael@0 4370 NETWORK_APNRETRY_ORIGIN: 3,
michael@0 4371 NETWORK_APNRETRY_MAXRETRIES: 10,
michael@0 4372
michael@0 4373 // Event timer for connection retries
michael@0 4374 timer: null,
michael@0 4375
michael@0 4376 /**
michael@0 4377 * nsINetworkInterface Implementation
michael@0 4378 */
michael@0 4379
michael@0 4380 state: Ci.nsINetworkInterface.NETWORK_STATE_UNKNOWN,
michael@0 4381
michael@0 4382 get type() {
michael@0 4383 if (this.connectedTypes.indexOf("default") != -1) {
michael@0 4384 return this.NETWORK_TYPE_MOBILE;
michael@0 4385 }
michael@0 4386 if (this.connectedTypes.indexOf("mms") != -1) {
michael@0 4387 return this.NETWORK_TYPE_MOBILE_MMS;
michael@0 4388 }
michael@0 4389 if (this.connectedTypes.indexOf("supl") != -1) {
michael@0 4390 return this.NETWORK_TYPE_MOBILE_SUPL;
michael@0 4391 }
michael@0 4392 if (this.connectedTypes.indexOf("ims") != -1) {
michael@0 4393 return this.NETWORK_TYPE_MOBILE_IMS;
michael@0 4394 }
michael@0 4395 if (this.connectedTypes.indexOf("dun") != -1) {
michael@0 4396 return this.NETWORK_TYPE_MOBILE_DUN;
michael@0 4397 }
michael@0 4398
michael@0 4399 return this.NETWORK_TYPE_MOBILE_OTHERS;
michael@0 4400 },
michael@0 4401
michael@0 4402 name: null,
michael@0 4403
michael@0 4404 ips: null,
michael@0 4405
michael@0 4406 prefixLengths: null,
michael@0 4407
michael@0 4408 gateways: null,
michael@0 4409
michael@0 4410 dnses: null,
michael@0 4411
michael@0 4412 get httpProxyHost() {
michael@0 4413 return this.apnSetting.proxy || "";
michael@0 4414 },
michael@0 4415
michael@0 4416 get httpProxyPort() {
michael@0 4417 return this.apnSetting.port || "";
michael@0 4418 },
michael@0 4419
michael@0 4420 /**
michael@0 4421 * nsIRilNetworkInterface Implementation
michael@0 4422 */
michael@0 4423
michael@0 4424 get serviceId() {
michael@0 4425 return this.dataConnectionHandler.clientId;
michael@0 4426 },
michael@0 4427
michael@0 4428 get iccId() {
michael@0 4429 let iccInfo = this.dataConnectionHandler.radioInterface.rilContext.iccInfo;
michael@0 4430 return iccInfo && iccInfo.iccid;
michael@0 4431 },
michael@0 4432
michael@0 4433 get mmsc() {
michael@0 4434 if (!this.inConnectedTypes("mms")) {
michael@0 4435 if (DEBUG) this.debug("Error! Only MMS network can get MMSC.");
michael@0 4436 throw Cr.NS_ERROR_UNEXPECTED;
michael@0 4437 }
michael@0 4438
michael@0 4439 let mmsc = this.apnSetting.mmsc;
michael@0 4440 if (!mmsc) {
michael@0 4441 try {
michael@0 4442 mmsc = Services.prefs.getCharPref("ril.mms.mmsc");
michael@0 4443 } catch (e) {
michael@0 4444 mmsc = "";
michael@0 4445 }
michael@0 4446 }
michael@0 4447
michael@0 4448 return mmsc;
michael@0 4449 },
michael@0 4450
michael@0 4451 get mmsProxy() {
michael@0 4452 if (!this.inConnectedTypes("mms")) {
michael@0 4453 if (DEBUG) this.debug("Error! Only MMS network can get MMS proxy.");
michael@0 4454 throw Cr.NS_ERROR_UNEXPECTED;
michael@0 4455 }
michael@0 4456
michael@0 4457 let proxy = this.apnSetting.mmsproxy;
michael@0 4458 if (!proxy) {
michael@0 4459 try {
michael@0 4460 proxy = Services.prefs.getCharPref("ril.mms.mmsproxy");
michael@0 4461 } catch (e) {
michael@0 4462 proxy = "";
michael@0 4463 }
michael@0 4464 }
michael@0 4465
michael@0 4466 return proxy;
michael@0 4467 },
michael@0 4468
michael@0 4469 get mmsPort() {
michael@0 4470 if (!this.inConnectedTypes("mms")) {
michael@0 4471 if (DEBUG) this.debug("Error! Only MMS network can get MMS port.");
michael@0 4472 throw Cr.NS_ERROR_UNEXPECTED;
michael@0 4473 }
michael@0 4474
michael@0 4475 let port = this.apnSetting.mmsport;
michael@0 4476 if (!port) {
michael@0 4477 try {
michael@0 4478 port = Services.prefs.getIntPref("ril.mms.mmsport");
michael@0 4479 } catch (e) {
michael@0 4480 port = -1;
michael@0 4481 }
michael@0 4482 }
michael@0 4483
michael@0 4484 return port;
michael@0 4485 },
michael@0 4486
michael@0 4487 getAddresses: function (ips, prefixLengths) {
michael@0 4488 ips.value = this.ips.slice();
michael@0 4489 prefixLengths.value = this.prefixLengths.slice();
michael@0 4490
michael@0 4491 return this.ips.length;
michael@0 4492 },
michael@0 4493
michael@0 4494 getGateways: function (count) {
michael@0 4495 if (count) {
michael@0 4496 count.value = this.gateways.length;
michael@0 4497 }
michael@0 4498 return this.gateways.slice();
michael@0 4499 },
michael@0 4500
michael@0 4501 getDnses: function (count) {
michael@0 4502 if (count) {
michael@0 4503 count.value = this.dnses.length;
michael@0 4504 }
michael@0 4505 return this.dnses.slice();
michael@0 4506 },
michael@0 4507
michael@0 4508 debug: function(s) {
michael@0 4509 dump("-*- RILNetworkInterface[" + this.dataConnectionHandler.clientId + ":" +
michael@0 4510 this.type + "]: " + s + "\n");
michael@0 4511 },
michael@0 4512
michael@0 4513 dataCallError: function(message) {
michael@0 4514 if (message.apn != this.apnSetting.apn) {
michael@0 4515 return;
michael@0 4516 }
michael@0 4517 if (DEBUG) this.debug("Data call error on APN: " + message.apn);
michael@0 4518 this.reset();
michael@0 4519 },
michael@0 4520
michael@0 4521 dataCallStateChanged: function(datacall) {
michael@0 4522 if (this.cid && this.cid != datacall.cid) {
michael@0 4523 // If data call for this connection existed but cid mismatched,
michael@0 4524 // it means this datacall state change is not for us.
michael@0 4525 return;
michael@0 4526 }
michael@0 4527 // If data call for this connection does not exist, it could be state
michael@0 4528 // change for new data call. We only update data call state change
michael@0 4529 // if APN name matched.
michael@0 4530 if (!this.cid && datacall.apn != this.apnSetting.apn) {
michael@0 4531 return;
michael@0 4532 }
michael@0 4533 if (DEBUG) {
michael@0 4534 this.debug("Data call ID: " + datacall.cid + ", interface name: " +
michael@0 4535 datacall.ifname + ", APN name: " + datacall.apn);
michael@0 4536 }
michael@0 4537 if (this.connecting &&
michael@0 4538 (datacall.state == RIL.GECKO_NETWORK_STATE_CONNECTING ||
michael@0 4539 datacall.state == RIL.GECKO_NETWORK_STATE_CONNECTED)) {
michael@0 4540 this.connecting = false;
michael@0 4541 this.cid = datacall.cid;
michael@0 4542 this.name = datacall.ifname;
michael@0 4543 for (let entry of datacall.addresses) {
michael@0 4544 this.ips.push(entry.address);
michael@0 4545 this.prefixLengths.push(entry.prefixLength);
michael@0 4546 }
michael@0 4547 this.gateways = datacall.gateways.slice();
michael@0 4548 this.dnses = datacall.dnses.slice();
michael@0 4549 if (!this.registeredAsNetworkInterface) {
michael@0 4550 gNetworkManager.registerNetworkInterface(this);
michael@0 4551 this.registeredAsNetworkInterface = true;
michael@0 4552 }
michael@0 4553 }
michael@0 4554 // In current design, we don't update status of secondary APN if it shares
michael@0 4555 // same APN name with the default APN. In this condition, this.cid will
michael@0 4556 // not be set and we don't want to update its status.
michael@0 4557 if (this.cid == null) {
michael@0 4558 return;
michael@0 4559 }
michael@0 4560
michael@0 4561 if (this.state == datacall.state) {
michael@0 4562 if (datacall.state != RIL.GECKO_NETWORK_STATE_CONNECTED) {
michael@0 4563 return;
michael@0 4564 }
michael@0 4565 // State remains connected, check for minor changes.
michael@0 4566 let changed = false;
michael@0 4567 if (this.ips.length != datacall.addresses.length) {
michael@0 4568 changed = true;
michael@0 4569 this.ips = [];
michael@0 4570 this.prefixLengths = [];
michael@0 4571 for (let entry of datacall.addresses) {
michael@0 4572 this.ips.push(entry.address);
michael@0 4573 this.prefixLengths.push(entry.prefixLength);
michael@0 4574 }
michael@0 4575 }
michael@0 4576
michael@0 4577 let reduceFunc = function(aRhs, aChanged, aElement, aIndex) {
michael@0 4578 return aChanged || (aElement != aRhs[aIndex]);
michael@0 4579 };
michael@0 4580 for (let field of ["gateways", "dnses"]) {
michael@0 4581 let lhs = this[field], rhs = datacall[field];
michael@0 4582 if (lhs.length != rhs.length ||
michael@0 4583 lhs.reduce(reduceFunc.bind(null, rhs), false)) {
michael@0 4584 changed = true;
michael@0 4585 this[field] = rhs.slice();
michael@0 4586 }
michael@0 4587 }
michael@0 4588
michael@0 4589 if (changed) {
michael@0 4590 if (DEBUG) this.debug("Notify for data call minor changes.");
michael@0 4591 Services.obs.notifyObservers(this,
michael@0 4592 kNetworkInterfaceStateChangedTopic,
michael@0 4593 null);
michael@0 4594 }
michael@0 4595 return;
michael@0 4596 }
michael@0 4597
michael@0 4598 this.state = datacall.state;
michael@0 4599
michael@0 4600 Services.obs.notifyObservers(this,
michael@0 4601 kNetworkInterfaceStateChangedTopic,
michael@0 4602 null);
michael@0 4603
michael@0 4604 if ((this.state == RIL.GECKO_NETWORK_STATE_UNKNOWN ||
michael@0 4605 this.state == RIL.GECKO_NETWORK_STATE_DISCONNECTED) &&
michael@0 4606 this.registeredAsNetworkInterface) {
michael@0 4607 gNetworkManager.unregisterNetworkInterface(this);
michael@0 4608 this.registeredAsNetworkInterface = false;
michael@0 4609 this.cid = null;
michael@0 4610 this.connectedTypes = [];
michael@0 4611
michael@0 4612 this.ips = [];
michael@0 4613 this.prefixLengths = [];
michael@0 4614 this.dnses = [];
michael@0 4615 this.gateways = [];
michael@0 4616 }
michael@0 4617
michael@0 4618 // In case the data setting changed while the datacall was being started or
michael@0 4619 // ended, let's re-check the setting and potentially adjust the datacall
michael@0 4620 // state again.
michael@0 4621 let apnSettings = this.dataConnectionHandler.apnSettings;
michael@0 4622 if (apnSettings.byType.default &&
michael@0 4623 (apnSettings.byType.default.apn == this.apnSetting.apn)) {
michael@0 4624 this.dataConnectionHandler.updateRILNetworkInterface();
michael@0 4625 }
michael@0 4626 },
michael@0 4627
michael@0 4628 // Helpers
michael@0 4629
michael@0 4630 cid: null,
michael@0 4631 registeredAsDataCallCallback: false,
michael@0 4632 registeredAsNetworkInterface: false,
michael@0 4633 connecting: false,
michael@0 4634 apnSetting: null,
michael@0 4635
michael@0 4636 // APN failed connections. Retry counter
michael@0 4637 apnRetryCounter: 0,
michael@0 4638
michael@0 4639 connectedTypes: null,
michael@0 4640
michael@0 4641 inConnectedTypes: function(type) {
michael@0 4642 return this.connectedTypes.indexOf(type) != -1;
michael@0 4643 },
michael@0 4644
michael@0 4645 get connected() {
michael@0 4646 return this.state == RIL.GECKO_NETWORK_STATE_CONNECTED;
michael@0 4647 },
michael@0 4648
michael@0 4649 connect: function(apntype) {
michael@0 4650 if (apntype && !this.inConnectedTypes(apntype)) {
michael@0 4651 this.connectedTypes.push(apntype);
michael@0 4652 }
michael@0 4653
michael@0 4654 if (this.connecting || this.connected) {
michael@0 4655 return;
michael@0 4656 }
michael@0 4657
michael@0 4658 // When the retry mechanism is running in background and someone calls
michael@0 4659 // disconnect(), this.connectedTypes.length has chances to become 0.
michael@0 4660 if (!this.connectedTypes.length) {
michael@0 4661 return;
michael@0 4662 }
michael@0 4663
michael@0 4664 if (!this.registeredAsDataCallCallback) {
michael@0 4665 this.dataConnectionHandler.registerDataCallCallback(this);
michael@0 4666 this.registeredAsDataCallCallback = true;
michael@0 4667 }
michael@0 4668
michael@0 4669 if (!this.apnSetting.apn) {
michael@0 4670 if (DEBUG) this.debug("APN name is empty, nothing to do.");
michael@0 4671 return;
michael@0 4672 }
michael@0 4673
michael@0 4674 if (DEBUG) {
michael@0 4675 this.debug("Going to set up data connection with APN " +
michael@0 4676 this.apnSetting.apn);
michael@0 4677 }
michael@0 4678 let radioInterface = this.dataConnectionHandler.radioInterface;
michael@0 4679 let radioTechType = radioInterface.rilContext.data.type;
michael@0 4680 let radioTechnology = RIL.GECKO_RADIO_TECH.indexOf(radioTechType);
michael@0 4681 let authType = RIL.RIL_DATACALL_AUTH_TO_GECKO.indexOf(this.apnSetting.authtype);
michael@0 4682 // Use the default authType if the value in database is invalid.
michael@0 4683 // For the case that user might not select the authentication type.
michael@0 4684 if (authType == -1) {
michael@0 4685 if (DEBUG) {
michael@0 4686 this.debug("Invalid authType " + this.apnSetting.authtype);
michael@0 4687 }
michael@0 4688 authType = RIL.RIL_DATACALL_AUTH_TO_GECKO.indexOf(RIL.GECKO_DATACALL_AUTH_DEFAULT);
michael@0 4689 }
michael@0 4690 let pdpType = RIL.GECKO_DATACALL_PDP_TYPE_IP;
michael@0 4691 if (RILQUIRKS_HAVE_IPV6) {
michael@0 4692 pdpType = !radioInterface.rilContext.data.roaming
michael@0 4693 ? this.apnSetting.protocol
michael@0 4694 : this.apnSetting.roaming_protocol;
michael@0 4695 if (RIL.RIL_DATACALL_PDP_TYPES.indexOf(pdpType) < 0) {
michael@0 4696 if (DEBUG) {
michael@0 4697 this.debug("Invalid pdpType '" + pdpType + "', using '" +
michael@0 4698 RIL.GECKO_DATACALL_PDP_TYPE_DEFAULT + "'");
michael@0 4699 }
michael@0 4700 pdpType = RIL.GECKO_DATACALL_PDP_TYPE_DEFAULT;
michael@0 4701 }
michael@0 4702 }
michael@0 4703 radioInterface.setupDataCall(radioTechnology,
michael@0 4704 this.apnSetting.apn,
michael@0 4705 this.apnSetting.user,
michael@0 4706 this.apnSetting.password,
michael@0 4707 authType,
michael@0 4708 pdpType);
michael@0 4709 this.connecting = true;
michael@0 4710 },
michael@0 4711
michael@0 4712 reset: function() {
michael@0 4713 let apnRetryTimer;
michael@0 4714 this.connecting = false;
michael@0 4715 // We will retry the connection in increasing times
michael@0 4716 // based on the function: time = A * numer_of_retries^2 + B
michael@0 4717 if (this.apnRetryCounter >= this.NETWORK_APNRETRY_MAXRETRIES) {
michael@0 4718 this.apnRetryCounter = 0;
michael@0 4719 this.timer = null;
michael@0 4720 this.connectedTypes = [];
michael@0 4721 if (DEBUG) this.debug("Too many APN Connection retries - STOP retrying");
michael@0 4722 return;
michael@0 4723 }
michael@0 4724
michael@0 4725 apnRetryTimer = this.NETWORK_APNRETRY_FACTOR *
michael@0 4726 (this.apnRetryCounter * this.apnRetryCounter) +
michael@0 4727 this.NETWORK_APNRETRY_ORIGIN;
michael@0 4728 this.apnRetryCounter++;
michael@0 4729 if (DEBUG) {
michael@0 4730 this.debug("Data call - APN Connection Retry Timer (secs-counter): " +
michael@0 4731 apnRetryTimer + "-" + this.apnRetryCounter);
michael@0 4732 }
michael@0 4733
michael@0 4734 if (this.timer == null) {
michael@0 4735 // Event timer for connection retries
michael@0 4736 this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
michael@0 4737 }
michael@0 4738 this.timer.initWithCallback(this, apnRetryTimer * 1000,
michael@0 4739 Ci.nsITimer.TYPE_ONE_SHOT);
michael@0 4740 },
michael@0 4741
michael@0 4742 disconnect: function(apntype) {
michael@0 4743 let index = this.connectedTypes.indexOf(apntype);
michael@0 4744 if (index != -1) {
michael@0 4745 this.connectedTypes.splice(index, 1);
michael@0 4746 }
michael@0 4747
michael@0 4748 if (this.connectedTypes.length) {
michael@0 4749 return;
michael@0 4750 }
michael@0 4751
michael@0 4752 if (this.state == RIL.GECKO_NETWORK_STATE_DISCONNECTING ||
michael@0 4753 this.state == RIL.GECKO_NETWORK_STATE_DISCONNECTED ||
michael@0 4754 this.state == RIL.GECKO_NETWORK_STATE_UNKNOWN) {
michael@0 4755 return;
michael@0 4756 }
michael@0 4757 let reason = RIL.DATACALL_DEACTIVATE_NO_REASON;
michael@0 4758 if (DEBUG) this.debug("Going to disconnet data connection " + this.cid);
michael@0 4759 this.dataConnectionHandler.radioInterface.deactivateDataCall(this.cid,
michael@0 4760 reason);
michael@0 4761 },
michael@0 4762
michael@0 4763 // Entry method for timer events. Used to reconnect to a failed APN
michael@0 4764 notify: function(timer) {
michael@0 4765 this.connect();
michael@0 4766 },
michael@0 4767
michael@0 4768 shutdown: function() {
michael@0 4769 this.timer = null;
michael@0 4770 }
michael@0 4771
michael@0 4772 };
michael@0 4773
michael@0 4774 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([RadioInterfaceLayer]);

mercurial