gecko/dom/system/gonk/nfc_worker.js
2013-11-11 18:05:01 -08:00

435 lines
13 KiB
JavaScript

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