Bug 804671 - Part 2: Support PROVIDE_LOCAL_INFO in RIL. r=allstars.chh
authorEdgar Chen <echen@mozilla.com>
Wed, 21 Nov 2012 11:23:39 +0800
changeset 124561 0bd9ff852c5e33d6f64bc70e605c7d2f841da67f
parent 124560 1ffb3bd63f71ea20e4101cbbd7d891d41976a043
child 124562 7169b3dfe53a7ebe96160b2f5963ee122bee34f9
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersallstars
bugs804671
milestone20.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 804671 - Part 2: Support PROVIDE_LOCAL_INFO in RIL. r=allstars.chh
dom/system/gonk/ril_consts.js
dom/system/gonk/ril_worker.js
--- a/dom/system/gonk/ril_consts.js
+++ b/dom/system/gonk/ril_consts.js
@@ -550,26 +550,37 @@ this.COMPREHENSIONTLV_TAG_SUBADDRESS = 0
 this.COMPREHENSIONTLV_TAG_SMS_TPDU = 0x0b;
 this.COMPREHENSIONTLV_TAG_TEXT_STRING = 0x0d;
 this.COMPREHENSIONTLV_TAG_TONE = 0x0e;
 this.COMPREHENSIONTLV_TAG_ITEM = 0x0f;
 this.COMPREHENSIONTLV_TAG_ITEM_ID = 0x10;
 this.COMPREHENSIONTLV_TAG_RESPONSE_LENGTH = 0x11;
 this.COMPREHENSIONTLV_TAG_FILE_LIST = 0x12;
 this.COMPREHENSIONTLV_TAG_LOCATION_INFO = 0x13;
+this.COMPREHENSIONTLV_TAG_IMEI = 0x14;
 this.COMPREHENSIONTLV_TAG_HELP_REQUEST = 0x15;
+this.COMPREHENSIONTLV_TAG_NMR = 0x16;
 this.COMPREHENSIONTLV_TAG_DEFAULT_TEXT = 0x17;
 this.COMPREHENSIONTLV_TAG_CAUSE = 0x1a;
 this.COMPREHENSIONTLV_TAG_LOCATION_STATUS = 0x1b;
 this.COMPREHENSIONTLV_TAG_TRANSACTION_ID = 0x1c;
 this.COMPREHENSIONTLV_TAG_EVENT_LIST = 0x19;
 this.COMPREHENSIONTLV_TAG_ICON_ID = 0x1e;
 this.COMPREHENSIONTLV_TAG_ICON_ID_LIST = 0x1f;
+this.COMPREHENSIONTLV_TAG_DATE_TIME_ZONE = 0x26;
 this.COMPREHENSIONTLV_TAG_IMMEDIATE_RESPONSE = 0x2b;
+this.COMPREHENSIONTLV_TAG_LANGUAGE = 0x2d;
 this.COMPREHENSIONTLV_TAG_URL = 0x31;
+this.COMPREHENSIONTLV_TAG_ACCESS_TECH = 0x3f;
+this.COMPREHENSIONTLV_TAG_SERVICE_RECORD = 0x41;
+this.COMPREHENSIONTLV_TAG_IMEISV = 0x62;
+this.COMPREHENSIONTLV_TAG_BATTERY_STATE = 0x63;
+this.COMPREHENSIONTLV_TAG_NETWORK_SEARCH_MODE = 0x65;
+this.COMPREHENSIONTLV_TAG_MEID = 0x6d;
+this.COMPREHENSIONTLV_TAG_BROADCAST_NETWORK_INFO = 0x7a;
 
 // Tags for Service Provider Display Information TLV
 this.SPDI_TAG_SPDI = 0xa3;
 this.SPDI_TAG_PLMN_LIST = 0x80;
 
 // Device identifiers, see TS 11.14, clause 12.7
 this.STK_DEVICE_ID_KEYPAD = 0x01;
 this.STK_DEVICE_ID_DISPLAY = 0x02;
