1 // Copyright (c) 2013 Intel Corporation. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
6 var _next_reply_id = 0;
8 var postMessage = function(msg, callback) {
9 var reply_id = _next_reply_id;
11 _callbacks[reply_id] = callback;
12 msg.reply_id = reply_id.toString();
13 extension.postMessage(JSON.stringify(msg));
16 extension.setMessageListener(function(json) {
17 var msg = JSON.parse(json);
18 if (msg.cmd == 'BondedDevice')
19 handleBondedDevice(msg);
20 else if (msg.cmd == 'DeviceFound')
21 handleDeviceFound(msg);
22 else if (msg.cmd == 'DiscoveryFinished')
23 handleDiscoveryFinished();
24 else if (msg.cmd == 'DeviceRemoved')
25 handleDeviceRemoved(msg.Address);
26 else if (msg.cmd == 'DeviceUpdated')
27 handleDeviceUpdated(msg);
28 else if (msg.cmd == 'AdapterUpdated')
29 handleAdapterUpdated(msg);
30 else if (msg.cmd == 'RFCOMMSocketAccept')
31 handleRFCOMMSocketAccept(msg);
32 else if (msg.cmd == 'SocketHasData')
33 handleSocketHasData(msg);
34 else if (msg.cmd == 'SocketClosed')
35 handleSocketClosed(msg);
37 if (msg.reply_id) { // Then we are dealing with postMessage return.
38 var reply_id = msg.reply_id;
39 var callback = _callbacks[reply_id];
42 delete _callbacks[reply_id];
45 // do not print error log when the postmessage was not initiated by JS
47 console.log('Invalid reply_id from Tizen Bluetooth: ' + reply_id);
53 this.found_devices = []; // Filled while a Discovering.
54 this.known_devices = []; // Keeps Managed and Found devices.
55 this.discovery_callbacks = {};
57 this.service_handlers = [];
59 this.change_listener = null;
62 function validateAddress(address) {
63 if (typeof address !== 'string')
66 var regExp = /([\dA-F][\dA-F]:){5}[\dA-F][\dA-F]/i;
68 if (!address.match(regExp))
74 Adapter.prototype.checkServiceAvailability = function(errorCallback) {
75 if (adapter.isReady && defaultAdapter.powered)
79 var error = new tizen.WebAPIError(tizen.WebAPIException.SERVICE_NOT_AVAILABLE_ERR);
86 Adapter.prototype.indexOfDevice = function(devices, address) {
87 for (var i = 0; i < devices.length; i++) {
88 if (devices[i].address == address)
94 Adapter.prototype.addDevice = function(device, on_discovery) {
95 var new_device = false;
98 var index = this.indexOfDevice(this.found_devices, device.address);
100 this.found_devices.push(device);
103 this.found_devices[index] = device;
108 var i = this.indexOfDevice(this.known_devices, device.address);
110 this.known_devices.push(device);
112 this.known_devices[i] = device;
117 Adapter.prototype.updateDevice = function(device) {
118 var index = this.indexOfDevice(this.known_devices, device.address);
120 this.known_devices.push(device);
122 this.known_devices[index]._updateProperties(device);
125 // This holds the adapter the Bluetooth backend is currently using.
126 // In BlueZ 4, for instance, this would represent the "default adapter".
127 // BlueZ 5 has no such concept, so this will hold the currently available
128 // adapter, which can be just the first one found.
129 var adapter = new Adapter();
131 var deepCopyDevices = function(devices) {
132 var copiedDevices = [];
133 for (var i = 0; i < devices.length; i++)
134 copiedDevices[i] = devices[i]._clone();
136 return copiedDevices;
139 var handleBondedDevice = function(msg) {
140 var device = new BluetoothDevice(msg);
141 adapter.addDevice(device, false);
144 var handleDeviceFound = function(msg) {
145 var device = new BluetoothDevice(msg);
146 var is_new = adapter.addDevice(device, msg.found_on_discovery);
148 // FIXME(jeez): we are not returning a deep copy so we can keep
149 // the devices up-to-date. We have to find a better way to handle this.
150 if (is_new && msg.found_on_discovery && adapter.discovery_callbacks.ondevicefound)
151 adapter.discovery_callbacks.ondevicefound(device);
154 var handleDiscoveryFinished = function() {
155 // FIXME(jeez): we are not returning a deep copy so we can keep
156 // the devices up-to-date. We have to find a better way to handle this.
157 if (typeof adapter.discovery_callbacks.onfinished === 'function')
158 adapter.discovery_callbacks.onfinished(adapter.found_devices);
160 adapter.found_devices = [];
161 adapter.discovery_callbacks = {};
164 var handleDeviceRemoved = function(address) {
165 var foundDevices = adapter.found_devices;
166 var knownDevices = adapter.known_devices;
168 for (var i = 0; i < foundDevices.length; i++) {
169 if (foundDevices[i].address === address) {
170 foundDevices.splice(i, 1);
175 for (var i = 0; i < knownDevices.length; i++) {
176 if (knownDevices[i].address === address) {
177 knownDevices.splice(i, 1);
183 var handleDeviceUpdated = function(msg) {
184 var device = new BluetoothDevice(msg);
185 adapter.updateDevice(device);
188 var handleAdapterUpdated = function(msg) {
189 var listener = adapter.change_listener;
192 _addConstProperty(defaultAdapter, 'name', msg.Name);
193 if (listener && listener.onnamechanged) {
194 adapter.change_listener.onnamechanged(msg.Name);
199 _addConstProperty(defaultAdapter, 'address', msg.Address);
202 var powered = (msg.Powered === 'true') ? true : false;
203 _addConstProperty(defaultAdapter, 'powered', powered);
204 if (listener && listener.onstatechanged) {
205 adapter.change_listener.onstatechanged(powered);
209 if (msg.Discoverable) {
210 var visibility = (msg.Discoverable === 'true') ? true : false;
212 if (defaultAdapter.visible !== visibility && listener && listener.onvisibilitychanged) {
213 adapter.change_listener.onvisibilitychanged(visibility);
215 _addConstProperty(defaultAdapter, 'visible', visibility);
218 defaultAdapter.isReady = true;
221 var handleRFCOMMSocketAccept = function(msg) {
222 for (var i in adapter.service_handlers) {
223 var server = adapter.service_handlers[i];
224 // FIXME(clecou) BlueZ4 backend compares rfcomm channel number but this parameter
225 // is not available in Tizen C API so we check socket fd.
226 // A better approach would be to adapt backends instances to have a single JSON protocol.
227 if (server.channel === msg.channel || server.server_fd === msg.socket_fd) {
228 var j = adapter.indexOfDevice(adapter.known_devices, msg.peer);
229 var peer = adapter.known_devices[j];
231 var socket = new BluetoothSocket(server.uuid, peer, msg);
233 adapter.sockets.push(socket);
235 _addConstProperty(server, 'isConnected', true);
237 if (server.onconnect && typeof server.onconnect === 'function')
238 server.onconnect(socket);
244 var handleSocketHasData = function(msg) {
245 for (var i in adapter.sockets) {
246 var socket = adapter.sockets[i];
247 if (socket.socket_fd === msg.socket_fd) {
248 socket.data = msg.data;
250 if (socket.onmessage && typeof socket.onmessage === 'function')
259 var handleSocketClosed = function(msg) {
260 for (var i in adapter.sockets) {
261 var socket = adapter.sockets[i];
262 if (socket.socket_fd === msg.socket_fd) {
263 if (socket.onclose && typeof socket.onmessage === 'function')
266 _addConstProperty(socket, 'isConnected', false);
272 var defaultAdapter = new BluetoothAdapter();
274 exports.getDefaultAdapter = function() {
276 'cmd': 'GetDefaultAdapter'
278 var result = JSON.parse(extension.internal.sendSyncMessage(JSON.stringify(msg)));
281 _addConstProperty(defaultAdapter, 'name', result.name);
282 _addConstProperty(defaultAdapter, 'address', result.address);
283 _addConstProperty(defaultAdapter, 'powered', result.powered);
284 _addConstProperty(defaultAdapter, 'visible', result.visible);
286 if (result.hasOwnProperty('address') && result.address != '')
287 adapter.isReady = true;
289 adapter.isReady = false;
290 throw new tizen.WebAPIException(tizen.WebAPIException.UNKNOWN_ERR);
293 return defaultAdapter;
296 exports.deviceMajor = {};
298 'MISC': { value: 0x00, configurable: false, writable: false },
299 'COMPUTER': { value: 0x01, configurable: false, writable: false },
300 'PHONE': { value: 0x02, configurable: false, writable: false },
301 'NETWORK': { value: 0x03, configurable: false, writable: false },
302 'AUDIO_VIDEO': { value: 0x04, configurable: false, writable: false },
303 'PERIPHERAL': { value: 0x05, configurable: false, writable: false },
304 'IMAGING': { value: 0x06, configurable: false, writable: false },
305 'WEARABLE': { value: 0x07, configurable: false, writable: false },
306 'TOY': { value: 0x08, configurable: false, writable: false },
307 'HEALTH': { value: 0x09, configurable: false, writable: false },
308 'UNCATEGORIZED': { value: 0x1F, configurable: false, writable: false }
310 Object.defineProperties(exports.deviceMajor, deviceMajor);
312 exports.deviceMinor = {};
314 'COMPUTER_UNCATEGORIZED': { value: 0x00, configurable: false, writable: false },
315 'COMPUTER_DESKTOP': { value: 0x01, configurable: false, writable: false },
316 'COMPUTER_SERVER': { value: 0x02, configurable: false, writable: false },
317 'COMPUTER_LAPTOP': { value: 0x03, configurable: false, writable: false },
318 'COMPUTER_HANDHELD_PC_OR_PDA': { value: 0x04, configurable: false, writable: false },
319 'COMPUTER_PALM_PC_OR_PDA': { value: 0x05, configurable: false, writable: false },
320 'COMPUTER_WEARABLE': { value: 0x06, configurable: false, writable: false },
321 'PHONE_UNCATEGORIZED': { value: 0x00, configurable: false, writable: false },
322 'PHONE_CELLULAR': { value: 0x01, configurable: false, writable: false },
323 'PHONE_CORDLESS': { value: 0x02, configurable: false, writable: false },
324 'PHONE_SMARTPHONE': { value: 0x03, configurable: false, writable: false },
325 'PHONE_MODEM_OR_GATEWAY': { value: 0x04, configurable: false, writable: false },
326 'PHONE_ISDN': { value: 0x05, configurable: false, writable: false },
327 'AV_UNRECOGNIZED': { value: 0x00, configurable: false, writable: false },
328 'AV_WEARABLE_HEADSET': { value: 0x01, configurable: false, writable: false },
329 'AV_HANDSFREE': { value: 0x02, configurable: false, writable: false },
330 'AV_MICROPHONE': { value: 0x04, configurable: false, writable: false },
331 'AV_LOUDSPEAKER': { value: 0x05, configurable: false, writable: false },
332 'AV_HEADPHONES': { value: 0x06, configurable: false, writable: false },
333 'AV_PORTABLE_AUDIO': { value: 0x07, configurable: false, writable: false },
334 'AV_CAR_AUDIO': { value: 0x08, configurable: false, writable: false },
335 'AV_SETTOP_BOX': { value: 0x09, configurable: false, writable: false },
336 'AV_HIFI': { value: 0x0a, configurable: false, writable: false },
337 'AV_VCR': { value: 0x0b, configurable: false, writable: false },
338 'AV_VIDEO_CAMERA': { value: 0x0c, configurable: false, writable: false },
339 'AV_CAMCORDER': { value: 0x0d, configurable: false, writable: false },
340 'AV_MONITOR': { value: 0x0e, configurable: false, writable: false },
341 'AV_DISPLAY_AND_LOUDSPEAKER': { value: 0x0f, configurable: false, writable: false },
342 'AV_VIDEO_CONFERENCING': { value: 0x10, configurable: false, writable: false },
343 'AV_GAMING_TOY': { value: 0x12, configurable: false, writable: false },
344 'PERIPHERAL_UNCATEGORIZED': { value: 0, configurable: false, writable: false },
345 'PERIPHERAL_KEYBOARD': { value: 0x10, configurable: false, writable: false },
346 'PERIPHERAL_POINTING_DEVICE': { value: 0x20, configurable: false, writable: false },
347 'PERIPHERAL_KEYBOARD_AND_POINTING_DEVICE': { value: 0x30, configurable: false, writable: false },
348 'PERIPHERAL_JOYSTICK': { value: 0x01, configurable: false, writable: false },
349 'PERIPHERAL_GAMEPAD': { value: 0x02, configurable: false, writable: false },
350 'PERIPHERAL_REMOTE_CONTROL': { value: 0x03, configurable: false, writable: false },
351 'PERIPHERAL_SENSING_DEVICE': { value: 0x04, configurable: false, writable: false },
352 'PERIPHERAL_DEGITIZER_TABLET': { value: 0x05, configurable: false, writable: false },
353 'PERIPHERAL_CARD_READER': { value: 0x06, configurable: false, writable: false },
354 'PERIPHERAL_DIGITAL_PEN': { value: 0x07, configurable: false, writable: false },
355 'PERIPHERAL_HANDHELD_SCANNER': { value: 0x08, configurable: false, writable: false },
356 'PERIPHERAL_HANDHELD_INPUT_DEVICE': { value: 0x09, configurable: false, writable: false },
357 'IMAGING_UNCATEGORIZED': { value: 0x00, configurable: false, writable: false },
358 'IMAGING_DISPLAY': { value: 0x04, configurable: false, writable: false },
359 'IMAGING_CAMERA': { value: 0x08, configurable: false, writable: false },
360 'IMAGING_SCANNER': { value: 0x10, configurable: false, writable: false },
361 'IMAGING_PRINTER': { value: 0x20, configurable: false, writable: false },
362 'WEARABLE_WRITST_WATCH': { value: 0x01, configurable: false, writable: false },
363 'WEARABLE_PAGER': { value: 0x02, configurable: false, writable: false },
364 'WEARABLE_JACKET': { value: 0x03, configurable: false, writable: false },
365 'WEARABLE_HELMET': { value: 0x04, configurable: false, writable: false },
366 'WEARABLE_GLASSES': { value: 0x05, configurable: false, writable: false },
367 'TOY_ROBOT': { value: 0x01, configurable: false, writable: false },
368 'TOY_VEHICLE': { value: 0x02, configurable: false, writable: false },
369 'TOY_DOLL': { value: 0x03, configurable: false, writable: false },
370 'TOY_CONTROLLER': { value: 0x04, configurable: false, writable: false },
371 'TOY_GAME': { value: 0x05, configurable: false, writable: false },
372 'HEALTH_UNDEFINED': { value: 0x00, configurable: false, writable: false },
373 'HEALTH_BLOOD_PRESSURE_MONITOR': { value: 0x01, configurable: false, writable: false },
374 'HEALTH_THERMOMETER': { value: 0x02, configurable: false, writable: false },
375 'HEALTH_WEIGHING_SCALE': { value: 0x03, configurable: false, writable: false },
376 'HEALTH_GLUCOSE_METER': { value: 0x04, configurable: false, writable: false },
377 'HEALTH_PULSE_OXIMETER': { value: 0x05, configurable: false, writable: false },
378 'HEALTH_PULSE_RATE_MONITOR': { value: 0x06, configurable: false, writable: false },
379 'HEALTH_DATA_DISPLAY': { value: 0x07, configurable: false, writable: false },
380 'HEALTH_STEP_COUNTER': { value: 0x08, configurable: false, writable: false },
381 'HEALTH_BODY_COMPOSITION_ANALYZER': { value: 0x09, configurable: false, writable: false },
382 'HEALTH_PEAK_FLOW_MONITOR': { value: 0x0a, configurable: false, writable: false },
383 'HEALTH_MEDICATION_MONITOR': { value: 0x0b, configurable: false, writable: false },
384 'HEALTH_KNEE_PROSTHESIS': { value: 0x0c, configurable: false, writable: false },
385 'HEALTH_ANKLE_PROSTHESIS': { value: 0x0d, configurable: false, writable: false }
387 Object.defineProperties(exports.deviceMinor, deviceMinor);
389 exports.deviceService = {};
390 var deviceService = {
391 'LIMITED_DISCOVERABILITY': { value: 0x0001, configurable: false, writable: false },
392 'POSITIONING': { value: 0x0008, configurable: false, writable: false },
393 'NETWORKING': { value: 0x0010, configurable: false, writable: false },
394 'RENDERING': { value: 0x0020, configurable: false, writable: false },
395 'CAPTURING': { value: 0x0040, configurable: false, writable: false },
396 'OBJECT_TRANSFER': { value: 0x0080, configurable: false, writable: false },
397 'AUDIO': { value: 0x0100, configurable: false, writable: false },
398 'TELEPHONY': { value: 0x0200, configurable: false, writable: false },
399 'INFORMATION': { value: 0x0400, configurable: false, writable: false }
401 Object.defineProperties(exports.deviceService, deviceService);
403 function _addConstProperty(obj, propertyKey, propertyValue) {
404 Object.defineProperty(obj, propertyKey, {
411 function BluetoothAdapter() {
412 _addConstProperty(this, 'name', '');
413 _addConstProperty(this, 'address', '00:00:00:00:00:00');
414 _addConstProperty(this, 'powered', false);
415 _addConstProperty(this, 'visible', false);
418 BluetoothAdapter.prototype.setName = function(name, successCallback, errorCallback) {
419 if (!xwalk.utils.validateArguments('s?ff', arguments)) {
420 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
423 if (adapter.checkServiceAvailability(errorCallback))
427 'cmd': 'SetAdapterProperty',
432 postMessage(msg, function(result) {
433 if (result.error != 0) {
435 var error = new tizen.WebAPIError(tizen.WebAPIException.INVALID_VALUES_ERR);
436 errorCallback(error);
439 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
448 BluetoothAdapter.prototype.setPowered = function(state, successCallback, errorCallback) {
449 if (!xwalk.utils.validateArguments('b?ff', arguments)) {
450 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
453 if ((state === defaultAdapter.powered) && successCallback) {
459 'cmd': 'SetAdapterProperty',
460 'property': 'Powered',
464 postMessage(msg, function(result) {
465 if (result.error != 0) {
467 var error = new tizen.WebAPIError(tizen.WebAPIException.INVALID_VALUES_ERR);
468 errorCallback(error);
472 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
480 BluetoothAdapter.prototype.setVisible = function(mode, successCallback, errorCallback, timeout) {
481 if (!xwalk.utils.validateArguments('b?ffn', arguments)) {
482 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
485 if (adapter.checkServiceAvailability(errorCallback))
488 if (timeout === undefined || typeof timeout !== 'number' || timeout < 0)
489 timeout = 180; // According to tizen.bluetooth documentation.
492 'cmd': 'SetAdapterProperty',
493 'property': 'Discoverable',
498 postMessage(msg, function(result) {
499 if (result.error != 0) {
501 var error = new tizen.WebAPIError(tizen.WebAPIException.INVALID_VALUES_ERR);
502 errorCallback(error);
505 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
514 BluetoothAdapter.prototype.discoverDevices = function(discoverySuccessCallback, errorCallback) {
515 if (!xwalk.utils.validateArguments('o?f', arguments)) {
516 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
519 if (!xwalk.utils.validateObject(discoverySuccessCallback, 'ffff',
520 ['onstarted', 'ondevicefound', 'ondevicedisappeared', 'onfinished'])) {
521 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
524 if (adapter.checkServiceAvailability(errorCallback))
528 'cmd': 'DiscoverDevices'
530 postMessage(msg, function(result) {
531 if (result.error != 0) {
533 var error = new tizen.WebAPIError(tizen.WebAPIException.UNKNOWN_ERR);
534 errorCallback(error);
539 adapter.discovery_callbacks = discoverySuccessCallback;
541 if (discoverySuccessCallback && discoverySuccessCallback.onstarted)
542 discoverySuccessCallback.onstarted();
546 BluetoothAdapter.prototype.stopDiscovery = function(successCallback, errorCallback) {
547 if (!xwalk.utils.validateArguments('?ff', arguments)) {
548 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
551 if (adapter.checkServiceAvailability(errorCallback))
555 'cmd': 'StopDiscovery'
557 postMessage(msg, function(result) {
558 if (result.error != 0) {
560 var error = new tizen.WebAPIError(tizen.WebAPIException.UNKNOWN_ERR);
561 errorCallback(error);
569 handleDiscoveryFinished();
573 BluetoothAdapter.prototype.getKnownDevices = function(deviceArraySuccessCallback, errorCallback) {
574 if (!xwalk.utils.validateArguments('f?f', arguments)) {
575 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
578 if (adapter.checkServiceAvailability(errorCallback))
581 // FIXME(jeez): we are not returning a deep copy so we can keep
582 // the devices up-to-date. We have to find a better way to handle this.
583 deviceArraySuccessCallback(adapter.known_devices);
586 BluetoothAdapter.prototype.getDevice = function(address, deviceSuccessCallback, errorCallback) {
587 if (!xwalk.utils.validateArguments('sf?f', arguments)) {
588 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
591 if (!validateAddress(address)) {
592 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
595 if (adapter.checkServiceAvailability(errorCallback))
598 var index = adapter.indexOfDevice(adapter.known_devices, address);
600 var error = new tizen.WebAPIError(tizen.WebAPIException.NOT_FOUND_ERR);
601 errorCallback(error);
605 deviceSuccessCallback(adapter.known_devices[index]);
608 BluetoothAdapter.prototype.createBonding = function(address, successCallback, errorCallback) {
609 if (!xwalk.utils.validateArguments('sf?f', arguments)) {
610 throw new tizen.WebAPIError(tizen.WebAPIException.TYPE_MISMATCH_ERR);
613 if (!validateAddress(address)) {
614 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
617 if (adapter.checkServiceAvailability(errorCallback))
621 'cmd': 'CreateBonding',
625 postMessage(msg, function(result) {
628 if (result.error != 0) {
630 var error = new tizen.WebAPIError(tizen.WebAPIException.INVALID_VALUES_ERR);
631 errorCallback(error);
634 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
638 if (successCallback) {
639 var known_devices = adapter.known_devices;
640 for (var i = 0; i < known_devices.length; i++) {
641 if (known_devices[i].address === address) {
642 cb_device = known_devices[i];
647 // FIXME(clecou) Update known device state here when using C API Tizen backend
648 // BlueZ backends update the device state automatically when catching dbus signals.
649 // A better approach would be to adapt backends instances to have a single JSON protocol.
651 _addConstProperty(adapter.known_devices[i], 'isBonded', true);
653 successCallback(cb_device);
658 BluetoothAdapter.prototype.destroyBonding = function(address, successCallback, errorCallback) {
659 if (!xwalk.utils.validateArguments('s?ff', arguments)) {
660 throw new tizen.WebAPIError(tizen.WebAPIException.TYPE_MISMATCH_ERR);
663 if (!validateAddress(address)) {
664 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
667 if (adapter.checkServiceAvailability(errorCallback))
671 'cmd': 'DestroyBonding',
675 postMessage(msg, function(result) {
678 if (result.error != 0) {
680 var error_type = (result.error == 1) ?
681 tizen.WebAPIException.NOT_FOUND_ERR : tizen.WebAPIException.UNKNOWN_ERR;
682 var error = new tizen.WebAPIError(error_type);
683 errorCallback(error);
686 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
690 if (successCallback) {
691 var known_devices = adapter.known_devices;
692 for (var i = 0; i < known_devices.length; i++) {
693 if (known_devices[i].address === address) {
694 cb_device = known_devices[i];
699 // FIXME(clecou) Update known device state here when using C API Tizen backend
700 // BlueZ backends update the device state automatically when catching dbus signals
701 // A better approach would be to adapt backends instances to have a single JSON protocol.
703 _addConstProperty(adapter.known_devices[i], 'isBonded', false);
705 successCallback(cb_device);
710 BluetoothAdapter.prototype.registerRFCOMMServiceByUUID =
711 function(uuid, name, serviceSuccessCallback, errorCallback) {
712 if (!xwalk.utils.validateArguments('ssf?f', arguments)) {
713 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
716 if (adapter.checkServiceAvailability(errorCallback))
720 'cmd': 'RFCOMMListen',
725 postMessage(msg, function(result) {
726 if (result.error != 0) {
729 if (result.error == 1)
730 error = new tizen.WebAPIError(tizen.WebAPIException.NOT_FOUND_ERR);
732 error = new tizen.WebAPIError(tizen.WebAPIException.UNKNOWN_ERR);
733 errorCallback(error);
738 var service = new BluetoothServiceHandler(uuid, name, result);
739 adapter.service_handlers.push(service);
741 if (serviceSuccessCallback) {
742 serviceSuccessCallback(service);
747 BluetoothAdapter.prototype.setChangeListener = function(listener) {
748 if (!xwalk.utils.validateArguments('o', arguments)) {
749 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
752 if (!xwalk.utils.validateObject(listener, 'fff',
753 ['onstatechanged', 'onnamechanged', 'onvisibilitychanged'])) {
754 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
757 adapter.change_listener = listener;
760 BluetoothAdapter.prototype.unsetChangeListener = function() {
761 adapter.change_listener = null;
764 var _deviceClassMask = {
770 function BluetoothDevice(msg) {
772 _addConstProperty(this, 'name', '');
773 _addConstProperty(this, 'address', '');
774 _addConstProperty(this, 'deviceClass', new BluetoothClass());
775 _addConstProperty(this, 'isBonded', false);
776 _addConstProperty(this, 'isTrusted', false);
777 _addConstProperty(this, 'isConnected', false);
778 _addConstProperty(this, 'uuids', []);
782 _addConstProperty(this, 'name', msg.Alias);
783 _addConstProperty(this, 'address', msg.Address);
785 _addConstProperty(this, 'deviceClass', new BluetoothClass());
786 _addConstProperty(this.deviceClass, 'minor', (msg.ClassMinor >> 2) & _deviceClassMask.MINOR);
787 _addConstProperty(this.deviceClass, 'major', msg.ClassMajor & _deviceClassMask.MAJOR);
789 _addConstProperty(this, 'isBonded', (msg.Paired == 'true') ? true : false);
790 _addConstProperty(this, 'isTrusted', (msg.Trusted == 'true') ? true : false);
791 _addConstProperty(this, 'isConnected', (msg.Connected == 'true') ? true : false);
794 if (typeof msg.UUIDs === 'string') {
795 // FIXME(clecou) BlueZ backend sends a string to convert it into an array
796 // A better approach would be to adapt backends instances to have a single JSON protocol.
797 var uuids_array = [];
798 uuids_array = msg.UUIDs.substring(msg.UUIDs.indexOf('[') + 1,
799 msg.UUIDs.indexOf(']')).split(',');
800 for (var i = 0; i < uuids_array.length; i++) {
801 uuids_array[i] = uuids_array[i].substring(2, uuids_array[i].length - 1);
803 _addConstProperty(this, 'uuids', uuids_array);
805 // Tizen C API backend directly sends an array
806 _addConstProperty(this, 'uuids', msg.UUIDs);
810 var services = (msg.ClassService >> 13) & _deviceClassMask.SERVICE;
811 var services_array = [];
813 // 11 is the number of bits in _deviceClassMask.SERVICE
814 for (var i = 0; i < 11; i++)
815 if ((services & (1 << i)) !== 0)
816 services_array.push(1 << i);
818 _addConstProperty(this.deviceClass, 'services', services_array);
821 BluetoothDevice.prototype.connectToServiceByUUID =
822 function(uuid, socketSuccessCallback, errorCallback) {
824 if (!xwalk.utils.validateArguments('sf?f', arguments)) {
825 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
828 if (adapter.checkServiceAvailability(errorCallback))
831 var uuid_found = false;
832 for (var i = 0; i < this.uuids.length; i++) {
833 if (this.uuids[i] == uuid) {
838 if (uuid_found == false) {
839 var error = new tizen.WebAPIError(tizen.WebAPIException.NOT_FOUND_ERR);
840 errorCallback(error);
844 'cmd': 'ConnectToService',
846 'address' : this.address
849 postMessage(msg, function(result) {
850 if (result.error != 0) {
852 var error = new tizen.WebAPIError(tizen.WebAPIException.UNKNOWN_ERR);
853 errorCallback(error);
856 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
860 if (socketSuccessCallback) {
861 var socket_cb = new BluetoothSocket(result.uuid, this, result);
862 socketSuccessCallback(socket_cb);
867 BluetoothDevice.prototype._clone = function() {
868 var clone = new BluetoothDevice();
869 _addConstProperty(clone, 'name', this.name);
870 _addConstProperty(clone, 'address', this.address);
871 _addConstProperty(clone, 'deviceClass', this.deviceClass);
872 _addConstProperty(clone, 'isBonded', this.isBonded);
873 _addConstProperty(clone, 'isTrusted', this.isTrusted);
874 _addConstProperty(clone, 'isConnected', this.isConnected);
876 var uuids_array = [];
877 for (var i = 0; i < this.uuids.length; i++)
878 uuids_array[i] = this.uuids[i];
880 _addConstProperty(clone, 'uuids', uuids_array);
885 BluetoothDevice.prototype._updateProperties = function(device) {
886 if (device.hasOwnProperty('name'))
887 _addConstProperty(this, 'name', device.name);
888 if (device.hasOwnProperty('address'))
889 _addConstProperty(this, 'address', device.address);
890 if (device.hasOwnProperty('deviceClass'))
891 _addConstProperty(this, 'deviceClass', device.deviceClass);
892 if (device.hasOwnProperty('isBonded'))
893 _addConstProperty(this, 'isBonded', device.isBonded);
894 if (device.hasOwnProperty('isTrusted'))
895 _addConstProperty(this, 'isTrusted', device.isTrusted);
896 if (device.hasOwnProperty('isConnected'))
897 _addConstProperty(this, 'isConnected', device.isConnected);
899 if (device.hasOwnProperty('uuids')) {
900 for (var i = 0; i < this.uuids.length; i++)
901 this.uuids[i] = device.uuids[i];
905 function BluetoothSocket(uuid, peer, msg) {
906 _addConstProperty(this, 'uuid', uuid);
907 _addConstProperty(this, 'peer', peer);
908 _addConstProperty(this, 'state', BluetoothSocketState.OPEN);
910 this.onmessage = null;
916 this.channel = msg.channel;
917 this.socket_fd = msg.socket_fd;
921 var BluetoothSocketState = {
925 Object.defineProperty(BluetoothSocket, 'BluetoothSocketState', {
928 value: BluetoothSocketState
932 BluetoothSocket.prototype.writeData = function(data) {
933 // make sure that socket is connected and opened.
934 if (this.state == BluetoothSocketState.CLOSE) {
935 throw new tizen.WebAPIException(tizen.WebAPIException.UNKNOWN_ERR);
940 'cmd': 'SocketWriteData',
942 'socket_fd': this.socket_fd
944 var result = JSON.parse(extension.internal.sendSyncMessage(JSON.stringify(msg)));
949 BluetoothSocket.prototype.readData = function() {
953 BluetoothSocket.prototype.close = function() {
955 'cmd': 'CloseSocket',
956 'socket_fd': this.socket_fd
959 postMessage(msg, function(result) {
961 console.log('Can\'t close socket (' + this.socket_fd + ').');
962 throw new tizen.WebAPIException(tizen.WebAPIException.UNKNOWN_ERR);
966 // FIXME(clecou) Update socket object state only when using Tizen C API backend.
967 // BlueZ4 backend independently updates socket state based on a dbus callback mechanism.
968 // A better approach would be to adapt backends instances to have a single JSON protocol.
970 for (var i in adapter.sockets) {
971 var socket = adapter.sockets[i];
972 if (socket.socket_fd === msg.socket_fd) {
973 if (socket.onclose && typeof socket.onmessage === 'function') {
974 _addConstProperty(adapter.sockets[i], 'state', BluetoothSocketState.CLOSE);
983 function BluetoothClass() {}
984 BluetoothClass.prototype.hasService = function(service) {
985 for (var i = 0; i < this.services.length; i++)
986 if (this.services[i] === service)
992 function BluetoothServiceHandler(name, uuid, msg) {
993 _addConstProperty(this, 'name', name);
994 _addConstProperty(this, 'uuid', uuid);
995 _addConstProperty(this, 'isConnected', false);
998 this.server_fd = msg.server_fd;
999 this.sdp_handle = msg.sdp_handle;
1000 this.channel = msg.channel;
1004 BluetoothServiceHandler.prototype.unregister = function(successCallback, errorCallback) {
1005 if (!xwalk.utils.validateArguments('?ff', arguments)) {
1006 throw new tizen.WebAPIError(tizen.WebAPIException.TYPE_MISMATCH_ERR);
1009 if (adapter.checkServiceAvailability(errorCallback))
1013 'cmd': 'UnregisterServer',
1014 'server_fd': this.server_fd,
1015 'sdp_handle': this.sdp_handle
1018 postMessage(msg, function(result) {
1019 if (result.error != 0) {
1020 if (errorCallback) {
1021 var error = new tizen.WebAPIError(tizen.WebAPIException.UNKNOWN_ERR);
1022 errorCallback(error);
1025 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
1029 _addConstProperty(this, 'isConnected', false);
1030 if (successCallback)