1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/system/gonk/nfc_worker.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,433 @@ 1.4 +/* Copyright 2012 Mozilla Foundation and Mozilla contributors 1.5 + * 1.6 + * Licensed under the Apache License, Version 2.0 (the "License"); 1.7 + * you may not use this file except in compliance with the License. 1.8 + * You may obtain a copy of the License at 1.9 + * 1.10 + * http://www.apache.org/licenses/LICENSE-2.0 1.11 + * 1.12 + * Unless required by applicable law or agreed to in writing, software 1.13 + * distributed under the License is distributed on an "AS IS" BASIS, 1.14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.15 + * See the License for the specific language governing permissions and 1.16 + * limitations under the License. 1.17 + */ 1.18 + 1.19 +/* Copyright © 2013, Deutsche Telekom, Inc. */ 1.20 + 1.21 +"use strict"; 1.22 + 1.23 +importScripts("systemlibs.js", "nfc_consts.js"); 1.24 +importScripts("resource://gre/modules/workers/require.js"); 1.25 + 1.26 +// set to true in nfc_consts.js to see debug messages 1.27 +let DEBUG = DEBUG_WORKER; 1.28 + 1.29 +function getPaddingLen(len) { 1.30 + return (len % 4) ? (4 - len % 4) : 0; 1.31 +} 1.32 + 1.33 +let Buf = { 1.34 + __proto__: (function(){ 1.35 + return require("resource://gre/modules/workers/worker_buf.js").Buf; 1.36 + })(), 1.37 + 1.38 + init: function init() { 1.39 + this._init(); 1.40 + }, 1.41 + 1.42 + /** 1.43 + * Process one parcel. 1.44 + */ 1.45 + processParcel: function processParcel() { 1.46 + let pduType = this.readInt32(); 1.47 + if (DEBUG) debug("Number of bytes available in Parcel : " + this.readAvailable); 1.48 + NfcWorker.handleParcel(pduType, this.mCallback); 1.49 + }, 1.50 + 1.51 + /** 1.52 + * Start a new outgoing parcel. 1.53 + * 1.54 + * @param type 1.55 + * Integer specifying the request type. 1.56 + * @param callback 1.57 + */ 1.58 + newParcel: function newParcel(type, callback) { 1.59 + if (DEBUG) debug("New outgoing parcel of type " + type); 1.60 + this.mCallback = callback; 1.61 + // We're going to leave room for the parcel size at the beginning. 1.62 + this.outgoingIndex = this.PARCEL_SIZE_SIZE; 1.63 + this.writeInt32(type); 1.64 + }, 1.65 + 1.66 + simpleRequest: function simpleRequest(type) { 1.67 + this.newParcel(type); 1.68 + this.sendParcel(); 1.69 + }, 1.70 + 1.71 + onSendParcel: function onSendParcel(parcel) { 1.72 + postNfcMessage(parcel); 1.73 + }, 1.74 + 1.75 + /** 1.76 + * TODO: Bug 933593. Callback map of NFC_RESPONSE_XXX and RequestID 1.77 + * needs to be maintained 1.78 + */ 1.79 + mCallback: null, 1.80 +}; 1.81 + 1.82 +/** 1.83 + * Provide a high-level API representing NFC capabilities. 1.84 + * Rensponsible for converting NFC requests from Content process to binary data 1.85 + * and NFC Responses from binary data to dictionary objects. 1.86 + */ 1.87 +let NfcWorker = { 1.88 + /** 1.89 + * Handle incoming messages from the main UI thread. 1.90 + * 1.91 + * @param message 1.92 + * Object containing the message. Messages are supposed 1.93 + */ 1.94 + handleDOMMessage: function handleMessage(message) { 1.95 + if (DEBUG) debug("Received DOM message " + JSON.stringify(message)); 1.96 + let method = this[message.type]; 1.97 + if (typeof method != "function") { 1.98 + if (DEBUG) { 1.99 + debug("Don't know what to do with message " + JSON.stringify(message)); 1.100 + } 1.101 + return; 1.102 + } 1.103 + method.call(this, message); 1.104 + }, 1.105 + 1.106 + /** 1.107 + * Unmarshals a NDEF message 1.108 + */ 1.109 + unMarshallNdefMessage: function unMarshallNdefMessage() { 1.110 + let numOfRecords = Buf.readInt32(); 1.111 + debug("numOfRecords = " + numOfRecords); 1.112 + if (numOfRecords <= 0) { 1.113 + return null; 1.114 + } 1.115 + let records = []; 1.116 + 1.117 + for (let i = 0; i < numOfRecords; i++) { 1.118 + let tnf = Buf.readInt32() & 0xff; 1.119 + let typeLength = Buf.readInt32(); 1.120 + let type = Buf.readUint8Array(typeLength); 1.121 + let padding = getPaddingLen(typeLength); 1.122 + for (let i = 0; i < padding; i++) { 1.123 + Buf.readUint8(); 1.124 + } 1.125 + 1.126 + let idLength = Buf.readInt32(); 1.127 + let id = Buf.readUint8Array(idLength); 1.128 + padding = getPaddingLen(idLength); 1.129 + for (let i = 0; i < padding; i++) { 1.130 + Buf.readUint8(); 1.131 + } 1.132 + 1.133 + let payloadLength = Buf.readInt32(); 1.134 + let payload = Buf.readUint8Array(payloadLength); 1.135 + padding = getPaddingLen(payloadLength); 1.136 + for (let i = 0; i < padding; i++) { 1.137 + Buf.readUint8(); 1.138 + } 1.139 + records.push({tnf: tnf, 1.140 + type: type, 1.141 + id: id, 1.142 + payload: payload}); 1.143 + } 1.144 + return records; 1.145 + }, 1.146 + 1.147 + /** 1.148 + * Read and return NDEF data, if present. 1.149 + */ 1.150 + readNDEF: function readNDEF(message) { 1.151 + let cb = function callback() { 1.152 + let error = Buf.readInt32(); 1.153 + let sessionId = Buf.readInt32(); 1.154 + let records = this.unMarshallNdefMessage(); 1.155 + 1.156 + message.type = "ReadNDEFResponse"; 1.157 + message.sessionId = sessionId; 1.158 + message.records = records; 1.159 + message.status = (error === 0) ? GECKO_NFC_ERROR_SUCCESS : 1.160 + GECKO_NFC_ERROR_GENERIC_FAILURE; 1.161 + this.sendDOMMessage(message); 1.162 + } 1.163 + 1.164 + Buf.newParcel(NFC_REQUEST_READ_NDEF, cb); 1.165 + Buf.writeInt32(message.sessionId); 1.166 + Buf.sendParcel(); 1.167 + }, 1.168 + 1.169 + /** 1.170 + * Write to a target that accepts NDEF formattable data 1.171 + */ 1.172 + writeNDEF: function writeNDEF(message) { 1.173 + let cb = function callback() { 1.174 + let error = Buf.readInt32(); 1.175 + let sessionId = Buf.readInt32(); 1.176 + 1.177 + message.type = "WriteNDEFResponse"; 1.178 + message.sessionId = sessionId; 1.179 + message.status = (error === 0) ? GECKO_NFC_ERROR_SUCCESS : 1.180 + GECKO_NFC_ERROR_GENERIC_FAILURE; 1.181 + this.sendDOMMessage(message); 1.182 + }; 1.183 + 1.184 + Buf.newParcel(NFC_REQUEST_WRITE_NDEF, cb); 1.185 + Buf.writeInt32(message.sessionId); 1.186 + let records = message.records; 1.187 + let numRecords = records.length; 1.188 + Buf.writeInt32(numRecords); 1.189 + for (let i = 0; i < numRecords; i++) { 1.190 + let record = records[i]; 1.191 + Buf.writeInt32(record.tnf); 1.192 + 1.193 + let typeLength = record.type ? record.type.length : 0; 1.194 + Buf.writeInt32(typeLength); 1.195 + for (let j = 0; j < typeLength; j++) { 1.196 + Buf.writeUint8(record.type[j]); 1.197 + } 1.198 + let padding = getPaddingLen(typeLength); 1.199 + for (let i = 0; i < padding; i++) { 1.200 + Buf.writeUint8(0x00); 1.201 + } 1.202 + 1.203 + let idLength = record.id ? record.id.length : 0; 1.204 + Buf.writeInt32(idLength); 1.205 + for (let j = 0; j < idLength; j++) { 1.206 + Buf.writeUint8(record.id[j]); 1.207 + } 1.208 + padding = getPaddingLen(idLength); 1.209 + for (let i = 0; i < padding; i++) { 1.210 + Buf.writeUint8(0x00); 1.211 + } 1.212 + 1.213 + let payloadLength = record.payload ? record.payload.length : 0; 1.214 + Buf.writeInt32(payloadLength); 1.215 + for (let j = 0; j < payloadLength; j++) { 1.216 + Buf.writeUint8(record.payload[j]); 1.217 + } 1.218 + padding = getPaddingLen(payloadLength); 1.219 + for (let i = 0; i < padding; i++) { 1.220 + Buf.writeUint8(0x00); 1.221 + } 1.222 + } 1.223 + 1.224 + Buf.sendParcel(); 1.225 + }, 1.226 + 1.227 + /** 1.228 + * Make the NFC NDEF tag permanently read only 1.229 + */ 1.230 + makeReadOnlyNDEF: function makeReadOnlyNDEF(message) { 1.231 + let cb = function callback() { 1.232 + let error = Buf.readInt32(); 1.233 + let sessionId = Buf.readInt32(); 1.234 + 1.235 + message.type = "MakeReadOnlyNDEFResponse"; 1.236 + message.sessionId = sessionId; 1.237 + message.status = (error === 0) ? GECKO_NFC_ERROR_SUCCESS : 1.238 + GECKO_NFC_ERROR_GENERIC_FAILURE; 1.239 + this.sendDOMMessage(message); 1.240 + }; 1.241 + 1.242 + Buf.newParcel(NFC_REQUEST_MAKE_NDEF_READ_ONLY, cb); 1.243 + Buf.writeInt32(message.sessionId); 1.244 + Buf.sendParcel(); 1.245 + }, 1.246 + 1.247 + /** 1.248 + * Retrieve metadata describing the NDEF formatted data, if present. 1.249 + */ 1.250 + getDetailsNDEF: function getDetailsNDEF(message) { 1.251 + let cb = function callback() { 1.252 + let error = Buf.readInt32(); 1.253 + let sessionId = Buf.readInt32(); 1.254 + let isReadOnly = Buf.readUint8(); 1.255 + let canBeMadeReadOnly = Buf.readUint8(); 1.256 + // Ensure that padding is taken care here after reading two successive uint8's 1.257 + Buf.readUint8(); 1.258 + Buf.readUint8(); 1.259 + let maxSupportedLength = Buf.readInt32(); 1.260 + 1.261 + message.type = "GetDetailsNDEFResponse"; 1.262 + message.sessionId = sessionId; 1.263 + message.isReadOnly = isReadOnly; 1.264 + message.canBeMadeReadOnly = canBeMadeReadOnly; 1.265 + message.maxSupportedLength = maxSupportedLength; 1.266 + message.status = (error === 0) ? GECKO_NFC_ERROR_SUCCESS : 1.267 + GECKO_NFC_ERROR_GENERIC_FAILURE; 1.268 + this.sendDOMMessage(message); 1.269 + }; 1.270 + Buf.newParcel(NFC_REQUEST_GET_DETAILS, cb); 1.271 + Buf.writeInt32(message.sessionId); 1.272 + Buf.sendParcel(); 1.273 + }, 1.274 + 1.275 + 1.276 + /** 1.277 + * Open a connection to the NFC target. 1.278 + */ 1.279 + connect: function connect(message) { 1.280 + let cb = function callback() { 1.281 + let error = Buf.readInt32(); 1.282 + let sessionId = Buf.readInt32(); 1.283 + 1.284 + message.type = "ConnectResponse"; 1.285 + message.sessionId = sessionId; 1.286 + message.status = (error === 0) ? GECKO_NFC_ERROR_SUCCESS : 1.287 + GECKO_NFC_ERROR_GENERIC_FAILURE; 1.288 + this.sendDOMMessage(message); 1.289 + }; 1.290 + 1.291 + Buf.newParcel(NFC_REQUEST_CONNECT, cb); 1.292 + Buf.writeInt32(message.sessionId); 1.293 + Buf.writeInt32(message.techType); 1.294 + Buf.sendParcel(); 1.295 + }, 1.296 + 1.297 + /** 1.298 + * NFC Configuration 1.299 + */ 1.300 + config: function config(message) { 1.301 + let cb = function callback() { 1.302 + let error = Buf.readInt32(); 1.303 + 1.304 + message.type = "ConfigResponse"; 1.305 + message.status = (error === 0) ? GECKO_NFC_ERROR_SUCCESS : 1.306 + GECKO_NFC_ERROR_GENERIC_FAILURE; 1.307 + this.sendDOMMessage(message); 1.308 + }; 1.309 + 1.310 + Buf.newParcel(NFC_REQUEST_CONFIG , cb); 1.311 + Buf.writeInt32(message.powerLevel); 1.312 + Buf.sendParcel(); 1.313 + }, 1.314 + 1.315 + /** 1.316 + * Close connection to the NFC target. 1.317 + */ 1.318 + close: function close(message) { 1.319 + let cb = function callback() { 1.320 + let error = Buf.readInt32(); 1.321 + let sessionId = Buf.readInt32(); 1.322 + 1.323 + message.type = "CloseResponse"; 1.324 + message.sessionId = sessionId; 1.325 + message.status = (error === 0) ? GECKO_NFC_ERROR_SUCCESS : 1.326 + GECKO_NFC_ERROR_GENERIC_FAILURE; 1.327 + this.sendDOMMessage(message); 1.328 + }; 1.329 + 1.330 + Buf.newParcel(NFC_REQUEST_CLOSE , cb); 1.331 + Buf.writeInt32(message.sessionId); 1.332 + Buf.sendParcel(); 1.333 + }, 1.334 + 1.335 + handleParcel: function handleParcel(request_type, callback) { 1.336 + let method = this[request_type]; 1.337 + if (typeof method == "function") { 1.338 + if (DEBUG) debug("Handling parcel as " + method.name); 1.339 + method.call(this); 1.340 + } else if (typeof callback == "function") { 1.341 + callback.call(this, request_type); 1.342 + this.mCallback = null; 1.343 + } else { 1.344 + debug("Unable to handle ReqType:"+request_type); 1.345 + } 1.346 + }, 1.347 + 1.348 + /** 1.349 + * Send messages to the main UI thread. 1.350 + */ 1.351 + sendDOMMessage: function sendDOMMessage(message) { 1.352 + postMessage(message); 1.353 + } 1.354 +}; 1.355 + 1.356 +/** 1.357 + * Notification Handlers 1.358 + */ 1.359 +NfcWorker[NFC_NOTIFICATION_INITIALIZED] = function NFC_NOTIFICATION_INITIALIZED () { 1.360 + let status = Buf.readInt32(); 1.361 + let majorVersion = Buf.readInt32(); 1.362 + let minorVersion = Buf.readInt32(); 1.363 + debug("NFC_NOTIFICATION_INITIALIZED status:" + status); 1.364 + if ((majorVersion != NFC_MAJOR_VERSION) || (minorVersion != NFC_MINOR_VERSION)) { 1.365 + debug("Version Mismatch! Current Supported Version : " + 1.366 + NFC_MAJOR_VERSION + "." + NFC_MINOR_VERSION + 1.367 + " Received Version : " + majorVersion + "." + minorVersion); 1.368 + } 1.369 +}; 1.370 + 1.371 +NfcWorker[NFC_NOTIFICATION_TECH_DISCOVERED] = function NFC_NOTIFICATION_TECH_DISCOVERED() { 1.372 + debug("NFC_NOTIFICATION_TECH_DISCOVERED"); 1.373 + let techList = []; 1.374 + let records = null; 1.375 + 1.376 + let sessionId = Buf.readInt32(); 1.377 + let techCount = Buf.readInt32(); 1.378 + for (let count = 0; count < techCount; count++) { 1.379 + let tech = NFC_TECHS[Buf.readUint8()]; 1.380 + if (tech) { 1.381 + techList.push(tech); 1.382 + } 1.383 + } 1.384 + 1.385 + let padding = getPaddingLen(techCount); 1.386 + for (let i = 0; i < padding; i++) { 1.387 + Buf.readUint8(); 1.388 + } 1.389 + 1.390 + let ndefMsgCount = Buf.readInt32(); 1.391 + if (ndefMsgCount > 0) { 1.392 + records = this.unMarshallNdefMessage(); 1.393 + } 1.394 + this.sendDOMMessage({type: "techDiscovered", 1.395 + sessionId: sessionId, 1.396 + techList: techList, 1.397 + records: records}); 1.398 +}; 1.399 + 1.400 +NfcWorker[NFC_NOTIFICATION_TECH_LOST] = function NFC_NOTIFICATION_TECH_LOST() { 1.401 + debug("NFC_NOTIFICATION_TECH_LOST"); 1.402 + let sessionId = Buf.readInt32(); 1.403 + debug("sessionId = " + sessionId); 1.404 + this.sendDOMMessage({type: "techLost", 1.405 + sessionId: sessionId, 1.406 + }); 1.407 +}; 1.408 + 1.409 +/** 1.410 + * Global stuff. 1.411 + */ 1.412 + 1.413 +if (!this.debug) { 1.414 + // Debugging stub that goes nowhere. 1.415 + this.debug = function debug(message) { 1.416 + dump("Nfc Worker: " + message + "\n"); 1.417 + }; 1.418 +} 1.419 + 1.420 +// Initialize buffers. This is a separate function so that unit tests can 1.421 +// re-initialize the buffers at will. 1.422 +Buf.init(); 1.423 + 1.424 +function onNfcMessage(data) { 1.425 + Buf.processIncoming(data); 1.426 +}; 1.427 + 1.428 +onmessage = function onmessage(event) { 1.429 + NfcWorker.handleDOMMessage(event.data); 1.430 +}; 1.431 + 1.432 +onerror = function onerror(event) { 1.433 + debug("OnError: event: " + JSON.stringify(event)); 1.434 + debug("NFC Worker error " + event.message + " " + event.filename + ":" + 1.435 + event.lineno + ":\n"); 1.436 +};