Bug 793186 - MMI Codes: implement sendMMI() and cancelMMI() - Part 2: RIL functionality; r=marshall_law
authorFernando Jiménez <ferjmoreno@gmail.com>
Fri, 05 Oct 2012 16:09:00 +0200
changeset 115685 5dc2fdb593a70b43cac46042bcd5c4c1f9b1c291
parent 115684 a4e3e1e70afc5dcd080811b7805aae06b3091c7f
child 115686 ad8fa8df0ab76b1716ac7efee1c4c61fb8cf5c12
push id1708
push userakeybl@mozilla.com
push dateMon, 19 Nov 2012 21:10:21 +0000
treeherdermozilla-beta@27b14fe50103 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmarshall_law
bugs793186
milestone18.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 793186 - MMI Codes: implement sendMMI() and cancelMMI() - Part 2: RIL functionality; r=marshall_law
dom/system/gonk/RadioInterfaceLayer.js
dom/system/gonk/ril_consts.js
dom/system/gonk/ril_worker.js
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -507,19 +507,21 @@ RadioInterfaceLayer.prototype = {
       case "iccmbdn":
         ppmm.broadcastAsyncMessage("RIL:VoicemailNumberChanged", message);
         break;
       case "USSDReceived":
         debug("USSDReceived " + JSON.stringify(message));
         this.handleUSSDReceived(message);
         break;
       case "sendMMI":
+      case "sendUSSD":
         this.handleSendMMI(message);
         break;
       case "cancelMMI":
+      case "cancelUSSD":
         this.handleCancelMMI(message);
         break;
       case "stkcommand":
         this.handleStkProactiveCommand(message);
         break;
       case "stksessionend":
         ppmm.broadcastAsyncMessage("RIL:StkSessionEnd", null);
         break;
@@ -1518,23 +1520,26 @@ RadioInterfaceLayer.prototype = {
 
   getAvailableNetworks: function getAvailableNetworks(requestId) {
     this.worker.postMessage({rilMessageType: "getAvailableNetworks",
                              requestId: requestId});
   },
 
   sendMMI: function sendMMI(message) {
     debug("SendMMI " + JSON.stringify(message));
-    // TODO: process MMI (in the next patch :) )
-    message.rilMessageType = "sendUSSD";
+    message.rilMessageType = "sendMMI";
     this.worker.postMessage(message);
   },
 
   cancelMMI: function cancelMMI(message) {
-    debug("Cancel pending MMI");
+    // Some MMI codes trigger radio operations, but unfortunately the RIL only
+    // supports cancelling USSD requests so far. Despite that, in order to keep
+    // the API uniformity, we are wrapping the cancelUSSD function within the
+    // cancelMMI funcion.
+    debug("Cancel pending USSD");
     message.rilMessageType = "cancelUSSD";
     this.worker.postMessage(message);
   },
 
   selectNetworkAuto: function selectNetworkAuto(requestId) {
     this.worker.postMessage({rilMessageType: "selectNetworkAuto",
                              requestId: requestId});
   },
--- a/dom/system/gonk/ril_consts.js
+++ b/dom/system/gonk/ril_consts.js
@@ -1881,10 +1881,52 @@ const GECKO_RADIO_TECH = [
   "evdob",
   "ehrpd",
   "lte",
   "hspa+",
 ];
 
 const GECKO_VOICEMAIL_MESSAGE_COUNT_UNKNOWN = -1;
 
+// MMI procedure as defined in TS.22.030 6.5.2
+const MMI_PROCEDURE_ACTIVATION = "*";
+const MMI_PROCEDURE_DEACTIVATION = "#";
+const MMI_PROCEDURE_INTERROGATION = "*#";
+const MMI_PROCEDURE_REGISTRATION = "**";
+const MMI_PROCEDURE_ERASURE = "##";
+
+// MMI call forwarding service codes as defined in TS.22.030 Annex B
+const MMI_SC_CFU = "21";
+const MMI_SC_CF_BUSY = "67";
+const MMI_SC_CF_NO_REPLY = "61";
+const MMI_SC_CF_NOT_REACHABLE = "62";
+const MMI_SC_CF_ALL = "002";
+const MMI_SC_CF_ALL_CONDITIONAL = "004";
+
+// MMI service codes for PIN/PIN2/PUK/PUK2 management as defined in TS.22.030
+// sec 6.6
+const MMI_SC_PIN = "04";
+const MMI_SC_PIN2 = "042";
+const MMI_SC_PUK = "05";
+const MMI_SC_PUK2 = "052";
+
+// MMI service code for IMEI presentation as defined in TS.22.030 sec 6.7
+const MMI_SC_IMEI = "06";
+
+// MMI called line presentation service codes
+const MMI_SC_CLIP = "30";
+const MMI_SC_CLIR = "31";
+
+// MMI call waiting service code
+const MMI_SC_CALL_WAITING = "43";
+
+// MMI call barring service codes
+const MMI_SC_BAOC = "33";
+const MMI_SC_BAOIC = "331";
+const MMI_SC_BAOICxH = "332";
+const MMI_SC_BAIC = "35";
+const MMI_SC_BAICr = "351";
+const MMI_SC_BA_ALL = "330";
+const MMI_SC_BA_MO = "333";
+const MMI_SC_BA_MT = "353";
+
 // Allow this file to be imported via Components.utils.import().
 const EXPORTED_SYMBOLS = Object.keys(this);
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -58,16 +58,30 @@ const TLV_ITEM_ID_SIZE = 3;
 const TLV_HELP_REQUESTED_SIZE = 2;
 const TLV_EVENT_LIST_SIZE = 3;
 const TLV_LOCATION_STATUS_SIZE = 3;
 const TLV_LOCATION_INFO_GSM_SIZE = 9;
 const TLV_LOCATION_INFO_UMTS_SIZE = 11;
 
 const DEFAULT_EMERGENCY_NUMBERS = ["112", "911"];
 
+// MMI match groups
+const MMI_MATCH_GROUP_FULL_MMI = 1;
+const MMI_MATCH_GROUP_MMI_PROCEDURE = 2;
+const MMI_MATCH_GROUP_SERVICE_CODE = 3;
+const MMI_MATCH_GROUP_SIA = 5;
+const MMI_MATCH_GROUP_SIB = 7;
+const MMI_MATCH_GROUP_SIC = 9;
+const MMI_MATCH_GROUP_PWD_CONFIRM = 11;
+const MMI_MATCH_GROUP_DIALING_NUMBER = 12;
+
+const MMI_MAX_LENGTH_SHORT_CODE = 2;
+
+const MMI_END_OF_USSD = "#";
+
 let RILQUIRKS_CALLSTATE_EXTRA_UINT32 = libcutils.property_get("ro.moz.ril.callstate_extra_int");
 // This may change at runtime since in RIL v6 and later, we get the version
 // number via the UNSOLICITED_RIL_CONNECTED parcel.
 let RILQUIRKS_V5_LEGACY = libcutils.property_get("ro.moz.ril.v5_legacy");
 let RILQUIRKS_REQUEST_USE_DIAL_EMERGENCY_CALL = libcutils.property_get("ro.moz.ril.dial_emergency_call");
 let RILQUIRKS_MODEM_DEFAULTS_TO_EMERGENCY_MODE = libcutils.property_get("ro.moz.ril.emergency_by_default");
 let RILQUIRKS_SIM_APP_STATE_EXTRA_FIELDS = libcutils.property_get("ro.moz.ril.simstate_extra_field");
 
@@ -743,16 +757,31 @@ let RIL = {
      * Pending messages to be send in batch from requestNetworkInfo()
      */
     this._pendingNetworkInfo = {rilMessageType: "networkinfochanged"};
 
     /**
      * Mute or unmute the radio.
      */
     this._muted = true;
+
+    /**
+     * USSD session flag.
+     * Only one USSD session may exist at a time, and the session is assumed
+     * to exist until:
+     *    a) There's a call to cancelUSSD()
+     *    b) The implementation sends a UNSOLICITED_ON_USSD with a type code
+     *       of "0" (USSD-Notify/no further action) or "2" (session terminated)
+     */
+    this._ussdSession = null;
+
+   /**
+    * Regular expresion to parse MMI codes.
+    */
+    this._mmiRegExp = null;
   },
   
   get muted() {
     return this._muted;
   },
   set muted(val) {
     val = Boolean(val);
     if (this._muted != val) {
@@ -2195,16 +2224,193 @@ let RIL = {
   /**
    * Get failure casue code for the most recently failed PDP context.
    */
   getFailCauseCode: function getFailCauseCode(options) {
     Buf.simpleRequest(REQUEST_LAST_CALL_FAIL_CAUSE, options);
   },
 
   /**
+   * Helper to parse and process a MMI string.
+   */
+  _parseMMI: function _parseMMI(mmiString) {
+    if (!mmiString || !mmiString.length) {
+      return null;
+    }
+
+    // Regexp to parse and process the MMI code.
+    if (this._mmiRegExp == null) {
+      // The first group of the regexp takes the whole MMI string.
+      // The second group takes the MMI procedure that can be:
+      //    - Activation (*SC*SI#).
+      //    - Deactivation (#SC*SI#).
+      //    - Interrogation (*#SC*SI#).
+      //    - Registration (**SC*SI#).
+      //    - Erasure (##SC*SI#).
+      //  where SC = Service Code (2 or 3 digits) and SI = Supplementary Info
+      //  (variable length).
+      let pattern = "((\\*[*#]?|##?)";
+
+      // Third group of the regexp looks for the MMI Service code, which is a
+      // 2 or 3 digits that uniquely specifies the Supplementary Service
+      // associated with the MMI code.
+      pattern += "(\\d{2,3})";
+
+      // Groups from 4 to 9 looks for the MMI Supplementary Information SIA,
+      // SIB and SIC. SIA may comprise e.g. a PIN code or Directory Number,
+      // SIB may be used to specify the tele or bearer service and SIC to
+      // specify the value of the "No Reply Condition Timer". Where a particular
+      // service request does not require any SI, "*SI" is not entered. The use
+      // of SIA, SIB and SIC is optional and shall be entered in any of the
+      // following formats:
+      //    - *SIA*SIB*SIC#
+      //    - *SIA*SIB#
+      //    - *SIA**SIC#
+      //    - *SIA#
+      //    - **SIB*SIC#
+      //    - ***SISC#
+      pattern += "(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)";
+
+      // The eleventh group takes the password for the case of a password
+      // registration procedure.
+      pattern += "(\\*([^*#]*))?)?)?)?#)";
+
+      // The last group takes the dial string after the #.
+      pattern += "([^#]*)";
+
+      this._mmiRegExp = new RegExp(pattern);
+    }
+    let matches = this._mmiRegExp.exec(mmiString);
+
+    // If the regex does not apply over the MMI string, it can still be an MMI
+    // code. If the MMI String is a #-string (entry of any characters defined
+    // in the TS.23.038 Default Alphabet followed by #SEND) it shall be treated
+    // as a USSD code.
+    if (matches == null) {
+      if (mmiString.charAt(mmiString.length - 1) == MMI_END_OF_USSD) {
+        return {
+          fullMMI: mmiString
+        };
+      }
+      return null;
+    }
+
+    // After successfully executing the regular expresion over the MMI string,
+    // the following match groups should contain:
+    // 1 = full MMI string that might be used as a USSD request.
+    // 2 = MMI procedure.
+    // 3 = Service code.
+    // 5 = SIA.
+    // 7 = SIB.
+    // 9 = SIC.
+    // 11 = Password registration.
+    // 12 = Dialing number.
+    return {
+      fullMMI: matches[MMI_MATCH_GROUP_FULL_MMI],
+      procedure: matches[MMI_MATCH_GROUP_MMI_PROCEDURE],
+      serviceCode: matches[MMI_MATCH_GROUP_SERVICE_CODE],
+      sia: matches[MMI_MATCH_GROUP_SIA],
+      sib: matches[MMI_MATCH_GROUP_SIB],
+      sic: matches[MMI_MATCH_GROUP_SIC],
+      pwd: matches[MMI_MATCH_GROUP_PWD_CONFIRM],
+      dialNumber: matches[MMI_MATCH_GROUP_DIALING_NUMBER]
+    };
+  },
+
+  sendMMI: function sendMMI(options) {
+    if (DEBUG) {
+      debug("SendMMI " + JSON.stringify(options));
+    }
+    let mmiString = options.mmi;
+    let mmi = this._parseMMI(mmiString);
+
+    let _sendMMIError = (function _sendMMIError(errorMsg) {
+      options.rilMessageType = "sendMMI";
+      options.errorMsg = errorMsg;
+      this.sendDOMMessage(options);
+    }).bind(this);
+
+    if (mmi == null) {
+      if (this._ussdSession) {
+        options.ussd = mmiString;
+        this.sendUSSD(options);
+        return;
+      }
+      _sendMMIError("NO_VALID_MMI_STRING");
+      return;
+    }
+
+    if (DEBUG) {
+      debug("MMI " + JSON.stringify(mmi));
+    }
+
+    // We check if the MMI service code is supported and in that case we
+    // trigger the appropriate RIL request if possible.
+    let sc = mmi.serviceCode;
+
+    switch (sc) {
+      // Call forwarding
+      case MMI_SC_CFU:
+      case MMI_SC_CF_BUSY:
+      case MMI_SC_CF_NO_REPLY:
+      case MMI_SC_CF_NOT_REACHABLE:
+      case MMI_SC_CF_ALL:
+      case MMI_SC_CF_ALL_CONDITIONAL:
+        // TODO: Bug 793192 - MMI Codes: support call forwarding.
+        _sendMMIError("CALL_FORWARDING_NOT_SUPPORTED_VIA_MMI");
+        return;
+
+      // PIN/PIN2/PUK/PUK2
+      case MMI_SC_PIN:
+      case MMI_SC_PIN2:
+      case MMI_SC_PUK:
+      case MMI_SC_PUK2:
+        // TODO: Bug 793187 - MMI Codes: Support PIN/PIN2/PUK handling.
+        _sendMMIError("SIM_FUNCTION_NOT_SUPPORTED_VIA_MMI");
+        return;
+
+      // IMEI
+      case MMI_SC_IMEI:
+        // TODO: Bug 793189 - MMI Codes: get IMEI.
+        _sendMMIError("GET_IMEI_NOT_SUPPORTED_VIA_MMI");
+        return;
+
+      // Call barring
+      case MMI_SC_BAOC:
+      case MMI_SC_BAOIC:
+      case MMI_SC_BAOICxH:
+      case MMI_SC_BAIC:
+      case MMI_SC_BAICr:
+      case MMI_SC_BA_ALL:
+      case MMI_SC_BA_MO:
+      case MMI_SC_BA_MT:
+        _sendMMIError("CALL_BARRING_NOT_SUPPORTED_VIA_MMI");
+        return;
+
+      // Call waiting
+      case MMI_SC_CALL_WAITING:
+        _sendMMIError("CALL_WAITING_NOT_SUPPORTED_VIA_MMI");
+        return;
+    }
+
+    // If the MMI code is not a known code and is a recognized USSD request or
+    // a #-string, it shall still be sent as a USSD request.
+    if (mmi.fullMMI &&
+        (mmiString.charAt(mmiString.length - 1) == MMI_END_OF_USSD)) {
+      options.ussd = mmi.fullMMI;
+      this.sendUSSD(options);
+      return;
+    }
+
+    // At this point, the MMI string is considered as not valid MMI code and
+    // not valid USSD code.
+    _sendMMIError("NOT_VALID_MMI_STRING");
+  },
+
+  /**
    * Send USSD.
    *
    * @param ussd
    *        String containing the USSD code.
    *
    */
    sendUSSD: function sendUSSD(options) {
      Buf.newParcel(REQUEST_SEND_USSD, options);
@@ -4188,27 +4394,28 @@ RIL[REQUEST_SIM_IO] = function REQUEST_S
       options.onerror.call(this, options);
     }
     return;
   }
   this._processICCIO(options);
 };
 RIL[REQUEST_SEND_USSD] = function REQUEST_SEND_USSD(length, options) {
   if (DEBUG) {
-    debug("REQUEST_SEND_USSD " + JSON.stringify(options)); 
+    debug("REQUEST_SEND_USSD " + JSON.stringify(options));
   }
-  options.success = options.rilRequestError == 0 ? true : false;
+  options.success = this._ussdSession = options.rilRequestError == 0;
   options.errorMsg = RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError];
   this.sendDOMMessage(options);
 };
 RIL[REQUEST_CANCEL_USSD] = function REQUEST_CANCEL_USSD(length, options) {
   if (DEBUG) {
     debug("REQUEST_CANCEL_USSD" + JSON.stringify(options));
   }
-  options.success = options.rilRequestError == 0 ? true : false;
+  options.success = options.rilRequestError == 0;
+  this._ussdSession = !options.success;
   options.errorMsg = RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError];
   this.sendDOMMessage(options);
 };
 RIL[REQUEST_GET_CLIR] = null;
 RIL[REQUEST_SET_CLIR] = null;
 RIL[REQUEST_QUERY_CALL_FORWARD_STATUS] = null;
 RIL[REQUEST_SET_CALL_FORWARD] = null;
 RIL[REQUEST_QUERY_CALL_WAITING] = null;
@@ -4620,16 +4827,19 @@ RIL[UNSOLICITED_RESPONSE_NEW_SMS_ON_SIM]
   let info = Buf.readUint32List();
   //TODO
 };
 RIL[UNSOLICITED_ON_USSD] = function UNSOLICITED_ON_USSD() {
   let [typeCode, message] = Buf.readStringList();
   if (DEBUG) {
     debug("On USSD. Type Code: " + typeCode + " Message: " + message);
   }
+
+  this._ussdSession = (typeCode != "0" || typeCode != "2");
+
   // Empty message should not be progressed to the DOM.
   if (!message || message == "") {
     return;
   }
   this.sendDOMMessage({rilMessageType: "USSDReceived",
                        message: message});
 };
 RIL[UNSOLICITED_NITZ_TIME_RECEIVED] = function UNSOLICITED_NITZ_TIME_RECEIVED() {