diff --git a/dom/system/b2g/ril_worker.js b/dom/system/b2g/ril_worker.js
index 9bf07122af0..d3763336bc2 100644
--- a/dom/system/b2g/ril_worker.js
+++ b/dom/system/b2g/ril_worker.js
@@ -2295,33 +2295,80 @@ let GsmPDUHelper = {
*
* @param length
* Number of septets to read (*not* octets)
+ * @param paddingBits
+ * Number of padding bits in the first byte of user data.
+ * @param langIndex
+ * Table index used for normal 7-bit encoded character lookup.
+ * @param langShiftIndex
+ * Table index used for escaped 7-bit encoded character lookup.
*
* @return a string.
- *
- * TODO: support other alphabets
- * TODO: support escape chars
*/
- readSeptetsToString: function readSeptetsToString(length) {
+ readSeptetsToString: function readSeptetsToString(length, paddingBits, langIndex, langShiftIndex) {
let ret = "";
- let byteLength = Math.ceil(length * 7 / 8);
+ let byteLength = Math.ceil((length * 7 + paddingBits) / 8);
- let leftOver = 0;
- for (let i = 0; i < byteLength; i++) {
- let octet = this.readHexOctet();
- let shift = (i % 7);
- let leftOver_mask = (0xff << (7 - shift)) & 0xff;
- let septet_mask = (0xff >> (shift + 1));
-
- let septet = ((octet & septet_mask) << shift) | leftOver;
- ret += PDU_NL_LOCKING_SHIFT_TABLES[PDU_NL_IDENTIFIER_DEFAULT][septet];
- leftOver = (octet & leftOver_mask) >> (7 - shift);
-
- // Every 7th byte we have a whole septet left over that we can apply.
- if (shift == 6) {
- ret += PDU_NL_LOCKING_SHIFT_TABLES[PDU_NL_IDENTIFIER_DEFAULT][leftOver];
- leftOver = 0;
- }
+ /**
+ * |<- last byte in header ->|
+ * |<- incompleteBits ->|<- last header septet->|
+ * +===7===|===6===|===5===|===4===|===3===|===2===|===1===|===0===|
+ *
+ * |<- 1st byte in user data ->|
+ * |<- data septet 1 ->|<-paddingBits->|
+ * +===7===|===6===|===5===|===4===|===3===|===2===|===1===|===0===|
+ *
+ * |<- 2nd byte in user data ->|
+ * |<- data spetet 2 ->|<-ds1->|
+ * +===7===|===6===|===5===|===4===|===3===|===2===|===1===|===0===|
+ */
+ let data = 0;
+ let dataBits = 0;
+ if (paddingBits) {
+ data = this.readHexOctet() >> paddingBits;
+ dataBits = 8 - paddingBits;
+ --byteLength;
}
+
+ let escapeFound = false;
+ const langTable = PDU_NL_LOCKING_SHIFT_TABLES[langIndex];
+ const langShiftTable = PDU_NL_SINGLE_SHIFT_TABLES[langShiftIndex];
+ do {
+ // Read as much as fits in 32bit word
+ let bytesToRead = Math.min(byteLength, dataBits ? 3 : 4);
+ for (let i = 0; i < bytesToRead; i++) {
+ data |= this.readHexOctet() << dataBits;
+ dataBits += 8;
+ --byteLength;
+ }
+
+ // Consume available full septets
+ for (; dataBits >= 7; dataBits -= 7) {
+ let septet = data & 0x7F;
+ data >>>= 7;
+
+ if (escapeFound) {
+ escapeFound = false;
+ if (septet == PDU_NL_EXTENDED_ESCAPE) {
+ // According to 3GPP TS 23.038, section 6.2.1.1, NOTE 1, "On
+ // receipt of this code, a receiving entity shall display a space
+ // until another extensiion table is defined."
+ ret += " ";
+ } else if (septet == PDU_NL_RESERVED_CONTROL) {
+ // According to 3GPP TS 23.038 B.2, "This code represents a control
+ // character and therefore must not be used for language specific
+ // characters."
+ ret += " ";
+ } else {
+ ret += langShiftTable[septet];
+ }
+ } else if (septet == PDU_NL_EXTENDED_ESCAPE) {
+ escapeFound = true;
+ } else {
+ ret += langTable[septet];
+ }
+ }
+ } while (byteLength);
+
if (ret.length != length) {
ret = ret.slice(0, length);
}
@@ -2539,6 +2586,85 @@ let GsmPDUHelper = {
}
},
+ /**
+ * Read 1 + UDHL octets and construct user data header at return.
+ *
+ * @return A header object with properties contained in received message.
+ * The properties set include:
+ *
+ * - length: totoal length of the header, default 0.
+ *
- langIndex: used locking shift table index, default
+ * PDU_NL_IDENTIFIER_DEFAULT.
+ *
- langShiftIndex: used locking shift table index, default
+ * PDU_NL_IDENTIFIER_DEFAULT.
+ *
+ */
+ readUserDataHeader: function readUserDataHeader() {
+ let header = {
+ length: 0,
+ langIndex: PDU_NL_IDENTIFIER_DEFAULT,
+ langShiftIndex: PDU_NL_IDENTIFIER_DEFAULT
+ };
+
+ header.length = this.readHexOctet();
+ let dataAvailable = header.length;
+ while (dataAvailable >= 2) {
+ let id = this.readHexOctet();
+ let length = this.readHexOctet();
+ dataAvailable -= 2;
+
+ switch (id) {
+ case PDU_IEI_NATIONAL_LANGUAGE_SINGLE_SHIFT:
+ let langShiftIndex = this.readHexOctet();
+ --dataAvailable;
+ if (langShiftIndex < PDU_NL_SINGLE_SHIFT_TABLES.length) {
+ header.langShiftIndex = langShiftIndex;
+ }
+ break;
+ case PDU_IEI_NATIONAL_LANGUAGE_LOCKING_SHIFT:
+ let langIndex = this.readHexOctet();
+ --dataAvailable;
+ if (langIndex < PDU_NL_LOCKING_SHIFT_TABLES.length) {
+ header.langIndex = langIndex;
+ }
+ break;
+ default:
+ if (DEBUG) {
+ debug("readUserDataHeader: unsupported IEI(" + id
+ + "), " + length + " bytes.");
+ }
+
+ // Read out unsupported data
+ if (length) {
+ let octets;
+ if (DEBUG) octets = new Uint8Array(length);
+
+ for (let i = 0; i < length; i++) {
+ let octet = this.readHexOctet();
+ if (DEBUG) octets[i] = octet;
+ }
+ dataAvailable -= length;
+
+ if (DEBUG) debug("readUserDataHeader: " + Array.slice(octets));
+ }
+ break;
+ }
+ }
+
+ if (dataAvailable != 0) {
+ throw new Error("Illegal user data header found!");
+ }
+
+ return header;
+ },
+
+ /**
+ * Write out user data header.
+ *
+ * @param options
+ * Options containing information for user data header write-out. The
+ * `userDataHeaderLength` property must be correctly pre-calculated.
+ */
writeUserDataHeader: function writeUserDataHeader(options) {
this.writeHexOctet(options.userDataHeaderLength);
@@ -2559,7 +2685,7 @@ let GsmPDUHelper = {
* User data can be 7 bit (default alphabet) data, 8 bit data, or 16 bit
* (UCS2) data.
*/
- readUserData: function readUserData(length, codingScheme) {
+ readUserData: function readUserData(length, codingScheme, hasHeader) {
if (DEBUG) {
debug("Reading " + length + " bytes of user data.");
debug("Coding scheme: " + codingScheme);
@@ -2596,6 +2722,22 @@ let GsmPDUHelper = {
break;
}
+ let header;
+ let paddingBits = 0;
+ if (hasHeader) {
+ header = this.readUserDataHeader();
+
+ if (encoding == PDU_DCS_MSG_CODING_7BITS_ALPHABET) {
+ let headerBits = (header.length + 1) * 8;
+ let headerSeptets = Math.ceil(headerBits / 7);
+
+ length -= headerSeptets;
+ paddingBits = headerSeptets * 7 - headerBits;
+ } else {
+ length -= (header.length + 1);
+ }
+ }
+
if (DEBUG) debug("PDU: message encoding is " + encoding + " bit.");
switch (encoding) {
case PDU_DCS_MSG_CODING_7BITS_ALPHABET:
@@ -2605,7 +2747,11 @@ let GsmPDUHelper = {
if (DEBUG) debug("PDU error: user data is too long: " + length);
return null;
}
- return this.readSeptetsToString(length);
+
+ return this.readSeptetsToString(length,
+ paddingBits,
+ hasHeader ? header.langIndex : PDU_NL_IDENTIFIER_DEFAULT,
+ hasHeader ? header.langShiftIndex : PDU_NL_IDENTIFIER_DEFAULT);
case PDU_DCS_MSG_CODING_8BITS_ALPHABET:
// Unsupported.
return null;
@@ -2645,6 +2791,10 @@ let GsmPDUHelper = {
// First octet of this SMS-DELIVER or SMS-SUBMIT message
let firstOctet = this.readHexOctet();
+
+ // User data header indicator
+ let hasUserDataHeader = firstOctet & PDU_UDHI;
+
// if the sms is of SMS-SUBMIT type it would contain a TP-MR
let isSmsSubmit = firstOctet & PDU_MTI_SMS_SUBMIT;
if (isSmsSubmit) {
@@ -2722,7 +2872,9 @@ let GsmPDUHelper = {
// - TP-User-Data -
if (userDataLength > 0) {
- msg.body = this.readUserData(userDataLength, dataCodingScheme);
+ msg.body = this.readUserData(userDataLength,
+ dataCodingScheme,
+ hasUserDataHeader);
}
return msg;