michael@0: /* Copyright 2012 Mozilla Foundation and Mozilla contributors michael@0: * michael@0: * Licensed under the Apache License, Version 2.0 (the "License"); michael@0: * you may not use this file except in compliance with the License. michael@0: * You may obtain a copy of the License at michael@0: * michael@0: * http://www.apache.org/licenses/LICENSE-2.0 michael@0: * michael@0: * Unless required by applicable law or agreed to in writing, software michael@0: * distributed under the License is distributed on an "AS IS" BASIS, michael@0: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. michael@0: * See the License for the specific language governing permissions and michael@0: * limitations under the License. michael@0: */ michael@0: michael@0: /* Copyright © 2013, Deutsche Telekom, Inc. */ michael@0: michael@0: "use strict"; michael@0: michael@0: importScripts("systemlibs.js", "nfc_consts.js"); michael@0: importScripts("resource://gre/modules/workers/require.js"); michael@0: michael@0: // set to true in nfc_consts.js to see debug messages michael@0: let DEBUG = DEBUG_WORKER; michael@0: michael@0: function getPaddingLen(len) { michael@0: return (len % 4) ? (4 - len % 4) : 0; michael@0: } michael@0: michael@0: let Buf = { michael@0: __proto__: (function(){ michael@0: return require("resource://gre/modules/workers/worker_buf.js").Buf; michael@0: })(), michael@0: michael@0: init: function init() { michael@0: this._init(); michael@0: }, michael@0: michael@0: /** michael@0: * Process one parcel. michael@0: */ michael@0: processParcel: function processParcel() { michael@0: let pduType = this.readInt32(); michael@0: if (DEBUG) debug("Number of bytes available in Parcel : " + this.readAvailable); michael@0: NfcWorker.handleParcel(pduType, this.mCallback); michael@0: }, michael@0: michael@0: /** michael@0: * Start a new outgoing parcel. michael@0: * michael@0: * @param type michael@0: * Integer specifying the request type. michael@0: * @param callback michael@0: */ michael@0: newParcel: function newParcel(type, callback) { michael@0: if (DEBUG) debug("New outgoing parcel of type " + type); michael@0: this.mCallback = callback; michael@0: // We're going to leave room for the parcel size at the beginning. michael@0: this.outgoingIndex = this.PARCEL_SIZE_SIZE; michael@0: this.writeInt32(type); michael@0: }, michael@0: michael@0: simpleRequest: function simpleRequest(type) { michael@0: this.newParcel(type); michael@0: this.sendParcel(); michael@0: }, michael@0: michael@0: onSendParcel: function onSendParcel(parcel) { michael@0: postNfcMessage(parcel); michael@0: }, michael@0: michael@0: /** michael@0: * TODO: Bug 933593. Callback map of NFC_RESPONSE_XXX and RequestID michael@0: * needs to be maintained michael@0: */ michael@0: mCallback: null, michael@0: }; michael@0: michael@0: /** michael@0: * Provide a high-level API representing NFC capabilities. michael@0: * Rensponsible for converting NFC requests from Content process to binary data michael@0: * and NFC Responses from binary data to dictionary objects. michael@0: */ michael@0: let NfcWorker = { michael@0: /** michael@0: * Handle incoming messages from the main UI thread. michael@0: * michael@0: * @param message michael@0: * Object containing the message. Messages are supposed michael@0: */ michael@0: handleDOMMessage: function handleMessage(message) { michael@0: if (DEBUG) debug("Received DOM message " + JSON.stringify(message)); michael@0: let method = this[message.type]; michael@0: if (typeof method != "function") { michael@0: if (DEBUG) { michael@0: debug("Don't know what to do with message " + JSON.stringify(message)); michael@0: } michael@0: return; michael@0: } michael@0: method.call(this, message); michael@0: }, michael@0: michael@0: /** michael@0: * Unmarshals a NDEF message michael@0: */ michael@0: unMarshallNdefMessage: function unMarshallNdefMessage() { michael@0: let numOfRecords = Buf.readInt32(); michael@0: debug("numOfRecords = " + numOfRecords); michael@0: if (numOfRecords <= 0) { michael@0: return null; michael@0: } michael@0: let records = []; michael@0: michael@0: for (let i = 0; i < numOfRecords; i++) { michael@0: let tnf = Buf.readInt32() & 0xff; michael@0: let typeLength = Buf.readInt32(); michael@0: let type = Buf.readUint8Array(typeLength); michael@0: let padding = getPaddingLen(typeLength); michael@0: for (let i = 0; i < padding; i++) { michael@0: Buf.readUint8(); michael@0: } michael@0: michael@0: let idLength = Buf.readInt32(); michael@0: let id = Buf.readUint8Array(idLength); michael@0: padding = getPaddingLen(idLength); michael@0: for (let i = 0; i < padding; i++) { michael@0: Buf.readUint8(); michael@0: } michael@0: michael@0: let payloadLength = Buf.readInt32(); michael@0: let payload = Buf.readUint8Array(payloadLength); michael@0: padding = getPaddingLen(payloadLength); michael@0: for (let i = 0; i < padding; i++) { michael@0: Buf.readUint8(); michael@0: } michael@0: records.push({tnf: tnf, michael@0: type: type, michael@0: id: id, michael@0: payload: payload}); michael@0: } michael@0: return records; michael@0: }, michael@0: michael@0: /** michael@0: * Read and return NDEF data, if present. michael@0: */ michael@0: readNDEF: function readNDEF(message) { michael@0: let cb = function callback() { michael@0: let error = Buf.readInt32(); michael@0: let sessionId = Buf.readInt32(); michael@0: let records = this.unMarshallNdefMessage(); michael@0: michael@0: message.type = "ReadNDEFResponse"; michael@0: message.sessionId = sessionId; michael@0: message.records = records; michael@0: message.status = (error === 0) ? GECKO_NFC_ERROR_SUCCESS : michael@0: GECKO_NFC_ERROR_GENERIC_FAILURE; michael@0: this.sendDOMMessage(message); michael@0: } michael@0: michael@0: Buf.newParcel(NFC_REQUEST_READ_NDEF, cb); michael@0: Buf.writeInt32(message.sessionId); michael@0: Buf.sendParcel(); michael@0: }, michael@0: michael@0: /** michael@0: * Write to a target that accepts NDEF formattable data michael@0: */ michael@0: writeNDEF: function writeNDEF(message) { michael@0: let cb = function callback() { michael@0: let error = Buf.readInt32(); michael@0: let sessionId = Buf.readInt32(); michael@0: michael@0: message.type = "WriteNDEFResponse"; michael@0: message.sessionId = sessionId; michael@0: message.status = (error === 0) ? GECKO_NFC_ERROR_SUCCESS : michael@0: GECKO_NFC_ERROR_GENERIC_FAILURE; michael@0: this.sendDOMMessage(message); michael@0: }; michael@0: michael@0: Buf.newParcel(NFC_REQUEST_WRITE_NDEF, cb); michael@0: Buf.writeInt32(message.sessionId); michael@0: let records = message.records; michael@0: let numRecords = records.length; michael@0: Buf.writeInt32(numRecords); michael@0: for (let i = 0; i < numRecords; i++) { michael@0: let record = records[i]; michael@0: Buf.writeInt32(record.tnf); michael@0: michael@0: let typeLength = record.type ? record.type.length : 0; michael@0: Buf.writeInt32(typeLength); michael@0: for (let j = 0; j < typeLength; j++) { michael@0: Buf.writeUint8(record.type[j]); michael@0: } michael@0: let padding = getPaddingLen(typeLength); michael@0: for (let i = 0; i < padding; i++) { michael@0: Buf.writeUint8(0x00); michael@0: } michael@0: michael@0: let idLength = record.id ? record.id.length : 0; michael@0: Buf.writeInt32(idLength); michael@0: for (let j = 0; j < idLength; j++) { michael@0: Buf.writeUint8(record.id[j]); michael@0: } michael@0: padding = getPaddingLen(idLength); michael@0: for (let i = 0; i < padding; i++) { michael@0: Buf.writeUint8(0x00); michael@0: } michael@0: michael@0: let payloadLength = record.payload ? record.payload.length : 0; michael@0: Buf.writeInt32(payloadLength); michael@0: for (let j = 0; j < payloadLength; j++) { michael@0: Buf.writeUint8(record.payload[j]); michael@0: } michael@0: padding = getPaddingLen(payloadLength); michael@0: for (let i = 0; i < padding; i++) { michael@0: Buf.writeUint8(0x00); michael@0: } michael@0: } michael@0: michael@0: Buf.sendParcel(); michael@0: }, michael@0: michael@0: /** michael@0: * Make the NFC NDEF tag permanently read only michael@0: */ michael@0: makeReadOnlyNDEF: function makeReadOnlyNDEF(message) { michael@0: let cb = function callback() { michael@0: let error = Buf.readInt32(); michael@0: let sessionId = Buf.readInt32(); michael@0: michael@0: message.type = "MakeReadOnlyNDEFResponse"; michael@0: message.sessionId = sessionId; michael@0: message.status = (error === 0) ? GECKO_NFC_ERROR_SUCCESS : michael@0: GECKO_NFC_ERROR_GENERIC_FAILURE; michael@0: this.sendDOMMessage(message); michael@0: }; michael@0: michael@0: Buf.newParcel(NFC_REQUEST_MAKE_NDEF_READ_ONLY, cb); michael@0: Buf.writeInt32(message.sessionId); michael@0: Buf.sendParcel(); michael@0: }, michael@0: michael@0: /** michael@0: * Retrieve metadata describing the NDEF formatted data, if present. michael@0: */ michael@0: getDetailsNDEF: function getDetailsNDEF(message) { michael@0: let cb = function callback() { michael@0: let error = Buf.readInt32(); michael@0: let sessionId = Buf.readInt32(); michael@0: let isReadOnly = Buf.readUint8(); michael@0: let canBeMadeReadOnly = Buf.readUint8(); michael@0: // Ensure that padding is taken care here after reading two successive uint8's michael@0: Buf.readUint8(); michael@0: Buf.readUint8(); michael@0: let maxSupportedLength = Buf.readInt32(); michael@0: michael@0: message.type = "GetDetailsNDEFResponse"; michael@0: message.sessionId = sessionId; michael@0: message.isReadOnly = isReadOnly; michael@0: message.canBeMadeReadOnly = canBeMadeReadOnly; michael@0: message.maxSupportedLength = maxSupportedLength; michael@0: message.status = (error === 0) ? GECKO_NFC_ERROR_SUCCESS : michael@0: GECKO_NFC_ERROR_GENERIC_FAILURE; michael@0: this.sendDOMMessage(message); michael@0: }; michael@0: Buf.newParcel(NFC_REQUEST_GET_DETAILS, cb); michael@0: Buf.writeInt32(message.sessionId); michael@0: Buf.sendParcel(); michael@0: }, michael@0: michael@0: michael@0: /** michael@0: * Open a connection to the NFC target. michael@0: */ michael@0: connect: function connect(message) { michael@0: let cb = function callback() { michael@0: let error = Buf.readInt32(); michael@0: let sessionId = Buf.readInt32(); michael@0: michael@0: message.type = "ConnectResponse"; michael@0: message.sessionId = sessionId; michael@0: message.status = (error === 0) ? GECKO_NFC_ERROR_SUCCESS : michael@0: GECKO_NFC_ERROR_GENERIC_FAILURE; michael@0: this.sendDOMMessage(message); michael@0: }; michael@0: michael@0: Buf.newParcel(NFC_REQUEST_CONNECT, cb); michael@0: Buf.writeInt32(message.sessionId); michael@0: Buf.writeInt32(message.techType); michael@0: Buf.sendParcel(); michael@0: }, michael@0: michael@0: /** michael@0: * NFC Configuration michael@0: */ michael@0: config: function config(message) { michael@0: let cb = function callback() { michael@0: let error = Buf.readInt32(); michael@0: michael@0: message.type = "ConfigResponse"; michael@0: message.status = (error === 0) ? GECKO_NFC_ERROR_SUCCESS : michael@0: GECKO_NFC_ERROR_GENERIC_FAILURE; michael@0: this.sendDOMMessage(message); michael@0: }; michael@0: michael@0: Buf.newParcel(NFC_REQUEST_CONFIG , cb); michael@0: Buf.writeInt32(message.powerLevel); michael@0: Buf.sendParcel(); michael@0: }, michael@0: michael@0: /** michael@0: * Close connection to the NFC target. michael@0: */ michael@0: close: function close(message) { michael@0: let cb = function callback() { michael@0: let error = Buf.readInt32(); michael@0: let sessionId = Buf.readInt32(); michael@0: michael@0: message.type = "CloseResponse"; michael@0: message.sessionId = sessionId; michael@0: message.status = (error === 0) ? GECKO_NFC_ERROR_SUCCESS : michael@0: GECKO_NFC_ERROR_GENERIC_FAILURE; michael@0: this.sendDOMMessage(message); michael@0: }; michael@0: michael@0: Buf.newParcel(NFC_REQUEST_CLOSE , cb); michael@0: Buf.writeInt32(message.sessionId); michael@0: Buf.sendParcel(); michael@0: }, michael@0: michael@0: handleParcel: function handleParcel(request_type, callback) { michael@0: let method = this[request_type]; michael@0: if (typeof method == "function") { michael@0: if (DEBUG) debug("Handling parcel as " + method.name); michael@0: method.call(this); michael@0: } else if (typeof callback == "function") { michael@0: callback.call(this, request_type); michael@0: this.mCallback = null; michael@0: } else { michael@0: debug("Unable to handle ReqType:"+request_type); michael@0: } michael@0: }, michael@0: michael@0: /** michael@0: * Send messages to the main UI thread. michael@0: */ michael@0: sendDOMMessage: function sendDOMMessage(message) { michael@0: postMessage(message); michael@0: } michael@0: }; michael@0: michael@0: /** michael@0: * Notification Handlers michael@0: */ michael@0: NfcWorker[NFC_NOTIFICATION_INITIALIZED] = function NFC_NOTIFICATION_INITIALIZED () { michael@0: let status = Buf.readInt32(); michael@0: let majorVersion = Buf.readInt32(); michael@0: let minorVersion = Buf.readInt32(); michael@0: debug("NFC_NOTIFICATION_INITIALIZED status:" + status); michael@0: if ((majorVersion != NFC_MAJOR_VERSION) || (minorVersion != NFC_MINOR_VERSION)) { michael@0: debug("Version Mismatch! Current Supported Version : " + michael@0: NFC_MAJOR_VERSION + "." + NFC_MINOR_VERSION + michael@0: " Received Version : " + majorVersion + "." + minorVersion); michael@0: } michael@0: }; michael@0: michael@0: NfcWorker[NFC_NOTIFICATION_TECH_DISCOVERED] = function NFC_NOTIFICATION_TECH_DISCOVERED() { michael@0: debug("NFC_NOTIFICATION_TECH_DISCOVERED"); michael@0: let techList = []; michael@0: let records = null; michael@0: michael@0: let sessionId = Buf.readInt32(); michael@0: let techCount = Buf.readInt32(); michael@0: for (let count = 0; count < techCount; count++) { michael@0: let tech = NFC_TECHS[Buf.readUint8()]; michael@0: if (tech) { michael@0: techList.push(tech); michael@0: } michael@0: } michael@0: michael@0: let padding = getPaddingLen(techCount); michael@0: for (let i = 0; i < padding; i++) { michael@0: Buf.readUint8(); michael@0: } michael@0: michael@0: let ndefMsgCount = Buf.readInt32(); michael@0: if (ndefMsgCount > 0) { michael@0: records = this.unMarshallNdefMessage(); michael@0: } michael@0: this.sendDOMMessage({type: "techDiscovered", michael@0: sessionId: sessionId, michael@0: techList: techList, michael@0: records: records}); michael@0: }; michael@0: michael@0: NfcWorker[NFC_NOTIFICATION_TECH_LOST] = function NFC_NOTIFICATION_TECH_LOST() { michael@0: debug("NFC_NOTIFICATION_TECH_LOST"); michael@0: let sessionId = Buf.readInt32(); michael@0: debug("sessionId = " + sessionId); michael@0: this.sendDOMMessage({type: "techLost", michael@0: sessionId: sessionId, michael@0: }); michael@0: }; michael@0: michael@0: /** michael@0: * Global stuff. michael@0: */ michael@0: michael@0: if (!this.debug) { michael@0: // Debugging stub that goes nowhere. michael@0: this.debug = function debug(message) { michael@0: dump("Nfc Worker: " + message + "\n"); michael@0: }; michael@0: } michael@0: michael@0: // Initialize buffers. This is a separate function so that unit tests can michael@0: // re-initialize the buffers at will. michael@0: Buf.init(); michael@0: michael@0: function onNfcMessage(data) { michael@0: Buf.processIncoming(data); michael@0: }; michael@0: michael@0: onmessage = function onmessage(event) { michael@0: NfcWorker.handleDOMMessage(event.data); michael@0: }; michael@0: michael@0: onerror = function onerror(event) { michael@0: debug("OnError: event: " + JSON.stringify(event)); michael@0: debug("NFC Worker error " + event.message + " " + event.filename + ":" + michael@0: event.lineno + ":\n"); michael@0: };