michael@0: /* Any copyright is dedicated to the Public Domain. michael@0: http://creativecommons.org/publicdomain/zero/1.0/ */ michael@0: michael@0: subscriptLoader.loadSubScript("resource://gre/modules/ril_consts.js", this); michael@0: michael@0: const ESCAPE = "\uffff"; michael@0: const RESCTL = "\ufffe"; michael@0: michael@0: function run_test() { michael@0: run_next_test(); michael@0: } michael@0: michael@0: /** michael@0: * Verify receiving SMS-DELIVERY messages michael@0: */ michael@0: michael@0: function hexToNibble(nibble) { michael@0: nibble &= 0x0f; michael@0: if (nibble < 10) { michael@0: nibble += 48; // ASCII '0' michael@0: } else { michael@0: nibble += 55; // ASCII 'A' michael@0: } michael@0: return nibble; michael@0: } michael@0: michael@0: function pduToParcelData(pdu) { michael@0: let dataLength = 4 + pdu.length * 4 + 4; michael@0: let data = new Uint8Array(dataLength); michael@0: let offset = 0; michael@0: michael@0: // String length michael@0: data[offset++] = pdu.length & 0xFF; michael@0: data[offset++] = (pdu.length >> 8) & 0xFF; michael@0: data[offset++] = (pdu.length >> 16) & 0xFF; michael@0: data[offset++] = (pdu.length >> 24) & 0xFF; michael@0: michael@0: // PDU data michael@0: for (let i = 0; i < pdu.length; i++) { michael@0: let hi = (pdu[i] >>> 4) & 0x0F; michael@0: let lo = pdu[i] & 0x0F; michael@0: michael@0: data[offset++] = hexToNibble(hi); michael@0: data[offset++] = 0; michael@0: data[offset++] = hexToNibble(lo); michael@0: data[offset++] = 0; michael@0: } michael@0: michael@0: // String delimitor michael@0: data[offset++] = 0; michael@0: data[offset++] = 0; michael@0: data[offset++] = 0; michael@0: data[offset++] = 0; michael@0: michael@0: return data; michael@0: } michael@0: michael@0: function compose7bitPdu(lst, sst, data, septets) { michael@0: if ((lst == 0) && (sst == 0)) { michael@0: return [0x00, // SMSC michael@0: PDU_MTI_SMS_DELIVER, // firstOctet michael@0: 1, 0x00, 0, // senderAddress michael@0: 0x00, // protocolIdentifier michael@0: PDU_DCS_MSG_CODING_7BITS_ALPHABET, // dataCodingScheme michael@0: 0, 0, 0, 0, 0, 0, 0, // y m d h m s tz michael@0: septets] // userDataLength michael@0: .concat(data); michael@0: } michael@0: michael@0: return [0x00, // SMSC michael@0: PDU_MTI_SMS_DELIVER | PDU_UDHI, // firstOctet michael@0: 1, 0x00, 0, // senderAddress michael@0: 0x00, // protocolIdentifier michael@0: PDU_DCS_MSG_CODING_7BITS_ALPHABET, // dataCodingScheme michael@0: 0, 0, 0, 0, 0, 0, 0, // y m d h m s tz michael@0: 8 + septets, // userDataLength michael@0: 6, // user data header length michael@0: PDU_IEI_NATIONAL_LANGUAGE_LOCKING_SHIFT, 1, lst, // PDU_IEI_NATIONAL_LANGUAGE_LOCKING_SHIFT michael@0: PDU_IEI_NATIONAL_LANGUAGE_SINGLE_SHIFT, 1, sst] // PDU_IEI_NATIONAL_LANGUAGE_SINGLE_SHIFT michael@0: .concat(data); michael@0: } michael@0: michael@0: function composeUcs2Pdu(rawBytes) { michael@0: return [0x00, // SMSC michael@0: PDU_MTI_SMS_DELIVER, // firstOctet michael@0: 1, 0x00, 0, // senderAddress michael@0: 0x00, // protocolIdentifier michael@0: PDU_DCS_MSG_CODING_16BITS_ALPHABET, // dataCodingScheme michael@0: 0, 0, 0, 0, 0, 0, 0, // y m d h m s tz michael@0: rawBytes.length] // userDataLength michael@0: .concat(rawBytes); michael@0: } michael@0: michael@0: function newSmsParcel(pdu) { michael@0: return newIncomingParcel(-1, michael@0: RESPONSE_TYPE_UNSOLICITED, michael@0: UNSOLICITED_RESPONSE_NEW_SMS, michael@0: pduToParcelData(pdu)); michael@0: } michael@0: michael@0: function removeSpecialChar(str, needle) { michael@0: for (let i = 0; i < needle.length; i++) { michael@0: let pos; michael@0: while ((pos = str.indexOf(needle[i])) >= 0) { michael@0: str = str.substring(0, pos) + str.substring(pos + 1); michael@0: } michael@0: } michael@0: return str; michael@0: } michael@0: michael@0: function newWriteHexOctetAsUint8Worker() { michael@0: let worker = newWorker({ michael@0: postRILMessage: function(data) { michael@0: // Do nothing michael@0: }, michael@0: postMessage: function(message) { michael@0: // Do nothing michael@0: } michael@0: }); michael@0: michael@0: let context = worker.ContextPool._contexts[0]; michael@0: context.GsmPDUHelper.writeHexOctet = function(value) { michael@0: context.Buf.writeUint8(value); michael@0: }; michael@0: michael@0: return worker; michael@0: } michael@0: michael@0: function add_test_receiving_sms(expected, pdu) { michael@0: add_test(function test_receiving_sms() { michael@0: let worker = newWorker({ michael@0: postRILMessage: function(data) { michael@0: // Do nothing michael@0: }, michael@0: postMessage: function(message) { michael@0: do_print("fullBody: " + message.fullBody); michael@0: do_check_eq(expected, message.fullBody) michael@0: } michael@0: }); michael@0: michael@0: do_print("expect: " + expected); michael@0: do_print("pdu: " + pdu); michael@0: worker.onRILMessage(0, newSmsParcel(pdu)); michael@0: michael@0: run_next_test(); michael@0: }); michael@0: } michael@0: michael@0: let test_receiving_7bit_alphabets__ril; michael@0: let test_receiving_7bit_alphabets__worker; michael@0: function test_receiving_7bit_alphabets(lst, sst) { michael@0: if (!test_receiving_7bit_alphabets__ril) { michael@0: test_receiving_7bit_alphabets__ril = newRadioInterface(); michael@0: test_receiving_7bit_alphabets__worker = newWriteHexOctetAsUint8Worker(); michael@0: } michael@0: let ril = test_receiving_7bit_alphabets__ril; michael@0: let worker = test_receiving_7bit_alphabets__worker; michael@0: let context = worker.ContextPool._contexts[0]; michael@0: let helper = context.GsmPDUHelper; michael@0: let buf = context.Buf; michael@0: michael@0: function get7bitRawBytes(expected) { michael@0: buf.outgoingIndex = 0; michael@0: helper.writeStringAsSeptets(expected, 0, lst, sst); michael@0: michael@0: let subArray = buf.outgoingBytes.subarray(0, buf.outgoingIndex); michael@0: return Array.slice(subArray); michael@0: } michael@0: michael@0: let langTable = PDU_NL_LOCKING_SHIFT_TABLES[lst]; michael@0: let langShiftTable = PDU_NL_SINGLE_SHIFT_TABLES[sst]; michael@0: michael@0: let text = removeSpecialChar(langTable + langShiftTable, ESCAPE + RESCTL); michael@0: for (let i = 0; i < text.length;) { michael@0: let len = Math.min(70, text.length - i); michael@0: let expected = text.substring(i, i + len); michael@0: let septets = ril._countGsm7BitSeptets(expected, langTable, langShiftTable); michael@0: let rawBytes = get7bitRawBytes(expected); michael@0: let pdu = compose7bitPdu(lst, sst, rawBytes, septets); michael@0: add_test_receiving_sms(expected, pdu); michael@0: michael@0: i += len; michael@0: } michael@0: } michael@0: michael@0: function test_receiving_ucs2_alphabets(text) { michael@0: let worker = test_receiving_7bit_alphabets__worker; michael@0: let context = worker.ContextPool._contexts[0]; michael@0: let buf = context.Buf; michael@0: michael@0: function getUCS2RawBytes(expected) { michael@0: buf.outgoingIndex = 0; michael@0: context.GsmPDUHelper.writeUCS2String(expected); michael@0: michael@0: let subArray = buf.outgoingBytes.subarray(0, buf.outgoingIndex); michael@0: return Array.slice(subArray); michael@0: } michael@0: michael@0: for (let i = 0; i < text.length;) { michael@0: let len = Math.min(70, text.length - i); michael@0: let expected = text.substring(i, i + len); michael@0: let rawBytes = getUCS2RawBytes(expected); michael@0: let pdu = composeUcs2Pdu(rawBytes); michael@0: add_test_receiving_sms(expected, pdu); michael@0: michael@0: i += len; michael@0: } michael@0: } michael@0: michael@0: let ucs2str = ""; michael@0: for (let lst = 0; lst < PDU_NL_LOCKING_SHIFT_TABLES.length; lst++) { michael@0: ucs2str += PDU_NL_LOCKING_SHIFT_TABLES[lst]; michael@0: for (let sst = 0; sst < PDU_NL_SINGLE_SHIFT_TABLES.length; sst++) { michael@0: test_receiving_7bit_alphabets(lst, sst); michael@0: michael@0: if (lst == 0) { michael@0: ucs2str += PDU_NL_SINGLE_SHIFT_TABLES[sst]; michael@0: } michael@0: } michael@0: } michael@0: test_receiving_ucs2_alphabets(ucs2str); michael@0: michael@0: // Bug 820220: B2G SMS: wrong order and truncated content in multi-part messages michael@0: add_test(function test_sendSMS_UCS2_without_langIndex_langShiftIndex_defined() { michael@0: let worker = newWriteHexOctetAsUint8Worker(); michael@0: let context = worker.ContextPool._contexts[0]; michael@0: michael@0: context.Buf.sendParcel = function() { michael@0: // Each sendParcel() call represents one outgoing segment of a multipart michael@0: // SMS message. Here, we have the first segment send, so it's "Hello " michael@0: // only. michael@0: // michael@0: // 4(parcel size) + 4(request type) + 4(token) michael@0: // + 4(two messages) + 4(null SMSC) + 4(message string length) michael@0: // + 1(first octet) + 1(message reference) michael@0: // + 2(DA len, TOA) + 4(addr) michael@0: // + 1(pid) + 1(dcs) michael@0: // + 1(UDL) + 6(UDHL, type, len, ref, max, seq) michael@0: // + 12(2 * strlen("Hello ")) michael@0: // + 4(two delimitors) = 57 michael@0: // michael@0: // If we have additional 6(type, len, langIndex, type len, langShiftIndex) michael@0: // octets here, then bug 809553 is not fixed. michael@0: do_check_eq(this.outgoingIndex, 57); michael@0: michael@0: run_next_test(); michael@0: }; michael@0: michael@0: context.RIL.sendSMS({ michael@0: number: "1", michael@0: segmentMaxSeq: 2, michael@0: fullBody: "Hello World!", michael@0: dcs: PDU_DCS_MSG_CODING_16BITS_ALPHABET, michael@0: segmentRef16Bit: false, michael@0: userDataHeaderLength: 5, michael@0: requestStatusReport: true, michael@0: segments: [ michael@0: { michael@0: body: "Hello ", michael@0: encodedBodyLength: 12, michael@0: }, { michael@0: body: "World!", michael@0: encodedBodyLength: 12, michael@0: } michael@0: ], michael@0: }); michael@0: });