dom/system/gonk/RadioInterfaceLayer.js

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

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

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

     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  */
    16 "use strict";
    18 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
    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");
    27 var RIL = {};
    28 Cu.import("resource://gre/modules/ril_consts.js", RIL);
    30 // set to true in ril_consts.js to see debug messages
    31 var DEBUG = RIL.DEBUG_RIL;
    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;
    42 function debug(s) {
    43   dump("-*- RadioInterfaceLayer: " + s + "\n");
    44 }
    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";
    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";
    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";
    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}");
    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";
    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";
    92 const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
    94 const kPrefCellBroadcastDisabled = "ril.cellbroadcast.disabled";
    95 const kPrefClirModePreference = "ril.clirMode";
    96 const kPrefRilNumRadioInterfaces = "ril.numRadioInterfaces";
    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";
   103 const RADIO_POWER_OFF_TIMEOUT = 30000;
   104 const SMS_HANDLED_WAKELOCK_TIMEOUT = 5000;
   105 const HW_DEFAULT_CLIENT_ID = 0;
   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 ];
   135 const RIL_IPC_MOBILENETWORK_MSG_NAMES = [
   136   "RIL:GetLastKnownNetwork",
   137   "RIL:GetLastKnownHomeNetwork"
   138 ];
   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 ];
   158 const RIL_IPC_VOICEMAIL_MSG_NAMES = [
   159   "RIL:RegisterVoicemailMsg",
   160   "RIL:GetVoicemailInfo"
   161 ];
   163 const RIL_IPC_CELLBROADCAST_MSG_NAMES = [
   164   "RIL:RegisterCellBroadcastMsg"
   165 ];
   167 XPCOMUtils.defineLazyServiceGetter(this, "gPowerManagerService",
   168                                    "@mozilla.org/power/powermanagerservice;1",
   169                                    "nsIPowerManagerService");
   171 XPCOMUtils.defineLazyServiceGetter(this, "gMobileMessageService",
   172                                    "@mozilla.org/mobilemessage/mobilemessageservice;1",
   173                                    "nsIMobileMessageService");
   175 XPCOMUtils.defineLazyServiceGetter(this, "gSmsService",
   176                                    "@mozilla.org/sms/smsservice;1",
   177                                    "nsISmsService");
   179 XPCOMUtils.defineLazyServiceGetter(this, "gMobileMessageDatabaseService",
   180                                    "@mozilla.org/mobilemessage/rilmobilemessagedatabaseservice;1",
   181                                    "nsIRilMobileMessageDatabaseService");
   183 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
   184                                    "@mozilla.org/parentprocessmessagemanager;1",
   185                                    "nsIMessageBroadcaster");
   187 XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService",
   188                                    "@mozilla.org/settingsService;1",
   189                                    "nsISettingsService");
   191 XPCOMUtils.defineLazyServiceGetter(this, "gSystemMessenger",
   192                                    "@mozilla.org/system-message-internal;1",
   193                                    "nsISystemMessagesInternal");
   195 XPCOMUtils.defineLazyServiceGetter(this, "gNetworkManager",
   196                                    "@mozilla.org/network/manager;1",
   197                                    "nsINetworkManager");
   199 XPCOMUtils.defineLazyServiceGetter(this, "gTimeService",
   200                                    "@mozilla.org/time/timeservice;1",
   201                                    "nsITimeService");
   203 XPCOMUtils.defineLazyServiceGetter(this, "gSystemWorkerManager",
   204                                    "@mozilla.org/telephony/system-worker-manager;1",
   205                                    "nsISystemWorkerManager");
   207 XPCOMUtils.defineLazyServiceGetter(this, "gTelephonyProvider",
   208                                    "@mozilla.org/telephony/telephonyprovider;1",
   209                                    "nsIGonkTelephonyProvider");
   211 XPCOMUtils.defineLazyGetter(this, "WAP", function() {
   212   let wap = {};
   213   Cu.import("resource://gre/modules/WapPushManager.js", wap);
   214   return wap;
   215 });
   217 XPCOMUtils.defineLazyGetter(this, "PhoneNumberUtils", function() {
   218   let ns = {};
   219   Cu.import("resource://gre/modules/PhoneNumberUtils.jsm", ns);
   220   return ns.PhoneNumberUtils;
   221 });
   223 XPCOMUtils.defineLazyGetter(this, "gMessageManager", function() {
   224   return {
   225     QueryInterface: XPCOMUtils.generateQI([Ci.nsIMessageListener,
   226                                            Ci.nsIObserver]),
   228     ril: null,
   230     // Manage message targets in terms of topic. Only the authorized and
   231     // registered contents can receive related messages.
   232     targetsByTopic: {},
   233     topics: [],
   235     targetMessageQueue: [],
   236     ready: false,
   238     init: function(ril) {
   239       this.ril = ril;
   241       Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
   242       Services.obs.addObserver(this, kSysMsgListenerReadyObserverTopic, false);
   243       this._registerMessageListeners();
   244     },
   246     _shutdown: function() {
   247       this.ril = null;
   249       Services.obs.removeObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
   250       this._unregisterMessageListeners();
   251     },
   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     },
   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     },
   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       }
   302       if (targets.indexOf(target) != -1) {
   303         if (DEBUG) debug("Already registered this target!");
   304         return;
   305       }
   307       targets.push(target);
   308       if (DEBUG) debug("Registered " + topic + " target: " + target);
   309     },
   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       }
   320       // Unregister the target for a specified topic.
   321       let targets = this.targetsByTopic[topic];
   322       if (!targets) {
   323         return;
   324       }
   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     },
   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       }
   348       messageQueue.push(msg);
   349     },
   351     _sendTargetMessage: function(topic, message, options) {
   352       if (!this.ready) {
   353         this._enqueueTargetMessage(topic, message, options);
   354         return;
   355       }
   357       let targets = this.targetsByTopic[topic];
   358       if (!targets) {
   359         return;
   360       }
   362       for (let target of targets) {
   363         target.sendAsyncMessage(message, options);
   364       }
   365     },
   367     _resendQueuedTargetMessage: function() {
   368       this.ready = true;
   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.
   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     },
   382     /**
   383      * nsIMessageListener interface methods.
   384      */
   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       }
   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       }
   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       }
   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       }
   463       if (msg.name === "RIL:SetRadioEnabled") {
   464         // Special handler for SetRadioEnabled.
   465         return gRadioEnabledController.receiveMessage(msg);
   466       }
   468       return radioInterface.receiveMessage(msg);
   469     },
   471     /**
   472      * nsIObserver interface methods.
   473      */
   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     },
   487     sendMobileConnectionMessage: function(message, clientId, data) {
   488       this._sendTargetMessage("mobileconnection", message, {
   489         clientId: clientId,
   490         data: data
   491       });
   492     },
   494     sendVoicemailMessage: function(message, clientId, data) {
   495       this._sendTargetMessage("voicemail", message, {
   496         clientId: clientId,
   497         data: data
   498       });
   499     },
   501     sendCellBroadcastMessage: function(message, clientId, data) {
   502       this._sendTargetMessage("cellbroadcast", message, {
   503         clientId: clientId,
   504         data: data
   505       });
   506     },
   508     sendIccMessage: function(message, clientId, data) {
   509       this._sendTargetMessage("icc", message, {
   510         clientId: clientId,
   511         data: data
   512       });
   513     }
   514   };
   515 });
   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;
   527   return {
   528     init: function(ril) {
   529       _ril = ril;
   530     },
   532     receiveCardState: function(clientId) {
   533       if (_allCardStateInitialized) {
   534         return;
   535       }
   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     },
   545     receiveMessage: function(msg) {
   546       if (DEBUG) debug("RadioControl: receiveMessage: " + JSON.stringify(msg));
   547       _pendingMessages.push(msg);
   548       this._startProcessingPending();
   549     },
   551     isDeactivatingDataCalls: function() {
   552       return _request !== null;
   553     },
   555     finishDeactivatingDataCalls: function(clientId) {
   556       if (DEBUG) debug("RadioControl: finishDeactivatingDataCalls: " + clientId);
   557       let deferred = _deactivatingDeferred[clientId];
   558       if (deferred) {
   559         deferred.resolve();
   560       }
   561     },
   563     _startProcessingPending: function() {
   564       if (!_isProcessingPending) {
   565         if (DEBUG) debug("RadioControl: start dequeue");
   566         _isProcessingPending = true;
   567         this._processNextMessage();
   568       }
   569     },
   571     _processNextMessage: function() {
   572       if (_pendingMessages.length === 0 || !_allCardStateInitialized) {
   573         if (DEBUG) debug("RadioControl: stop dequeue");
   574         _isProcessingPending = false;
   575         return;
   576       }
   578       let msg = _pendingMessages.shift();
   579       this._handleMessage(msg);
   580     },
   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     },
   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     },
   598     _isRadioAbleToEnableAtClient: function(clientId, numCards) {
   599       if (!RILQUIRKS_RADIO_OFF_WO_CARD) {
   600         return true;
   601       }
   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.
   607       if (this._isCardPresentAtClient(clientId)) {
   608         return true;
   609       }
   611       numCards = numCards == null ? this._getNumCards() : numCards;
   612       if (clientId === HW_DEFAULT_CLIENT_ID && numCards === 0) {
   613         return true;
   614       }
   616       return false;
   617     },
   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);
   624       if (!radioInterface.isValidStateForSetRadioEnabled()) {
   625         radioInterface.setRadioEnabledResponse(msg.target, msg.json.data,
   626                                                "InvalidStateError");
   627         this._processNextMessage();
   628         return;
   629       }
   631       if (radioInterface.isDummyForSetRadioEnabled(msg.json.data)) {
   632         radioInterface.setRadioEnabledResponse(msg.target, msg.json.data);
   633         this._processNextMessage();
   634         return;
   635       }
   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         }
   645         this._processNextMessage();
   646       } else {
   647         _request = function() {
   648           radioInterface.receiveMessage(msg);
   649         };
   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         }
   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         });
   669         this._createTimer();
   670       }
   671     },
   673     _deactivateDataCalls: function() {
   674       if (DEBUG) debug("RadioControl: deactivating data calls...");
   675       _deactivatingDeferred = {};
   677       let promise = Promise.resolve();
   678       for (let i = 0, N = _ril.numRadioInterfaces; i < N; ++i) {
   679         promise = promise.then(this._deactivateDataCallsForClient(i));
   680       }
   682       return promise;
   683     },
   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     },
   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     },
   703     _cancelTimer: function() {
   704       if (_timer) {
   705         _timer.cancel();
   706       }
   707     },
   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 });
   721 XPCOMUtils.defineLazyGetter(this, "gDataConnectionManager", function () {
   722   return {
   723     QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
   724                                            Ci.nsISettingsServiceCallback]),
   726     _connectionHandlers: null,
   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,
   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,
   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,
   741     // Pending function to execute when we are notified that another data call has
   742     // been disconnected.
   743     _pendingDataCallRequest: null,
   745     debug: function(s) {
   746       dump("-*- DataConnectionManager: " + s + "\n");
   747     },
   749     init: function(ril) {
   750       if (!ril) {
   751         return;
   752       }
   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       }
   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);
   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     },
   775     getConnectionHandler: function(clientId) {
   776       return this._connectionHandlers[clientId];
   777     },
   779     _handleDataClientIdChange: function(newDefault) {
   780       if (this._dataDefaultClientId === newDefault) {
   781          return;
   782       }
   783       this._dataDefaultClientId = newDefault;
   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       }
   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;
   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       }
   818       oldSettings.oldEnabled = oldSettings.enabled;
   819       oldSettings.enabled = false;
   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;
   832           this._currentDataClientId = this._dataDefaultClientId;
   833           newConnHandler.updateRILNetworkInterface();
   834         };
   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       }
   844       newSettings.oldEnabled = newSettings.enabled;
   845       newSettings.enabled = true;
   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     },
   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     },
   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;
   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           }
   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     },
   936     handleError: function(errorMessage) {
   937       if (DEBUG) {
   938         this.debug("There was an error while reading RIL settings.");
   939       }
   940     },
   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 });
   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) {}
   996     return 1;
   997   })());
   998 } catch (e) {}
  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 };
  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   }),
  1023   // nsIDOMMozGsmIccInfo
  1025   msisdn: null
  1026 };
  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   }),
  1040   // nsIDOMMozCdmaIccInfo
  1042   mdn: null,
  1043   prlVersion: 0
  1044 };
  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 = [];
  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   };
  1067 DataConnectionHandler.prototype = {
  1068   clientId: 0,
  1069   radioInterface: null,
  1070   // Data calls setting.
  1071   dataCallSettings: null,
  1072   apnSettings: null,
  1074   // Apn settings to be setup after data call are cleared.
  1075   _pendingApnSettings: null,
  1077   debug: function(s) {
  1078     dump("-*- DataConnectionHandler[" + this.clientId + "]: " + s + "\n");
  1079   },
  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();
  1088     this.clientId = null;
  1089     this.radioInterface = null;
  1090   },
  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   },
  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;
  1114       try {
  1115         let handler = callback[name];
  1116         if (typeof handler !== "function") {
  1117           throw new Error("No handler for " + name);
  1119         handler.apply(callback, args);
  1120       } catch (e) {
  1121         if (DEBUG) {
  1122           this.debug("callback handler for " + name + " threw an exception: " + e);
  1126   },
  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;
  1141     if (DEBUG) this.debug("setupApnSettings: " + JSON.stringify(newApnSettings));
  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);
  1148       this.unregisterDataCallCallback(apnSetting.iface);
  1149       delete apnSetting.iface;
  1151     this.apnSettings.byApn = {};
  1152     this.apnSettings.byType = {};
  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;
  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 || "");
  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);
  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];
  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);
  1184   },
  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;
  1197     return true;
  1198   },
  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;
  1210     return false;
  1211   },
  1213   updateApnSettings: function(newApnSettings) {
  1214     if (!newApnSettings) {
  1215       return;
  1217     if (this._pendingApnSettings) {
  1218       // Change of apn settings in process, just update to the newest.
  1219       this._pengingApnSettings = newApnSettings;
  1220       return;
  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;
  1235     if (isDeactivatingDataCalls) {
  1236       // Defer apn settings setup until all data calls are cleared.
  1237       this._pendingApnSettings = newApnSettings;
  1238       return;
  1240     this._setupApnSettings(newApnSettings);
  1241   },
  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.");
  1249       return;
  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");
  1259       return;
  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.");
  1271       return;
  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.");
  1280       return;
  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.");
  1293       return;
  1295     let wifi_active = false;
  1296     if (gNetworkManager.active &&
  1297         gNetworkManager.active.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
  1298       wifi_active = true;
  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.");
  1309       this.deactivateDataCallByType("default");
  1310       return;
  1313     if (defaultDataCallConnected && wifi_active) {
  1314       if (DEBUG) {
  1315         this.debug("Disconnect data call when Wifi is connected.");
  1317       this.deactivateDataCallByType("default");
  1318       return;
  1321     if (!this.dataCallSettings.enabled || defaultDataCallConnected) {
  1322       if (DEBUG) {
  1323         this.debug("Data call settings: nothing to do.");
  1325       return;
  1327     if (dataInfo.roaming && !this.dataCallSettings.roamingEnabled) {
  1328       if (DEBUG) {
  1329         this.debug("We're roaming, but data roaming is disabled.");
  1331       return;
  1333     if (wifi_active) {
  1334       if (DEBUG) {
  1335         this.debug("Don't connect data call when Wifi is connected.");
  1337       return;
  1339     if (this._pendingApnSettings) {
  1340       if (DEBUG) this.debug("We're changing apn settings, ignore any changes.");
  1341       return;
  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;
  1352     if (DEBUG) {
  1353       this.debug("Data call settings: connect data call.");
  1355     this.setupDataCallByType("default");
  1356   },
  1358   getDataCallStateByType: function(apnType) {
  1359     let apnSetting = this.apnSettings.byType[apnType];
  1360     if (!apnSetting) {
  1361        return RIL.GECKO_NETWORK_STATE_UNKNOWN;
  1363     if (!apnSetting.iface.inConnectedTypes(apnType)) {
  1364       return RIL.GECKO_NETWORK_STATE_DISCONNECTED;
  1366     return apnSetting.iface.state;
  1367   },
  1369   setupDataCallByType: function(apnType) {
  1370     if (DEBUG) {
  1371       this.debug("setupDataCallByType: " + apnType);
  1373     let apnSetting = this.apnSettings.byType[apnType];
  1374     if (!apnSetting) {
  1375       if (DEBUG) {
  1376         this.debug("No apn setting for type: " + apnType);
  1378       return;
  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;
  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);
  1401       gNetworkManager.registerNetworkInterface(apnSetting.iface);
  1403       Services.obs.notifyObservers(apnSetting.iface,
  1404                                    kNetworkInterfaceStateChangedTopic,
  1405                                    null);
  1407   },
  1409   deactivateDataCallByType: function(apnType) {
  1410     if (DEBUG) {
  1411       this.debug("deactivateDataCallByType: " + apnType);
  1413     let apnSetting = this.apnSettings.byType[apnType];
  1414     if (!apnSetting) {
  1415       if (DEBUG) {
  1416         this.debug("No apn setting for type: " + apnType);
  1418       return;
  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);
  1434       gNetworkManager.registerNetworkInterface(apnSetting.iface);
  1436       Services.obs.notifyObservers(apnSetting.iface,
  1437                                    kNetworkInterfaceStateChangedTopic,
  1438                                    null);
  1440   },
  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;
  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);
  1459   },
  1461   registerDataCallCallback: function(callback) {
  1462     if (this._dataCallbacks.indexOf(callback) != -1) {
  1463       throw new Error("Already registered this callback: " + callback);
  1465     this._dataCallbacks.push(callback);
  1466     if (DEBUG) {
  1467       this.debug("Registering callback: " + callback);
  1469   },
  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);
  1479   },
  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);
  1495     this._deliverDataCallCallback("dataCallError", [message]);
  1496   },
  1498   /**
  1499    * Handle data call state changes.
  1500    */
  1501   handleDataCallState: function(datacall) {
  1502     this._deliverDataCallCallback("dataCallStateChanged", [datacall]);
  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.");
  1512         gRadioEnabledController.finishDeactivatingDataCalls(this.clientId);
  1515       if (this._pendingApnSettings) {
  1516         if (DEBUG) {
  1517           this.debug("Setup pending apn settings.");
  1519         this._setupApnSettings(this._pendingApnSettings);
  1520         this._pendingApnSettings = null;
  1521         this.updateRILNetworkInterface();
  1524   },
  1525 };
  1527 function RadioInterfaceLayer() {
  1528   let workerMessenger = new WorkerMessenger();
  1529   workerMessenger.init();
  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));
  1538   Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
  1540   gMessageManager.init(this);
  1541   gRadioEnabledController.init(this);
  1542   gDataConnectionManager.init(this);
  1544 RadioInterfaceLayer.prototype = {
  1546   classID:   RADIOINTERFACELAYER_CID,
  1547   classInfo: XPCOMUtils.generateCI({classID: RADIOINTERFACELAYER_CID,
  1548                                     classDescription: "RadioInterfaceLayer",
  1549                                     interfaces: [Ci.nsIRadioInterfaceLayer]}),
  1551   QueryInterface: XPCOMUtils.generateQI([Ci.nsIRadioInterfaceLayer,
  1552                                          Ci.nsIObserver]),
  1554   /**
  1555    * nsIObserver interface methods.
  1556    */
  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();
  1564         this.radioInterfaces = null;
  1565         Services.obs.removeObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
  1566         break;
  1568   },
  1570   /**
  1571    * nsIRadioInterfaceLayer interface methods.
  1572    */
  1574   getRadioInterface: function(clientId) {
  1575     return this.radioInterfaces[clientId];
  1576   },
  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 });
  1584 };
  1586 XPCOMUtils.defineLazyGetter(RadioInterfaceLayer.prototype,
  1587                             "numRadioInterfaces", function() {
  1588   try {
  1589     return Services.prefs.getIntPref(kPrefRilNumRadioInterfaces);
  1590   } catch(e) {}
  1592   return 1;
  1593 });
  1595 function WorkerMessenger() {
  1596   // Initial owning attributes.
  1597   this.radioInterfaces = [];
  1598   this.tokenCallbackMap = {};
  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);
  1604 WorkerMessenger.prototype = {
  1605   radioInterfaces: null,
  1606   worker: null,
  1608   // This gets incremented each time we send out a message.
  1609   token: 1,
  1611   // Maps tokens we send out with messages to the message callback.
  1612   tokenCallbackMap: null,
  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     };
  1641     try {
  1642       options.cellBroadcastDisabled =
  1643         Services.prefs.getBoolPref(kPrefCellBroadcastDisabled);
  1644     } catch(e) {}
  1646     try {
  1647       options.clirMode = Services.prefs.getIntPref(kPrefClirModePreference);
  1648     } catch(e) {}
  1650     this.send(null, "setInitialOptions", options);
  1651   },
  1653   debug: function(aClientId, aMessage) {
  1654     // We use the same debug subject with RadioInterface's here.
  1655     dump("-*- RadioInterface[" + aClientId + "]: " + aMessage + "\n");
  1656   },
  1658   onerror: function(event) {
  1659     if (DEBUG) {
  1660       this.debug("X", "Got an error: " + event.filename + ":" +
  1661                  event.lineno + ": " + event.message + "\n");
  1663     event.preventDefault();
  1664   },
  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;
  1676     if (DEBUG) {
  1677       this.debug(clientId, "Received message from worker: " + JSON.stringify(message));
  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;
  1688     let callback = this.tokenCallbackMap[message.rilMessageToken];
  1689     if (!callback) {
  1690       if (DEBUG) this.debug(clientId, "Ignore orphan token: " + message.rilMessageToken);
  1691       return;
  1694     let keep = false;
  1695     try {
  1696       keep = callback(message);
  1697     } catch(e) {
  1698       if (DEBUG) this.debug(clientId, "callback throws an exception: " + e);
  1701     if (!keep) {
  1702       delete this.tokenCallbackMap[message.rilMessageToken];
  1704   },
  1706   registerClient: function(aClientId, aRadioInterface) {
  1707     if (DEBUG) this.debug(aClientId, "Starting RIL Worker");
  1709     // Keep a reference so that we can dispatch unsolicited messages to it.
  1710     this.radioInterfaces[aClientId] = aRadioInterface;
  1712     this.send(null, "registerClient", { clientId: aClientId });
  1713     gSystemWorkerManager.registerRilWorker(aClientId, this.worker);
  1714   },
  1716   /**
  1717    * Send arbitrary message to worker.
  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 || {};
  1734     message.rilMessageClientId = clientId;
  1735     message.rilMessageToken = this.token;
  1736     this.token++;
  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;
  1749     message.rilMessageType = rilMessageType;
  1750     this.worker.postMessage(message);
  1751   },
  1753   /**
  1754    * Send message to worker and return worker reply to RILContentHelper.
  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.
  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));
  1775 };
  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);
  1786   this.supportedNetworkTypes = this.getSupportedNetworkTypes();
  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,
  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   };
  1818   this.voicemailInfo = {
  1819     number: null,
  1820     displayName: null
  1821   };
  1823   this.operatorInfo = {};
  1825   let lock = gSettingsService.createLock();
  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);
  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);
  1835   // Set "time.clock.automatic-update.available" to false when starting up.
  1836   this.setClockAutoUpdateAvailable(false);
  1838   // Set "time.timezone.automatic-update.available" to false when starting up.
  1839   this.setTimezoneAutoUpdateAvailable(false);
  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);
  1845   Services.obs.addObserver(this, kMozSettingsChangedObserverTopic, false);
  1846   Services.obs.addObserver(this, kSysClockChangeObserverTopic, false);
  1847   Services.obs.addObserver(this, kScreenStateChangedTopic, false);
  1849   Services.obs.addObserver(this, kNetworkConnStateChangedTopic, false);
  1850   Services.obs.addObserver(this, kNetworkActiveChangedTopic, false);
  1851   Services.prefs.addObserver(kPrefCellBroadcastDisabled, this, false);
  1853   this.portAddressedSmsApps = {};
  1854   this.portAddressedSmsApps[WAP.WDP_PORT_PUSH] = this.handleSmsWdpPortPush.bind(this);
  1856   this._receivedSmsSegmentsMap = {};
  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"));
  1866 RadioInterface.prototype = {
  1868   classID:   RADIOINTERFACE_CID,
  1869   classInfo: XPCOMUtils.generateCI({classID: RADIOINTERFACE_CID,
  1870                                     classDescription: "RadioInterface",
  1871                                     interfaces: [Ci.nsIRadioInterface]}),
  1873   QueryInterface: XPCOMUtils.generateQI([Ci.nsIRadioInterface,
  1874                                          Ci.nsIObserver,
  1875                                          Ci.nsISettingsServiceCallback]),
  1877   // A private wrapped WorkerMessenger instance.
  1878   workerMessenger: null,
  1880   debug: function(s) {
  1881     dump("-*- RadioInterface[" + this.clientId + "]: " + s + "\n");
  1882   },
  1884   shutdown: function() {
  1885     // Release the CPU wake lock for handling the received SMS.
  1886     this._releaseSmsHandledWakeLock();
  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   },
  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;
  1904       destInfo[key] = srcInfo[key];
  1906   },
  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;
  1917     for (let key in srcInfo) {
  1918       if (key === "rilMessageType") {
  1919         continue;
  1921       if (srcInfo[key] !== destInfo[key]) {
  1922         return true;
  1926     return false;
  1927   },
  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;
  1945     if (DEBUG) this.debug("Supported Network Types: " + supportedNetworkTypes);
  1946     return supportedNetworkTypes;
  1947   },
  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;
  2082     return null;
  2083   },
  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           });
  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);
  2203   },
  2205   /**
  2206    * Get phone number from iccInfo.
  2208    * If the icc card is gsm card, the phone number is in msisdn.
  2209    * @see nsIDOMMozGsmIccInfo
  2211    * Otherwise, the phone number is in mdn.
  2212    * @see nsIDOMMozCdmaIccInfo
  2213    */
  2214   getPhoneNumber: function() {
  2215     let iccInfo = this.rilContext.iccInfo;
  2217     if (!iccInfo) {
  2218       return null;
  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;
  2227     // Workaround an xpconnect issue with undefined string objects.
  2228     // See bug 808220
  2229     if (number === undefined || number === "undefined") {
  2230       return null;
  2233     return number;
  2234   },
  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;
  2242     if (!iccInfo) {
  2243       return null;
  2246     let iccId = iccInfo.iccid;
  2248     // Workaround an xpconnect issue with undefined string objects.
  2249     // See bug 808220
  2250     if (iccId === undefined || iccId === "undefined") {
  2251       return null;
  2254     return iccId;
  2255   },
  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;
  2264     // This should not be an error, but a mismatch.
  2265     if (mvnoData.length > imsi.length) {
  2266       return false;
  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;
  2275     return true;
  2276   },
  2278   matchMvno: function(target, message) {
  2279     if (DEBUG) this.debug("matchMvno: " + JSON.stringify(message));
  2281     if (!message || !message.mvnoType || !message.mvnoData) {
  2282       message.errorMsg = RIL.GECKO_ERROR_INVALID_PARAMETER;
  2284     // Currently we only support imsi matching.
  2285     if (message.mvnoType != "imsi") {
  2286       message.errorMsg = RIL.GECKO_ERROR_MODE_NOT_SUPPORTED;
  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;
  2293     if (!message.errorMsg) {
  2294       message.result = this.isImsiMatches(message.mvnoData);
  2297     target.sendAsyncMessage("RIL:MatchMvno", {
  2298       clientId: this.clientId,
  2299       data: message
  2300     });
  2301   },
  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];
  2310     // Batch the *InfoChanged messages together
  2311     if (voiceMessage) {
  2312       this.updateVoiceConnection(voiceMessage, true);
  2315     if (dataMessage) {
  2316       this.updateDataConnection(dataMessage, true);
  2319     if (operatorMessage) {
  2320       this.handleOperatorChange(operatorMessage, true);
  2323     if (signalMessage) {
  2324       this.handleSignalStrengthChange(signalMessage, true);
  2327     let voice = this.rilContext.voice;
  2328     let data = this.rilContext.data;
  2330     this.checkRoamingBetweenOperators(voice);
  2331     this.checkRoamingBetweenOperators(data);
  2333     if (voiceMessage || operatorMessage || signalMessage) {
  2334       gMessageManager.sendMobileConnectionMessage("RIL:VoiceInfoChanged",
  2335                                                   this.clientId, voice);
  2337     if (dataMessage || operatorMessage || signalMessage) {
  2338       gMessageManager.sendMobileConnectionMessage("RIL:DataInfoChanged",
  2339                                                   this.clientId, data);
  2342     if (selectionMessage) {
  2343       this.updateNetworkSelectionMode(selectionMessage);
  2345   },
  2347   /**
  2348     * Fix the roaming. RIL can report roaming in some case it is not
  2349     * really the case. See bug 787967
  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;
  2359     if (!iccInfo || !operator ||
  2360         state != RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED) {
  2361       return;
  2364     let spn = iccInfo.spn && iccInfo.spn.toLowerCase();
  2365     let longName = operator.longName && operator.longName.toLowerCase();
  2366     let shortName = operator.shortName && operator.shortName.toLowerCase();
  2368     let equalsLongName = longName && (spn == longName);
  2369     let equalsShortName = shortName && (spn == shortName);
  2370     let equalsMcc = iccInfo.mcc == operator.mcc;
  2372     registration.roaming = registration.roaming &&
  2373                            !(equalsMcc && (equalsLongName || equalsShortName));
  2374   },
  2376   /**
  2377    * Handle data connection changes.
  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;
  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;
  2403     if (!batch) {
  2404       gMessageManager.sendMobileConnectionMessage("RIL:VoiceInfoChanged",
  2405                                                   this.clientId, voiceInfo);
  2407   },
  2409   /**
  2410    * Handle the data connection's state has changed.
  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;
  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;
  2444     if (!batch) {
  2445       gMessageManager.sendMobileConnectionMessage("RIL:DataInfoChanged",
  2446                                                   this.clientId, dataInfo);
  2449     let connHandler = gDataConnectionManager.getConnectionHandler(this.clientId);
  2450     connHandler.updateRILNetworkInterface();
  2451   },
  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];
  2459       target.sendAsyncMessage("RIL:GetPreferredNetworkType", {
  2460         clientId: this.clientId,
  2461         data: response
  2462       });
  2463       return false;
  2464     }).bind(this));
  2465   },
  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;
  2477     message.networkType = networkType;
  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   },
  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;
  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;
  2507       return false;
  2508     }).bind(this));
  2509   },
  2511   /**
  2512    * Handle signal strength changes.
  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);
  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);
  2540   },
  2542   /**
  2543    * Handle operator information changes.
  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;
  2554     if (this.isInfoChanged(message, operatorInfo)) {
  2555       this.updateInfo(message, operatorInfo);
  2557       // Update lastKnownNetwork
  2558       if (message.mcc && message.mnc) {
  2559         this._lastKnownNetwork = message.mcc + "-" + message.mnc;
  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);
  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);
  2574   },
  2576   handleOtaStatus: function(message) {
  2577     if (message.status < 0 ||
  2578         RIL.CDMA_OTA_PROVISION_STATUS_TO_GECKO.length <= message.status) {
  2579       return;
  2582     let status = RIL.CDMA_OTA_PROVISION_STATUS_TO_GECKO[message.status];
  2584     gMessageManager.sendMobileConnectionMessage("RIL:OtaStatusChanged",
  2585                                                 this.clientId, status);
  2586   },
  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;
  2597   },
  2599   handleRadioStateChange: function(message) {
  2600     let newState = message.radioState;
  2601     if (this.rilContext.radioState == newState) {
  2602       return;
  2604     this.rilContext.radioState = newState;
  2605     this.handleDetailedRadioStateChanged(this._convertRadioState(newState));
  2607     //TODO Should we notify this change as a card state change?
  2608   },
  2610   handleDetailedRadioStateChanged: function(state) {
  2611     if (this.rilContext.detailedRadioState == state) {
  2612       return;
  2614     this.rilContext.detailedRadioState = state;
  2615     gMessageManager.sendMobileConnectionMessage("RIL:RadioStateChanged",
  2616                                                 this.clientId, state);
  2617   },
  2619   setDataRegistration: function(attach) {
  2620     this.workerMessenger.send("setDataRegistration", {attach: attach});
  2621   },
  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   },
  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   },
  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   },
  2651   /**
  2652    * Handle WDP port push PDU. Constructor WDP bearer information and deliver
  2653    * to WapPushManager.
  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!");
  2664       return;
  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   },
  2679   /**
  2680    * A helper to broadcast the system message to launch registered apps
  2681    * like Costcontrol, Notification and Message app... etc.
  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);
  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   },
  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,
  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");
  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);
  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   },
  2735   _releaseSmsHandledWakeLock: function() {
  2736     if (DEBUG) this.debug("Releasing the CPU wake lock for handling SMS.");
  2737     if (this._smsHandledWakeLockTimer) {
  2738       this._smsHandledWakeLockTimer.cancel();
  2740     if (this._smsHandledWakeLock) {
  2741       this._smsHandledWakeLock.unlock();
  2742       this._smsHandledWakeLock = null;
  2744   },
  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,
  2753   /**
  2754    * Helper for processing received multipart SMS.
  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) {
  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;
  2768       return aSegment;
  2771     // Handle Concatenation for Class 0 SMS
  2772     let hash = aSegment.sender + ":" +
  2773                aSegment.segmentRef + ":" +
  2774                aSegment.segmentMaxSeq;
  2775     let seq = aSegment.segmentSeq;
  2777     let options = this._receivedSmsSegmentsMap[hash];
  2778     if (!options) {
  2779       options = aSegment;
  2780       this._receivedSmsSegmentsMap[hash] = options;
  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));
  2790       return null;
  2793     if (options.receivedSegments > 0) {
  2794       // Update received timestamp.
  2795       options.timestamp = aSegment.timestamp;
  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;
  2803     options.receivedSegments++;
  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;
  2816       if (!options.destinationPort && aSegment.destinationPort) {
  2817         options.destinationPort = aSegment.destinationPort;
  2821     if (options.receivedSegments < options.segmentMaxSeq) {
  2822       if (DEBUG) {
  2823         this.debug("Got segment no." + seq + " of a multipart SMS: " +
  2824                            JSON.stringify(options));
  2826       return null;
  2829     // Remove from map
  2830     delete this._receivedSmsSegmentsMap[hash];
  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;
  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];
  2848     } else {
  2849       options.fullBody = options.segments.join("");
  2852     // Remove handy fields after completing the concatenation.
  2853     delete options.receivedSegments;
  2854     delete options.segments;
  2856     if (DEBUG) {
  2857       this.debug("Got full multipart SMS: " + JSON.stringify(options));
  2860     return options;
  2861   },
  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;
  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;
  2896     return segment;
  2897   },
  2899   /**
  2900    * Helper to purge complete message.
  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;
  2911     // Purge partial message body
  2912     delete aMessage.data;
  2913     delete aMessage.body;
  2914   },
  2916   /**
  2917    * handle concatenation of received SMS.
  2918    */
  2919   handleSmsMultipart: function(aMessage) {
  2920     if (DEBUG) this.debug("handleSmsMultipart: " + JSON.stringify(aMessage));
  2922     this._acquireSmsHandledWakeLock();
  2924     let segment = this._createSavableSmsSegment(aMessage);
  2926     let isMultipart = (segment.segmentMaxSeq && (segment.segmentMaxSeq > 1));
  2927     let messageClass = segment.messageClass;
  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);
  2935         // else Ack will be sent after further process in handleSmsReceived.
  2936       } else {
  2937         this.sendAckSms(aRvOfIncompleteMsg, segment);
  2939     }.bind(this);
  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
  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       });
  2961   },
  2963   portAddressedSmsApps: null,
  2964   handleSmsReceived: function(message) {
  2965     if (DEBUG) this.debug("handleSmsReceived: " + JSON.stringify(message));
  2967     if (message.messageType == RIL.PDU_CDMA_MSG_TYPE_BROADCAST) {
  2968       gMessageManager.sendCellBroadcastMessage("RIL:CellBroadcastReceived",
  2969                                                this.clientId, message);
  2970       return true;
  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);
  2981       return true;
  2984     if (message.encoding == RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
  2985       // Don't know how to handle binary data yet.
  2986       return true;
  2989     message.type = "sms";
  2990     message.sender = message.sender || null;
  2991     message.receiver = this.getPhoneNumber();
  2992     message.body = message.fullBody = message.fullBody || null;
  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;
  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);
  3016       Services.obs.notifyObservers(domMessage,
  3017                                    kSilentSmsReceivedObserverTopic,
  3018                                    null);
  3019       return true;
  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 });
  3030       mwi.returnNumber = message.sender;
  3031       mwi.returnMessage = message.fullBody;
  3032       gMessageManager.sendVoicemailMessage("RIL:VoicemailNotification",
  3033                                            this.clientId, mwi);
  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;
  3042     let notifyReceived = function notifyReceived(rv, domMessage) {
  3043       let success = Components.isSuccessCode(rv);
  3045       this.sendAckSms(rv, message);
  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);
  3053         return;
  3056       this.broadcastSmsSystemMessage(kSmsReceivedObserverTopic, domMessage);
  3057       Services.obs.notifyObservers(domMessage, kSmsReceivedObserverTopic, null);
  3058     }.bind(this);
  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;
  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);
  3085       notifyReceived(Cr.NS_OK, domMessage);
  3088     // SMS ACK will be sent in notifyReceived. Return false here.
  3089     return false;
  3090   },
  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;
  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;
  3108     this.workerMessenger.send("ackSMS", { result: result });
  3110   },
  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   },
  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   },
  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   },
  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);
  3157   },
  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);
  3167     // Cache the latest NITZ message whenever receiving it.
  3168     this._lastNitzMessage = message;
  3170     // Set the received NITZ clock if the setting is enabled.
  3171     if (this._clockAutoUpdateEnabled) {
  3172       this.setClockByNitz(message);
  3174     // Set the received NITZ timezone if the setting is enabled.
  3175     if (this._timezoneAutoUpdateEnabled) {
  3176       this.setTimezoneByNitz(message);
  3178   },
  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;
  3189     if (this._lastNitzMessage) {
  3190       if (DEBUG) debug("SNTP: NITZ available, discard SNTP");
  3191       return;
  3193     gTimeService.set(Date.now() + offset);
  3194   },
  3196   handleIccMbdn: function(message) {
  3197     let voicemailInfo = this.voicemailInfo;
  3199     voicemailInfo.number = message.number;
  3200     voicemailInfo.displayName = message.alphaId;
  3202     gMessageManager.sendVoicemailMessage("RIL:VoicemailInfoChanged",
  3203                                          this.clientId, voicemailInfo);
  3204   },
  3206   handleIccInfoChange: function(message) {
  3207     let oldSpn = this.rilContext.iccInfo ? this.rilContext.iccInfo.spn : null;
  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();
  3221       if (!this.isInfoChanged(message, this.rilContext.iccInfo)) {
  3222         return;
  3225       this.updateInfo(message, this.rilContext.iccInfo);
  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);
  3234     // Update lastKnownSimMcc.
  3235     if (message.mcc) {
  3236       try {
  3237         Services.prefs.setCharPref("ril.lastKnownSimMcc",
  3238                                    message.mcc.toString());
  3239       } catch (e) {}
  3242     // Update lastKnownHomeNetwork.
  3243     if (message.mcc && message.mnc) {
  3244       this._lastKnownHomeNetwork = message.mcc + "-" + message.mnc;
  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);
  3259       if (dataRoaming != data.roaming) {
  3260         gMessageManager.sendMobileConnectionMessage("RIL:DataInfoChanged",
  3261                                                     this.clientId, data);
  3264   },
  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   },
  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});
  3281     gMessageManager.sendIccMessage("RIL:StkCommand", this.clientId, message);
  3282   },
  3284   handleExitEmergencyCbMode: function(message) {
  3285     if (DEBUG) this.debug("handleExitEmergencyCbMode: " + JSON.stringify(message));
  3286     gMessageManager.sendRequestResults("RIL:ExitEmergencyCbMode", message);
  3287   },
  3289   // nsIObserver
  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 });
  3306         break;
  3307       case kSysClockChangeObserverTopic:
  3308         let offset = parseInt(data, 10);
  3309         if (this._lastNitzMessage) {
  3310           this._lastNitzMessage.receiveTimeInMS += offset;
  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;
  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;
  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;
  3334         // SNTP won't update unless the SNTP is already expired.
  3335         if (this._sntp.isExpired()) {
  3336           this._sntp.request();
  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;
  3348         if (dataInfo.connected !== connected) {
  3349           dataInfo.connected = connected;
  3350           gMessageManager.sendMobileConnectionMessage("RIL:DataInfoChanged",
  3351                                                       this.clientId, dataInfo);
  3353         break;
  3354       case kScreenStateChangedTopic:
  3355         this.workerMessenger.send("setScreenState", { on: (data === "on") });
  3356         break;
  3358   },
  3360   supportedNetworkTypes: null,
  3362   // Flag to determine whether to update system clock automatically. It
  3363   // corresponds to the "time.clock.automatic-update.enabled" setting.
  3364   _clockAutoUpdateEnabled: null,
  3366   // Flag to determine whether to update system timezone automatically. It
  3367   // corresponds to the "time.clock.automatic-update.enabled" setting.
  3368   _timezoneAutoUpdateEnabled: null,
  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,
  3374   // Object that handles SNTP.
  3375   _sntp: null,
  3377   // Cell Broadcast settings values.
  3378   _cellBroadcastSearchList: null,
  3380   // Operator's mcc-mnc.
  3381   _lastKnownNetwork: null,
  3383   // ICC's mcc-mnc.
  3384   _lastKnownHomeNetwork: null,
  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!");
  3397         // Restore the setting to the current value.
  3398         this.setClockAutoUpdateAvailable(isClockAutoUpdateAvailable);
  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!");
  3412         // Restore the setting to the current value.
  3413         this.setTimezoneAutoUpdateAvailable(isTimezoneAutoUpdateAvailable);
  3417     this.handle(aName, aResult);
  3418   },
  3420   // nsISettingsServiceCallback
  3421   handle: function(aName, aResult) {
  3422     switch(aName) {
  3423       case kSettingsClockAutoUpdateEnabled:
  3424         this._clockAutoUpdateEnabled = aResult;
  3425         if (!this._clockAutoUpdateEnabled) {
  3426           break;
  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();
  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;
  3448           if (buildTime > Date.now()) {
  3449             gTimeService.set(buildTime);
  3452         break;
  3453       case kSettingsTimezoneAutoUpdateEnabled:
  3454         this._timezoneAutoUpdateEnabled = aResult;
  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);
  3462         break;
  3463       case kSettingsCellBroadcastSearchList:
  3464         if (DEBUG) {
  3465           this.debug("'" + kSettingsCellBroadcastSearchList +
  3466             "' is now " + JSON.stringify(aResult));
  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;
  3473   },
  3475   handleError: function(aErrorMessage) {
  3476     if (DEBUG) {
  3477       this.debug("There was an error while reading RIL settings.");
  3479   },
  3481   // nsIRadioInterface
  3483   rilContext: null,
  3485   // Handle phone functions of nsIRILContentHelper
  3487   _sendCfStateChanged: function(message) {
  3488     gMessageManager.sendMobileConnectionMessage("RIL:CfStateChanged",
  3489                                                 this.clientId, message);
  3490   },
  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);
  3499     } catch (e) {}
  3500   },
  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);
  3511       target.sendAsyncMessage("RIL:SendMMI", {
  3512         clientId: this.clientId,
  3513         data: response
  3514       });
  3515       return false;
  3516     }).bind(this));
  3517   },
  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   },
  3532   setCallingLineIdRestriction: function(target, message) {
  3533     if (DEBUG) {
  3534       this.debug("setCallingLineIdRestriction: " + JSON.stringify(message));
  3536     this.workerMessenger.send("setCLIR", message, (function(response) {
  3537       if (response.success) {
  3538         this._updateCallingLineIdRestrictionPref(response.clirMode);
  3540       target.sendAsyncMessage("RIL:SetCallingLineIdRestriction", {
  3541         clientId: this.clientId,
  3542         data: response
  3543       });
  3544       return false;
  3545     }).bind(this));
  3546   },
  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   },
  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   },
  3560   setRadioEnabledResponse: function(target, message, errorMsg) {
  3561     if (errorMsg) {
  3562       message.errorMsg = errorMsg;
  3565     target.sendAsyncMessage("RIL:SetRadioEnabled", {
  3566       clientId: this.clientId,
  3567       data: message
  3568     });
  3569   },
  3571   setRadioEnabled: function(target, message) {
  3572     if (DEBUG) {
  3573       this.debug("setRadioEnabled: " + JSON.stringify(message));
  3576     if (!this.isValidStateForSetRadioEnabled()) {
  3577       this.setRadioEnabledResponse(target, message, "InvalidStateError");
  3578       return;
  3581     if (this.isDummyForSetRadioEnabled(message)) {
  3582       this.setRadioEnabledResponse(target, message);
  3583       return;
  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);
  3593       this.setRadioEnabledResponse(target, response);
  3594       return false;
  3595     }).bind(this);
  3597     this.setRadioEnabledInternal(message, callback);
  3598   },
  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   },
  3607   /**
  3608    * List of tuples of national language identifier pairs.
  3610    * TODO: Support static/runtime settings, see bug 733331.
  3611    */
  3612   enabledGsmTableTuples: [
  3613     [RIL.PDU_NL_IDENTIFIER_DEFAULT, RIL.PDU_NL_IDENTIFIER_DEFAULT],
  3614   ],
  3616   /**
  3617    * Use 16-bit reference number for concatenated outgoint messages.
  3619    * TODO: Support static/runtime settings, see bug 733331.
  3620    */
  3621   segmentRef16Bit: false,
  3623   /**
  3624    * Get valid SMS concatenation reference number.
  3625    */
  3626   _segmentRef: 0,
  3627   get nextSegmentRef() {
  3628     let ref = this._segmentRef++;
  3630     this._segmentRef %= (this.segmentRef16Bit ? 65535 : 255);
  3632     // 0 is not a valid SMS concatenation reference number.
  3633     return ref + 1;
  3634   },
  3636   /**
  3637    * Calculate encoded length using specified locking/single shift table
  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.
  3649    * @return encoded length in septets.
  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;
  3662       let septet = langTable.indexOf(c);
  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;
  3670       if (septet >= 0) {
  3671         length++;
  3672         continue;
  3675       septet = langShiftTable.indexOf(c);
  3676       if (septet < 0) {
  3677         if (!strict7BitEncoding) {
  3678           return -1;
  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;
  3693         continue;
  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;
  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;
  3711     return length;
  3712   },
  3714   /**
  3715    * Calculate user data length of specified message string encoded in GSM 7Bit
  3716    * alphabets.
  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.
  3724    * @return null or an options object with attributes `dcs`,
  3725    *         `userDataHeaderLength`, `encodedFullBodyLength`, `langIndex`,
  3726    *         `langShiftIndex`, `segmentMaxSeq` set.
  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];
  3736       const langTable = RIL.PDU_NL_LOCKING_SHIFT_TABLES[langIndex];
  3737       const langShiftTable = RIL.PDU_NL_SINGLE_SHIFT_TABLES[langShiftIndex];
  3739       let bodySeptets = this._countGsm7BitSeptets(message,
  3740                                                   langTable,
  3741                                                   langShiftTable,
  3742                                                   strict7BitEncoding);
  3743       if (bodySeptets < 0) {
  3744         continue;
  3747       let headerLen = 0;
  3748       if (langIndex != RIL.PDU_NL_IDENTIFIER_DEFAULT) {
  3749         headerLen += 3; // IEI + len + langIndex
  3751       if (langShiftIndex != RIL.PDU_NL_IDENTIFIER_DEFAULT) {
  3752         headerLen += 3; // IEI + len + langShiftIndex
  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;
  3764       let segments = Math.ceil(bodySeptets / segmentSeptets);
  3765       let userDataSeptets = bodySeptets + headerSeptets * segments;
  3766       if (userDataSeptets >= minUserDataSeptets) {
  3767         continue;
  3770       minUserDataSeptets = userDataSeptets;
  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       };
  3783     return options;
  3784   },
  3786   /**
  3787    * Calculate user data length of specified message string encoded in UCS2.
  3789    * @param message
  3790    *        a message string to be encoded.
  3792    * @return an options object with attributes `dcs`, `userDataHeaderLength`,
  3793    *         `encodedFullBodyLength`, `segmentMaxSeq` set.
  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;
  3808     let segments = Math.ceil(bodyChars / segmentChars);
  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   },
  3819   /**
  3820    * Calculate user data length and its encoding.
  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.
  3828    * @return an options object with some or all of following attributes set:
  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);
  3854     if (DEBUG) this.debug("_calculateUserDataLength: " + JSON.stringify(options));
  3855     return options;
  3856   },
  3858   /**
  3859    * Fragment GSM 7-Bit encodable string for transmission.
  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.
  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;
  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;
  3893       let septet = langTable.indexOf(c);
  3894       if (septet == RIL.PDU_NL_EXTENDED_ESCAPE) {
  3895         continue;
  3898       if (septet >= 0) {
  3899         inc = 1;
  3900       } else {
  3901         septet = langShiftTable.indexOf(c);
  3902         if (septet == RIL.PDU_NL_RESERVED_CONTROL) {
  3903           continue;
  3906         inc = 2;
  3907         if (septet < 0) {
  3908           if (!strict7BitEncoding) {
  3909             throw new Error("Given text cannot be encoded with GSM 7-bit Alphabet!");
  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;
  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;
  3934     if (len) {
  3935       ret.push({
  3936         body: body,
  3937         encodedBodyLength: len,
  3938       });
  3941     return ret;
  3942   },
  3944   /**
  3945    * Fragment UCS2 encodable string for transmission.
  3947    * @param text
  3948    *        text string to be fragmented.
  3949    * @param segmentChars
  3950    *        Number of available characters per segment.
  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;
  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       });
  3973     return ret;
  3974   },
  3976   /**
  3977    * Fragment string for transmission.
  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.
  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.
  3992    * @return Populated options object.
  3993    */
  3994   _fragmentText: function(text, options, strict7BitEncoding) {
  3995     if (!options) {
  3996       options = this._calculateUserDataLength(text, strict7BitEncoding);
  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);
  4011     // Re-sync options.segmentMaxSeq with actual length of returning array.
  4012     options.segmentMaxSeq = options.segments.length;
  4014     return options;
  4015   },
  4017   getSegmentInfoForText: function(text, request) {
  4018     let strict7BitEncoding;
  4019     try {
  4020       strict7BitEncoding = Services.prefs.getBoolPref("dom.sms.strict7BitEncoding");
  4021     } catch (e) {
  4022       strict7BitEncoding = false;
  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;
  4034     } else {
  4035       charsInLastSegment = 0;
  4038     let result = gMobileMessageService
  4039                  .createSmsSegmentInfo(options.segmentMaxSeq,
  4040                                        options.segmentChars,
  4041                                        options.segmentChars - charsInLastSegment);
  4042     request.notifySegmentInfoForTextGot(result);
  4043   },
  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);
  4054     }).bind(this));
  4055   },
  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;
  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;
  4074     options.requestStatusReport = requestStatusReport && !silent;
  4075     if (options.segmentMaxSeq > 1) {
  4076       options.segmentRef16Bit = this.segmentRef16Bit;
  4077       options.segmentRef = this.nextSegmentRef;
  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;
  4089       if (!silent) {
  4090         Services.obs.notifyObservers(domMessage, kSmsSendingObserverTopic, null);
  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;
  4108       if (errorCode) {
  4109         if (silent) {
  4110           request.notifySendMessageFailed(errorCode);
  4111           return;
  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;
  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       };
  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;
  4151           if (context.silent) {
  4152             context.request.notifySendMessageFailed(error);
  4153             return false;
  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.
  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)
  4181             let topic = (response.deliveryStatus ==
  4182                          RIL.GECKO_SMS_DELIVERY_STATUS_SUCCESS)
  4183                         ? kSmsDeliverySuccessObserverTopic
  4184                         : kSmsDeliveryErrorObserverTopic;
  4186             // Broadcasting a "sms-delivery-success" system message to open apps.
  4187             if (topic == kSmsDeliverySuccessObserverTopic) {
  4188               this.broadcastSmsSystemMessage(topic, domMessage);
  4191             // Notifying observers the delivery status is updated.
  4192             Services.obs.notifyObservers(domMessage, topic, null);
  4193           }).bind(this));
  4195           // Send transaction has ended completely.
  4196           return false;
  4197         } // End of message delivery.
  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;
  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)
  4232           if (context.requestStatusReport) {
  4233             context.sms = domMessage;
  4236           this.broadcastSmsSystemMessage(kSmsSentObserverTopic, domMessage);
  4237           context.request.notifyMessageSent(domMessage);
  4238           Services.obs.notifyObservers(domMessage, kSmsSentObserverTopic, null);
  4239         }).bind(this));
  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.
  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     };
  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;
  4277     let id = gMobileMessageDatabaseService.saveSendingMessage(
  4278       sendingMessage, notifyResult);
  4279   },
  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   },
  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   },
  4295   // TODO: Bug 904514 - [meta] NetworkManager enhancement
  4296   getDataCallStateByType: function(apntype) {
  4297     let connHandler = gDataConnectionManager.getConnectionHandler(this.clientId);
  4298     return connHandler.getDataCallStateByType(apntype);
  4299   },
  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   },
  4310   deactivateDataCall: function(cid, reason) {
  4311     this.workerMessenger.send("deactivateDataCall", { cid: cid,
  4312                                                       reason: reason });
  4313   },
  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);
  4324 };
  4326 function RILNetworkInterface(dataConnectionHandler, apnSetting) {
  4327   this.dataConnectionHandler = dataConnectionHandler;
  4328   this.apnSetting = apnSetting;
  4329   this.connectedTypes = [];
  4331   this.ips = [];
  4332   this.prefixLengths = [];
  4333   this.dnses = [];
  4334   this.gateways = [];
  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]),
  4346   // nsINetworkInterface
  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,
  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,
  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,
  4373   // Event timer for connection retries
  4374   timer: null,
  4376   /**
  4377    * nsINetworkInterface Implementation
  4378    */
  4380   state: Ci.nsINetworkInterface.NETWORK_STATE_UNKNOWN,
  4382   get type() {
  4383     if (this.connectedTypes.indexOf("default") != -1) {
  4384       return this.NETWORK_TYPE_MOBILE;
  4386     if (this.connectedTypes.indexOf("mms") != -1) {
  4387       return this.NETWORK_TYPE_MOBILE_MMS;
  4389     if (this.connectedTypes.indexOf("supl") != -1) {
  4390       return this.NETWORK_TYPE_MOBILE_SUPL;
  4392     if (this.connectedTypes.indexOf("ims") != -1) {
  4393       return this.NETWORK_TYPE_MOBILE_IMS;
  4395     if (this.connectedTypes.indexOf("dun") != -1) {
  4396       return this.NETWORK_TYPE_MOBILE_DUN;
  4399     return this.NETWORK_TYPE_MOBILE_OTHERS;
  4400   },
  4402   name: null,
  4404   ips: null,
  4406   prefixLengths: null,
  4408   gateways: null,
  4410   dnses: null,
  4412   get httpProxyHost() {
  4413     return this.apnSetting.proxy || "";
  4414   },
  4416   get httpProxyPort() {
  4417     return this.apnSetting.port || "";
  4418   },
  4420   /**
  4421    * nsIRilNetworkInterface Implementation
  4422    */
  4424   get serviceId() {
  4425     return this.dataConnectionHandler.clientId;
  4426   },
  4428   get iccId() {
  4429     let iccInfo = this.dataConnectionHandler.radioInterface.rilContext.iccInfo;
  4430     return iccInfo && iccInfo.iccid;
  4431   },
  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;
  4439     let mmsc = this.apnSetting.mmsc;
  4440     if (!mmsc) {
  4441       try {
  4442         mmsc = Services.prefs.getCharPref("ril.mms.mmsc");
  4443       } catch (e) {
  4444         mmsc = "";
  4448     return mmsc;
  4449   },
  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;
  4457     let proxy = this.apnSetting.mmsproxy;
  4458     if (!proxy) {
  4459       try {
  4460         proxy = Services.prefs.getCharPref("ril.mms.mmsproxy");
  4461       } catch (e) {
  4462         proxy = "";
  4466     return proxy;
  4467   },
  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;
  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;
  4484     return port;
  4485   },
  4487   getAddresses: function (ips, prefixLengths) {
  4488     ips.value = this.ips.slice();
  4489     prefixLengths.value = this.prefixLengths.slice();
  4491     return this.ips.length;
  4492   },
  4494   getGateways: function (count) {
  4495     if (count) {
  4496       count.value = this.gateways.length;
  4498     return this.gateways.slice();
  4499   },
  4501   getDnses: function (count) {
  4502     if (count) {
  4503       count.value = this.dnses.length;
  4505     return this.dnses.slice();
  4506   },
  4508   debug: function(s) {
  4509     dump("-*- RILNetworkInterface[" + this.dataConnectionHandler.clientId + ":" +
  4510          this.type + "]: " + s + "\n");
  4511   },
  4513   dataCallError: function(message) {
  4514     if (message.apn != this.apnSetting.apn) {
  4515       return;
  4517     if (DEBUG) this.debug("Data call error on APN: " + message.apn);
  4518     this.reset();
  4519   },
  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;
  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;
  4533     if (DEBUG) {
  4534       this.debug("Data call ID: " + datacall.cid + ", interface name: " +
  4535                  datacall.ifname + ", APN name: " + datacall.apn);
  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);
  4547       this.gateways = datacall.gateways.slice();
  4548       this.dnses = datacall.dnses.slice();
  4549       if (!this.registeredAsNetworkInterface) {
  4550         gNetworkManager.registerNetworkInterface(this);
  4551         this.registeredAsNetworkInterface = true;
  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;
  4561     if (this.state == datacall.state) {
  4562       if (datacall.state != RIL.GECKO_NETWORK_STATE_CONNECTED) {
  4563         return;
  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);
  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();
  4589       if (changed) {
  4590         if (DEBUG) this.debug("Notify for data call minor changes.");
  4591         Services.obs.notifyObservers(this,
  4592                                      kNetworkInterfaceStateChangedTopic,
  4593                                      null);
  4595       return;
  4598     this.state = datacall.state;
  4600     Services.obs.notifyObservers(this,
  4601                                  kNetworkInterfaceStateChangedTopic,
  4602                                  null);
  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 = [];
  4612       this.ips = [];
  4613       this.prefixLengths = [];
  4614       this.dnses = [];
  4615       this.gateways = [];
  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();
  4626   },
  4628   // Helpers
  4630   cid: null,
  4631   registeredAsDataCallCallback: false,
  4632   registeredAsNetworkInterface: false,
  4633   connecting: false,
  4634   apnSetting: null,
  4636   // APN failed connections. Retry counter
  4637   apnRetryCounter: 0,
  4639   connectedTypes: null,
  4641   inConnectedTypes: function(type) {
  4642     return this.connectedTypes.indexOf(type) != -1;
  4643   },
  4645   get connected() {
  4646     return this.state == RIL.GECKO_NETWORK_STATE_CONNECTED;
  4647   },
  4649   connect: function(apntype) {
  4650     if (apntype && !this.inConnectedTypes(apntype)) {
  4651       this.connectedTypes.push(apntype);
  4654     if (this.connecting || this.connected) {
  4655       return;
  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;
  4664     if (!this.registeredAsDataCallCallback) {
  4665       this.dataConnectionHandler.registerDataCallCallback(this);
  4666       this.registeredAsDataCallCallback = true;
  4669     if (!this.apnSetting.apn) {
  4670       if (DEBUG) this.debug("APN name is empty, nothing to do.");
  4671       return;
  4674     if (DEBUG) {
  4675       this.debug("Going to set up data connection with APN " +
  4676                  this.apnSetting.apn);
  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);
  4688       authType = RIL.RIL_DATACALL_AUTH_TO_GECKO.indexOf(RIL.GECKO_DATACALL_AUTH_DEFAULT);
  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 + "'");
  4700         pdpType = RIL.GECKO_DATACALL_PDP_TYPE_DEFAULT;
  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   },
  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;
  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);
  4734     if (this.timer == null) {
  4735       // Event timer for connection retries
  4736       this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
  4738     this.timer.initWithCallback(this, apnRetryTimer * 1000,
  4739                                 Ci.nsITimer.TYPE_ONE_SHOT);
  4740   },
  4742   disconnect: function(apntype) {
  4743     let index = this.connectedTypes.indexOf(apntype);
  4744     if (index != -1) {
  4745       this.connectedTypes.splice(index, 1);
  4748     if (this.connectedTypes.length) {
  4749       return;
  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;
  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   },
  4763   // Entry method for timer events. Used to reconnect to a failed APN
  4764   notify: function(timer) {
  4765     this.connect();
  4766   },
  4768   shutdown: function() {
  4769     this.timer = null;
  4772 };
  4774 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([RadioInterfaceLayer]);

mercurial