@@ -591,16 +602,17 @@ this.STK_CMD_SEND_SMS = 0x13;
 this.STK_CMD_SEND_DTMF = 0x14;
 this.STK_CMD_LAUNCH_BROWSER = 0x15;
 this.STK_CMD_PLAY_TONE = 0x20;
 this.STK_CMD_DISPLAY_TEXT = 0x21;
 this.STK_CMD_GET_INKEY = 0x22;
 this.STK_CMD_GET_INPUT = 0x23;
 this.STK_CMD_SELECT_ITEM = 0x24;
 this.STK_CMD_SET_UP_MENU = 0x25;
+this.STK_CMD_PROVIDE_LOCAL_INFO = 0x26;
 this.STK_CMD_SET_UP_IDLE_MODE_TEXT = 0x28;
 
 // STK Result code.
 // TS 11.14, clause 12.12
 
 // Results '0X' and '1X' indicate that the command has been performed.
 
 // Command performed successfully.
@@ -776,16 +788,33 @@ this.STK_TONE_TYPE_GENERAL_BEEP         
 this.STK_TONE_TYPE_POSITIVE_ACK_TONE        = 0x11;
 this.STK_TONE_TYPE_NEGATIVE_ACK_TONE        = 0x12;
 
 // Time unit.
 this.STK_TIME_UNIT_MINUTE       = 0x00;
 this.STK_TIME_UNIT_SECOND       = 0x01;
 this.STK_TIME_UNIT_TENTH_SECOND = 0x02;
 
+// Local Information type.
+this.STK_LOCAL_INFO_NNA = 0x00;
+this.STK_LOCAL_INFO_IMEI = 0x01;
+this.STK_LOCAL_INFO_NMR_FOR_NNA = 0x02;
+this.STK_LOCAL_INFO_DATE_TIME_ZONE = 0x03;
+this.STK_LOCAL_INFO_LANGUAGE = 0x04;
+this.STK_LOCAL_INFO_ACCESS_TECH = 0x06;
+this.STK_LOCAL_INFO_ESN = 0x07;
+this.STK_LOCAL_INFO_IMEISV = 0x08;
+this.STK_LOCAL_INFO_SEARCH_MODE = 0x09;
+this.STK_LOCAL_INFO_CHARGE_STATE = 0x0A;
+this.STK_LOCAL_INFO_MEID = 0x0B;
+this.STK_LOCAL_INFO_BROADCAST_NETWORK_INFO = 0x0D;
+this.STK_LOCAL_INFO_MULTIPLE_ACCESS_TECH = 0x0E;
+this.STK_LOCAL_INFO_INFO_FOR_MULTIPLE_ACCESS_TECH = 0x0F;
+this.STK_LOCAL_INFO_NMR_FOR_MULTIPLE_ACCESS_TECH = 0x10;
+
 /**
  * Supported Terminal Facilities.
  *
  * value = 1, supported.
  *         0, not supported.
  */
 this.STK_TERMINAL_SUPPORT_PROFILE_DOWNLOAD             = 1;
 this.STK_TERMINAL_SUPPORT_SMS_PP_DOWNLOAD              = 1;
@@ -815,28 +844,46 @@ this.STK_TERMINAL_SUPPORT_PROACTIVE_POLL
 this.STK_TERMINAL_SUPPORT_PROACTIVE_REFRESH            = 1;
 
 this.STK_TERMINAL_SUPPORT_PROACTIVE_SELECT_ITEM        = 1;
 this.STK_TERMINAL_SUPPORT_PROACTIVE_SEND_SMS           = 1;
 this.STK_TERMINAL_SUPPORT_PROACTIVE_SEND_SS            = 1;
 this.STK_TERMINAL_SUPPORT_PROACTIVE_SEND_USSD          = 1;
 this.STK_TERMINAL_SUPPORT_PROACTIVE_SET_UP_CALL        = 1;
 this.STK_TERMINAL_SUPPORT_PROACTIVE_SET_UP_MENU        = 1;
