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);
36 else { // Then we are dealing with postMessage return.
37 var reply_id = msg.reply_id;
38 var callback = _callbacks[reply_id];
41 delete _callbacks[reply_id];
44 // do not print error log when the postmessage was not initiated by JS
46 console.log('Invalid reply_id from Tizen Bluetooth: ' + reply_id);
52 this.found_devices = []; // Filled while a Discovering.
53 this.known_devices = []; // Keeps Managed and Found devices.
54 this.discovery_callbacks = {};
56 this.service_handlers = [];
58 this.change_listener = null;
61 function validateAddress(address) {
62 if (typeof address !== 'string')
65 var regExp = /([\dA-F][\dA-F]:){5}[\dA-F][\dA-F]/i;
67 if (!address.match(regExp))
73 Adapter.prototype.checkServiceAvailability = function(errorCallback) {
74 if (adapter.isReady && defaultAdapter.powered)
78 var error = new tizen.WebAPIError(tizen.WebAPIException.SERVICE_NOT_AVAILABLE_ERR);
85 Adapter.prototype.indexOfDevice = function(devices, address) {
86 for (var i = 0; i < devices.length; i++) {
87 if (devices[i].address == address)
93 Adapter.prototype.addDevice = function(device, on_discovery) {
94 var new_device = false;
97 var index = this.indexOfDevice(this.found_devices, device.address);
99 this.found_devices.push(device);
102 this.found_devices[index] = device;
107 var i = this.indexOfDevice(this.known_devices, device.address);
109 this.known_devices.push(device);
111 this.known_devices[i] = device;
116 Adapter.prototype.updateDevice = function(device) {
117 var index = this.indexOfDevice(this.known_devices, device.address);
119 this.known_devices.push(device);
121 this.known_devices[index]._updateProperties(device);
124 // This holds the adapter the Bluetooth backend is currently using.
125 // In BlueZ 4, for instance, this would represent the "default adapter".
126 // BlueZ 5 has no such concept, so this will hold the currently available
127 // adapter, which can be just the first one found.
128 var adapter = new Adapter();
130 var deepCopyDevices = function(devices) {
131 var copiedDevices = [];
132 for (var i = 0; i < devices.length; i++)
133 copiedDevices[i] = devices[i]._clone();
135 return copiedDevices;
138 var handleBondedDevice = function(msg) {
139 var device = new BluetoothDevice(msg);
140 adapter.addDevice(device, false);
143 var handleDeviceFound = function(msg) {
144 var device = new BluetoothDevice(msg);
145 var is_new = adapter.addDevice(device, msg.found_on_discovery);
147 // FIXME(jeez): we are not returning a deep copy so we can keep
148 // the devices up-to-date. We have to find a better way to handle this.
149 if (is_new && msg.found_on_discovery && adapter.discovery_callbacks.ondevicefound)
150 adapter.discovery_callbacks.ondevicefound(device);
153 var handleDiscoveryFinished = function() {
154 // FIXME(jeez): we are not returning a deep copy so we can keep
155 // the devices up-to-date. We have to find a better way to handle this.
156 if (typeof adapter.discovery_callbacks.onfinished === 'function')
157 adapter.discovery_callbacks.onfinished(adapter.found_devices);
159 adapter.found_devices = [];
160 adapter.discovery_callbacks = {};
163 var handleDeviceRemoved = function(address) {
164 var foundDevices = adapter.found_devices;
165 var knownDevices = adapter.known_devices;
167 for (var i = 0; i < foundDevices.length; i++) {
168 if (foundDevices[i].address === address) {
169 foundDevices.splice(i, 1);
174 for (var i = 0; i < knownDevices.length; i++) {
175 if (knownDevices[i].address === address) {
176 knownDevices.splice(i, 1);
182 var handleDeviceUpdated = function(msg) {
183 var device = new BluetoothDevice(msg);
184 adapter.updateDevice(device);
187 var handleAdapterUpdated = function(msg) {
188 var listener = adapter.change_listener;
191 _addConstProperty(defaultAdapter, 'name', msg.Name);
192 if (listener && listener.onnamechanged) {
193 adapter.change_listener.onnamechanged(msg.Name);
198 _addConstProperty(defaultAdapter, 'address', msg.Address);
201 var powered = (msg.Powered === 'true') ? true : false;
202 _addConstProperty(defaultAdapter, 'powered', powered);
203 if (defaultAdapter.powered !== powered && listener && listener.onstatechanged) {
204 adapter.change_listener.onstatechanged(powered);
208 if (msg.Discoverable) {
209 var visibility = (msg.Discoverable === 'true') ? true : false;
211 if (defaultAdapter.visible !== visibility && listener && listener.onvisibilitychanged) {
212 adapter.change_listener.onvisibilitychanged(visibility);
214 _addConstProperty(defaultAdapter, 'visible', visibility);
217 defaultAdapter.isReady = true;
220 var handleRFCOMMSocketAccept = function(msg) {
221 for (var i in adapter.service_handlers) {
222 var server = adapter.service_handlers[i];
223 // FIXME(clecou) BlueZ4 backend compares rfcomm channel number but this parameter
224 // is not available in Tizen C API so we check socket fd.
225 // A better approach would be to adapt backends instances to have a single JSON protocol.
226 if (server.channel === msg.channel || server.server_fd === msg.socket_fd) {
227 var j = adapter.indexOfDevice(adapter.known_devices, msg.peer);
228 var peer = adapter.known_devices[j];
230 var socket = new BluetoothSocket(server.uuid, peer, msg);
232 adapter.sockets.push(socket);
234 _addConstProperty(server, 'isConnected', true);
236 if (server.onconnect && typeof server.onconnect === 'function')
237 server.onconnect(socket);
243 var handleSocketHasData = function(msg) {
244 for (var i in adapter.sockets) {
245 var socket = adapter.sockets[i];
246 if (socket.socket_fd === msg.socket_fd) {
247 socket.data = msg.data;
249 if (socket.onmessage && typeof socket.onmessage === 'function')
258 var handleSocketClosed = function(msg) {
259 for (var i in adapter.sockets) {
260 var socket = adapter.sockets[i];
261 if (socket.socket_fd === msg.socket_fd) {
262 if (socket.onclose && typeof socket.onmessage === 'function')
265 _addConstProperty(socket, 'isConnected', false);
271 var defaultAdapter = new BluetoothAdapter();
273 exports.getDefaultAdapter = function() {
275 'cmd': 'GetDefaultAdapter'
277 var result = JSON.parse(extension.internal.sendSyncMessage(JSON.stringify(msg)));
280 _addConstProperty(defaultAdapter, 'name', result.name);
281 _addConstProperty(defaultAdapter, 'address', result.address);
282 _addConstProperty(defaultAdapter, 'powered', result.powered);
283 _addConstProperty(defaultAdapter, 'visible', result.visible);
285 if (result.hasOwnProperty('address') && result.address != '')
286 adapter.isReady = true;
288 adapter.isReady = false;
289 throw new tizen.WebAPIException(tizen.WebAPIException.UNKNOWN_ERR);
292 return defaultAdapter;
295 exports.deviceMajor = {};
297 'MISC': { value: 0x00, configurable: false, writable: false },
298 'COMPUTER': { value: 0x01, configurable: false, writable: false },
299 'PHONE': { value: 0x02, configurable: false, writable: false },
300 'NETWORK': { value: 0x03, configurable: false, writable: false },
301 'AUDIO_VIDEO': { value: 0x04, configurable: false, writable: false },
302 'PERIPHERAL': { value: 0x05, configurable: false, writable: false },
303 'IMAGING': { value: 0x06, configurable: false, writable: false },
304 'WEARABLE': { value: 0x07, configurable: false, writable: false },
305 'TOY': { value: 0x08, configurable: false, writable: false },
306 'HEALTH': { value: 0x09, configurable: false, writable: false },
307 'UNCATEGORIZED': { value: 0x1F, configurable: false, writable: false }
309 Object.defineProperties(exports.deviceMajor, deviceMajor);
311 exports.deviceMinor = {};
313 'COMPUTER_UNCATEGORIZED': { value: 0x00, configurable: false, writable: false },
314 'COMPUTER_DESKTOP': { value: 0x01, configurable: false, writable: false },
315 'COMPUTER_SERVER': { value: 0x02, configurable: false, writable: false },
316 'COMPUTER_LAPTOP': { value: 0x03, configurable: false, writable: false },
317 'COMPUTER_HANDHELD_PC_OR_PDA': { value: 0x04, configurable: false, writable: false },
318 'COMPUTER_PALM_PC_OR_PDA': { value: 0x05, configurable: false, writable: false },
319 'COMPUTER_WEARABLE': { value: 0x06, configurable: false, writable: false },
320 'PHONE_UNCATEGORIZED': { value: 0x00, configurable: false, writable: false },
321 'PHONE_CELLULAR': { value: 0x01, configurable: false, writable: false },
322 'PHONE_CORDLESS': { value: 0x02, configurable: false, writable: false },
323 'PHONE_SMARTPHONE': { value: 0x03, configurable: false, writable: false },
324 'PHONE_MODEM_OR_GATEWAY': { value: 0x04, configurable: false, writable: false },
325 'PHONE_ISDN': { value: 0x05, configurable: false, writable: false },
326 'AV_UNRECOGNIZED': { value: 0x00, configurable: false, writable: false },
327 'AV_WEARABLE_HEADSET': { value: 0x01, configurable: false, writable: false },
328 'AV_HANDSFREE': { value: 0x02, configurable: false, writable: false },
329 'AV_MICROPHONE': { value: 0x04, configurable: false, writable: false },
330 'AV_LOUDSPEAKER': { value: 0x05, configurable: false, writable: false },
331 'AV_HEADPHONES': { value: 0x06, configurable: false, writable: false },
332 'AV_PORTABLE_AUDIO': { value: 0x07, configurable: false, writable: false },
333 'AV_CAR_AUDIO': { value: 0x08, configurable: false, writable: false },
334 'AV_SETTOP_BOX': { value: 0x09, configurable: false, writable: false },
335 'AV_HIFI': { value: 0x0a, configurable: false, writable: false },
336 'AV_VCR': { value: 0x0b, configurable: false, writable: false },
337 'AV_VIDEO_CAMERA': { value: 0x0c, configurable: false, writable: false },
338 'AV_CAMCORDER': { value: 0x0d, configurable: false, writable: false },
339 'AV_MONITOR': { value: 0x0e, configurable: false, writable: false },
340 'AV_DISPLAY_AND_LOUDSPEAKER': { value: 0x0f, configurable: false, writable: false },
341 'AV_VIDEO_CONFERENCING': { value: 0x10, configurable: false, writable: false },
342 'AV_GAMING_TOY': { value: 0x12, configurable: false, writable: false },
343 'PERIPHERAL_UNCATEGORIZED': { value: 0, configurable: false, writable: false },
344 'PERIPHERAL_KEYBOARD': { value: 0x10, configurable: false, writable: false },
345 'PERIPHERAL_POINTING_DEVICE': { value: 0x20, configurable: false, writable: false },
346 'PERIPHERAL_KEYBOARD_AND_POINTING_DEVICE': { value: 0x30, configurable: false, writable: false },
347 'PERIPHERAL_JOYSTICK': { value: 0x01, configurable: false, writable: false },
348 'PERIPHERAL_GAMEPAD': { value: 0x02, configurable: false, writable: false },
349 'PERIPHERAL_REMOTE_CONTROL': { value: 0x03, configurable: false, writable: false },
350 'PERIPHERAL_SENSING_DEVICE': { value: 0x04, configurable: false, writable: false },
351 'PERIPHERAL_DEGITIZER_TABLET': { value: 0x05, configurable: false, writable: false },
352 'PERIPHERAL_CARD_READER': { value: 0x06, configurable: false, writable: false },
353 'PERIPHERAL_DIGITAL_PEN': { value: 0x07, configurable: false, writable: false },
354 'PERIPHERAL_HANDHELD_SCANNER': { value: 0x08, configurable: false, writable: false },
355 'PERIPHERAL_HANDHELD_INPUT_DEVICE': { value: 0x09, configurable: false, writable: false },
356 'IMAGING_UNCATEGORIZED': { value: 0x00, configurable: false, writable: false },
357 'IMAGING_DISPLAY': { value: 0x04, configurable: false, writable: false },
358 'IMAGING_CAMERA': { value: 0x08, configurable: false, writable: false },
359 'IMAGING_SCANNER': { value: 0x10, configurable: false, writable: false },
360 'IMAGING_PRINTER': { value: 0x20, configurable: false, writable: false },
361 'WEARABLE_WRITST_WATCH': { value: 0x01, configurable: false, writable: false },
362 'WEARABLE_PAGER': { value: 0x02, configurable: false, writable: false },
363 'WEARABLE_JACKET': { value: 0x03, configurable: false, writable: false },
364 'WEARABLE_HELMET': { value: 0x04, configurable: false, writable: false },
365 'WEARABLE_GLASSES': { value: 0x05, configurable: false, writable: false },
366 'TOY_ROBOT': { value: 0x01, configurable: false, writable: false },
367 'TOY_VEHICLE': { value: 0x02, configurable: false, writable: false },
368 'TOY_DOLL': { value: 0x03, configurable: false, writable: false },
369 'TOY_CONTROLLER': { value: 0x04, configurable: false, writable: false },
370 'TOY_GAME': { value: 0x05, configurable: false, writable: false },
371 'HEALTH_UNDEFINED': { value: 0x00, configurable: false, writable: false },
372 'HEALTH_BLOOD_PRESSURE_MONITOR': { value: 0x01, configurable: false, writable: false },
373 'HEALTH_THERMOMETER': { value: 0x02, configurable: false, writable: false },
374 'HEALTH_WEIGHING_SCALE': { value: 0x03, configurable: false, writable: false },
375 'HEALTH_GLUCOSE_METER': { value: 0x04, configurable: false, writable: false },
376 'HEALTH_PULSE_OXIMETER': { value: 0x05, configurable: false, writable: false },
377 'HEALTH_PULSE_RATE_MONITOR': { value: 0x06, configurable: false, writable: false },
378 'HEALTH_DATA_DISPLAY': { value: 0x07, configurable: false, writable: false },
379 'HEALTH_STEP_COUNTER': { value: 0x08, configurable: false, writable: false },
380 'HEALTH_BODY_COMPOSITION_ANALYZER': { value: 0x09, configurable: false, writable: false },
381 'HEALTH_PEAK_FLOW_MONITOR': { value: 0x0a, configurable: false, writable: false },
382 'HEALTH_MEDICATION_MONITOR': { value: 0x0b, configurable: false, writable: false },
383 'HEALTH_KNEE_PROSTHESIS': { value: 0x0c, configurable: false, writable: false },
384 'HEALTH_ANKLE_PROSTHESIS': { value: 0x0d, configurable: false, writable: false }
386 Object.defineProperties(exports.deviceMinor, deviceMinor);
388 exports.deviceService = {};
389 var deviceService = {
390 'LIMITED_DISCOVERABILITY': { value: 0x0001, configurable: false, writable: false },
391 'POSITIONING': { value: 0x0008, configurable: false, writable: false },
392 'NETWORKING': { value: 0x0010, configurable: false, writable: false },
393 'RENDERING': { value: 0x0020, configurable: false, writable: false },
394 'CAPTURING': { value: 0x0040, configurable: false, writable: false },
395 'OBJECT_TRANSFER': { value: 0x0080, configurable: false, writable: false },
396 'AUDIO': { value: 0x0100, configurable: false, writable: false },
397 'TELEPHONY': { value: 0x0200, configurable: false, writable: false },
398 'INFORMATION': { value: 0x0400, configurable: false, writable: false }
400 Object.defineProperties(exports.deviceService, deviceService);
402 function _addConstProperty(obj, propertyKey, propertyValue) {
403 Object.defineProperty(obj, propertyKey, {
410 function BluetoothAdapter() {
411 _addConstProperty(this, 'name', '');
412 _addConstProperty(this, 'address', '00:00:00:00:00:00');
413 _addConstProperty(this, 'powered', false);
414 _addConstProperty(this, 'visible', false);
417 BluetoothAdapter.prototype.setName = function(name, successCallback, errorCallback) {
418 if (!xwalk.utils.validateArguments('s?ff', arguments)) {
419 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
422 if (adapter.checkServiceAvailability(errorCallback))
426 'cmd': 'SetAdapterProperty',
431 postMessage(msg, function(result) {
432 if (result.error != 0) {
434 var error = new tizen.WebAPIError(tizen.WebAPIException.INVALID_VALUES_ERR);
435 errorCallback(error);
438 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
442 _addConstProperty(defaultAdapter, 'name', name);
449 BluetoothAdapter.prototype.setPowered = function(state, successCallback, errorCallback) {
450 if (!xwalk.utils.validateArguments('b?ff', arguments)) {
451 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
454 if ((state === defaultAdapter.powered) && successCallback) {
460 'cmd': 'SetAdapterProperty',
461 'property': 'Powered',
465 postMessage(msg, function(result) {
466 if (result.error != 0) {
468 var error = new tizen.WebAPIError(tizen.WebAPIException.INVALID_VALUES_ERR);
469 errorCallback(error);
473 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
476 _addConstProperty(defaultAdapter, 'powered', state);
483 BluetoothAdapter.prototype.setVisible = function(mode, successCallback, errorCallback, timeout) {
484 if (!xwalk.utils.validateArguments('b?ffn', arguments)) {
485 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
488 if (adapter.checkServiceAvailability(errorCallback))
491 if (timeout === undefined || typeof timeout !== 'number' || timeout < 0)
492 timeout = 180; // According to tizen.bluetooth documentation.
495 'cmd': 'SetAdapterProperty',
496 'property': 'Discoverable',
501 postMessage(msg, function(result) {
502 if (result.error != 0) {
504 var error = new tizen.WebAPIError(tizen.WebAPIException.INVALID_VALUES_ERR);
505 errorCallback(error);
508 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
512 _addConstProperty(defaultAdapter, 'visible', mode);
519 BluetoothAdapter.prototype.discoverDevices = function(discoverySuccessCallback, errorCallback) {
520 if (!xwalk.utils.validateArguments('o?f', arguments)) {
521 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
524 if (!xwalk.utils.validateObject(discoverySuccessCallback, 'ffff',
525 ['onstarted', 'ondevicefound', 'ondevicedisappeared', 'onfinished'])) {
526 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
529 if (adapter.checkServiceAvailability(errorCallback))
533 'cmd': 'DiscoverDevices'
535 postMessage(msg, function(result) {
536 if (result.error != 0) {
538 var error = new tizen.WebAPIError(tizen.WebAPIException.UNKNOWN_ERR);
539 errorCallback(error);
544 adapter.discovery_callbacks = discoverySuccessCallback;
546 if (discoverySuccessCallback && discoverySuccessCallback.onstarted)
547 discoverySuccessCallback.onstarted();
551 BluetoothAdapter.prototype.stopDiscovery = function(successCallback, errorCallback) {
552 if (!xwalk.utils.validateArguments('?ff', arguments)) {
553 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
556 if (adapter.checkServiceAvailability(errorCallback))
560 'cmd': 'StopDiscovery'
562 postMessage(msg, function(result) {
563 if (result.error != 0) {
565 var error = new tizen.WebAPIError(tizen.WebAPIException.UNKNOWN_ERR);
566 errorCallback(error);
574 handleDiscoveryFinished();
578 BluetoothAdapter.prototype.getKnownDevices = function(deviceArraySuccessCallback, errorCallback) {
579 if (!xwalk.utils.validateArguments('f?f', arguments)) {
580 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
583 if (adapter.checkServiceAvailability(errorCallback))
586 // FIXME(jeez): we are not returning a deep copy so we can keep
587 // the devices up-to-date. We have to find a better way to handle this.
588 deviceArraySuccessCallback(adapter.known_devices);
591 BluetoothAdapter.prototype.getDevice = function(address, deviceSuccessCallback, errorCallback) {
592 if (!xwalk.utils.validateArguments('sf?f', arguments)) {
593 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
596 if (!validateAddress(address)) {
597 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
600 if (adapter.checkServiceAvailability(errorCallback))
603 var index = adapter.indexOfDevice(adapter.known_devices, address);
605 var error = new tizen.WebAPIError(tizen.WebAPIException.NOT_FOUND_ERR);
606 errorCallback(error);
610 deviceSuccessCallback(adapter.known_devices[index]);
613 BluetoothAdapter.prototype.createBonding = function(address, successCallback, errorCallback) {
614 if (!xwalk.utils.validateArguments('sf?f', arguments)) {
615 throw new tizen.WebAPIError(tizen.WebAPIException.TYPE_MISMATCH_ERR);
618 if (!validateAddress(address)) {
619 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
622 if (adapter.checkServiceAvailability(errorCallback))
626 'cmd': 'CreateBonding',
630 postMessage(msg, function(result) {
633 if (result.error != 0) {
635 var error = new tizen.WebAPIError(tizen.WebAPIException.INVALID_VALUES_ERR);
636 errorCallback(error);
639 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
643 if (successCallback) {
644 var known_devices = adapter.known_devices;
645 for (var i = 0; i < known_devices.length; i++) {
646 if (known_devices[i].address === address) {
647 cb_device = known_devices[i];
652 // FIXME(clecou) Update known device state here when using C API Tizen backend
653 // BlueZ backends update the device state automatically when catching dbus signals.
654 // A better approach would be to adapt backends instances to have a single JSON protocol.
656 _addConstProperty(adapter.known_devices[i], 'isBonded', true);
658 successCallback(cb_device);
663 BluetoothAdapter.prototype.destroyBonding = function(address, successCallback, errorCallback) {
664 if (!xwalk.utils.validateArguments('s?ff', arguments)) {
665 throw new tizen.WebAPIError(tizen.WebAPIException.TYPE_MISMATCH_ERR);
668 if (!validateAddress(address)) {
669 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
672 if (adapter.checkServiceAvailability(errorCallback))
676 'cmd': 'DestroyBonding',
680 postMessage(msg, function(result) {
683 if (result.error != 0) {
685 var error_type = (result.error == 1) ?
686 tizen.WebAPIException.NOT_FOUND_ERR : tizen.WebAPIException.UNKNOWN_ERR;
687 var error = new tizen.WebAPIError(error_type);
688 errorCallback(error);
691 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
695 if (successCallback) {
696 var known_devices = adapter.known_devices;
697 for (var i = 0; i < known_devices.length; i++) {
698 if (known_devices[i].address === address) {
699 cb_device = known_devices[i];
704 // FIXME(clecou) Update known device state here when using C API Tizen backend
705 // BlueZ backends update the device state automatically when catching dbus signals
706 // A better approach would be to adapt backends instances to have a single JSON protocol.
708 _addConstProperty(adapter.known_devices[i], 'isBonded', false);
710 successCallback(cb_device);
715 BluetoothAdapter.prototype.registerRFCOMMServiceByUUID =
716 function(uuid, name, serviceSuccessCallback, errorCallback) {
717 if (!xwalk.utils.validateArguments('ssf?f', arguments)) {
718 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
721 if (adapter.checkServiceAvailability(errorCallback))
725 'cmd': 'RFCOMMListen',
730 postMessage(msg, function(result) {
731 if (result.error != 0) {
734 if (result.error == 1)
735 error = new tizen.WebAPIError(tizen.WebAPIException.NOT_FOUND_ERR);
737 error = new tizen.WebAPIError(tizen.WebAPIException.UNKNOWN_ERR);
738 errorCallback(error);
743 var service = new BluetoothServiceHandler(uuid, name, result);
744 adapter.service_handlers.push(service);
746 if (serviceSuccessCallback) {
747 serviceSuccessCallback(service);
752 BluetoothAdapter.prototype.setChangeListener = function(listener) {
753 if (!xwalk.utils.validateArguments('o', arguments)) {
754 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
757 if (!xwalk.utils.validateObject(listener, 'fff',
758 ['onstatechanged', 'onnamechanged', 'onvisibilitychanged'])) {
759 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
762 adapter.change_listener = listener;
765 BluetoothAdapter.prototype.unsetChangeListener = function() {
766 adapter.change_listener = null;
769 var _deviceClassMask = {
775 function BluetoothDevice(msg) {
777 _addConstProperty(this, 'name', '');
778 _addConstProperty(this, 'address', '');
779 _addConstProperty(this, 'deviceClass', new BluetoothClass());
780 _addConstProperty(this, 'isBonded', false);
781 _addConstProperty(this, 'isTrusted', false);
782 _addConstProperty(this, 'isConnected', false);
783 _addConstProperty(this, 'uuids', []);
787 _addConstProperty(this, 'name', msg.Alias);
788 _addConstProperty(this, 'address', msg.Address);
790 _addConstProperty(this, 'deviceClass', new BluetoothClass());
791 _addConstProperty(this.deviceClass, 'minor', (msg.ClassMinor >> 2) & _deviceClassMask.MINOR);
792 _addConstProperty(this.deviceClass, 'major', msg.ClassMajor & _deviceClassMask.MAJOR);
794 _addConstProperty(this, 'isBonded', (msg.Paired == 'true') ? true : false);
795 _addConstProperty(this, 'isTrusted', (msg.Trusted == 'true') ? true : false);
796 _addConstProperty(this, 'isConnected', (msg.Connected == 'true') ? true : false);
799 if (typeof msg.UUIDs === 'string') {
800 // FIXME(clecou) BlueZ backend sends a string to convert it into an array
801 // A better approach would be to adapt backends instances to have a single JSON protocol.
802 var uuids_array = [];
803 uuids_array = msg.UUIDs.substring(msg.UUIDs.indexOf('[') + 1,
804 msg.UUIDs.indexOf(']')).split(',');
805 for (var i = 0; i < uuids_array.length; i++) {
806 uuids_array[i] = uuids_array[i].substring(2, uuids_array[i].length - 1);
808 _addConstProperty(this, 'uuids', uuids_array);
810 // Tizen C API backend directly sends an array
811 _addConstProperty(this, 'uuids', msg.UUIDs);
815 var services = (msg.ClassService >> 13) & _deviceClassMask.SERVICE;
816 var services_array = [];
818 // 11 is the number of bits in _deviceClassMask.SERVICE
819 for (var i = 0; i < 11; i++)
820 if ((services & (1 << i)) !== 0)
821 services_array.push(1 << i);
823 _addConstProperty(this.deviceClass, 'services', services_array);
826 BluetoothDevice.prototype.connectToServiceByUUID =
827 function(uuid, socketSuccessCallback, errorCallback) {
829 if (!xwalk.utils.validateArguments('sf?f', arguments)) {
830 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
833 if (adapter.checkServiceAvailability(errorCallback))
836 var uuid_found = false;
837 for (var i = 0; i < this.uuids.length; i++) {
838 if (this.uuids[i] == uuid) {
843 if (uuid_found == false) {
844 var error = new tizen.WebAPIError(tizen.WebAPIException.NOT_FOUND_ERR);
845 errorCallback(error);
849 'cmd': 'ConnectToService',
851 'address' : this.address
854 postMessage(msg, function(result) {
855 if (result.error != 0) {
857 var error = new tizen.WebAPIError(tizen.WebAPIException.UNKNOWN_ERR);
858 errorCallback(error);
861 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
865 if (socketSuccessCallback) {
866 var socket_cb = new BluetoothSocket(result.uuid, this, result);
867 socketSuccessCallback(socket_cb);
872 BluetoothDevice.prototype._clone = function() {
873 var clone = new BluetoothDevice();
874 _addConstProperty(clone, 'name', this.name);
875 _addConstProperty(clone, 'address', this.address);
876 _addConstProperty(clone, 'deviceClass', this.deviceClass);
877 _addConstProperty(clone, 'isBonded', this.isBonded);
878 _addConstProperty(clone, 'isTrusted', this.isTrusted);
879 _addConstProperty(clone, 'isConnected', this.isConnected);
881 var uuids_array = [];
882 for (var i = 0; i < this.uuids.length; i++)
883 uuids_array[i] = this.uuids[i];
885 _addConstProperty(clone, 'uuids', uuids_array);
890 BluetoothDevice.prototype._updateProperties = function(device) {
891 if (device.hasOwnProperty('name'))
892 _addConstProperty(this, 'name', device.name);
893 if (device.hasOwnProperty('address'))
894 _addConstProperty(this, 'address', device.address);
895 if (device.hasOwnProperty('deviceClass'))
896 _addConstProperty(this, 'deviceClass', device.deviceClass);
897 if (device.hasOwnProperty('isBonded'))
898 _addConstProperty(this, 'isBonded', device.isBonded);
899 if (device.hasOwnProperty('isTrusted'))
900 _addConstProperty(this, 'isTrusted', device.isTrusted);
901 if (device.hasOwnProperty('isConnected'))
902 _addConstProperty(this, 'isConnected', device.isConnected);
904 if (device.hasOwnProperty('uuids')) {
905 for (var i = 0; i < this.uuids.length; i++)
906 this.uuids[i] = device.uuids[i];
910 function BluetoothSocket(uuid, peer, msg) {
911 _addConstProperty(this, 'uuid', uuid);
912 _addConstProperty(this, 'peer', peer);
913 _addConstProperty(this, 'state', BluetoothSocketState.OPEN);
915 this.onmessage = null;
921 this.channel = msg.channel;
922 this.socket_fd = msg.socket_fd;
926 var BluetoothSocketState = {
930 Object.defineProperty(BluetoothSocket, 'BluetoothSocketState', {
933 value: BluetoothSocketState
937 BluetoothSocket.prototype.writeData = function(data) {
938 // make sure that socket is connected and opened.
939 if (this.state == BluetoothSocketState.CLOSE) {
940 throw new tizen.WebAPIException(tizen.WebAPIException.UNKNOWN_ERR);
945 'cmd': 'SocketWriteData',
947 'socket_fd': this.socket_fd
949 var result = JSON.parse(extension.internal.sendSyncMessage(JSON.stringify(msg)));
954 BluetoothSocket.prototype.readData = function() {
958 BluetoothSocket.prototype.close = function() {
960 'cmd': 'CloseSocket',
961 'socket_fd': this.socket_fd
964 postMessage(msg, function(result) {
966 console.log('Can\'t close socket (' + this.socket_fd + ').');
967 throw new tizen.WebAPIException(tizen.WebAPIException.UNKNOWN_ERR);
971 // FIXME(clecou) Update socket object state only when using Tizen C API backend.
972 // BlueZ4 backend independently updates socket state based on a dbus callback mechanism.
973 // A better approach would be to adapt backends instances to have a single JSON protocol.
975 for (var i in adapter.sockets) {
976 var socket = adapter.sockets[i];
977 if (socket.socket_fd === msg.socket_fd) {
978 if (socket.onclose && typeof socket.onmessage === 'function') {
979 _addConstProperty(adapter.sockets[i], 'state', BluetoothSocketState.CLOSE);
988 function BluetoothClass() {}
989 BluetoothClass.prototype.hasService = function(service) {
990 for (var i = 0; i < this.services.length; i++)
991 if (this.services[i] === service)
997 function BluetoothServiceHandler(name, uuid, msg) {
998 _addConstProperty(this, 'name', name);
999 _addConstProperty(this, 'uuid', uuid);
1000 _addConstProperty(this, 'isConnected', false);
1003 this.server_fd = msg.server_fd;
1004 this.sdp_handle = msg.sdp_handle;
1005 this.channel = msg.channel;
1009 BluetoothServiceHandler.prototype.unregister = function(successCallback, errorCallback) {
1010 if (!xwalk.utils.validateArguments('?ff', arguments)) {
1011 throw new tizen.WebAPIError(tizen.WebAPIException.TYPE_MISMATCH_ERR);
1014 if (adapter.checkServiceAvailability(errorCallback))
1018 'cmd': 'UnregisterServer',
1019 'server_fd': this.server_fd,
1020 'sdp_handle': this.sdp_handle
1023 postMessage(msg, function(result) {
1024 if (result.error != 0) {
1025 if (errorCallback) {
1026 var error = new tizen.WebAPIError(tizen.WebAPIException.UNKNOWN_ERR);
1027 errorCallback(error);
1030 throw new tizen.WebAPIException(tizen.WebAPIException.TYPE_MISMATCH_ERR);
1034 _addConstProperty(this, 'isConnected', false);
1035 if (successCallback)