diff -r 000000000000 -r 6474c204b198 dom/system/gonk/NfcContentHelper.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dom/system/gonk/NfcContentHelper.js Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,475 @@ +/* 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"; + +const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/DOMRequestHelper.jsm"); + +let NFC = {}; +Cu.import("resource://gre/modules/nfc_consts.js", NFC); + +Cu.import("resource://gre/modules/systemlibs.js"); +const NFC_ENABLED = libcutils.property_get("ro.moz.nfc.enabled", "false") === "true"; + +// set to true to in nfc_consts.js to see debug messages +let DEBUG = NFC.DEBUG_CONTENT_HELPER; + +let debug; +if (DEBUG) { + debug = function (s) { + dump("-*- NfcContentHelper: " + s + "\n"); + }; +} else { + debug = function (s) {}; +} + +const NFCCONTENTHELPER_CID = + Components.ID("{4d72c120-da5f-11e1-9b23-0800200c9a66}"); + +const NFC_IPC_MSG_NAMES = [ + "NFC:ReadNDEFResponse", + "NFC:WriteNDEFResponse", + "NFC:GetDetailsNDEFResponse", + "NFC:MakeReadOnlyNDEFResponse", + "NFC:ConnectResponse", + "NFC:CloseResponse", + "NFC:CheckP2PRegistrationResponse", + "NFC:PeerEvent", + "NFC:NotifySendFileStatusResponse", + "NFC:ConfigResponse" +]; + +XPCOMUtils.defineLazyServiceGetter(this, "cpmm", + "@mozilla.org/childprocessmessagemanager;1", + "nsISyncMessageSender"); + +function GetDetailsNDEFResponse(details) { + this.canBeMadeReadOnly = details.canBeMadeReadOnly; + this.isReadOnly = details.isReadOnly; + this.maxSupportedLength = details.maxSupportedLength; +} +GetDetailsNDEFResponse.prototype = { + __exposedProps__ : {canBeMadeReadOnly: 'r', + isReadOnly: 'r', + maxSupportedLength: 'r'} +}; + +function NfcContentHelper() { + this.initDOMRequestHelper(/* aWindow */ null, NFC_IPC_MSG_NAMES); + Services.obs.addObserver(this, "xpcom-shutdown", false); + + this._requestMap = []; + + // Maintains an array of PeerEvent related callbacks, mainly + // one for 'peerReady' and another for 'peerLost'. + this.peerEventsCallbackMap = {}; +} + +NfcContentHelper.prototype = { + __proto__: DOMRequestIpcHelper.prototype, + + QueryInterface: XPCOMUtils.generateQI([Ci.nsINfcContentHelper, + Ci.nsISupportsWeakReference, + Ci.nsIObserver]), + classID: NFCCONTENTHELPER_CID, + classInfo: XPCOMUtils.generateCI({ + classID: NFCCONTENTHELPER_CID, + classDescription: "NfcContentHelper", + interfaces: [Ci.nsINfcContentHelper] + }), + + _requestMap: null, + peerEventsCallbackMap: null, + + encodeNDEFRecords: function encodeNDEFRecords(records) { + let encodedRecords = []; + for (let i = 0; i < records.length; i++) { + let record = records[i]; + encodedRecords.push({ + tnf: record.tnf, + type: record.type, + id: record.id, + payload: record.payload, + }); + } + return encodedRecords; + }, + + // NFC interface: + setSessionToken: function setSessionToken(sessionToken) { + if (sessionToken == null) { + throw Components.Exception("No session token!", + Cr.NS_ERROR_UNEXPECTED); + return; + } + // Report session to Nfc.js only. + cpmm.sendAsyncMessage("NFC:SetSessionToken", { + sessionToken: sessionToken, + }); + }, + + // NFCTag interface + getDetailsNDEF: function getDetailsNDEF(window, sessionToken) { + if (window == null) { + throw Components.Exception("Can't get window object", + Cr.NS_ERROR_UNEXPECTED); + } + let request = Services.DOMRequest.createRequest(window); + let requestId = btoa(this.getRequestId(request)); + this._requestMap[requestId] = window; + + cpmm.sendAsyncMessage("NFC:GetDetailsNDEF", { + requestId: requestId, + sessionToken: sessionToken + }); + return request; + }, + + readNDEF: function readNDEF(window, sessionToken) { + if (window == null) { + throw Components.Exception("Can't get window object", + Cr.NS_ERROR_UNEXPECTED); + } + let request = Services.DOMRequest.createRequest(window); + let requestId = btoa(this.getRequestId(request)); + this._requestMap[requestId] = window; + + cpmm.sendAsyncMessage("NFC:ReadNDEF", { + requestId: requestId, + sessionToken: sessionToken + }); + return request; + }, + + writeNDEF: function writeNDEF(window, records, sessionToken) { + if (window == null) { + throw Components.Exception("Can't get window object", + Cr.NS_ERROR_UNEXPECTED); + } + let request = Services.DOMRequest.createRequest(window); + let requestId = btoa(this.getRequestId(request)); + this._requestMap[requestId] = window; + + let encodedRecords = this.encodeNDEFRecords(records); + cpmm.sendAsyncMessage("NFC:WriteNDEF", { + requestId: requestId, + sessionToken: sessionToken, + records: encodedRecords + }); + return request; + }, + + makeReadOnlyNDEF: function makeReadOnlyNDEF(window, sessionToken) { + if (window == null) { + throw Components.Exception("Can't get window object", + Cr.NS_ERROR_UNEXPECTED); + } + + let request = Services.DOMRequest.createRequest(window); + let requestId = btoa(this.getRequestId(request)); + this._requestMap[requestId] = window; + + cpmm.sendAsyncMessage("NFC:MakeReadOnlyNDEF", { + requestId: requestId, + sessionToken: sessionToken + }); + return request; + }, + + connect: function connect(window, techType, sessionToken) { + if (window == null) { + throw Components.Exception("Can't get window object", + Cr.NS_ERROR_UNEXPECTED); + } + let request = Services.DOMRequest.createRequest(window); + let requestId = btoa(this.getRequestId(request)); + this._requestMap[requestId] = window; + + cpmm.sendAsyncMessage("NFC:Connect", { + requestId: requestId, + sessionToken: sessionToken, + techType: techType + }); + return request; + }, + + close: function close(window, sessionToken) { + if (window == null) { + throw Components.Exception("Can't get window object", + Cr.NS_ERROR_UNEXPECTED); + } + let request = Services.DOMRequest.createRequest(window); + let requestId = btoa(this.getRequestId(request)); + this._requestMap[requestId] = window; + + cpmm.sendAsyncMessage("NFC:Close", { + requestId: requestId, + sessionToken: sessionToken + }); + return request; + }, + + sendFile: function sendFile(window, data, sessionToken) { + if (window == null) { + throw Components.Exception("Can't get window object", + Cr.NS_ERROR_UNEXPECTED); + } + let request = Services.DOMRequest.createRequest(window); + let requestId = btoa(this.getRequestId(request)); + this._requestMap[requestId] = window; + + cpmm.sendAsyncMessage("NFC:SendFile", { + requestId: requestId, + sessionToken: sessionToken, + blob: data.blob + }); + return request; + }, + + notifySendFileStatus: function notifySendFileStatus(window, status, + requestId) { + if (window == null) { + throw Components.Exception("Can't get window object", + Cr.NS_ERROR_UNEXPECTED); + } + + cpmm.sendAsyncMessage("NFC:NotifySendFileStatus", { + status: status, + requestId: requestId + }); + }, + + registerTargetForPeerEvent: function registerTargetForPeerEvent(window, + appId, event, callback) { + if (window == null) { + throw Components.Exception("Can't get window object", + Cr.NS_ERROR_UNEXPECTED); + } + this.peerEventsCallbackMap[event] = callback; + cpmm.sendAsyncMessage("NFC:RegisterPeerTarget", { + appId: appId, + event: event + }); + }, + + unregisterTargetForPeerEvent: function unregisterTargetForPeerEvent(window, + appId, event) { + if (window == null) { + throw Components.Exception("Can't get window object", + Cr.NS_ERROR_UNEXPECTED); + } + let callback = this.peerEventsCallbackMap[event]; + if (callback != null) { + delete this.peerEventsCallbackMap[event]; + } + + cpmm.sendAsyncMessage("NFC:UnregisterPeerTarget", { + appId: appId, + event: event + }); + }, + + checkP2PRegistration: function checkP2PRegistration(window, appId) { + if (window == null) { + throw Components.Exception("Can't get window object", + Cr.NS_ERROR_UNEXPECTED); + } + let request = Services.DOMRequest.createRequest(window); + let requestId = btoa(this.getRequestId(request)); + this._requestMap[requestId] = window; + + cpmm.sendAsyncMessage("NFC:CheckP2PRegistration", { + appId: appId, + requestId: requestId + }); + return request; + }, + + notifyUserAcceptedP2P: function notifyUserAcceptedP2P(window, appId) { + if (window == null) { + throw Components.Exception("Can't get window object", + Cr.NS_ERROR_UNEXPECTED); + } + + cpmm.sendAsyncMessage("NFC:NotifyUserAcceptedP2P", { + appId: appId + }); + }, + + startPoll: function startPoll(window) { + if (window == null) { + throw Components.Exception("Can't get window object", + Cr.NS_ERROR_UNEXPECTED); + } + + let request = Services.DOMRequest.createRequest(window); + let requestId = btoa(this.getRequestId(request)); + this._requestMap[requestId] = window; + + cpmm.sendAsyncMessage("NFC:StartPoll", + {requestId: requestId}); + return request; + }, + + stopPoll: function stopPoll(window) { + if (window == null) { + throw Components.Exception("Can't get window object", + Cr.NS_ERROR_UNEXPECTED); + } + + let request = Services.DOMRequest.createRequest(window); + let requestId = btoa(this.getRequestId(request)); + this._requestMap[requestId] = window; + + cpmm.sendAsyncMessage("NFC:StopPoll", + {requestId: requestId}); + return request; + }, + + powerOff: function powerOff(window) { + if (window == null) { + throw Components.Exception("Can't get window object", + Cr.NS_ERROR_UNEXPECTED); + } + + let request = Services.DOMRequest.createRequest(window); + let requestId = btoa(this.getRequestId(request)); + this._requestMap[requestId] = window; + + cpmm.sendAsyncMessage("NFC:PowerOff", + {requestId: requestId}); + return request; + }, + + // nsIObserver + observe: function observe(subject, topic, data) { + if (topic == "xpcom-shutdown") { + this.destroyDOMRequestHelper(); + Services.obs.removeObserver(this, "xpcom-shutdown"); + cpmm = null; + } + }, + + // nsIMessageListener + + fireRequestSuccess: function fireRequestSuccess(requestId, result) { + let request = this.takeRequest(requestId); + if (!request) { + debug("not firing success for id: " + requestId + + ", result: " + JSON.stringify(result)); + return; + } + + debug("fire request success, id: " + requestId + + ", result: " + JSON.stringify(result)); + Services.DOMRequest.fireSuccess(request, result); + }, + + fireRequestError: function fireRequestError(requestId, error) { + let request = this.takeRequest(requestId); + if (!request) { + debug("not firing error for id: " + requestId + + ", error: " + JSON.stringify(error)); + return; + } + + debug("fire request error, id: " + requestId + + ", result: " + JSON.stringify(error)); + Services.DOMRequest.fireError(request, error); + }, + + receiveMessage: function receiveMessage(message) { + debug("Message received: " + JSON.stringify(message)); + let result = message.json; + + switch (message.name) { + case "NFC:ReadNDEFResponse": + this.handleReadNDEFResponse(result); + break; + case "NFC:GetDetailsNDEFResponse": + this.handleGetDetailsNDEFResponse(result); + break; + case "NFC:ConnectResponse": // Fall through. + case "NFC:CloseResponse": + case "NFC:WriteNDEFResponse": + case "NFC:MakeReadOnlyNDEFResponse": + case "NFC:CheckP2PRegistrationResponse": + case "NFC:NotifySendFileStatusResponse": + case "NFC:ConfigResponse": + if (result.status !== NFC.GECKO_NFC_ERROR_SUCCESS) { + this.fireRequestError(atob(result.requestId), result.status); + } else { + this.fireRequestSuccess(atob(result.requestId), result); + } + break; + case "NFC:PeerEvent": + let callback = this.peerEventsCallbackMap[result.event]; + if (callback) { + callback.peerNotification(result.event, result.sessionToken); + } else { + debug("PeerEvent: No valid callback registered for the event " + + result.event); + } + break; + } + }, + + handleReadNDEFResponse: function handleReadNDEFResponse(result) { + let requester = this._requestMap[result.requestId]; + if (!requester) { + debug("Response Invalid requestId=" + result.requestId); + return; + } + delete this._requestMap[result.requestId]; + + if (result.status !== NFC.GECKO_NFC_ERROR_SUCCESS) { + this.fireRequestError(atob(result.requestId), result.status); + return; + } + + let requestId = atob(result.requestId); + let ndefMsg = []; + let records = result.records; + for (let i = 0; i < records.length; i++) { + let record = records[i]; + ndefMsg.push(new requester.MozNDEFRecord(record.tnf, + record.type, + record.id, + record.payload)); + } + this.fireRequestSuccess(requestId, ndefMsg); + }, + + handleGetDetailsNDEFResponse: function handleGetDetailsNDEFResponse(result) { + if (result.status !== NFC.GECKO_NFC_ERROR_SUCCESS) { + this.fireRequestError(atob(result.requestId), result.status); + return; + } + + let requestId = atob(result.requestId); + let result = new GetDetailsNDEFResponse(result); + this.fireRequestSuccess(requestId, result); + }, +}; + +if (NFC_ENABLED) { + this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NfcContentHelper]); +}