-this.STK_TERMINAL_SUPPORT_PROACTIVE_LOCAL_INFO         = 0;
+this.STK_TERMINAL_SUPPORT_PROACTIVE_LOCAL_INFO         = 1;
 this.STK_TERMINAL_SUPPORT_PROACTIVE_LOCAL_INFO_NMR     = 0;
 
 this.STK_TERMINAL_SUPPORT_PROACTIVE_SET_UP_EVENT_LIST  = 1;
 this.STK_TERMINAL_SUPPORT_EVENT_MT_CALL                = 1;
 this.STK_TERMINAL_SUPPORT_EVENT_CALL_CONNECTED         = 1;
 this.STK_TERMINAL_SUPPORT_EVENT_CALL_DISCONNECTED      = 1;
 this.STK_TERMINAL_SUPPORT_EVENT_LOCATION_STATUS        = 1;
 this.STK_TERMINAL_SUPPORT_EVENT_USER_ACTIVITY          = 0;
 this.STK_TERMINAL_SUPPORT_EVENT_IDLE_SCREEN_AVAILABLE  = 0;
 this.STK_TERMINAL_SUPPORT_EVENT_CARD_READER_STATUS     = 0;
 
+this.STK_TERMINAL_SUPPORT_PROACTIVE_TIMER_START_STOP   = 0;
+this.STK_TERMINAL_SUPPORT_PROACTIVE_TIMER_GET_CURRENT  = 0;
+this.STK_TERMINAL_SUPPORT_PROACTIVE_LOCAL_INFO_DATE    = 1;
+this.STK_TERMINAL_SUPPORT_GET_INKEY                    = 1;
+this.STK_TERMINAL_SUPPORT_SET_UP_IDLE_MODE_TEXT        = 1;
+this.STK_TERMINAL_SUPPORT_RUN_AT_COMMAND               = 0;
+this.STK_TERMINAL_SUPPORT_SET_UP_CALL                  = 1;
+this.STK_TERMINAL_SUPPORT_CALL_CONTROL_BY_NNA          = 0;
+
+this.STK_TERMINAL_SUPPORT_DISPLAY_TEXT                      = 1;
+this.STK_TERMINAL_SUPPORT_SEND_DTMF_COMMAND                 = 1;
+this.STK_TERMINAL_SUPPORT_PROACTIVE_LOCAL_INFO_NMR          = 0;
+this.STK_TERMINAL_SUPPORT_PROACTIVE_LOCAL_INFO_LANGUAGE     = 1;
+this.STK_TERMINAL_SUPPORT_PROACTIVE_LOCAL_INFO_TIME_ADVANCE = 0;
+this.STK_TERMINAL_SUPPORT_PROACTIVE_LANGUAGE_NOTIFICATION   = 0;
+this.STK_TERMINAL_SUPPORT_PROACTIVE_LAUNCH_BROWSER          = 1;
+this.STK_TERMINAL_SUPPORT_PROACTIVE_LOCAL_INFO_ACCESS_TECH  = 0;
+
 this.STK_TERMINAL_PROFILE_DOWNLOAD =
   (STK_TERMINAL_SUPPORT_PROFILE_DOWNLOAD << 0) |
   (STK_TERMINAL_SUPPORT_SMS_PP_DOWNLOAD  << 1) |
   (STK_TERMINAL_SUPPORT_CELL_BROADCAST_DATA_DOWNLOAD  << 2) |
   (STK_TERMINAL_SUPPORT_MENU_SELECTION << 3) |
   (STK_TERMINAL_SUPPORT_SIM_DATA_DOWNLOAD_ERROR << 4) |
   (STK_TERMINAL_SUPPORT_TIMER_EXPIRATION << 5) |
   (STK_TERMINAL_SUPPORT_USSD_IN_CALL_CONTROL << 6) |
