|
1 /* Copyright 2012 Mozilla Foundation and Mozilla contributors |
|
2 * |
|
3 * Licensed under the Apache License, Version 2.0 (the "License"); |
|
4 * you may not use this file except in compliance with the License. |
|
5 * You may obtain a copy of the License at |
|
6 * |
|
7 * http://www.apache.org/licenses/LICENSE-2.0 |
|
8 * |
|
9 * Unless required by applicable law or agreed to in writing, software |
|
10 * distributed under the License is distributed on an "AS IS" BASIS, |
|
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
12 * See the License for the specific language governing permissions and |
|
13 * limitations under the License. |
|
14 */ |
|
15 |
|
16 /* Copyright © 2013, Deutsche Telekom, Inc. */ |
|
17 |
|
18 "use strict"; |
|
19 |
|
20 importScripts("systemlibs.js", "nfc_consts.js"); |
|
21 importScripts("resource://gre/modules/workers/require.js"); |
|
22 |
|
23 // set to true in nfc_consts.js to see debug messages |
|
24 let DEBUG = DEBUG_WORKER; |
|
25 |
|
26 function getPaddingLen(len) { |
|
27 return (len % 4) ? (4 - len % 4) : 0; |
|
28 } |
|
29 |
|
30 let Buf = { |
|
31 __proto__: (function(){ |
|
32 return require("resource://gre/modules/workers/worker_buf.js").Buf; |
|
33 })(), |
|
34 |
|
35 init: function init() { |
|
36 this._init(); |
|
37 }, |
|
38 |
|
39 /** |
|
40 * Process one parcel. |
|
41 */ |
|
42 processParcel: function processParcel() { |
|
43 let pduType = this.readInt32(); |
|
44 if (DEBUG) debug("Number of bytes available in Parcel : " + this.readAvailable); |
|
45 NfcWorker.handleParcel(pduType, this.mCallback); |
|
46 }, |
|
47 |
|
48 /** |
|
49 * Start a new outgoing parcel. |
|
50 * |
|
51 * @param type |
|
52 * Integer specifying the request type. |
|
53 * @param callback |
|
54 */ |
|
55 newParcel: function newParcel(type, callback) { |
|
56 if (DEBUG) debug("New outgoing parcel of type " + type); |
|
57 this.mCallback = callback; |
|
58 // We're going to leave room for the parcel size at the beginning. |
|
59 this.outgoingIndex = this.PARCEL_SIZE_SIZE; |
|
60 this.writeInt32(type); |
|
61 }, |
|
62 |
|
63 simpleRequest: function simpleRequest(type) { |
|
64 this.newParcel(type); |
|
65 this.sendParcel(); |
|
66 }, |
|
67 |
|
68 onSendParcel: function onSendParcel(parcel) { |
|
69 postNfcMessage(parcel); |
|
70 }, |
|
71 |
|
72 /** |
|
73 * TODO: Bug 933593. Callback map of NFC_RESPONSE_XXX and RequestID |
|
74 * needs to be maintained |
|
75 */ |
|
76 mCallback: null, |
|
77 }; |
|
78 |
|
79 /** |
|
80 * Provide a high-level API representing NFC capabilities. |
|
81 * Rensponsible for converting NFC requests from Content process to binary data |
|
82 * and NFC Responses from binary data to dictionary objects. |
|
83 */ |
|
84 let NfcWorker = { |
|
85 /** |
|
86 * Handle incoming messages from the main UI thread. |
|
87 * |
|
88 * @param message |
|
89 * Object containing the message. Messages are supposed |
|
90 */ |
|
91 handleDOMMessage: function handleMessage(message) { |
|
92 if (DEBUG) debug("Received DOM message " + JSON.stringify(message)); |
|
93 let method = this[message.type]; |
|
94 if (typeof method != "function") { |
|
95 if (DEBUG) { |
|
96 debug("Don't know what to do with message " + JSON.stringify(message)); |
|
97 } |
|
98 return; |
|
99 } |
|
100 method.call(this, message); |
|
101 }, |
|
102 |
|
103 /** |
|
104 * Unmarshals a NDEF message |
|
105 */ |
|
106 unMarshallNdefMessage: function unMarshallNdefMessage() { |
|
107 let numOfRecords = Buf.readInt32(); |
|
108 debug("numOfRecords = " + numOfRecords); |
|
109 if (numOfRecords <= 0) { |
|
110 return null; |
|
111 } |
|
112 let records = []; |
|
113 |
|
114 for (let i = 0; i < numOfRecords; i++) { |
|
115 let tnf = Buf.readInt32() & 0xff; |
|
116 let typeLength = Buf.readInt32(); |
|
117 let type = Buf.readUint8Array(typeLength); |
|
118 let padding = getPaddingLen(typeLength); |
|
119 for (let i = 0; i < padding; i++) { |
|
120 Buf.readUint8(); |
|
121 } |
|
122 |
|
123 let idLength = Buf.readInt32(); |
|
124 let id = Buf.readUint8Array(idLength); |
|
125 padding = getPaddingLen(idLength); |
|
126 for (let i = 0; i < padding; i++) { |
|
127 Buf.readUint8(); |
|
128 } |
|
129 |
|
130 let payloadLength = Buf.readInt32(); |
|
131 let payload = Buf.readUint8Array(payloadLength); |
|
132 padding = getPaddingLen(payloadLength); |
|
133 for (let i = 0; i < padding; i++) { |
|
134 Buf.readUint8(); |
|
135 } |
|
136 records.push({tnf: tnf, |
|
137 type: type, |
|
138 id: id, |
|
139 payload: payload}); |
|
140 } |
|
141 return records; |
|
142 }, |
|
143 |
|
144 /** |
|
145 * Read and return NDEF data, if present. |
|
146 */ |
|
147 readNDEF: function readNDEF(message) { |
|
148 let cb = function callback() { |
|
149 let error = Buf.readInt32(); |
|
150 let sessionId = Buf.readInt32(); |
|
151 let records = this.unMarshallNdefMessage(); |
|
152 |
|
153 message.type = "ReadNDEFResponse"; |
|
154 message.sessionId = sessionId; |
|
155 message.records = records; |
|
156 message.status = (error === 0) ? GECKO_NFC_ERROR_SUCCESS : |
|
157 GECKO_NFC_ERROR_GENERIC_FAILURE; |
|
158 this.sendDOMMessage(message); |
|
159 } |
|
160 |
|
161 Buf.newParcel(NFC_REQUEST_READ_NDEF, cb); |
|
162 Buf.writeInt32(message.sessionId); |
|
163 Buf.sendParcel(); |
|
164 }, |
|
165 |
|
166 /** |
|
167 * Write to a target that accepts NDEF formattable data |
|
168 */ |
|
169 writeNDEF: function writeNDEF(message) { |
|
170 let cb = function callback() { |
|
171 let error = Buf.readInt32(); |
|
172 let sessionId = Buf.readInt32(); |
|
173 |
|
174 message.type = "WriteNDEFResponse"; |
|
175 message.sessionId = sessionId; |
|
176 message.status = (error === 0) ? GECKO_NFC_ERROR_SUCCESS : |
|
177 GECKO_NFC_ERROR_GENERIC_FAILURE; |
|
178 this.sendDOMMessage(message); |
|
179 }; |
|
180 |
|
181 Buf.newParcel(NFC_REQUEST_WRITE_NDEF, cb); |
|
182 Buf.writeInt32(message.sessionId); |
|
183 let records = message.records; |
|
184 let numRecords = records.length; |
|
185 Buf.writeInt32(numRecords); |
|
186 for (let i = 0; i < numRecords; i++) { |
|
187 let record = records[i]; |
|
188 Buf.writeInt32(record.tnf); |
|
189 |
|
190 let typeLength = record.type ? record.type.length : 0; |
|
191 Buf.writeInt32(typeLength); |
|
192 for (let j = 0; j < typeLength; j++) { |
|
193 Buf.writeUint8(record.type[j]); |
|
194 } |
|
195 let padding = getPaddingLen(typeLength); |
|
196 for (let i = 0; i < padding; i++) { |
|
197 Buf.writeUint8(0x00); |
|
198 } |
|
199 |
|
200 let idLength = record.id ? record.id.length : 0; |
|
201 Buf.writeInt32(idLength); |
|
202 for (let j = 0; j < idLength; j++) { |
|
203 Buf.writeUint8(record.id[j]); |
|
204 } |
|
205 padding = getPaddingLen(idLength); |
|
206 for (let i = 0; i < padding; i++) { |
|
207 Buf.writeUint8(0x00); |
|
208 } |
|
209 |
|
210 let payloadLength = record.payload ? record.payload.length : 0; |
|
211 Buf.writeInt32(payloadLength); |
|
212 for (let j = 0; j < payloadLength; j++) { |
|
213 Buf.writeUint8(record.payload[j]); |
|
214 } |
|
215 padding = getPaddingLen(payloadLength); |
|
216 for (let i = 0; i < padding; i++) { |
|
217 Buf.writeUint8(0x00); |
|
218 } |
|
219 } |
|
220 |
|
221 Buf.sendParcel(); |
|
222 }, |
|
223 |
|
224 /** |
|
225 * Make the NFC NDEF tag permanently read only |
|
226 */ |
|
227 makeReadOnlyNDEF: function makeReadOnlyNDEF(message) { |
|
228 let cb = function callback() { |
|
229 let error = Buf.readInt32(); |
|
230 let sessionId = Buf.readInt32(); |
|
231 |
|
232 message.type = "MakeReadOnlyNDEFResponse"; |
|
233 message.sessionId = sessionId; |
|
234 message.status = (error === 0) ? GECKO_NFC_ERROR_SUCCESS : |
|
235 GECKO_NFC_ERROR_GENERIC_FAILURE; |
|
236 this.sendDOMMessage(message); |
|
237 }; |
|
238 |
|
239 Buf.newParcel(NFC_REQUEST_MAKE_NDEF_READ_ONLY, cb); |
|
240 Buf.writeInt32(message.sessionId); |
|
241 Buf.sendParcel(); |
|
242 }, |
|
243 |
|
244 /** |
|
245 * Retrieve metadata describing the NDEF formatted data, if present. |
|
246 */ |
|
247 getDetailsNDEF: function getDetailsNDEF(message) { |
|
248 let cb = function callback() { |
|
249 let error = Buf.readInt32(); |
|
250 let sessionId = Buf.readInt32(); |
|
251 let isReadOnly = Buf.readUint8(); |
|
252 let canBeMadeReadOnly = Buf.readUint8(); |
|
253 // Ensure that padding is taken care here after reading two successive uint8's |
|
254 Buf.readUint8(); |
|
255 Buf.readUint8(); |
|
256 let maxSupportedLength = Buf.readInt32(); |
|
257 |
|
258 message.type = "GetDetailsNDEFResponse"; |
|
259 message.sessionId = sessionId; |
|
260 message.isReadOnly = isReadOnly; |
|
261 message.canBeMadeReadOnly = canBeMadeReadOnly; |
|
262 message.maxSupportedLength = maxSupportedLength; |
|
263 message.status = (error === 0) ? GECKO_NFC_ERROR_SUCCESS : |
|
264 GECKO_NFC_ERROR_GENERIC_FAILURE; |
|
265 this.sendDOMMessage(message); |
|
266 }; |
|
267 Buf.newParcel(NFC_REQUEST_GET_DETAILS, cb); |
|
268 Buf.writeInt32(message.sessionId); |
|
269 Buf.sendParcel(); |
|
270 }, |
|
271 |
|
272 |
|
273 /** |
|
274 * Open a connection to the NFC target. |
|
275 */ |
|
276 connect: function connect(message) { |
|
277 let cb = function callback() { |
|
278 let error = Buf.readInt32(); |
|
279 let sessionId = Buf.readInt32(); |
|
280 |
|
281 message.type = "ConnectResponse"; |
|
282 message.sessionId = sessionId; |
|
283 message.status = (error === 0) ? GECKO_NFC_ERROR_SUCCESS : |
|
284 GECKO_NFC_ERROR_GENERIC_FAILURE; |
|
285 this.sendDOMMessage(message); |
|
286 }; |
|
287 |
|
288 Buf.newParcel(NFC_REQUEST_CONNECT, cb); |
|
289 Buf.writeInt32(message.sessionId); |
|
290 Buf.writeInt32(message.techType); |
|
291 Buf.sendParcel(); |
|
292 }, |
|
293 |
|
294 /** |
|
295 * NFC Configuration |
|
296 */ |
|
297 config: function config(message) { |
|
298 let cb = function callback() { |
|
299 let error = Buf.readInt32(); |
|
300 |
|
301 message.type = "ConfigResponse"; |
|
302 message.status = (error === 0) ? GECKO_NFC_ERROR_SUCCESS : |
|
303 GECKO_NFC_ERROR_GENERIC_FAILURE; |
|
304 this.sendDOMMessage(message); |
|
305 }; |
|
306 |
|
307 Buf.newParcel(NFC_REQUEST_CONFIG , cb); |
|
308 Buf.writeInt32(message.powerLevel); |
|
309 Buf.sendParcel(); |
|
310 }, |
|
311 |
|
312 /** |
|
313 * Close connection to the NFC target. |
|
314 */ |
|
315 close: function close(message) { |
|
316 let cb = function callback() { |
|
317 let error = Buf.readInt32(); |
|
318 let sessionId = Buf.readInt32(); |
|
319 |
|
320 message.type = "CloseResponse"; |
|
321 message.sessionId = sessionId; |
|
322 message.status = (error === 0) ? GECKO_NFC_ERROR_SUCCESS : |
|
323 GECKO_NFC_ERROR_GENERIC_FAILURE; |
|
324 this.sendDOMMessage(message); |
|
325 }; |
|
326 |
|
327 Buf.newParcel(NFC_REQUEST_CLOSE , cb); |
|
328 Buf.writeInt32(message.sessionId); |
|
329 Buf.sendParcel(); |
|
330 }, |
|
331 |
|
332 handleParcel: function handleParcel(request_type, callback) { |
|
333 let method = this[request_type]; |
|
334 if (typeof method == "function") { |
|
335 if (DEBUG) debug("Handling parcel as " + method.name); |
|
336 method.call(this); |
|
337 } else if (typeof callback == "function") { |
|
338 callback.call(this, request_type); |
|
339 this.mCallback = null; |
|
340 } else { |
|
341 debug("Unable to handle ReqType:"+request_type); |
|
342 } |
|
343 }, |
|
344 |
|
345 /** |
|
346 * Send messages to the main UI thread. |
|
347 */ |
|
348 sendDOMMessage: function sendDOMMessage(message) { |
|
349 postMessage(message); |
|
350 } |
|
351 }; |
|
352 |
|
353 /** |
|
354 * Notification Handlers |
|
355 */ |
|
356 NfcWorker[NFC_NOTIFICATION_INITIALIZED] = function NFC_NOTIFICATION_INITIALIZED () { |
|
357 let status = Buf.readInt32(); |
|
358 let majorVersion = Buf.readInt32(); |
|
359 let minorVersion = Buf.readInt32(); |
|
360 debug("NFC_NOTIFICATION_INITIALIZED status:" + status); |
|
361 if ((majorVersion != NFC_MAJOR_VERSION) || (minorVersion != NFC_MINOR_VERSION)) { |
|
362 debug("Version Mismatch! Current Supported Version : " + |
|
363 NFC_MAJOR_VERSION + "." + NFC_MINOR_VERSION + |
|
364 " Received Version : " + majorVersion + "." + minorVersion); |
|
365 } |
|
366 }; |
|
367 |
|
368 NfcWorker[NFC_NOTIFICATION_TECH_DISCOVERED] = function NFC_NOTIFICATION_TECH_DISCOVERED() { |
|
369 debug("NFC_NOTIFICATION_TECH_DISCOVERED"); |
|
370 let techList = []; |
|
371 let records = null; |
|
372 |
|
373 let sessionId = Buf.readInt32(); |
|
374 let techCount = Buf.readInt32(); |
|
375 for (let count = 0; count < techCount; count++) { |
|
376 let tech = NFC_TECHS[Buf.readUint8()]; |
|
377 if (tech) { |
|
378 techList.push(tech); |
|
379 } |
|
380 } |
|
381 |
|
382 let padding = getPaddingLen(techCount); |
|
383 for (let i = 0; i < padding; i++) { |
|
384 Buf.readUint8(); |
|
385 } |
|
386 |
|
387 let ndefMsgCount = Buf.readInt32(); |
|
388 if (ndefMsgCount > 0) { |
|
389 records = this.unMarshallNdefMessage(); |
|
390 } |
|
391 this.sendDOMMessage({type: "techDiscovered", |
|
392 sessionId: sessionId, |
|
393 techList: techList, |
|
394 records: records}); |
|
395 }; |
|
396 |
|
397 NfcWorker[NFC_NOTIFICATION_TECH_LOST] = function NFC_NOTIFICATION_TECH_LOST() { |
|
398 debug("NFC_NOTIFICATION_TECH_LOST"); |
|
399 let sessionId = Buf.readInt32(); |
|
400 debug("sessionId = " + sessionId); |
|
401 this.sendDOMMessage({type: "techLost", |
|
402 sessionId: sessionId, |
|
403 }); |
|
404 }; |
|
405 |
|
406 /** |
|
407 * Global stuff. |
|
408 */ |
|
409 |
|
410 if (!this.debug) { |
|
411 // Debugging stub that goes nowhere. |
|
412 this.debug = function debug(message) { |
|
413 dump("Nfc Worker: " + message + "\n"); |
|
414 }; |
|
415 } |
|
416 |
|
417 // Initialize buffers. This is a separate function so that unit tests can |
|
418 // re-initialize the buffers at will. |
|
419 Buf.init(); |
|
420 |
|
421 function onNfcMessage(data) { |
|
422 Buf.processIncoming(data); |
|
423 }; |
|
424 |
|
425 onmessage = function onmessage(event) { |
|
426 NfcWorker.handleDOMMessage(event.data); |
|
427 }; |
|
428 |
|
429 onerror = function onerror(event) { |
|
430 debug("OnError: event: " + JSON.stringify(event)); |
|
431 debug("NFC Worker error " + event.message + " " + event.filename + ":" + |
|
432 event.lineno + ":\n"); |
|
433 }; |