dom/system/gonk/RadioInterfaceLayer.js

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

mercurial