@@ -877,26 +924,46 @@ this.STK_TERMINAL_PROFILE_EVENT =
   (STK_TERMINAL_SUPPORT_EVENT_MT_CALL << 1) |
   (STK_TERMINAL_SUPPORT_EVENT_CALL_CONNECTED << 2) |
   (STK_TERMINAL_SUPPORT_EVENT_CALL_DISCONNECTED << 3) |
   (STK_TERMINAL_SUPPORT_EVENT_LOCATION_STATUS << 4) |
   (STK_TERMINAL_SUPPORT_EVENT_USER_ACTIVITY << 5) |
   (STK_TERMINAL_SUPPORT_EVENT_IDLE_SCREEN_AVAILABLE << 6) |
   (STK_TERMINAL_SUPPORT_EVENT_CARD_READER_STATUS << 7);
 
+this.STK_TERMINAL_PROFILE_PROACTIVE_3 =
+  (STK_TERMINAL_SUPPORT_PROACTIVE_TIMER_START_STOP << 0) |
+  (STK_TERMINAL_SUPPORT_PROACTIVE_TIMER_GET_CURRENT << 1) |
+  (STK_TERMINAL_SUPPORT_PROACTIVE_LOCAL_INFO_DATE << 2) |
+  (STK_TERMINAL_SUPPORT_GET_INKEY << 3) |
+  (STK_TERMINAL_SUPPORT_SET_UP_IDLE_MODE_TEXT << 4) |
+  (STK_TERMINAL_SUPPORT_RUN_AT_COMMAND << 5) |
+  (STK_TERMINAL_SUPPORT_SET_UP_CALL << 6) |
+  (STK_TERMINAL_SUPPORT_CALL_CONTROL_BY_NNA << 7);
+
+this.STK_TERMINAL_PROFILE_PROACTIVE_4 =
+  (STK_TERMINAL_SUPPORT_DISPLAY_TEXT << 0) |
+  (STK_TERMINAL_SUPPORT_SEND_DTMF_COMMAND << 1) |
+  (STK_TERMINAL_SUPPORT_PROACTIVE_LOCAL_INFO_NMR << 2) |
+  (STK_TERMINAL_SUPPORT_PROACTIVE_LOCAL_INFO_LANGUAGE << 3) |
+  (STK_TERMINAL_SUPPORT_PROACTIVE_LOCAL_INFO_TIME_ADVANCE << 4) |
+  (STK_TERMINAL_SUPPORT_PROACTIVE_LANGUAGE_NOTIFICATION << 5) |
+  (STK_TERMINAL_SUPPORT_PROACTIVE_LAUNCH_BROWSER << 6) |
+  (STK_TERMINAL_SUPPORT_PROACTIVE_LOCAL_INFO_ACCESS_TECH << 7);
+
 this.STK_SUPPORTED_TERMINAL_PROFILE = [
   STK_TERMINAL_PROFILE_DOWNLOAD,
   STK_TERMINAL_PROFILE_OTHER,
   STK_TERMINAL_PROFILE_PROACTIVE_1,
   STK_TERMINAL_PROFILE_PROACTIVE_2,
   STK_TERMINAL_PROFILE_EVENT,
   0x00, // Event extension
   0x00, // Multiple card proactive commands
-  0x00, // Proactive Commands
-  0x00, // Proactive Commands
+  STK_TERMINAL_PROFILE_PROACTIVE_3,
+  STK_TERMINAL_PROFILE_PROACTIVE_4,
   0x00, // Softkey support
   0x00, // Softkey information
   0x00, // BIP proactive commands
   0x00, // BIP supported bearers
   0x00, // Screen height
   0x00, // Screen width
   0x00, // 16, Screen effects
   0x00, // 17, BIP supported transport interface
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -55,16 +55,19 @@ const TLV_COMMAND_DETAILS_SIZE = 5;
 const TLV_DEVICE_ID_SIZE = 4;
 const TLV_RESULT_SIZE = 3;
 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 TLV_IMEI_SIZE = 10;
+const TLV_DATE_TIME_ZONE_SIZE = 9;
+const TLV_LANGUAGE_SIZE = 4;
 
 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;
@@ -3113,21 +3116,23 @@ let RIL = {
     Buf.writeUint32(0);
     Buf.sendParcel();
   },
 
   /**
    * Send STK terminal response.
    *
    * @param command
+   * @param deviceIdentities
    * @param resultCode
    * @param [optional] itemIdentifier
    * @param [optional] input
    * @param [optional] isYesNo
    * @param [optional] hasConfirmed
+   * @param [optional] localInfo
    */
   sendStkTerminalResponse: function sendStkTerminalResponse(response) {
     if (response.hasConfirmed !== undefined) {
       this.stkHandleCallSetup(response);
       return;
     }
 
     let token = Buf.newParcel(REQUEST_STK_SEND_TERMINAL_RESPONSE);
@@ -3149,16 +3154,28 @@ let RIL = {
     }
 
     // 1 octets = 2 chars.
     let size = (TLV_COMMAND_DETAILS_SIZE +
                 TLV_DEVICE_ID_SIZE +
                 TLV_RESULT_SIZE +
                 (response.itemIdentifier ? TLV_ITEM_ID_SIZE : 0) +
                 (textLen ? textLen + 3 : 0)) * 2;
+    if (response.localInfo) {
+      let localInfo = response.localInfo;
+      size = size +
+             (((localInfo.locationInfo ?
+               (localInfo.locationInfo.gsmCellId > 0xffff ?
+                 TLV_LOCATION_INFO_UMTS_SIZE :
+                 TLV_LOCATION_INFO_GSM_SIZE) :
+               0) +
+             (localInfo.imei ? TLV_IMEI_SIZE : 0) +
+             (localInfo.date ? TLV_DATE_TIME_ZONE_SIZE : 0) +
+             (localInfo.language ? TLV_LANGUAGE_SIZE : 0)) * 2);
+    }
     Buf.writeUint32(size);
 
     // Command Details
     GsmPDUHelper.writeHexOctet(COMPREHENSIONTLV_TAG_COMMAND_DETAILS |
                                COMPREHENSIONTLV_FLAG_CR);
     GsmPDUHelper.writeHexOctet(3);
     if (response.command) {
       GsmPDUHelper.writeHexOctet(command.commandNumber);
@@ -3233,16 +3250,50 @@ let RIL = {
             for (let i = 0; i < textLen; i++) {
               GsmPDUHelper.writeHexOctet(text.charCodeAt(i));
             }
             break;
         }
       }
     }
 
+    // Local Information
+    if (response.localInfo) {
+      let localInfo = response.localInfo;
+
+      // Location Infomation
+      if (localInfo.locationInfo) {
+        ComprehensionTlvHelper.writeLocationInfoTlv(localInfo.locationInfo);
+      }
+
+      // IMEI
+      if (localInfo.imei) {
+        let imei = localInfo.imei;
+        if(imei.length == 15) {
+          imei = imei + "0";
+        }
+
+        GsmPDUHelper.writeHexOctet(COMPREHENSIONTLV_TAG_IMEI);
+        GsmPDUHelper.writeHexOctet(8);
+        for (let i = 0; i < imei.length / 2; i++) {
+          GsmPDUHelper.writeHexOctet(parseInt(imei.substr(i * 2, 2), 16));
+        }
+      }
+
+      // Date and Time Zone
+      if (localInfo.date) {
+        ComprehensionTlvHelper.writeDateTimeZoneTlv(localInfo.date);
+      }
+
+      // Language
+      if (localInfo.language) {
+        ComprehensionTlvHelper.writeLanguageTlv(localInfo.language);
+      }
+    }
+
     Buf.writeUint32(0);
     Buf.sendParcel();
   },
 
   /**
    * Send STK Envelope(Menu Selection) command.
    *
    * @param itemIdentifier
@@ -6116,16 +6167,30 @@ let GsmPDUHelper = {
    * @return the corresponding BCD number.
    */
   octetToBCD: function octetToBCD(octet) {
     return ((octet & 0xf0) <= 0x90) * ((octet >> 4) & 0x0f) +
            ((octet & 0x0f) <= 0x09) * (octet & 0x0f) * 10;
   },
 
   /**
+   * Convert a BCD number to an octet (number)
+   *
+   * Only take two digits with absolute value.
+   *
+   * @param bcd
+   *
+   * @return the corresponding octet.
+   */
+  BCDToOctet: function BCDToOctet(bcd) {
+    bcd = Math.abs(bcd);
+    return ((bcd % 10) << 4) + (Math.floor(bcd / 10) % 10);
+  },
+
+  /**
    * Convert a semi-octet (number) to a GSM BCD char.
    */
   bcdChars: "0123456789*#,;",
   semiOctetToBcdChar: function semiOctetToBcdChar(semiOctet) {
     if (semiOctet >= 14) {
       throw new RangeError();
     }
 
@@ -6200,16 +6265,34 @@ let GsmPDUHelper = {
     }
     for (let i = 0; i < data.length; i += 2) {
       Buf.writeUint16(data.charCodeAt(i + 1));
       Buf.writeUint16(data.charCodeAt(i));
     }
   },
 
   /**
+   * Write numerical data as swapped nibble BCD.
+   * If the number of digit of data is even, add '0' at the beginning.
+   *
+   * @param data
+   *        Data to write (as a string or a number)
+   */
+  writeSwappedNibbleBCDNum: function writeSwappedNibbleBCDNum(data) {
+    data = data.toString();
+    if (data.length % 2) {
+      data = "0" + data;
+    }
+    for (let i = 0; i < data.length; i += 2) {
+      Buf.writeUint16(data.charCodeAt(i + 1));
+      Buf.writeUint16(data.charCodeAt(i));
+    }
+  },
+
+  /**
    * Read user data, convert to septets, look up relevant characters in a
    * 7-bit alphabet, and construct string.
    *
    * @param length
    *        Number of septets to read (*not* octets)
    * @param paddingBits
    *        Number of padding bits in the first byte of user data.
    * @param langIndex
@@ -7024,16 +7107,53 @@ let GsmPDUHelper = {
     let tzOffset = this.octetToBCD(tzOctet & ~0x08) * 15 * 60 * 1000;
     tzOffset = (tzOctet & 0x08) ? -tzOffset : tzOffset;
     timestamp -= tzOffset;
 
     return timestamp;
   },
 
   /**
+   * Write GSM TP-Service-Centre-Time-Stamp(TP-SCTS).
+   *
+   * @see 3GPP TS 23.040 9.2.3.11
+   */
+  writeTimestamp: function writeTimestamp(date) {
+    this.writeSwappedNibbleBCDNum(date.getFullYear() - PDU_TIMESTAMP_YEAR_OFFSET);
+
+    // The value returned by getMonth() is an integer between 0 and 11.
+    // 0 is corresponds to January, 1 to February, and so on.
+    this.writeSwappedNibbleBCDNum(date.getMonth() + 1);
+    this.writeSwappedNibbleBCDNum(date.getDate());
+    this.writeSwappedNibbleBCDNum(date.getHours());
+    this.writeSwappedNibbleBCDNum(date.getMinutes());
+    this.writeSwappedNibbleBCDNum(date.getSeconds());
+
+    // the value returned by getTimezoneOffset() is the difference,
+    // in minutes, between UTC and local time.
+    // For example, if your time zone is UTC+10 (Australian Eastern Standard Time),
+    // -600 will be returned.
+    // In TS 23.040 9.2.3.11, the Time Zone field of TP-SCTS indicates
+    // the different between the local time and GMT.
+    // And expressed in quarters of an hours. (so need to divid by 15)
+    let zone = date.getTimezoneOffset() / 15;
+    let octet = this.BCDToOctet(zone);
+
+    // the bit3 of the Time Zone field represents the algebraic sign.
+    // (0: positive, 1: negative).
+    // For example, if the time zone is -0800 GMT,
+    // 480 will be returned by getTimezoneOffset().
+    // In this case, need to mark sign bit as 1. => 0x08
+    if (zone > 0) {
+      octet = octet | 0x08;
+    }
+    this.writeHexOctet(octet);
+  },
+
+  /**
    * User data can be 7 bit (default alphabet) data, 8 bit data, or 16 bit
    * (UCS2) data.
    *
    * @param msg
    *        message object for output.
    * @param length
    *        length of user data to read in octets.
    */
