1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/cellbroadcast/tests/marionette/test_cellbroadcast_gsm.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,481 @@ 1.4 +/* Any copyright is dedicated to the Public Domain. 1.5 + * http://creativecommons.org/publicdomain/zero/1.0/ */ 1.6 + 1.7 +MARIONETTE_TIMEOUT = 30000; 1.8 + 1.9 +const PDU_DCS_CODING_GROUP_BITS = 0xF0; 1.10 +const PDU_DCS_MSG_CODING_7BITS_ALPHABET = 0x00; 1.11 +const PDU_DCS_MSG_CODING_8BITS_ALPHABET = 0x04; 1.12 +const PDU_DCS_MSG_CODING_16BITS_ALPHABET = 0x08; 1.13 + 1.14 +const PDU_DCS_MSG_CLASS_BITS = 0x03; 1.15 +const PDU_DCS_MSG_CLASS_NORMAL = 0xFF; 1.16 +const PDU_DCS_MSG_CLASS_0 = 0x00; 1.17 +const PDU_DCS_MSG_CLASS_ME_SPECIFIC = 0x01; 1.18 +const PDU_DCS_MSG_CLASS_SIM_SPECIFIC = 0x02; 1.19 +const PDU_DCS_MSG_CLASS_TE_SPECIFIC = 0x03; 1.20 +const PDU_DCS_MSG_CLASS_USER_1 = 0x04; 1.21 +const PDU_DCS_MSG_CLASS_USER_2 = 0x05; 1.22 + 1.23 +const GECKO_SMS_MESSAGE_CLASSES = {}; 1.24 +GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_NORMAL] = "normal"; 1.25 +GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_0] = "class-0"; 1.26 +GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_ME_SPECIFIC] = "class-1"; 1.27 +GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_SIM_SPECIFIC] = "class-2"; 1.28 +GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_TE_SPECIFIC] = "class-3"; 1.29 +GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_USER_1] = "user-1"; 1.30 +GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_USER_2] = "user-2"; 1.31 + 1.32 +const CB_MESSAGE_SIZE_GSM = 88; 1.33 + 1.34 +const CB_GSM_MESSAGEID_ETWS_BEGIN = 0x1100; 1.35 +const CB_GSM_MESSAGEID_ETWS_END = 0x1107; 1.36 + 1.37 +const CB_GSM_GEOGRAPHICAL_SCOPE_NAMES = [ 1.38 + "cell-immediate", 1.39 + "plmn", 1.40 + "location-area", 1.41 + "cell" 1.42 +]; 1.43 + 1.44 +const CB_ETWS_WARNING_TYPE_NAMES = [ 1.45 + "earthquake", 1.46 + "tsunami", 1.47 + "earthquake-tsunami", 1.48 + "test", 1.49 + "other" 1.50 +]; 1.51 + 1.52 +const CB_DCS_LANG_GROUP_1 = [ 1.53 + "de", "en", "it", "fr", "es", "nl", "sv", "da", "pt", "fi", 1.54 + "no", "el", "tr", "hu", "pl", null 1.55 +]; 1.56 +const CB_DCS_LANG_GROUP_2 = [ 1.57 + "cs", "he", "ar", "ru", "is", null, null, null, null, null, 1.58 + null, null, null, null, null, null 1.59 +]; 1.60 + 1.61 +const CB_MAX_CONTENT_7BIT = Math.floor((CB_MESSAGE_SIZE_GSM - 6) * 8 / 7); 1.62 +const CB_MAX_CONTENT_UCS2 = Math.floor((CB_MESSAGE_SIZE_GSM - 6) / 2); 1.63 + 1.64 +const BODY_7BITS = "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" 1.65 + + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" 1.66 + + "@@@@@@@@@@@@@"; // 93 ascii chars. 1.67 +const BODY_7BITS_IND = BODY_7BITS.substr(3); 1.68 +const BODY_UCS2 = "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" 1.69 + + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" 1.70 + + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" 1.71 + + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" 1.72 + + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" 1.73 + + "\u0000"; // 41 unicode chars. 1.74 +const BODY_UCS2_IND = BODY_UCS2.substr(1); 1.75 + 1.76 +SpecialPowers.addPermission("cellbroadcast", true, document); 1.77 +SpecialPowers.addPermission("mobileconnection", true, document); 1.78 + 1.79 +is(BODY_7BITS.length, CB_MAX_CONTENT_7BIT, "BODY_7BITS.length"); 1.80 +is(BODY_7BITS_IND.length, CB_MAX_CONTENT_7BIT - 3, "BODY_7BITS_IND.length"); 1.81 +is(BODY_UCS2.length, CB_MAX_CONTENT_UCS2, "BODY_UCS2.length"); 1.82 +is(BODY_UCS2_IND.length, CB_MAX_CONTENT_UCS2 - 1, "BODY_UCS2_IND.length") 1.83 + 1.84 +let cbs = window.navigator.mozCellBroadcast; 1.85 +ok(cbs instanceof window.MozCellBroadcast, 1.86 + "mozCellBroadcast is instanceof " + cbs.constructor); 1.87 + 1.88 +let pendingEmulatorCmdCount = 0; 1.89 +function sendCellBroadcastMessage(pdu, callback) { 1.90 + pendingEmulatorCmdCount++; 1.91 + 1.92 + let cmd = "cbs pdu " + pdu; 1.93 + runEmulatorCmd(cmd, function(result) { 1.94 + pendingEmulatorCmdCount--; 1.95 + 1.96 + is(result[0], "OK", "Emulator response"); 1.97 + 1.98 + if (callback) { 1.99 + window.setTimeout(callback, 0); 1.100 + } 1.101 + }); 1.102 +} 1.103 + 1.104 +function buildHexStr(n, numSemiOctets) { 1.105 + let str = n.toString(16); 1.106 + ok(str.length <= numSemiOctets); 1.107 + while (str.length < numSemiOctets) { 1.108 + str = "0" + str; 1.109 + } 1.110 + return str; 1.111 +} 1.112 + 1.113 +function seq(end, begin) { 1.114 + let result = []; 1.115 + for (let i = begin || 0; i < end; i++) { 1.116 + result.push(i); 1.117 + } 1.118 + return result; 1.119 +} 1.120 + 1.121 +function repeat(func, array, oncomplete) { 1.122 + (function do_call(index) { 1.123 + let next = index < (array.length - 1) ? do_call.bind(null, index + 1) : oncomplete; 1.124 + func.apply(null, [array[index], next]); 1.125 + })(0); 1.126 +} 1.127 + 1.128 +function doTestHelper(pdu, nextTest, checkFunc) { 1.129 + cbs.addEventListener("received", function onreceived(event) { 1.130 + cbs.removeEventListener("received", onreceived); 1.131 + 1.132 + checkFunc(event.message); 1.133 + 1.134 + window.setTimeout(nextTest, 0); 1.135 + }); 1.136 + 1.137 + if (Array.isArray(pdu)) { 1.138 + repeat(sendCellBroadcastMessage, pdu); 1.139 + } else { 1.140 + sendCellBroadcastMessage(pdu); 1.141 + } 1.142 +} 1.143 + 1.144 +/** 1.145 + * Tests receiving Cell Broadcast messages, event instance type, all attributes 1.146 + * of CellBroadcastMessage exist. 1.147 + */ 1.148 +function testGsmMessageAttributes() { 1.149 + log("Test GSM Cell Broadcast message attributes"); 1.150 + 1.151 + cbs.addEventListener("received", function onreceived(event) { 1.152 + cbs.removeEventListener("received", onreceived); 1.153 + 1.154 + // Bug 838542: following check throws an exception and fails this case. 1.155 + // ok(event instanceof MozCellBroadcastEvent, 1.156 + // "event is instanceof " + event.constructor) 1.157 + ok(event, "event is valid"); 1.158 + 1.159 + let message = event.message; 1.160 + ok(message, "event.message is valid"); 1.161 + 1.162 + // Attributes other than `language` and `body` should always be assigned. 1.163 + ok(message.gsmGeographicalScope != null, "message.gsmGeographicalScope"); 1.164 + ok(message.messageCode != null, "message.messageCode"); 1.165 + ok(message.messageId != null, "message.messageId"); 1.166 + ok(message.language != null, "message.language"); 1.167 + ok(message.body != null, "message.body"); 1.168 + ok(message.messageClass != null, "message.messageClass"); 1.169 + ok(message.timestamp != null, "message.timestamp"); 1.170 + ok('etws' in message, "message.etws"); 1.171 + if (message.etws) { 1.172 + ok('warningType' in message.etws, "message.etws.warningType"); 1.173 + ok(message.etws.emergencyUserAlert != null, "message.etws.emergencyUserAlert"); 1.174 + ok(message.etws.popup != null, "message.etws.popup"); 1.175 + } 1.176 + ok(message.cdmaServiceCategory != null, "message.cdmaServiceCategory"); 1.177 + 1.178 + window.setTimeout(testReceiving_GSM_GeographicalScope, 0); 1.179 + }); 1.180 + 1.181 + // Here we use a simple GSM message for test. 1.182 + let pdu = buildHexStr(0, CB_MESSAGE_SIZE_GSM * 2); 1.183 + sendCellBroadcastMessage(pdu); 1.184 +} 1.185 + 1.186 +function testReceiving_GSM_GeographicalScope() { 1.187 + log("Test receiving GSM Cell Broadcast - Geographical Scope"); 1.188 + 1.189 + function do_test(gs, nextTest) { 1.190 + let pdu = buildHexStr(((gs & 0x03) << 14), 4) 1.191 + + buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 2) * 2); 1.192 + 1.193 + doTestHelper(pdu, nextTest, function(message) { 1.194 + is(message.gsmGeographicalScope, CB_GSM_GEOGRAPHICAL_SCOPE_NAMES[gs], 1.195 + "message.gsmGeographicalScope"); 1.196 + }); 1.197 + } 1.198 + 1.199 + repeat(do_test, seq(CB_GSM_GEOGRAPHICAL_SCOPE_NAMES.length), 1.200 + testReceiving_GSM_MessageCode); 1.201 +} 1.202 + 1.203 +function testReceiving_GSM_MessageCode() { 1.204 + log("Test receiving GSM Cell Broadcast - Message Code"); 1.205 + 1.206 + // Message Code has 10 bits, and is ORed into a 16 bits 'serial' number. Here 1.207 + // we test every single bit to verify the operation doesn't go wrong. 1.208 + let messageCodesToTest = [ 1.209 + 0x000, 0x001, 0x002, 0x004, 0x008, 0x010, 0x020, 0x040, 1.210 + 0x080, 0x100, 0x200, 0x251 1.211 + ]; 1.212 + 1.213 + function do_test(messageCode, nextTest) { 1.214 + let pdu = buildHexStr(((messageCode & 0x3FF) << 4), 4) 1.215 + + buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 2) * 2); 1.216 + 1.217 + doTestHelper(pdu, nextTest, function(message) { 1.218 + is(message.messageCode, messageCode, "message.messageCode"); 1.219 + }); 1.220 + } 1.221 + 1.222 + repeat(do_test, messageCodesToTest, testReceiving_GSM_MessageId); 1.223 +} 1.224 + 1.225 +function testReceiving_GSM_MessageId() { 1.226 + log("Test receiving GSM Cell Broadcast - Message Identifier"); 1.227 + 1.228 + // Message Identifier has 16 bits, but no bitwise operation is needed. 1.229 + // Test some selected values only. 1.230 + let messageIdsToTest = [ 1.231 + 0x0000, 0x0001, 0x0010, 0x0100, 0x1000, 0x1111, 0x8888, 0x8811, 1.232 + ]; 1.233 + 1.234 + function do_test(messageId, nextTest) { 1.235 + let pdu = buildHexStr(0, 4) 1.236 + + buildHexStr((messageId & 0xFFFF), 4) 1.237 + + buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 4) * 2); 1.238 + 1.239 + doTestHelper(pdu, nextTest, function(message) { 1.240 + is(message.messageId, messageId, "message.messageId"); 1.241 + ok(message.etws == null, "message.etws"); 1.242 + }); 1.243 + } 1.244 + 1.245 + repeat(do_test, messageIdsToTest, testReceiving_GSM_Language_and_Body); 1.246 +} 1.247 + 1.248 +// Copied from GsmPDUHelper.readCbDataCodingScheme 1.249 +function decodeDataCodingScheme(dcs) { 1.250 + let language = null; 1.251 + let hasLanguageIndicator = false; 1.252 + let encoding = PDU_DCS_MSG_CODING_7BITS_ALPHABET; 1.253 + let messageClass = PDU_DCS_MSG_CLASS_NORMAL; 1.254 + 1.255 + switch (dcs & PDU_DCS_CODING_GROUP_BITS) { 1.256 + case 0x00: // 0000 1.257 + language = CB_DCS_LANG_GROUP_1[dcs & 0x0F]; 1.258 + break; 1.259 + 1.260 + case 0x10: // 0001 1.261 + switch (dcs & 0x0F) { 1.262 + case 0x00: 1.263 + hasLanguageIndicator = true; 1.264 + break; 1.265 + case 0x01: 1.266 + encoding = PDU_DCS_MSG_CODING_16BITS_ALPHABET; 1.267 + hasLanguageIndicator = true; 1.268 + break; 1.269 + } 1.270 + break; 1.271 + 1.272 + case 0x20: // 0010 1.273 + language = CB_DCS_LANG_GROUP_2[dcs & 0x0F]; 1.274 + break; 1.275 + 1.276 + case 0x40: // 01xx 1.277 + case 0x50: 1.278 + //case 0x60: 1.279 + //case 0x70: 1.280 + case 0x90: // 1001 1.281 + encoding = (dcs & 0x0C); 1.282 + if (encoding == 0x0C) { 1.283 + encoding = PDU_DCS_MSG_CODING_7BITS_ALPHABET; 1.284 + } 1.285 + messageClass = (dcs & PDU_DCS_MSG_CLASS_BITS); 1.286 + break; 1.287 + 1.288 + case 0xF0: 1.289 + encoding = (dcs & 0x04) ? PDU_DCS_MSG_CODING_8BITS_ALPHABET 1.290 + : PDU_DCS_MSG_CODING_7BITS_ALPHABET; 1.291 + switch(dcs & PDU_DCS_MSG_CLASS_BITS) { 1.292 + case 0x01: messageClass = PDU_DCS_MSG_CLASS_USER_1; break; 1.293 + case 0x02: messageClass = PDU_DCS_MSG_CLASS_USER_2; break; 1.294 + case 0x03: messageClass = PDU_DCS_MSG_CLASS_TE_SPECIFIC; break; 1.295 + } 1.296 + break; 1.297 + 1.298 + case 0x30: // 0011 (Reserved) 1.299 + case 0x80: // 1000 (Reserved) 1.300 + case 0xA0: // 1010..1100 (Reserved) 1.301 + case 0xB0: 1.302 + case 0xC0: 1.303 + break; 1.304 + default: 1.305 + throw new Error("Unsupported CBS data coding scheme: " + dcs); 1.306 + } 1.307 + 1.308 + return [encoding, language, hasLanguageIndicator, 1.309 + GECKO_SMS_MESSAGE_CLASSES[messageClass]]; 1.310 +} 1.311 + 1.312 +function testReceiving_GSM_Language_and_Body() { 1.313 + log("Test receiving GSM Cell Broadcast - Language & Body"); 1.314 + 1.315 + function do_test(dcs) { 1.316 + let encoding, language, indicator, messageClass; 1.317 + try { 1.318 + [encoding, language, indicator, messageClass] = decodeDataCodingScheme(dcs); 1.319 + } catch (e) { 1.320 + // Unsupported coding group, skip. 1.321 + let nextGroup = (dcs & PDU_DCS_CODING_GROUP_BITS) + 0x10; 1.322 + window.setTimeout(do_test.bind(null, nextGroup), 0); 1.323 + return; 1.324 + } 1.325 + 1.326 + let pdu = buildHexStr(0, 8) 1.327 + + buildHexStr(dcs, 2) 1.328 + + buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 5) * 2); 1.329 + 1.330 + let nextTest = (dcs < 0xFF) ? do_test.bind(null, dcs + 1) 1.331 + : testReceiving_GSM_Timestamp; 1.332 + doTestHelper(pdu, nextTest, function(message) { 1.333 + if (language) { 1.334 + is(message.language, language, "message.language"); 1.335 + } else if (indicator) { 1.336 + is(message.language, "@@", "message.language"); 1.337 + } else { 1.338 + ok(message.language == null, "message.language"); 1.339 + } 1.340 + 1.341 + switch (encoding) { 1.342 + case PDU_DCS_MSG_CODING_7BITS_ALPHABET: 1.343 + is(message.body, indicator ? BODY_7BITS_IND : BODY_7BITS, "message.body"); 1.344 + break; 1.345 + case PDU_DCS_MSG_CODING_8BITS_ALPHABET: 1.346 + ok(message.body == null, "message.body"); 1.347 + break; 1.348 + case PDU_DCS_MSG_CODING_16BITS_ALPHABET: 1.349 + is(message.body, indicator ? BODY_UCS2_IND : BODY_UCS2, "message.body"); 1.350 + break; 1.351 + } 1.352 + 1.353 + is(message.messageClass, messageClass, "message.messageClass"); 1.354 + }); 1.355 + } 1.356 + 1.357 + do_test(0); 1.358 +} 1.359 + 1.360 +function testReceiving_GSM_Timestamp() { 1.361 + log("Test receiving GSM Cell Broadcast - Timestamp"); 1.362 + 1.363 + let pdu = buildHexStr(0, CB_MESSAGE_SIZE_GSM * 2); 1.364 + doTestHelper(pdu, testReceiving_GSM_WarningType, function(message) { 1.365 + // Cell Broadcast messages do not contain a timestamp field (however, ETWS 1.366 + // does). We only check the timestamp doesn't go too far (60 seconds) here. 1.367 + let msMessage = message.timestamp.getTime(); 1.368 + let msNow = Date.now(); 1.369 + ok(Math.abs(msMessage - msNow) < (1000 * 60), "message.timestamp"); 1.370 + }); 1.371 +} 1.372 + 1.373 +function testReceiving_GSM_WarningType() { 1.374 + log("Test receiving GSM Cell Broadcast - Warning Type"); 1.375 + 1.376 + let messageIdsToTest = []; 1.377 + for (let i = CB_GSM_MESSAGEID_ETWS_BEGIN; i <= CB_GSM_MESSAGEID_ETWS_END; i++) { 1.378 + messageIdsToTest.push(i); 1.379 + } 1.380 + 1.381 + function do_test(messageId, nextTest) { 1.382 + let pdu = buildHexStr(0, 4) 1.383 + + buildHexStr((messageId & 0xFFFF), 4) 1.384 + + buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 4) * 2); 1.385 + 1.386 + doTestHelper(pdu, nextTest, function(message) { 1.387 + is(message.messageId, messageId, "message.messageId"); 1.388 + ok(message.etws != null, "message.etws"); 1.389 + 1.390 + let offset = messageId - CB_GSM_MESSAGEID_ETWS_BEGIN; 1.391 + if (offset < CB_ETWS_WARNING_TYPE_NAMES.length) { 1.392 + is(message.etws.warningType, CB_ETWS_WARNING_TYPE_NAMES[offset], 1.393 + "message.etws.warningType"); 1.394 + } else { 1.395 + ok(message.etws.warningType == null, "message.etws.warningType"); 1.396 + } 1.397 + }); 1.398 + } 1.399 + 1.400 + repeat(do_test, messageIdsToTest, testReceiving_GSM_EmergencyUserAlert); 1.401 +} 1.402 + 1.403 +function doTestEmergencyUserAlert_or_Popup(name, mask, nextTest) { 1.404 + let pdu = buildHexStr(mask, 4) 1.405 + + buildHexStr(CB_GSM_MESSAGEID_ETWS_BEGIN, 4) 1.406 + + buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 4) * 2); 1.407 + 1.408 + doTestHelper(pdu, nextTest, function(message) { 1.409 + is(message.messageId, CB_GSM_MESSAGEID_ETWS_BEGIN, "message.messageId"); 1.410 + ok(message.etws != null, "message.etws"); 1.411 + is(message.etws[name], mask != 0, "message.etws." + name); 1.412 + }); 1.413 +} 1.414 + 1.415 +function testReceiving_GSM_EmergencyUserAlert() { 1.416 + log("Test receiving GSM Cell Broadcast - Emergency User Alert"); 1.417 + 1.418 + repeat(doTestEmergencyUserAlert_or_Popup.bind(null, "emergencyUserAlert"), 1.419 + [0x2000, 0x0000], testReceiving_GSM_Popup); 1.420 +} 1.421 + 1.422 +function testReceiving_GSM_Popup() { 1.423 + log("Test receiving GSM Cell Broadcast - Popup"); 1.424 + 1.425 + repeat(doTestEmergencyUserAlert_or_Popup.bind(null, "popup"), 1.426 + [0x1000, 0x0000], testReceiving_GSM_Multipart); 1.427 +} 1.428 + 1.429 +function testReceiving_GSM_Multipart() { 1.430 + log("Test receiving GSM Cell Broadcast - Multipart Messages"); 1.431 + 1.432 + function do_test(numParts, nextTest) { 1.433 + let pdus = []; 1.434 + for (let i = 1; i <= numParts; i++) { 1.435 + let pdu = buildHexStr(0, 10) 1.436 + + buildHexStr((i << 4) + numParts, 2) 1.437 + + buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 6) * 2); 1.438 + pdus.push(pdu); 1.439 + } 1.440 + 1.441 + doTestHelper(pdus, nextTest, function(message) { 1.442 + is(message.body.length, (numParts * CB_MAX_CONTENT_7BIT), 1.443 + "message.body"); 1.444 + }); 1.445 + } 1.446 + 1.447 + repeat(do_test, seq(16, 1), testReceiving_GSM_ServiceCategory); 1.448 +} 1.449 + 1.450 +function testReceiving_GSM_ServiceCategory() { 1.451 + log("Test receiving GSM Cell Broadcast - Service Category"); 1.452 + 1.453 + cbs.addEventListener("received", function onreceived(event) { 1.454 + cbs.removeEventListener("received", onreceived); 1.455 + 1.456 + let message = event.message; 1.457 + 1.458 + // Bug 910091 1.459 + // "Service Category" is not defined in GSM. We should always get '0' here. 1.460 + is(message.cdmaServiceCategory, 0, "message.cdmaServiceCategory"); 1.461 + 1.462 + window.setTimeout(cleanUp, 0); 1.463 + }); 1.464 + 1.465 + let pdu = buildHexStr(0, CB_MESSAGE_SIZE_GSM * 2); 1.466 + sendCellBroadcastMessage(pdu); 1.467 +} 1.468 + 1.469 +function cleanUp() { 1.470 + if (pendingEmulatorCmdCount > 0) { 1.471 + window.setTimeout(cleanUp, 100); 1.472 + return; 1.473 + } 1.474 + 1.475 + SpecialPowers.removePermission("mobileconnection", document); 1.476 + SpecialPowers.removePermission("cellbroadcast", true, document); 1.477 + 1.478 + finish(); 1.479 +} 1.480 + 1.481 +waitFor(testGsmMessageAttributes, function() { 1.482 + return navigator.mozMobileConnections[0].voice.connected; 1.483 +}); 1.484 +