dom/cellbroadcast/tests/marionette/test_cellbroadcast_gsm.js

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:7148379cfe0c
1 /* Any copyright is dedicated to the Public Domain.
2 * http://creativecommons.org/publicdomain/zero/1.0/ */
3
4 MARIONETTE_TIMEOUT = 30000;
5
6 const PDU_DCS_CODING_GROUP_BITS = 0xF0;
7 const PDU_DCS_MSG_CODING_7BITS_ALPHABET = 0x00;
8 const PDU_DCS_MSG_CODING_8BITS_ALPHABET = 0x04;
9 const PDU_DCS_MSG_CODING_16BITS_ALPHABET = 0x08;
10
11 const PDU_DCS_MSG_CLASS_BITS = 0x03;
12 const PDU_DCS_MSG_CLASS_NORMAL = 0xFF;
13 const PDU_DCS_MSG_CLASS_0 = 0x00;
14 const PDU_DCS_MSG_CLASS_ME_SPECIFIC = 0x01;
15 const PDU_DCS_MSG_CLASS_SIM_SPECIFIC = 0x02;
16 const PDU_DCS_MSG_CLASS_TE_SPECIFIC = 0x03;
17 const PDU_DCS_MSG_CLASS_USER_1 = 0x04;
18 const PDU_DCS_MSG_CLASS_USER_2 = 0x05;
19
20 const GECKO_SMS_MESSAGE_CLASSES = {};
21 GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_NORMAL] = "normal";
22 GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_0] = "class-0";
23 GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_ME_SPECIFIC] = "class-1";
24 GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_SIM_SPECIFIC] = "class-2";
25 GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_TE_SPECIFIC] = "class-3";
26 GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_USER_1] = "user-1";
27 GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_USER_2] = "user-2";
28
29 const CB_MESSAGE_SIZE_GSM = 88;
30
31 const CB_GSM_MESSAGEID_ETWS_BEGIN = 0x1100;
32 const CB_GSM_MESSAGEID_ETWS_END = 0x1107;
33
34 const CB_GSM_GEOGRAPHICAL_SCOPE_NAMES = [
35 "cell-immediate",
36 "plmn",
37 "location-area",
38 "cell"
39 ];
40
41 const CB_ETWS_WARNING_TYPE_NAMES = [
42 "earthquake",
43 "tsunami",
44 "earthquake-tsunami",
45 "test",
46 "other"
47 ];
48
49 const CB_DCS_LANG_GROUP_1 = [
50 "de", "en", "it", "fr", "es", "nl", "sv", "da", "pt", "fi",
51 "no", "el", "tr", "hu", "pl", null
52 ];
53 const CB_DCS_LANG_GROUP_2 = [
54 "cs", "he", "ar", "ru", "is", null, null, null, null, null,
55 null, null, null, null, null, null
56 ];
57
58 const CB_MAX_CONTENT_7BIT = Math.floor((CB_MESSAGE_SIZE_GSM - 6) * 8 / 7);
59 const CB_MAX_CONTENT_UCS2 = Math.floor((CB_MESSAGE_SIZE_GSM - 6) / 2);
60
61 const BODY_7BITS = "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
62 + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
63 + "@@@@@@@@@@@@@"; // 93 ascii chars.
64 const BODY_7BITS_IND = BODY_7BITS.substr(3);
65 const BODY_UCS2 = "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
66 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
67 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
68 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
69 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
70 + "\u0000"; // 41 unicode chars.
71 const BODY_UCS2_IND = BODY_UCS2.substr(1);
72
73 SpecialPowers.addPermission("cellbroadcast", true, document);
74 SpecialPowers.addPermission("mobileconnection", true, document);
75
76 is(BODY_7BITS.length, CB_MAX_CONTENT_7BIT, "BODY_7BITS.length");
77 is(BODY_7BITS_IND.length, CB_MAX_CONTENT_7BIT - 3, "BODY_7BITS_IND.length");
78 is(BODY_UCS2.length, CB_MAX_CONTENT_UCS2, "BODY_UCS2.length");
79 is(BODY_UCS2_IND.length, CB_MAX_CONTENT_UCS2 - 1, "BODY_UCS2_IND.length")
80
81 let cbs = window.navigator.mozCellBroadcast;
82 ok(cbs instanceof window.MozCellBroadcast,
83 "mozCellBroadcast is instanceof " + cbs.constructor);
84
85 let pendingEmulatorCmdCount = 0;
86 function sendCellBroadcastMessage(pdu, callback) {
87 pendingEmulatorCmdCount++;
88
89 let cmd = "cbs pdu " + pdu;
90 runEmulatorCmd(cmd, function(result) {
91 pendingEmulatorCmdCount--;
92
93 is(result[0], "OK", "Emulator response");
94
95 if (callback) {
96 window.setTimeout(callback, 0);
97 }
98 });
99 }
100
101 function buildHexStr(n, numSemiOctets) {
102 let str = n.toString(16);
103 ok(str.length <= numSemiOctets);
104 while (str.length < numSemiOctets) {
105 str = "0" + str;
106 }
107 return str;
108 }
109
110 function seq(end, begin) {
111 let result = [];
112 for (let i = begin || 0; i < end; i++) {
113 result.push(i);
114 }
115 return result;
116 }
117
118 function repeat(func, array, oncomplete) {
119 (function do_call(index) {
120 let next = index < (array.length - 1) ? do_call.bind(null, index + 1) : oncomplete;
121 func.apply(null, [array[index], next]);
122 })(0);
123 }
124
125 function doTestHelper(pdu, nextTest, checkFunc) {
126 cbs.addEventListener("received", function onreceived(event) {
127 cbs.removeEventListener("received", onreceived);
128
129 checkFunc(event.message);
130
131 window.setTimeout(nextTest, 0);
132 });
133
134 if (Array.isArray(pdu)) {
135 repeat(sendCellBroadcastMessage, pdu);
136 } else {
137 sendCellBroadcastMessage(pdu);
138 }
139 }
140
141 /**
142 * Tests receiving Cell Broadcast messages, event instance type, all attributes
143 * of CellBroadcastMessage exist.
144 */
145 function testGsmMessageAttributes() {
146 log("Test GSM Cell Broadcast message attributes");
147
148 cbs.addEventListener("received", function onreceived(event) {
149 cbs.removeEventListener("received", onreceived);
150
151 // Bug 838542: following check throws an exception and fails this case.
152 // ok(event instanceof MozCellBroadcastEvent,
153 // "event is instanceof " + event.constructor)
154 ok(event, "event is valid");
155
156 let message = event.message;
157 ok(message, "event.message is valid");
158
159 // Attributes other than `language` and `body` should always be assigned.
160 ok(message.gsmGeographicalScope != null, "message.gsmGeographicalScope");
161 ok(message.messageCode != null, "message.messageCode");
162 ok(message.messageId != null, "message.messageId");
163 ok(message.language != null, "message.language");
164 ok(message.body != null, "message.body");
165 ok(message.messageClass != null, "message.messageClass");
166 ok(message.timestamp != null, "message.timestamp");
167 ok('etws' in message, "message.etws");
168 if (message.etws) {
169 ok('warningType' in message.etws, "message.etws.warningType");
170 ok(message.etws.emergencyUserAlert != null, "message.etws.emergencyUserAlert");
171 ok(message.etws.popup != null, "message.etws.popup");
172 }
173 ok(message.cdmaServiceCategory != null, "message.cdmaServiceCategory");
174
175 window.setTimeout(testReceiving_GSM_GeographicalScope, 0);
176 });
177
178 // Here we use a simple GSM message for test.
179 let pdu = buildHexStr(0, CB_MESSAGE_SIZE_GSM * 2);
180 sendCellBroadcastMessage(pdu);
181 }
182
183 function testReceiving_GSM_GeographicalScope() {
184 log("Test receiving GSM Cell Broadcast - Geographical Scope");
185
186 function do_test(gs, nextTest) {
187 let pdu = buildHexStr(((gs & 0x03) << 14), 4)
188 + buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 2) * 2);
189
190 doTestHelper(pdu, nextTest, function(message) {
191 is(message.gsmGeographicalScope, CB_GSM_GEOGRAPHICAL_SCOPE_NAMES[gs],
192 "message.gsmGeographicalScope");
193 });
194 }
195
196 repeat(do_test, seq(CB_GSM_GEOGRAPHICAL_SCOPE_NAMES.length),
197 testReceiving_GSM_MessageCode);
198 }
199
200 function testReceiving_GSM_MessageCode() {
201 log("Test receiving GSM Cell Broadcast - Message Code");
202
203 // Message Code has 10 bits, and is ORed into a 16 bits 'serial' number. Here
204 // we test every single bit to verify the operation doesn't go wrong.
205 let messageCodesToTest = [
206 0x000, 0x001, 0x002, 0x004, 0x008, 0x010, 0x020, 0x040,
207 0x080, 0x100, 0x200, 0x251
208 ];
209
210 function do_test(messageCode, nextTest) {
211 let pdu = buildHexStr(((messageCode & 0x3FF) << 4), 4)
212 + buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 2) * 2);
213
214 doTestHelper(pdu, nextTest, function(message) {
215 is(message.messageCode, messageCode, "message.messageCode");
216 });
217 }
218
219 repeat(do_test, messageCodesToTest, testReceiving_GSM_MessageId);
220 }
221
222 function testReceiving_GSM_MessageId() {
223 log("Test receiving GSM Cell Broadcast - Message Identifier");
224
225 // Message Identifier has 16 bits, but no bitwise operation is needed.
226 // Test some selected values only.
227 let messageIdsToTest = [
228 0x0000, 0x0001, 0x0010, 0x0100, 0x1000, 0x1111, 0x8888, 0x8811,
229 ];
230
231 function do_test(messageId, nextTest) {
232 let pdu = buildHexStr(0, 4)
233 + buildHexStr((messageId & 0xFFFF), 4)
234 + buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 4) * 2);
235
236 doTestHelper(pdu, nextTest, function(message) {
237 is(message.messageId, messageId, "message.messageId");
238 ok(message.etws == null, "message.etws");
239 });
240 }
241
242 repeat(do_test, messageIdsToTest, testReceiving_GSM_Language_and_Body);
243 }
244
245 // Copied from GsmPDUHelper.readCbDataCodingScheme
246 function decodeDataCodingScheme(dcs) {
247 let language = null;
248 let hasLanguageIndicator = false;
249 let encoding = PDU_DCS_MSG_CODING_7BITS_ALPHABET;
250 let messageClass = PDU_DCS_MSG_CLASS_NORMAL;
251
252 switch (dcs & PDU_DCS_CODING_GROUP_BITS) {
253 case 0x00: // 0000
254 language = CB_DCS_LANG_GROUP_1[dcs & 0x0F];
255 break;
256
257 case 0x10: // 0001
258 switch (dcs & 0x0F) {
259 case 0x00:
260 hasLanguageIndicator = true;
261 break;
262 case 0x01:
263 encoding = PDU_DCS_MSG_CODING_16BITS_ALPHABET;
264 hasLanguageIndicator = true;
265 break;
266 }
267 break;
268
269 case 0x20: // 0010
270 language = CB_DCS_LANG_GROUP_2[dcs & 0x0F];
271 break;
272
273 case 0x40: // 01xx
274 case 0x50:
275 //case 0x60:
276 //case 0x70:
277 case 0x90: // 1001
278 encoding = (dcs & 0x0C);
279 if (encoding == 0x0C) {
280 encoding = PDU_DCS_MSG_CODING_7BITS_ALPHABET;
281 }
282 messageClass = (dcs & PDU_DCS_MSG_CLASS_BITS);
283 break;
284
285 case 0xF0:
286 encoding = (dcs & 0x04) ? PDU_DCS_MSG_CODING_8BITS_ALPHABET
287 : PDU_DCS_MSG_CODING_7BITS_ALPHABET;
288 switch(dcs & PDU_DCS_MSG_CLASS_BITS) {
289 case 0x01: messageClass = PDU_DCS_MSG_CLASS_USER_1; break;
290 case 0x02: messageClass = PDU_DCS_MSG_CLASS_USER_2; break;
291 case 0x03: messageClass = PDU_DCS_MSG_CLASS_TE_SPECIFIC; break;
292 }
293 break;
294
295 case 0x30: // 0011 (Reserved)
296 case 0x80: // 1000 (Reserved)
297 case 0xA0: // 1010..1100 (Reserved)
298 case 0xB0:
299 case 0xC0:
300 break;
301 default:
302 throw new Error("Unsupported CBS data coding scheme: " + dcs);
303 }
304
305 return [encoding, language, hasLanguageIndicator,
306 GECKO_SMS_MESSAGE_CLASSES[messageClass]];
307 }
308
309 function testReceiving_GSM_Language_and_Body() {
310 log("Test receiving GSM Cell Broadcast - Language & Body");
311
312 function do_test(dcs) {
313 let encoding, language, indicator, messageClass;
314 try {
315 [encoding, language, indicator, messageClass] = decodeDataCodingScheme(dcs);
316 } catch (e) {
317 // Unsupported coding group, skip.
318 let nextGroup = (dcs & PDU_DCS_CODING_GROUP_BITS) + 0x10;
319 window.setTimeout(do_test.bind(null, nextGroup), 0);
320 return;
321 }
322
323 let pdu = buildHexStr(0, 8)
324 + buildHexStr(dcs, 2)
325 + buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 5) * 2);
326
327 let nextTest = (dcs < 0xFF) ? do_test.bind(null, dcs + 1)
328 : testReceiving_GSM_Timestamp;
329 doTestHelper(pdu, nextTest, function(message) {
330 if (language) {
331 is(message.language, language, "message.language");
332 } else if (indicator) {
333 is(message.language, "@@", "message.language");
334 } else {
335 ok(message.language == null, "message.language");
336 }
337
338 switch (encoding) {
339 case PDU_DCS_MSG_CODING_7BITS_ALPHABET:
340 is(message.body, indicator ? BODY_7BITS_IND : BODY_7BITS, "message.body");
341 break;
342 case PDU_DCS_MSG_CODING_8BITS_ALPHABET:
343 ok(message.body == null, "message.body");
344 break;
345 case PDU_DCS_MSG_CODING_16BITS_ALPHABET:
346 is(message.body, indicator ? BODY_UCS2_IND : BODY_UCS2, "message.body");
347 break;
348 }
349
350 is(message.messageClass, messageClass, "message.messageClass");
351 });
352 }
353
354 do_test(0);
355 }
356
357 function testReceiving_GSM_Timestamp() {
358 log("Test receiving GSM Cell Broadcast - Timestamp");
359
360 let pdu = buildHexStr(0, CB_MESSAGE_SIZE_GSM * 2);
361 doTestHelper(pdu, testReceiving_GSM_WarningType, function(message) {
362 // Cell Broadcast messages do not contain a timestamp field (however, ETWS
363 // does). We only check the timestamp doesn't go too far (60 seconds) here.
364 let msMessage = message.timestamp.getTime();
365 let msNow = Date.now();
366 ok(Math.abs(msMessage - msNow) < (1000 * 60), "message.timestamp");
367 });
368 }
369
370 function testReceiving_GSM_WarningType() {
371 log("Test receiving GSM Cell Broadcast - Warning Type");
372
373 let messageIdsToTest = [];
374 for (let i = CB_GSM_MESSAGEID_ETWS_BEGIN; i <= CB_GSM_MESSAGEID_ETWS_END; i++) {
375 messageIdsToTest.push(i);
376 }
377
378 function do_test(messageId, nextTest) {
379 let pdu = buildHexStr(0, 4)
380 + buildHexStr((messageId & 0xFFFF), 4)
381 + buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 4) * 2);
382
383 doTestHelper(pdu, nextTest, function(message) {
384 is(message.messageId, messageId, "message.messageId");
385 ok(message.etws != null, "message.etws");
386
387 let offset = messageId - CB_GSM_MESSAGEID_ETWS_BEGIN;
388 if (offset < CB_ETWS_WARNING_TYPE_NAMES.length) {
389 is(message.etws.warningType, CB_ETWS_WARNING_TYPE_NAMES[offset],
390 "message.etws.warningType");
391 } else {
392 ok(message.etws.warningType == null, "message.etws.warningType");
393 }
394 });
395 }
396
397 repeat(do_test, messageIdsToTest, testReceiving_GSM_EmergencyUserAlert);
398 }
399
400 function doTestEmergencyUserAlert_or_Popup(name, mask, nextTest) {
401 let pdu = buildHexStr(mask, 4)
402 + buildHexStr(CB_GSM_MESSAGEID_ETWS_BEGIN, 4)
403 + buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 4) * 2);
404
405 doTestHelper(pdu, nextTest, function(message) {
406 is(message.messageId, CB_GSM_MESSAGEID_ETWS_BEGIN, "message.messageId");
407 ok(message.etws != null, "message.etws");
408 is(message.etws[name], mask != 0, "message.etws." + name);
409 });
410 }
411
412 function testReceiving_GSM_EmergencyUserAlert() {
413 log("Test receiving GSM Cell Broadcast - Emergency User Alert");
414
415 repeat(doTestEmergencyUserAlert_or_Popup.bind(null, "emergencyUserAlert"),
416 [0x2000, 0x0000], testReceiving_GSM_Popup);
417 }
418
419 function testReceiving_GSM_Popup() {
420 log("Test receiving GSM Cell Broadcast - Popup");
421
422 repeat(doTestEmergencyUserAlert_or_Popup.bind(null, "popup"),
423 [0x1000, 0x0000], testReceiving_GSM_Multipart);
424 }
425
426 function testReceiving_GSM_Multipart() {
427 log("Test receiving GSM Cell Broadcast - Multipart Messages");
428
429 function do_test(numParts, nextTest) {
430 let pdus = [];
431 for (let i = 1; i <= numParts; i++) {
432 let pdu = buildHexStr(0, 10)
433 + buildHexStr((i << 4) + numParts, 2)
434 + buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 6) * 2);
435 pdus.push(pdu);
436 }
437
438 doTestHelper(pdus, nextTest, function(message) {
439 is(message.body.length, (numParts * CB_MAX_CONTENT_7BIT),
440 "message.body");
441 });
442 }
443
444 repeat(do_test, seq(16, 1), testReceiving_GSM_ServiceCategory);
445 }
446
447 function testReceiving_GSM_ServiceCategory() {
448 log("Test receiving GSM Cell Broadcast - Service Category");
449
450 cbs.addEventListener("received", function onreceived(event) {
451 cbs.removeEventListener("received", onreceived);
452
453 let message = event.message;
454
455 // Bug 910091
456 // "Service Category" is not defined in GSM. We should always get '0' here.
457 is(message.cdmaServiceCategory, 0, "message.cdmaServiceCategory");
458
459 window.setTimeout(cleanUp, 0);
460 });
461
462 let pdu = buildHexStr(0, CB_MESSAGE_SIZE_GSM * 2);
463 sendCellBroadcastMessage(pdu);
464 }
465
466 function cleanUp() {
467 if (pendingEmulatorCmdCount > 0) {
468 window.setTimeout(cleanUp, 100);
469 return;
470 }
471
472 SpecialPowers.removePermission("mobileconnection", document);
473 SpecialPowers.removePermission("cellbroadcast", true, document);
474
475 finish();
476 }
477
478 waitFor(testGsmMessageAttributes, function() {
479 return navigator.mozMobileConnections[0].voice.connected;
480 });
481

mercurial