@@ -7733,16 +7853,19 @@ let StkCommandParamsFactory = {
         param = this.processMoreTime(cmdDetails, ctlvs);
         break;
       case STK_CMD_POLL_INTERVAL:
         param = this.processPollInterval(cmdDetails, ctlvs);
         break;
       case STK_CMD_POLL_OFF:
         param = this.processPollOff(cmdDetails, ctlvs);
         break;
+      case STK_CMD_PROVIDE_LOCAL_INFO:
+        param = this.processProvideLocalInfo(cmdDetails, ctlvs);
+        break;
       case STK_CMD_SET_UP_EVENT_LIST:
         param = this.processSetUpEventList(cmdDetails, ctlvs);
         break;
       case STK_CMD_SET_UP_MENU:
       case STK_CMD_SELECT_ITEM:
         param = this.processSelectItem(cmdDetails, ctlvs);
         break;
       case STK_CMD_DISPLAY_TEXT:
@@ -8140,16 +8263,31 @@ let StkCommandParamsFactory = {
     if (ctlv) {
       playTone.duration = ctlv.value;
     }
 
     // vibrate is only defined in TS 102.223
     playTone.isVibrate = (cmdDetails.commandQualifier & 0x01) != 0x00;
 
     return playTone;
+  },
+
+  /**
+   * Construct a param for Provide Local Information
+   *
+   * @param cmdDetails
+   *        The value object of CommandDetails TLV.
+   * @param ctlvs
+   *        The all TLVs in this proactive command.
+   */
+  processProvideLocalInfo: function processProvideLocalInfo(cmdDetails, ctlvs) {
+    let provideLocalInfo = {
+      localInfoType: cmdDetails.commandQualifier
+    };
+    return provideLocalInfo;
   }
 };
 
 let StkProactiveCmdHelper = {
   retrieve: function retrieve(tag, length) {
     switch (tag) {
       case COMPREHENSIONTLV_TAG_COMMAND_DETAILS:
         return this.retrieveCommandDetails(length);
@@ -8659,16 +8797,34 @@ let ComprehensionTlvHelper = {
 
     // TS 04.08, clause 10.5.4.11: ext bit = 1 + 7 bits for cause.
     // +-----------------+----------------------------------+
     // | Ext = 1 (1 bit) |          Cause (7 bits)          |
     // +-----------------+----------------------------------+
     GsmPDUHelper.writeHexOctet(0x80 | cause);
   },
 
+  writeDateTimeZoneTlv: function writeDataTimeZoneTlv(date) {
+    GsmPDUHelper.writeHexOctet(COMPREHENSIONTLV_TAG_DATE_TIME_ZONE);
+    GsmPDUHelper.writeHexOctet(7);
+    GsmPDUHelper.writeTimestamp(date);
+  },
+
+  writeLanguageTlv: function writeLanguageTlv(language) {
+    GsmPDUHelper.writeHexOctet(COMPREHENSIONTLV_TAG_LANGUAGE);
+    GsmPDUHelper.writeHexOctet(2);
+
+    // ISO 639-1, Alpha-2 code
+    // TS 123.038, clause 6.2.1, GSM 7 bit Default Alphabet
+    GsmPDUHelper.writeHexOctet(
+      PDU_NL_LOCKING_SHIFT_TABLES[PDU_NL_IDENTIFIER_DEFAULT].indexOf(language[0]));
+    GsmPDUHelper.writeHexOctet(
+      PDU_NL_LOCKING_SHIFT_TABLES[PDU_NL_IDENTIFIER_DEFAULT].indexOf(language[1]));
+  },
+
   getSizeOfLengthOctets: function getSizeOfLengthOctets(length) {
     if (length >= 0x10000) {
       return 4; // 0x83, len_1, len_2, len_3
     } else if (length >= 0x100) {
       return 3; // 0x82, len_1, len_2
     } else if (length >= 0x80) {
       return 2; // 0x81, len
     } else {