Merge branch 'tizen_4.0' into tizen_5.0
[platform/core/api/webapi-plugins.git] / src / bluetooth / bluetooth_api.js
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Apache License, Version 2.0 (the "License");
5  *    you may not use this file except in compliance with the License.
6  *    You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16
17 var T = xwalk.utils.type;
18 var Converter = xwalk.utils.converter;
19 var AV = xwalk.utils.validator;
20 var Privilege = xwalk.utils.privilege;
21 var privUtils_ = xwalk.utils;
22 var native = new xwalk.utils.NativeManager(extension);
23
24 // class BluetoothClassDeviceMajor /////////////////////////////////////////
25 var BluetoothClassDeviceMajor = function() {
26     Object.defineProperties(this, {
27         MISC: { value: 0x00, writable: false, enumerable: true },
28         COMPUTER: { value: 0x01, writable: false, enumerable: true },
29         PHONE: { value: 0x02, writable: false, enumerable: true },
30         NETWORK: { value: 0x03, writable: false, enumerable: true },
31         AUDIO_VIDEO: { value: 0x04, writable: false, enumerable: true },
32         PERIPHERAL: { value: 0x05, writable: false, enumerable: true },
33         IMAGING: { value: 0x06, writable: false, enumerable: true },
34         WEARABLE: { value: 0x07, writable: false, enumerable: true },
35         TOY: { value: 0x08, writable: false, enumerable: true },
36         HEALTH: { value: 0x09, writable: false, enumerable: true },
37         UNCATEGORIZED: { value: 0x1f, writable: false, enumerable: true }
38     });
39 };
40
41 // class BluetoothClassDeviceMinor /////////////////////////////////////////
42 var BluetoothClassDeviceMinor = function() {
43     Object.defineProperties(this, {
44         COMPUTER_UNCATEGORIZED: { value: 0x00, writable: false, enumerable: true },
45         COMPUTER_DESKTOP: { value: 0x01, writable: false, enumerable: true },
46         COMPUTER_SERVER: { value: 0x02, writable: false, enumerable: true },
47         COMPUTER_LAPTOP: { value: 0x03, writable: false, enumerable: true },
48         COMPUTER_HANDHELD_PC_OR_PDA: { value: 0x04, writable: false, enumerable: true },
49         COMPUTER_PALM_PC_OR_PDA: { value: 0x05, writable: false, enumerable: true },
50         COMPUTER_WEARABLE: { value: 0x06, writable: false, enumerable: true },
51
52         PHONE_UNCATEGORIZED: { value: 0x00, writable: false, enumerable: true },
53         PHONE_CELLULAR: { value: 0x01, writable: false, enumerable: true },
54         PHONE_CORDLESS: { value: 0x02, writable: false, enumerable: true },
55         PHONE_SMARTPHONE: { value: 0x03, writable: false, enumerable: true },
56         PHONE_MODEM_OR_GATEWAY: { value: 0x04, writable: false, enumerable: true },
57         PHONE_ISDN: { value: 0x05, writable: false, enumerable: true },
58
59         AV_UNRECOGNIZED: { value: 0x00, writable: false, enumerable: true },
60         AV_WEARABLE_HEADSET: { value: 0x01, writable: false, enumerable: true },
61         AV_HANDSFREE: { value: 0x02, writable: false, enumerable: true },
62         AV_MICROPHONE: { value: 0x04, writable: false, enumerable: true },
63         AV_LOUDSPEAKER: { value: 0x05, writable: false, enumerable: true },
64         AV_HEADPHONES: { value: 0x06, writable: false, enumerable: true },
65         AV_PORTABLE_AUDIO: { value: 0x07, writable: false, enumerable: true },
66         AV_CAR_AUDIO: { value: 0x08, writable: false, enumerable: true },
67         AV_SETTOP_BOX: { value: 0x09, writable: false, enumerable: true },
68         AV_HIFI: { value: 0x0a, writable: false, enumerable: true },
69         AV_VCR: { value: 0x0b, writable: false, enumerable: true },
70         AV_VIDEO_CAMERA: { value: 0x0c, writable: false, enumerable: true },
71         AV_CAMCORDER: { value: 0x0d, writable: false, enumerable: true },
72         AV_MONITOR: { value: 0x0e, writable: false, enumerable: true },
73         AV_DISPLAY_AND_LOUDSPEAKER: { value: 0x0f, writable: false, enumerable: true },
74         AV_VIDEO_CONFERENCING: { value: 0x10, writable: false, enumerable: true },
75         AV_GAMING_TOY: { value: 0x12, writable: false, enumerable: true },
76
77         PERIPHERAL_UNCATEGORIZED: { value: 0x00, writable: false, enumerable: true },
78         PERIPHERAL_KEYBOARD: { value: 0x10, writable: false, enumerable: true },
79         PERIPHERAL_POINTING_DEVICE: { value: 0x20, writable: false, enumerable: true },
80         PERIPHERAL_KEYBOARD_AND_POINTING_DEVICE: {
81             value: 0x30,
82             writable: false,
83             enumerable: true
84         },
85         PERIPHERAL_JOYSTICK: { value: 0x01, writable: false, enumerable: true },
86         PERIPHERAL_GAMEPAD: { value: 0x02, writable: false, enumerable: true },
87         PERIPHERAL_REMOTE_CONTROL: { value: 0x03, writable: false, enumerable: true },
88         PERIPHERAL_SENSING_DEVICE: { value: 0x04, writable: false, enumerable: true },
89         PERIPHERAL_DEGITIZER_TABLET: { value: 0x05, writable: false, enumerable: true },
90         PERIPHERAL_CARD_READER: { value: 0x06, writable: false, enumerable: true },
91         PERIPHERAL_DIGITAL_PEN: { value: 0x07, writable: false, enumerable: true },
92         PERIPHERAL_HANDHELD_SCANNER: { value: 0x08, writable: false, enumerable: true },
93         PERIPHERAL_HANDHELD_INPUT_DEVICE: {
94             value: 0x09,
95             writable: false,
96             enumerable: true
97         },
98
99         IMAGING_UNCATEGORIZED: { value: 0x00, writable: false, enumerable: true },
100         IMAGING_DISPLAY: { value: 0x04, writable: false, enumerable: true },
101         IMAGING_CAMERA: { value: 0x08, writable: false, enumerable: true },
102         IMAGING_SCANNER: { value: 0x10, writable: false, enumerable: true },
103         IMAGING_PRINTER: { value: 0x20, writable: false, enumerable: true },
104
105         WEARABLE_WRITST_WATCH: { value: 0x01, writable: false, enumerable: true },
106         WEARABLE_PAGER: { value: 0x02, writable: false, enumerable: true },
107         WEARABLE_JACKET: { value: 0x03, writable: false, enumerable: true },
108         WEARABLE_HELMET: { value: 0x04, writable: false, enumerable: true },
109         WEARABLE_GLASSES: { value: 0x05, writable: false, enumerable: true },
110
111         TOY_ROBOT: { value: 0x01, writable: false, enumerable: true },
112         TOY_VEHICLE: { value: 0x02, writable: false, enumerable: true },
113         TOY_DOLL: { value: 0x03, writable: false, enumerable: true },
114         TOY_CONTROLLER: { value: 0x04, writable: false, enumerable: true },
115         TOY_GAME: { value: 0x05, writable: false, enumerable: true },
116
117         HEALTH_UNDEFINED: { value: 0x00, writable: false, enumerable: true },
118         HEALTH_BLOOD_PRESSURE_MONITOR: { value: 0x01, writable: false, enumerable: true },
119         HEALTH_THERMOMETER: { value: 0x02, writable: false, enumerable: true },
120         HEALTH_WEIGHING_SCALE: { value: 0x03, writable: false, enumerable: true },
121         HEALTH_GLUCOSE_METER: { value: 0x04, writable: false, enumerable: true },
122         HEALTH_PULSE_OXIMETER: { value: 0x05, writable: false, enumerable: true },
123         HEALTH_PULSE_RATE_MONITOR: { value: 0x06, writable: false, enumerable: true },
124         HEALTH_DATA_DISPLAY: { value: 0x07, writable: false, enumerable: true },
125         HEALTH_STEP_COUNTER: { value: 0x08, writable: false, enumerable: true },
126         HEALTH_BODY_COMPOSITION_ANALYZER: {
127             value: 0x09,
128             writable: false,
129             enumerable: true
130         },
131         HEALTH_PEAK_FLOW_MONITOR: { value: 0x0a, writable: false, enumerable: true },
132         HEALTH_MEDICATION_MONITOR: { value: 0x0b, writable: false, enumerable: true },
133         HEALTH_KNEE_PROSTHESIS: { value: 0x0c, writable: false, enumerable: true },
134         HEALTH_ANKLE_PROSTHESIS: { value: 0x0d, writable: false, enumerable: true }
135     });
136 };
137
138 // class BluetoothClassDeviceService ///////////////////////////////////////
139 var BluetoothClassDeviceService = function() {
140     Object.defineProperties(this, {
141         LIMITED_DISCOVERABILITY: { value: 0x0001, writable: false, enumerable: true },
142         POSITIONING: { value: 0x0008, writable: false, enumerable: true },
143         NETWORKING: { value: 0x0010, writable: false, enumerable: true },
144         RENDERING: { value: 0x0020, writable: false, enumerable: true },
145         CAPTURING: { value: 0x0040, writable: false, enumerable: true },
146         OBJECT_TRANSFER: { value: 0x0080, writable: false, enumerable: true },
147         AUDIO: { value: 0x0100, writable: false, enumerable: true },
148         TELEPHONY: { value: 0x0200, writable: false, enumerable: true },
149         INFORMATION: { value: 0x0400, writable: false, enumerable: true }
150     });
151 };
152
153 //class tizen.BluetoothLEServiceData //////////////////////////
154 tizen.BluetoothLEServiceData = function(d) {
155     AV.isConstructorCall(this, tizen.BluetoothLEServiceData);
156     var uuid_ = '';
157     var data_ = '';
158
159     Object.defineProperties(this, {
160         uuid: {
161             enumerable: true,
162             get: function() {
163                 return uuid_;
164             },
165             set: function(v) {
166                 uuid_ = Converter.toString(v);
167             }
168         },
169         data: {
170             enumerable: true,
171             get: function() {
172                 return data_;
173             },
174             set: function(v) {
175                 data_ = Converter.toString(v);
176             }
177         }
178     });
179
180     if (arguments.length >= 2) {
181         // public constructor
182         this.uuid = arguments[0];
183         this.data = arguments[1];
184     } else if (d && T.isObject(d)) {
185         // internal constructor
186         this.uuid = d.uuid;
187         this.data = d.data;
188     } else {
189         uuid_ = undefined;
190         data_ = undefined;
191     }
192 };
193
194 //class BluetoothLEAdvertiseData //////////////////////////
195 tizen.BluetoothLEAdvertiseData = function(dict) {
196     AV.isConstructorCall(this, tizen.BluetoothLEAdvertiseData);
197     var includeName_ = false;
198     var uuids_ = null;
199     var solicitationuuids_ = null;
200     var appearance_ = null;
201     var includeTxPowerLevel_ = false;
202     var serviceData_ = null;
203     var manufacturerData_ = null;
204
205     Object.defineProperties(this, {
206         includeName: {
207             enumerable: true,
208             get: function() {
209                 return includeName_;
210             },
211             set: function(v) {
212                 includeName_ = Converter.toBoolean(v, true);
213             }
214         },
215         uuids: {
216             enumerable: true,
217             get: function() {
218                 return uuids_;
219             },
220             set: function(v) {
221                 if (T.isNull(v)) {
222                     uuids_ = v;
223                 } else if (T.isArray(v)) {
224                     for (var i = 0; i < v.length; ++i) {
225                         if (!T.isString(v[i])) {
226                             v[i] = Converter.toString(v[i]);
227                         }
228                     }
229                     uuids_ = v;
230                 }
231             }
232         },
233         solicitationuuids: {
234             enumerable: true,
235             get: function() {
236                 return solicitationuuids_;
237             },
238             set: function(v) {
239                 if (T.isNull(v)) {
240                     solicitationuuids_ = v;
241                 } else if (T.isArray(v)) {
242                     for (var i = 0; i < v.length; ++i) {
243                         if (!T.isString(v[i])) {
244                             v[i] = Converter.toString(v[i]);
245                         }
246                     }
247                     solicitationuuids_ = v;
248                 }
249             }
250         },
251         appearance: {
252             enumerable: true,
253             get: function() {
254                 return appearance_;
255             },
256             set: function(v) {
257                 appearance_ = Converter.toUnsignedLong(v, true);
258             }
259         },
260         includeTxPowerLevel: {
261             enumerable: true,
262             get: function() {
263                 return includeTxPowerLevel_;
264             },
265             set: function(v) {
266                 includeTxPowerLevel_ = Converter.toBoolean(v, true);
267             }
268         },
269         serviceData: {
270             enumerable: true,
271             get: function() {
272                 return serviceData_;
273             },
274             set: function(v) {
275                 if (T.isNull(v) || v instanceof tizen.BluetoothLEServiceData) {
276                     serviceData_ = v;
277                 }
278             }
279         },
280         manufacturerData: {
281             enumerable: true,
282             get: function() {
283                 return manufacturerData_;
284             },
285             set: function(v) {
286                 if (T.isNull(v) || v instanceof tizen.BluetoothLEManufacturerData) {
287                     manufacturerData_ = v;
288                 }
289             }
290         }
291     });
292
293     if (T.isObject(dict)) {
294         var o = {};
295
296         // includeName
297         if (T.isNull(dict.includeName) || T.isBoolean(dict.includeName)) {
298             o.includeName = dict.includeName;
299         } else if (!T.isUndefined(dict.includeName)) {
300             return;
301         }
302
303         // uuids
304         if (T.isNull(dict.uuids)) {
305             o.uuids = dict.uuids;
306         } else if (T.isArray(dict.uuids)) {
307             for (var i = 0; i < dict.uuids.length; ++i) {
308                 if (!T.isString(dict.uuids[i])) {
309                     return;
310                 }
311             }
312             o.uuids = dict.uuids;
313         } else if (!T.isUndefined(dict.uuids)) {
314             return;
315         }
316
317         // solicitationuuids
318         if (T.isNull(dict.solicitationuuids)) {
319             o.solicitationuuids = dict.solicitationuuids;
320         } else if (T.isArray(dict.solicitationuuids)) {
321             for (var i = 0; i < dict.solicitationuuids.length; ++i) {
322                 if (!T.isString(dict.solicitationuuids[i])) {
323                     return;
324                 }
325             }
326             o.solicitationuuids = dict.solicitationuuids;
327         } else if (!T.isUndefined(dict.solicitationuuids)) {
328             return;
329         }
330
331         // appearance
332         if (T.isNull(dict.appearance) || T.isNumber(dict.appearance)) {
333             o.appearance = dict.appearance;
334         } else if (!T.isUndefined(dict.appearance)) {
335             return;
336         }
337
338         // includeTxPowerLevel
339         if (T.isNull(dict.includeTxPowerLevel) || T.isBoolean(dict.includeTxPowerLevel)) {
340             o.includeTxPowerLevel = dict.includeTxPowerLevel;
341         } else if (!T.isUndefined(dict.includeTxPowerLevel)) {
342             return;
343         }
344
345         // serviceData
346         if (
347             T.isNull(dict.serviceData) ||
348             dict.serviceData instanceof tizen.BluetoothLEServiceData
349         ) {
350             o.serviceData = dict.serviceData;
351         } else if (!T.isUndefined(dict.serviceData)) {
352             return;
353         }
354
355         // manufacturerData
356         if (
357             T.isNull(dict.manufacturerData) ||
358             dict.manufacturerData instanceof tizen.BluetoothLEManufacturerData
359         ) {
360             o.manufacturerData = dict.manufacturerData;
361         } else if (!T.isUndefined(dict.manufacturerData)) {
362             return;
363         }
364
365         for (var prop in o) {
366             if (o.hasOwnProperty(prop) && this.hasOwnProperty(prop)) {
367                 this[prop] = o[prop];
368             }
369         }
370     }
371 };
372
373 //class tizen.BluetoothLEManufacturerData //////////////////////////
374 tizen.BluetoothLEManufacturerData = function(d) {
375     AV.isConstructorCall(this, tizen.BluetoothLEManufacturerData);
376     var id_ = '';
377     var data_ = '';
378
379     Object.defineProperties(this, {
380         id: {
381             enumerable: true,
382             get: function() {
383                 return id_;
384             },
385             set: function(v) {
386                 id_ = Converter.toString(v);
387             }
388         },
389         data: {
390             enumerable: true,
391             get: function() {
392                 return data_;
393             },
394             set: function(v) {
395                 data_ = Converter.toString(v);
396             }
397         }
398     });
399
400     if (arguments.length >= 2) {
401         // public constructor
402         this.id = arguments[0];
403         this.data = arguments[1];
404     } else if (d && T.isObject(d)) {
405         // internal constructor
406         this.id = d.id;
407         this.data = d.data;
408     } else {
409         id_ = undefined;
410         data_ = undefined;
411     }
412 };
413
414 // class BluetoothClass //////////////////////////
415 var BluetoothClass = function(data) {
416     var services = [];
417     if (data) {
418         services = data.services;
419     }
420
421     Object.defineProperties(this, {
422         major: { value: data.major, writable: false, enumerable: true },
423         minor: { value: data.minor, writable: false, enumerable: true },
424         services: {
425             enumerable: true,
426             set: function() {},
427             get: function() {
428                 return services.slice();
429             }
430         }
431     });
432 };
433
434 var BluetoothClass_hasService = function() {
435     privUtils_.log('Entered BluetoothClass.hasService()');
436     privUtils_.checkPrivilegeAccess4Ver(
437         '2.4',
438         Privilege.BLUETOOTH,
439         Privilege.BLUETOOTH_GAP
440     );
441
442     var args = AV.validateMethod(arguments, [
443         {
444             name: 'service',
445             type: AV.Types.UNSIGNED_LONG
446         }
447     ]);
448
449     var size = this.services.length;
450     for (var i = 0; i < size; i++) {
451         if (this.services[i] === args.service) {
452             return true;
453         }
454     }
455     return false;
456 };
457
458 BluetoothClass.prototype.hasService = function() {
459     return BluetoothClass_hasService.apply(this, arguments);
460 };
461
462 // class BluetoothSocket //////////////////////////
463 var _BLUETOOTH_SOCKET_STATE_CLOSED = 'CLOSED';
464
465 function BluetoothSocketListeners() {
466     var that = this;
467     this.socketCallback = function(data) {
468         var event = data;
469         var socket = that.sockets[event.id];
470
471         if (socket) {
472             if ('onclose' === event.event) {
473                 // no more events
474                 that.removeListener(event.id);
475                 // change state
476                 Object.defineProperty(socket, 'state', {
477                     value: _BLUETOOTH_SOCKET_STATE_CLOSED
478                 });
479             }
480
481             var callback = socket[event.event];
482             if (T.isFunction(callback)) {
483                 callback();
484             }
485         } else {
486             privUtils_.log('Received event for an unknown socket: ' + event.id);
487         }
488     };
489 }
490
491 BluetoothSocketListeners.prototype.sockets = {};
492
493 BluetoothSocketListeners.prototype.addListener = function(socket) {
494     if (T.isEmptyObject(this.sockets)) {
495         native.addListener('BLUETOOTH_SOCKET_STATE_CHANGED', this.socketCallback);
496     }
497
498     this.sockets[socket._id] = socket;
499 };
500
501 BluetoothSocketListeners.prototype.removeListener = function(id) {
502     delete this.sockets[id];
503
504     if (T.isEmptyObject(this.sockets)) {
505         native.removeListener('BLUETOOTH_SOCKET_STATE_CHANGED', this.socketCallback);
506     }
507 };
508
509 var _bluetoothSocketListeners = new BluetoothSocketListeners();
510
511 var BluetoothSocket = function(data) {
512     Object.defineProperties(this, {
513         uuid: { value: data.uuid, writable: false, enumerable: true },
514         state: {
515             value: data.state,
516             writable: false,
517             enumerable: true,
518             configurable: true
519         },
520         peer: {
521             value: new BluetoothDevice(data.peer),
522             writable: false,
523             enumerable: true
524         },
525         onmessage: { value: null, writable: true, enumerable: true },
526         onclose: { value: null, writable: true, enumerable: true },
527         _id: { value: data.id, writable: false, enumerable: false }
528     });
529
530     _bluetoothSocketListeners.addListener(this);
531 };
532
533 BluetoothSocket.prototype.writeData = function() {
534     privUtils_.log('Entered BluetoothSocket.writeData()');
535
536     var args = AV.validateMethod(arguments, [
537         {
538             name: 'data',
539             type: AV.Types.ARRAY,
540             values: AV.Types.BYTE
541         }
542     ]);
543
544     var callArgs = {
545         id: this._id,
546         data: args.data
547     };
548
549     var result = native.callSync('BluetoothSocket_writeData', callArgs);
550
551     if (native.isFailure(result)) {
552         throw native.getErrorObject(result);
553     } else {
554         return native.getResultObject(result);
555     }
556 };
557
558 BluetoothSocket.prototype.readData = function() {
559     privUtils_.log('Entered BluetoothSocket.readData()');
560
561     var callArgs = {
562         id: this._id
563     };
564
565     var result = native.callSync('BluetoothSocket_readData', callArgs);
566
567     if (native.isFailure(result)) {
568         throw native.getErrorObject(result);
569     } else {
570         return native.getResultObject(result);
571     }
572 };
573
574 BluetoothSocket.prototype.close = function() {
575     privUtils_.log('Entered BluetoothSocket.close()');
576
577     if (_BLUETOOTH_SOCKET_STATE_CLOSED !== this.state) {
578         var callArgs = {
579             id: this._id
580         };
581
582         var result = native.callSync('BluetoothSocket_close', callArgs);
583
584         if (native.isFailure(result)) {
585             throw native.getErrorObject(result);
586         }
587
588         // change state
589         Object.defineProperty(this, 'state', { value: _BLUETOOTH_SOCKET_STATE_CLOSED });
590     }
591 };
592
593 //class BluetoothLEDevice //////////////////////////
594 var BluetoothLEDevice = function(data) {
595     var address = '',
596         name = null,
597         txpowerlevel = null,
598         appearance = null,
599         uuids = null,
600         solicitationuuids = null,
601         serviceData = null,
602         manufacturerData = null,
603         rssi = null;
604
605     if (data) {
606         address = data.address;
607         name = data.name || null;
608         txpowerlevel = data.txpowerlevel || null;
609         appearance = data.appearance || null;
610         uuids = data.uuids || null;
611         solicitationuuids = data.solicitationuuids || null;
612         if (data.serviceData) {
613             serviceData = [];
614             data.serviceData.forEach(function(d) {
615                 serviceData.push(new tizen.BluetoothLEServiceData(d));
616             });
617         }
618         if (data.manufacturerData) {
619             manufacturerData = new tizen.BluetoothLEManufacturerData(
620                 data.manufacturerData
621             );
622         }
623         if (data.rssi) {
624             rssi = data.rssi;
625         }
626     }
627
628     Object.defineProperties(this, {
629         address: { value: address, writable: false, enumerable: true },
630         name: { value: name, writable: false, enumerable: true },
631         txpowerlevel: { value: txpowerlevel, writable: false, enumerable: true },
632         appearance: { value: appearance, writable: false, enumerable: true },
633         uuids: {
634             enumerable: true,
635             set: function() {},
636             get: function() {
637                 var service_uuids = uuids ? uuids.slice() : null;
638                 return service_uuids;
639             }
640         },
641         solicitationuuids: {
642             enumerable: true,
643             set: function() {},
644             get: function() {
645                 return solicitationuuids ? solicitationuuids.slice() : null;
646             }
647         },
648         serviceData: {
649             enumerable: true,
650             set: function() {},
651             get: function() {
652                 return serviceData ? serviceData.slice() : null;
653             }
654         },
655         manufacturerData: {
656             value: manufacturerData,
657             writable: false,
658             enumerable: true
659         },
660         rssi: { value: rssi, writable: false, enumerable: true }
661     });
662 };
663
664 BluetoothLEDevice.prototype.connect = function() {
665     privUtils_.log('Entered BluetoothLEDevice.connect()');
666     var args = AV.validateMethod(arguments, [
667         {
668             name: 'successCallback',
669             type: AV.Types.FUNCTION,
670             optional: true,
671             nullable: true
672         },
673         {
674             name: 'errorCallback',
675             type: AV.Types.FUNCTION,
676             optional: true,
677             nullable: true
678         }
679     ]);
680
681     var callback = function(result) {
682         if (native.isFailure(result)) {
683             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
684         } else {
685             native.callIfPossible(args.successCallback);
686         }
687     };
688     // Errors are handled by error callback
689     var result = native.call(
690         'BluetoothLEDevice_connect',
691         { address: this.address },
692         callback
693     );
694     if (native.isFailure(result)) {
695         throw native.getErrorObject(result);
696     }
697 };
698
699 BluetoothLEDevice.prototype.disconnect = function() {
700     privUtils_.log('Entered BluetoothLEDevice.disconnect()');
701     var args = AV.validateMethod(arguments, [
702         {
703             name: 'successCallback',
704             type: AV.Types.FUNCTION,
705             optional: true,
706             nullable: true
707         },
708         {
709             name: 'errorCallback',
710             type: AV.Types.FUNCTION,
711             optional: true,
712             nullable: true
713         }
714     ]);
715     var callback = function(result) {
716         if (native.isFailure(result)) {
717             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
718         } else {
719             native.callIfPossible(args.successCallback);
720         }
721     };
722
723     var result = native.call(
724         'BluetoothLEDevice_disconnect',
725         { address: this.address },
726         callback
727     );
728     if (native.isFailure(result)) {
729         throw native.getErrorObject(result);
730     }
731 };
732
733 BluetoothLEDevice.prototype.getService = function() {
734     privUtils_.log('Entered BluetoothLEDevice.getService()');
735     var args = AV.validateMethod(arguments, [
736         {
737             name: 'uuid',
738             type: AV.Types.STRING
739         }
740     ]);
741
742     var callArgs = {
743         uuid: args.uuid,
744         address: this.address
745     };
746
747     var result = native.callSync('BluetoothLEDevice_getService', callArgs);
748     if (native.isFailure(result)) {
749         throw native.getErrorObject(result);
750     } else {
751         return new BluetoothGATTService(native.getResultObject(result));
752     }
753 };
754
755 BluetoothLEDevice.prototype.getServiceAllUuids = function() {
756     privUtils_.log('Entered BluetoothLEDevice.getServiceAllUuids()');
757
758     var callArgs = {
759         address: this.address
760     };
761
762     var result = native.callSync('BluetoothLEDevice_getServiceAllUuids', callArgs);
763     if (native.isFailure(result)) {
764         throw native.getErrorObject(result);
765     } else {
766         var uuids = native.getResultObject(result);
767         return uuids;
768     }
769 };
770
771 BluetoothLEDevice.prototype.addConnectStateChangeListener = function() {
772     privUtils_.log('Entered BluetoothLEDevice.addConnectStateChangeListener()');
773     var args = AV.validateMethod(arguments, [
774         {
775             name: 'listener',
776             type: AV.Types.LISTENER,
777             values: ['onconnected', 'ondisconnected']
778         }
779     ]);
780
781     var that = this;
782
783     var func = function(event) {
784         if (event.address === that.address && args.listener[event.action]) {
785             args.listener[event.action](that);
786         }
787     };
788
789     var watchId = _bleConnectChangeListener.addListener(func);
790
791     return watchId;
792 };
793
794 BluetoothLEDevice.prototype.removeConnectStateChangeListener = function() {
795     privUtils_.log('Entered BluetoothLEDevice.removeConnectStateChangeListener()');
796
797     var args = AV.validateMethod(arguments, [
798         {
799             name: 'watchID',
800             type: AV.Types.LONG
801         }
802     ]);
803
804     _bleConnectChangeListener.removeListener(args.watchID);
805 };
806
807 // class BluetoothDevice //////////////////////////
808 var BluetoothDevice = function(data) {
809     var self = this;
810     function _getter(field) {
811         var callArgs = {};
812
813         callArgs.address = self.address;
814         callArgs.field = field;
815
816         var result = native.callSync('BluetoothDevice_getBoolValue', callArgs);
817
818         if (native.isFailure(result)) {
819             return false;
820         } else {
821             return native.getResultObject(result);
822         }
823     }
824
825     function isBondedGetter() {
826         return _getter('isBonded');
827     }
828
829     function isTrustedGetter() {
830         return _getter('isTrusted');
831     }
832
833     function isConnectedGetter() {
834         return _getter('isConnected');
835     }
836
837     var uuids = [];
838     if (data) {
839         uuids = data.uuids;
840     }
841
842     Object.defineProperties(this, {
843         name: { value: data.name, writable: false, enumerable: true },
844         address: { value: data.address, writable: false, enumerable: true },
845         deviceClass: {
846             value: new BluetoothClass(data.deviceClass),
847             writable: false,
848             enumerable: true
849         },
850         isBonded: {
851             enumerable: true,
852             set: function() {},
853             get: isBondedGetter
854         },
855         isTrusted: {
856             enumerable: true,
857             set: function() {},
858             get: isTrustedGetter
859         },
860         isConnected: {
861             enumerable: true,
862             set: function() {},
863             get: isConnectedGetter
864         },
865         uuids: {
866             enumerable: true,
867             set: function() {},
868             get: function() {
869                 return uuids.slice();
870             }
871         }
872     });
873 };
874
875 BluetoothDevice.prototype.connectToServiceByUUID = function() {
876     privUtils_.log('Entered BluetoothDevice.connectToServiceByUUID()');
877
878     var args = AV.validateMethod(arguments, [
879         {
880             name: 'uuid',
881             type: AV.Types.STRING
882         },
883         {
884             name: 'successCallback',
885             type: AV.Types.FUNCTION
886         },
887         {
888             name: 'errorCallback',
889             type: AV.Types.FUNCTION,
890             optional: true,
891             nullable: true
892         }
893     ]);
894
895     var callArgs = {
896         address: this.address,
897         uuid: args.uuid
898     };
899     var callback = function(result) {
900         if (native.isFailure(result)) {
901             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
902         } else {
903             args.successCallback(new BluetoothSocket(native.getResultObject(result)));
904         }
905     };
906
907     var result = native.call(
908         'BluetoothDevice_connectToServiceByUUID',
909         callArgs,
910         callback
911     );
912     if (native.isFailure(result)) {
913         throw native.getErrorObject(result);
914     }
915 };
916
917 // class BluetoothServiceHandler //////////////////////////
918 function BluetoothServiceListeners() {
919     var that = this;
920     this.serviceCallback = function(data) {
921         var e = data;
922         var service = that.services[e.uuid];
923         var result = new BluetoothSocket(e);
924
925         if (service) {
926             privUtils_.log(service);
927             service.onconnect(result);
928         }
929     };
930 }
931
932 BluetoothServiceListeners.prototype.services = {};
933
934 BluetoothServiceListeners.prototype.addListener = function(service) {
935     if (T.isEmptyObject(this.services)) {
936         native.addListener('BLUETOOTH_SERVICE_ONCONNECT', this.serviceCallback);
937     }
938
939     this.services[service.uuid] = service;
940 };
941
942 BluetoothServiceListeners.prototype.removeListener = function(uuid) {
943     delete this.services[uuid];
944
945     if (T.isEmptyObject(this.services)) {
946         native.removeListener('BLUETOOTH_SERVICE_ONCONNECT', this.serviceCallback);
947     }
948 };
949
950 var _bluetoothServiceListeners = new BluetoothServiceListeners();
951
952 var BluetoothServiceHandler = function(data) {
953     function isConnectedGetter() {
954         var callArgs = {
955             uuid: this.uuid
956         };
957
958         var result = native.callSync('BluetoothAdapter_isServiceConnected', {
959             uuid: this.uuid
960         });
961
962         if (native.isFailure(result)) {
963             return false;
964         } else {
965             return native.getResultObject(result);
966         }
967     }
968
969     Object.defineProperties(this, {
970         uuid: { value: data.uuid, writable: false, enumerable: true },
971         name: { value: data.name, writable: false, enumerable: true },
972         isConnected: {
973             enumerable: true,
974             set: function() {},
975             get: isConnectedGetter
976         },
977         onconnect: { value: null, writable: true, enumerable: true }
978     });
979
980     _bluetoothServiceListeners.addListener(this);
981 };
982
983 BluetoothServiceHandler.prototype.unregister = function() {
984     privUtils_.log('Entered BluetoothServiceHandler.unregister()');
985     var args = AV.validateMethod(arguments, [
986         {
987             name: 'successCallback',
988             type: AV.Types.FUNCTION,
989             optional: true,
990             nullable: true
991         },
992         {
993             name: 'errorCallback',
994             type: AV.Types.FUNCTION,
995             optional: true,
996             nullable: true
997         }
998     ]);
999
1000     var callArgs = {
1001         uuid: this.uuid
1002     };
1003
1004     var callback = function(result) {
1005         if (native.isFailure(result)) {
1006             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
1007         } else {
1008             native.callIfPossible(args.successCallback);
1009         }
1010     };
1011
1012     var result = native.call('BluetoothServiceHandler_unregister', callArgs, callback);
1013     if (native.isFailure(result)) {
1014         throw native.getErrorObject(result);
1015     }
1016
1017     _bluetoothServiceListeners.removeListener(this.uuid);
1018 };
1019
1020 // class BluetoothHealthApplication //////////////////////////
1021 function BluetoothHealthApplicationListeners() {
1022     var that = this;
1023     this.appCallback = function(data) {
1024         var event = data;
1025         var app = that.apps[event.id];
1026
1027         if (app) {
1028             var callback = app[event.event];
1029             if (T.isFunction(callback)) {
1030                 var param;
1031                 switch (event.event) {
1032                 case 'onconnect':
1033                     param = new BluetoothHealthChannel(native.getResultObject(event));
1034                     break;
1035
1036                 default:
1037                     privUtils_.log('Unknown event: ' + event.event);
1038                     break;
1039                 }
1040                 callback(param);
1041             }
1042         } else {
1043             privUtils_.log('Received event for an unknown application: ' + event.id);
1044         }
1045     };
1046 }
1047
1048 BluetoothHealthApplicationListeners.prototype.apps = {};
1049
1050 BluetoothHealthApplicationListeners.prototype.addListener = function(app) {
1051     if (T.isEmptyObject(this.apps)) {
1052         native.addListener('BLUETOOTH_HEALTH_APPLICATION_CHANGED', this.appCallback);
1053     }
1054
1055     this.apps[app._id] = app;
1056 };
1057
1058 BluetoothHealthApplicationListeners.prototype.removeListener = function(id) {
1059     delete this.apps[id];
1060
1061     if (T.isEmptyObject(this.apps)) {
1062         native.removeListener('BLUETOOTH_HEALTH_APPLICATION_CHANGED', this.appCallback);
1063     }
1064 };
1065
1066 var _bluetoothHealthApplicationListeners = new BluetoothHealthApplicationListeners();
1067
1068 var BluetoothHealthApplication = function(data) {
1069     Object.defineProperties(this, {
1070         dataType: { value: data.dataType, writable: false, enumerable: true },
1071         name: { value: data.name, writable: false, enumerable: true },
1072         onconnect: { value: null, writable: true, enumerable: true },
1073         _id: { value: data._id, writable: false, enumerable: false }
1074     });
1075
1076     _bluetoothHealthApplicationListeners.addListener(this);
1077 };
1078
1079 BluetoothHealthApplication.prototype.unregister = function() {
1080     privUtils_.log('Entered BluetoothHealthApplication.unregister()');
1081     var args = AV.validateMethod(arguments, [
1082         {
1083             name: 'successCallback',
1084             type: AV.Types.FUNCTION,
1085             optional: true,
1086             nullable: true
1087         },
1088         {
1089             name: 'errorCallback',
1090             type: AV.Types.FUNCTION,
1091             optional: true,
1092             nullable: true
1093         }
1094     ]);
1095
1096     var callArgs = { id: this._id };
1097
1098     var callback = function(result) {
1099         if (native.isFailure(result)) {
1100             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
1101         } else {
1102             native.callIfPossible(args.successCallback);
1103         }
1104     };
1105
1106     var result = native.call('BluetoothHealthApplication_unregister', callArgs, callback);
1107     if (native.isFailure(result)) {
1108         throw native.getErrorObject(result);
1109     }
1110
1111     _bluetoothHealthApplicationListeners.removeListener(this._id);
1112 };
1113
1114 // class BluetoothProfileHandler //////////////////////////
1115 var _BluetoothProfileType = {
1116     HEALTH: 'HEALTH'
1117 };
1118
1119 var BluetoothProfileHandler = function(data) {
1120     if (data) {
1121         Object.defineProperties(this, {
1122             profileType: { value: data.profileType, writable: false, enumerable: true }
1123         });
1124     }
1125 };
1126
1127 // class BluetoothHealthProfileHandler //////////////////////////
1128 var BluetoothHealthProfileHandler = function(data) {
1129     BluetoothProfileHandler.call(this, data);
1130 };
1131
1132 BluetoothHealthProfileHandler.prototype = new BluetoothProfileHandler();
1133
1134 BluetoothHealthProfileHandler.prototype.constructor = BluetoothProfileHandler;
1135
1136 BluetoothHealthProfileHandler.prototype.registerSinkApplication = function() {
1137     privUtils_.log('Entered BluetoothHealthProfileHandler.registerSinkApplication()');
1138
1139     var args = AV.validateMethod(arguments, [
1140         {
1141             name: 'dataType',
1142             type: AV.Types.LONG // there's no short type
1143         },
1144         {
1145             name: 'name',
1146             type: AV.Types.STRING
1147         },
1148         {
1149             name: 'successCallback',
1150             type: AV.Types.FUNCTION
1151         },
1152         {
1153             name: 'errorCallback',
1154             type: AV.Types.FUNCTION,
1155             optional: true,
1156             nullable: true
1157         }
1158     ]);
1159
1160     var callArgs = {
1161         dataType: args.dataType,
1162         name: args.name
1163     };
1164
1165     var callback = function(result) {
1166         if (native.isFailure(result)) {
1167             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
1168         } else {
1169             args.successCallback(
1170                 new BluetoothHealthApplication(native.getResultObject(result))
1171             );
1172         }
1173     };
1174
1175     var result = native.call(
1176         'BluetoothHealthProfileHandler_registerSinkApp',
1177         callArgs,
1178         callback
1179     );
1180     if (native.isFailure(result)) {
1181         throw native.getErrorObject(result);
1182     }
1183 };
1184
1185 BluetoothHealthProfileHandler.prototype.connectToSource = function() {
1186     privUtils_.log('Entered BluetoothHealthProfileHandler.connectToSource()');
1187
1188     var args = AV.validateMethod(arguments, [
1189         {
1190             name: 'peer',
1191             type: AV.Types.PLATFORM_OBJECT,
1192             values: BluetoothDevice
1193         },
1194         {
1195             name: 'application',
1196             type: AV.Types.PLATFORM_OBJECT,
1197             values: BluetoothHealthApplication
1198         },
1199         {
1200             name: 'successCallback',
1201             type: AV.Types.FUNCTION
1202         },
1203         {
1204             name: 'errorCallback',
1205             type: AV.Types.FUNCTION,
1206             optional: true,
1207             nullable: true
1208         }
1209     ]);
1210
1211     var callArgs = {
1212         address: args.peer.address,
1213         appId: args.application._id
1214     };
1215
1216     var callback = function(result) {
1217         if (native.isFailure(result)) {
1218             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
1219         } else {
1220             var channel = native.getResultObject(result);
1221             channel.peer = args.peer;
1222             channel.appId = args.application._id;
1223             args.successCallback(new BluetoothHealthChannel(channel));
1224         }
1225     };
1226
1227     var result = native.call(
1228         'BluetoothHealthProfileHandler_connectToSource',
1229         callArgs,
1230         callback
1231     );
1232     if (native.isFailure(result)) {
1233         throw native.getErrorObject(result);
1234     }
1235 };
1236
1237 // class BluetoothHealthChannel //////////////////////////
1238 var BluetoothHealthChannel = function(data) {
1239     Object.defineProperties(this, {
1240         peer: { value: data.peer, writable: false, enumerable: true },
1241         channelType: { value: data.channelType, writable: false, enumerable: true },
1242         application: {
1243             value: _bluetoothHealthApplicationListeners.apps[data.appId],
1244             writable: false,
1245             enumerable: true
1246         },
1247         isConnected: {
1248             value: data.isConnected,
1249             writable: false,
1250             enumerable: true,
1251             configurable: true
1252         },
1253         _id: { value: data._id, writable: false, enumerable: false }
1254     });
1255 };
1256
1257 BluetoothHealthChannel.prototype.close = function() {
1258     privUtils_.log('Entered BluetoothHealthChannel.close()');
1259
1260     if (this.isConnected) {
1261         var callArgs = {
1262             channel: this._id,
1263             address: this.peer.address
1264         };
1265
1266         var result = native.callSync('BluetoothHealthChannel_close', callArgs);
1267
1268         if (native.isFailure(result)) {
1269             throw native.getErrorObject(result);
1270         }
1271
1272         Object.defineProperty(this, 'isConnected', { value: false });
1273     }
1274 };
1275
1276 BluetoothHealthChannel.prototype.sendData = function() {
1277     privUtils_.log('Entered BluetoothHealthChannel.sendData()');
1278     var args = AV.validateMethod(arguments, [
1279         {
1280             name: 'data',
1281             type: AV.Types.ARRAY,
1282             values: AV.Types.BYTE
1283         }
1284     ]);
1285
1286     var callArgs = {
1287         channel: this._id,
1288         data: args.data
1289     };
1290
1291     var result = native.callSync('BluetoothHealthChannel_sendData', callArgs);
1292
1293     if (native.isFailure(result)) {
1294         throw native.getErrorObject(result);
1295     } else {
1296         return native.getResultObject(result);
1297     }
1298 };
1299
1300 var _healthListeners = {};
1301
1302 function _BluetoothHealthChannelChangeCallback(event) {
1303     var e = event;
1304     var callback = _healthListeners[e.id];
1305     var d;
1306
1307     switch (e.event) {
1308     case 'onmessage':
1309         d = e.data;
1310         break;
1311
1312     case 'onclose':
1313         break;
1314
1315     default:
1316         privUtils_.log('Unknown mode: ' + e.event);
1317         return;
1318     }
1319
1320     if (callback[e.event]) {
1321         callback[e.event](d);
1322     }
1323 }
1324
1325 var BluetoothHealthChannel_setListener = function() {
1326     privUtils_.log('Entered BluetoothHealthChannel.setListener()');
1327     privUtils_.checkPrivilegeAccess4Ver(
1328         '2.4',
1329         Privilege.BLUETOOTH,
1330         Privilege.BLUETOOTH_HEALTH
1331     );
1332     var args = AV.validateMethod(arguments, [
1333         {
1334             name: 'changeCallback',
1335             type: AV.Types.LISTENER,
1336             values: ['onmessage', 'onclose']
1337         }
1338     ]);
1339
1340     if (T.isEmptyObject(_healthListeners)) {
1341         native.addListener(
1342             'BluetoothHealthChannelChangeCallback',
1343             _BluetoothHealthChannelChangeCallback
1344         );
1345     }
1346     _healthListeners[this._id] = args.changeCallback;
1347 };
1348
1349 BluetoothHealthChannel.prototype.setListener = function() {
1350     BluetoothHealthChannel_setListener.apply(this, arguments);
1351 };
1352
1353 var BluetoothHealthChannel_unsetListener = function() {
1354     privUtils_.log('Entered BluetoothHealthChannel.unsetListener ()');
1355     if (T.isEmptyObject(_healthListeners)) {
1356         privUtils_.checkPrivilegeAccess4Ver(
1357             '2.4',
1358             Privilege.BLUETOOTH,
1359             Privilege.BLUETOOTH_HEALTH
1360         );
1361     }
1362
1363     delete _healthListeners[this._id];
1364
1365     if (T.isEmptyObject(_healthListeners)) {
1366         native.removeListener(
1367             'BluetoothHealthChannelChangeCallback',
1368             _BluetoothHealthChannelChangeCallback
1369         );
1370     }
1371 };
1372
1373 BluetoothHealthChannel.prototype.unsetListener = function() {
1374     BluetoothHealthChannel_unsetListener.apply(this, arguments);
1375 };
1376
1377 /**
1378  * Creates a manager for specified listener event.
1379  *
1380  * @param {string} name - name of the listener this manager handles
1381  * @param {function} callback - function to be invoked when event specified by the name
1382  *                              fires.
1383  *                              This function should return false if the callback
1384  *                              doesn't want to handle the event anymore, true otherwise.
1385  *                              This function should have following signature:
1386  *                              bool callback(event, successCallback, errorCallback);
1387  *
1388  * @return {object} object which allows to add or remove callbacks for specified listener
1389  */
1390 function _singleListenerBuilder(name, callback) {
1391     var listenerName = name;
1392     var successCallback;
1393     var errorCallback;
1394     var callbackFunction = callback;
1395     var listenerRegistered = false;
1396
1397     function innerCallback(event) {
1398         if (!callbackFunction(event, successCallback, errorCallback)) {
1399             removeListener();
1400         }
1401     }
1402
1403     function addListener(s, e) {
1404         successCallback = s;
1405         errorCallback = e;
1406
1407         if (!listenerRegistered) {
1408             native.addListener(listenerName, innerCallback);
1409             listenerRegistered = true;
1410         }
1411     }
1412
1413     function removeListener() {
1414         if (listenerRegistered) {
1415             native.removeListener(listenerName, innerCallback);
1416             listenerRegistered = false;
1417         }
1418
1419         successCallback = undefined;
1420         errorCallback = undefined;
1421     }
1422
1423     return {
1424         addListener: addListener,
1425         removeListener: removeListener
1426     };
1427 }
1428
1429 var _bleScanListener = _singleListenerBuilder('BluetoothLEScanCallback', function(
1430     event,
1431     successCallback,
1432     errorCallback
1433 ) {
1434     var d;
1435     var ret = true;
1436
1437     switch (event.action) {
1438     case 'onsuccess':
1439         d = new BluetoothLEDevice(event.data);
1440         break;
1441
1442     case 'onerror':
1443         if (errorCallback) {
1444             errorCallback(native.getErrorObject(event));
1445         }
1446         return ret;
1447
1448     default:
1449         privUtils_.log('Unknown mode: ' + event.action);
1450         return ret;
1451     }
1452     if (successCallback) {
1453         successCallback(d);
1454     }
1455
1456     return ret;
1457 });
1458
1459 var _bleAdvertiseListener = _singleListenerBuilder(
1460     'BluetoothLEAdvertiseCallback',
1461     function(event, successCallback, errorCallback) {
1462         var d;
1463         var ret = true;
1464
1465         switch (event.action) {
1466         case 'onstate':
1467             if (successCallback) {
1468                 successCallback(native.getResultObject(event));
1469                 if (native.getResultObject(event) == 'STOPPED') {
1470                     _bleAdvertiseListener.removeListener();
1471                 }
1472             }
1473             return ret;
1474
1475         case 'onerror':
1476             if (errorCallback) {
1477                 errorCallback(native.getErrorObject(event));
1478             }
1479             return ret;
1480
1481         default:
1482             privUtils_.log('Unknown mode: ' + event.action);
1483             return ret;
1484         }
1485     }
1486 );
1487
1488 //class BluetoothLEAdapter //////////////////////////
1489 var BluetoothLEAdapter = function() {};
1490
1491 BluetoothLEAdapter.prototype.startScan = function() {
1492     privUtils_.log('Entered BluetoothLEAdapter.startScan()');
1493     var args = AV.validateMethod(arguments, [
1494         {
1495             name: 'successCallback',
1496             type: AV.Types.FUNCTION
1497         },
1498         {
1499             name: 'errorCallback',
1500             type: AV.Types.FUNCTION,
1501             optional: true,
1502             nullable: true
1503         }
1504     ]);
1505
1506     var result = native.callSync('BluetoothLEAdapter_startScan', {});
1507     if (native.isFailure(result)) {
1508         throw native.getErrorObject(result);
1509     }
1510
1511     _bleScanListener.addListener(args.successCallback, args.errorCallback);
1512 };
1513
1514 BluetoothLEAdapter.prototype.stopScan = function() {
1515     privUtils_.log('Entered BluetoothLEAdapter.stopScan()');
1516
1517     var result = native.callSync('BluetoothLEAdapter_stopScan', {});
1518     if (native.isFailure(result)) {
1519         throw native.getErrorObject(result);
1520     }
1521
1522     _bleScanListener.removeListener();
1523 };
1524
1525 var _BluetoothAdvertisePacketType = {
1526     ADVERTISE: 'ADVERTISE',
1527     SCAN_RESPONSE: 'SCAN_RESPONSE'
1528 };
1529
1530 var _BluetoothAdvertisingMode = {
1531     BALANCED: 'BALANCED',
1532     LOW_LATENCY: 'LOW_LATENCY',
1533     LOW_ENERGY: 'LOW_ENERGY'
1534 };
1535
1536 BluetoothLEAdapter.prototype.startAdvertise = function() {
1537     privUtils_.log('Entered BluetoothLEAdapter.startAdvertise()');
1538     var args = AV.validateMethod(arguments, [
1539         {
1540             name: 'advertiseData',
1541             type: AV.Types.PLATFORM_OBJECT,
1542             values: tizen.BluetoothLEAdvertiseData
1543         },
1544         {
1545             name: 'packetType',
1546             type: AV.Types.ENUM,
1547             values: T.getValues(_BluetoothAdvertisePacketType)
1548         },
1549         {
1550             name: 'successCallback',
1551             type: AV.Types.FUNCTION
1552         },
1553         {
1554             name: 'errorCallback',
1555             type: AV.Types.FUNCTION,
1556             optional: true,
1557             nullable: true
1558         },
1559         {
1560             name: 'mode',
1561             type: AV.Types.ENUM,
1562             values: T.getValues(_BluetoothAdvertisingMode),
1563             optional: true,
1564             nullable: true
1565         },
1566         {
1567             name: 'connectable',
1568             type: AV.Types.BOOLEAN,
1569             optional: true,
1570             nullable: true
1571         }
1572     ]);
1573
1574     var callArgs = {
1575         advertiseData: args.advertiseData,
1576         packetType: args.packetType,
1577         mode: T.isNullOrUndefined(args.mode)
1578             ? _BluetoothAdvertisingMode.BALANCED
1579             : args.mode,
1580         connectable: T.isNullOrUndefined(args.connectable) ? true : args.connectable
1581     };
1582
1583     var result = native.callSync('BluetoothLEAdapter_startAdvertise', callArgs);
1584
1585     if (native.isFailure(result)) {
1586         throw native.getErrorObject(result);
1587     }
1588
1589     _bleAdvertiseListener.addListener(args.successCallback, args.errorCallback);
1590 };
1591
1592 BluetoothLEAdapter.prototype.stopAdvertise = function() {
1593     privUtils_.log('Entered BluetoothLEAdapter.stopAdvertise()');
1594
1595     var result = native.callSync('BluetoothLEAdapter_stopAdvertise', {});
1596
1597     if (native.isFailure(result)) {
1598         throw native.getErrorObject(result);
1599     }
1600 };
1601
1602 //class BluetoothGATTService //////////////////////////
1603 var BluetoothGATTService = function(data, address) {
1604     var handle_ = data.handle;
1605     var uuid_ = data.uuid;
1606     //address_ is needed to control if device is still connected
1607     var address_ = address || data.address;
1608     function servicesGetter() {
1609         var services = [];
1610         var result = native.callSync('BluetoothGATTService_getServices', {
1611             handle: handle_,
1612             address: address_
1613         });
1614         if (native.isSuccess(result)) {
1615             var resultObject = native.getResultObject(result);
1616             resultObject.forEach(function(s) {
1617                 services.push(new BluetoothGATTService(s, address_));
1618             });
1619         }
1620         return services;
1621     }
1622     function characteristicsGetter() {
1623         var characteristics = [];
1624         var result = native.callSync('BluetoothGATTService_getCharacteristics', {
1625             handle: handle_,
1626             uuid: uuid_,
1627             address: address_
1628         });
1629         if (native.isSuccess(result)) {
1630             var resultObject = native.getResultObject(result);
1631             resultObject.forEach(function(c) {
1632                 characteristics.push(new BluetoothGATTCharacteristic(c, address_));
1633             });
1634         }
1635         return characteristics;
1636     }
1637     Object.defineProperties(this, {
1638         uuid: { value: uuid_, writable: false, enumerable: true },
1639         services: { enumerable: true, set: function() {}, get: servicesGetter },
1640         characteristics: {
1641             enumerable: true,
1642             set: function() {},
1643             get: characteristicsGetter
1644         }
1645     });
1646 };
1647
1648 var toByteArray = function(array) {
1649     var d = [];
1650
1651     array.forEach(function(b) {
1652         d.push(Converter.toOctet(b));
1653     });
1654     return d;
1655 };
1656
1657 //class BluetoothGATTCharacteristic //////////////////////////
1658 var BluetoothGATTCharacteristic = function(data, address) {
1659     var handle_ = data.handle;
1660     var descriptors_ = [];
1661     var isBroadcast_ = false;
1662     var hasExtendedProperties_ = false;
1663     var isNotify_ = false;
1664     var isIndication_ = false;
1665     var isReadable_ = false;
1666     var isSignedWrite_ = false;
1667     var isWritable_ = false;
1668     var isWriteNoResponse_ = false;
1669     //address_ is needed to control if device is still connected
1670     var address_ = address;
1671
1672     if (T.isObject(data)) {
1673         data.descriptors.forEach(function(dd) {
1674             descriptors_.push(new BluetoothGATTDescriptor(dd, address_));
1675         });
1676         isBroadcast_ = data.isBroadcast;
1677         hasExtendedProperties_ = data.hasExtendedProperties;
1678         isNotify_ = data.isNotify;
1679         isIndication_ = data.isIndication;
1680         isReadable_ = data.isReadable;
1681         isSignedWrite_ = data.isSignedWrite;
1682         isWritable_ = data.isWritable;
1683         isWriteNoResponse_ = data.isWriteNoResponse;
1684     }
1685
1686     Object.defineProperties(this, {
1687         descriptors: {
1688             enumerable: true,
1689             get: function() {
1690                 return descriptors_.slice();
1691             },
1692             set: function() {}
1693         },
1694         isBroadcast: {
1695             enumerable: true,
1696             get: function() {
1697                 return isBroadcast_;
1698             },
1699             set: function() {}
1700         },
1701         hasExtendedProperties: {
1702             enumerable: true,
1703             get: function() {
1704                 return hasExtendedProperties_;
1705             },
1706             set: function() {}
1707         },
1708         isNotify: {
1709             enumerable: true,
1710             get: function() {
1711                 return isNotify_;
1712             },
1713             set: function() {}
1714         },
1715         isIndication: {
1716             enumerable: true,
1717             get: function() {
1718                 return isIndication_;
1719             },
1720             set: function() {}
1721         },
1722         isReadable: {
1723             enumerable: true,
1724             get: function() {
1725                 return isReadable_;
1726             },
1727             set: function() {}
1728         },
1729         isSignedWrite: {
1730             enumerable: true,
1731             get: function() {
1732                 return isSignedWrite_;
1733             },
1734             set: function() {}
1735         },
1736         isWritable: {
1737             enumerable: true,
1738             get: function() {
1739                 return isWritable_;
1740             },
1741             set: function() {}
1742         },
1743         isWriteNoResponse: {
1744             enumerable: true,
1745             get: function() {
1746                 return isWriteNoResponse_;
1747             },
1748             set: function() {}
1749         }
1750     });
1751
1752     this.readValue = function() {
1753         privUtils_.log('Entered BluetoothGATTCharacteristic.readValue()');
1754         var args = AV.validateMethod(arguments, [
1755             {
1756                 name: 'successCallback',
1757                 type: AV.Types.FUNCTION
1758             },
1759             {
1760                 name: 'errorCallback',
1761                 type: AV.Types.FUNCTION,
1762                 optional: true,
1763                 nullable: true
1764             }
1765         ]);
1766
1767         var callback = function(result) {
1768             if (native.isFailure(result)) {
1769                 native.callIfPossible(args.errorCallback, native.getErrorObject(result));
1770             } else {
1771                 var d = toByteArray(native.getResultObject(result));
1772                 args.successCallback(d);
1773             }
1774         };
1775
1776         var callArgs = { handle: handle_, address: address_ };
1777
1778         var result = native.call('BluetoothGATT_readValue', callArgs, callback);
1779
1780         if (native.isFailure(result)) {
1781             throw native.getErrorObject(result);
1782         }
1783     };
1784
1785     this.writeValue = function() {
1786         privUtils_.log('Entered BluetoothGATTCharacteristic.writeValue()');
1787         var args = AV.validateMethod(arguments, [
1788             {
1789                 name: 'value',
1790                 type: AV.Types.ARRAY,
1791                 values: AV.Types.BYTE
1792             },
1793             {
1794                 name: 'successCallback',
1795                 type: AV.Types.FUNCTION,
1796                 optional: true,
1797                 nullable: true
1798             },
1799             {
1800                 name: 'errorCallback',
1801                 type: AV.Types.FUNCTION,
1802                 optional: true,
1803                 nullable: true
1804             }
1805         ]);
1806
1807         var callback = function(result) {
1808             if (native.isFailure(result)) {
1809                 native.callIfPossible(args.errorCallback, native.getErrorObject(result));
1810             } else {
1811                 native.callIfPossible(args.successCallback);
1812             }
1813         };
1814
1815         var callArgs = {
1816             handle: handle_,
1817             value: toByteArray(args.value),
1818             address: address_
1819         };
1820
1821         var result = native.call('BluetoothGATT_writeValue', callArgs, callback);
1822
1823         if (native.isFailure(result)) {
1824             throw native.getErrorObject(result);
1825         }
1826     };
1827
1828     var addValueChangeListener = function() {
1829         privUtils_.log('Entered BluetoothGATTCharacteristic.addValueChangeListener()');
1830         privUtils_.checkPrivilegeAccess4Ver(
1831             '2.4',
1832             Privilege.BLUETOOTH,
1833             Privilege.BLUETOOTH_ADMIN
1834         );
1835         var args = AV.validateMethod(arguments, [
1836             {
1837                 name: 'callback',
1838                 type: AV.Types.FUNCTION
1839             }
1840         ]);
1841
1842         var callArgs = { handle: handle_, address: address_ };
1843
1844         var callback = function(event) {
1845             if (event.handle === handle_) {
1846                 args.callback(toByteArray(native.getResultObject(event)));
1847             }
1848         };
1849
1850         return _bluetoothGATTCharacteristicListener.addListener(callback, callArgs);
1851     };
1852
1853     this.addValueChangeListener = function() {
1854         return addValueChangeListener.apply(this, arguments);
1855     };
1856
1857     this.removeValueChangeListener = function() {
1858         privUtils_.log('Entered BluetoothGATTCharacteristic.removeValueChangeListener()');
1859
1860         var args = AV.validateMethod(arguments, [
1861             {
1862                 name: 'watchID',
1863                 type: AV.Types.LONG
1864             }
1865         ]);
1866
1867         var callArgs = { handle: handle_, address: address_ };
1868
1869         return _bluetoothGATTCharacteristicListener.removeListener(
1870             args.watchID,
1871             callArgs
1872         );
1873     };
1874 };
1875
1876 /**
1877  * Creates a manager for specified listener event. Manager handles multiple
1878  * registered listeners
1879  *
1880  * @param {string} name - name of the listener this manager handles
1881  * @param {function} callback - function to be invoked when event specified by the name
1882  *                              fires.
1883  *                              This function should have following signature:
1884  *                              void callback(listener, event);
1885  * @param {string} addListenerId - optional parameter. If specified, this native
1886  *                                 method will be called synchronously when
1887  *                                 listener is added.
1888  * @param {string} removeListenerId - optional parameter. If specified, this native
1889  *                                 method will be called synchronously when
1890  *                                 listener is removed.
1891  * @param {bool} repeatNativeCall - optional parameter. If specified, the addListenerId
1892  *                                 and removeListenerId methods will be called
1893  *                                 synchronously each time listener is added/removed.
1894  *                                 Otherwise they are going to be called just once: when
1895  *                                 first listener is added and last listener is removed.
1896  *
1897  * @return {object} object which allows to add or remove callbacks for specified listener
1898  */
1899 function _multipleListenerBuilder(
1900     name,
1901     callback,
1902     addListenerId,
1903     removeListenerId,
1904     repeatNativeCall
1905 ) {
1906     var listenerName = name;
1907     var addId = addListenerId;
1908     var removeId = removeListenerId;
1909     var callbackFunction = callback;
1910     var listeners = {};
1911     var nextId = 1;
1912     var jsListenerRegistered = false;
1913     var nativeListenerRegistered = false;
1914     var repeatNativeListenerCall = repeatNativeCall;
1915
1916     function innerCallback(event) {
1917         for (var watchId in listeners) {
1918             if (listeners.hasOwnProperty(watchId)) {
1919                 callbackFunction(listeners[watchId], event);
1920             }
1921         }
1922     }
1923
1924     function addListener(callback, args) {
1925         var id = ++nextId;
1926
1927         if (addId && (!nativeListenerRegistered || repeatNativeListenerCall)) {
1928             var result = native.callSync(addId, args || {});
1929             if (native.isFailure(result)) {
1930                 throw native.getErrorObject(result);
1931             }
1932             nativeListenerRegistered = true;
1933         }
1934
1935         if (!jsListenerRegistered) {
1936             native.addListener(listenerName, innerCallback);
1937             jsListenerRegistered = true;
1938         }
1939
1940         listeners[id] = callback;
1941         return id;
1942     }
1943
1944     function removeListener(watchId, args) {
1945         if (listeners.hasOwnProperty(watchId)) {
1946             delete listeners[watchId];
1947         }
1948
1949         if (
1950             removeId &&
1951             ((nativeListenerRegistered && T.isEmptyObject(listeners)) ||
1952                 repeatNativeListenerCall)
1953         ) {
1954             var result = native.callSync(removeId, args || {});
1955             if (native.isFailure(result)) {
1956                 throw native.getErrorObject(result);
1957             }
1958             nativeListenerRegistered = false;
1959         }
1960
1961         if (jsListenerRegistered && T.isEmptyObject(listeners)) {
1962             native.removeListener(listenerName, innerCallback);
1963             jsListenerRegistered = false;
1964         }
1965     }
1966
1967     return {
1968         addListener: addListener,
1969         removeListener: removeListener
1970     };
1971 }
1972
1973 var _bluetoothGATTCharacteristicListener = _multipleListenerBuilder(
1974     'BluetoothGATTCharacteristicValueChangeListener',
1975     function(listener, event) {
1976         listener(event);
1977     },
1978     'BluetoothGATTCharacteristic_addValueChangeListener',
1979     'BluetoothGATTCharacteristic_removeValueChangeListener',
1980     true
1981 );
1982
1983 var _bleConnectChangeListener = _multipleListenerBuilder(
1984     'BluetoothLEConnectChangeCallback',
1985     function(listener, event) {
1986         listener(event);
1987     },
1988     'BluetoothLEDevice_addConnectStateChangeListener',
1989     'BluetoothLEDevice_removeConnectStateChangeListener'
1990 );
1991
1992 //class BluetoothGATTDescriptor //////////////////////////
1993 var BluetoothGATTDescriptor = function(data, address) {
1994     var handle_ = data.handle;
1995     //address_ is needed to control if device is still connected
1996     var address_ = address;
1997
1998     this.readValue = function() {
1999         privUtils_.log('Entered BluetoothGATTDescriptor.readValue()');
2000         var args = AV.validateMethod(arguments, [
2001             {
2002                 name: 'successCallback',
2003                 type: AV.Types.FUNCTION
2004             },
2005             {
2006                 name: 'errorCallback',
2007                 type: AV.Types.FUNCTION,
2008                 optional: true,
2009                 nullable: true
2010             }
2011         ]);
2012
2013         var callback = function(result) {
2014             if (native.isFailure(result)) {
2015                 native.callIfPossible(args.errorCallback, native.getErrorObject(result));
2016             } else {
2017                 var d = toByteArray(native.getResultObject(result));
2018                 args.successCallback(d);
2019             }
2020         };
2021
2022         var callArgs = { handle: handle_, address: address_ };
2023
2024         var result = native.call('BluetoothGATT_readValue', callArgs, callback);
2025
2026         if (native.isFailure(result)) {
2027             throw native.getErrorObject(result);
2028         }
2029     };
2030
2031     this.writeValue = function() {
2032         privUtils_.log('Entered BluetoothGATTDescriptor.writeValue()');
2033         var args = AV.validateMethod(arguments, [
2034             {
2035                 name: 'value',
2036                 type: AV.Types.ARRAY,
2037                 values: AV.Types.BYTE
2038             },
2039             {
2040                 name: 'successCallback',
2041                 type: AV.Types.FUNCTION,
2042                 optional: true,
2043                 nullable: true
2044             },
2045             {
2046                 name: 'errorCallback',
2047                 type: AV.Types.FUNCTION,
2048                 optional: true,
2049                 nullable: true
2050             }
2051         ]);
2052
2053         var callback = function(result) {
2054             if (native.isFailure(result)) {
2055                 native.callIfPossible(args.errorCallback, native.getErrorObject(result));
2056             } else {
2057                 native.callIfPossible(args.successCallback);
2058             }
2059         };
2060
2061         var callArgs = {
2062             handle: handle_,
2063             value: toByteArray(args.value),
2064             address: address_
2065         };
2066
2067         var result = native.call('BluetoothGATT_writeValue', callArgs, callback);
2068
2069         if (native.isFailure(result)) {
2070             throw native.getErrorObject(result);
2071         }
2072     };
2073 };
2074
2075 // class BluetoothAdapter //////////////////////////
2076 var BluetoothAdapter = function() {
2077     function nameGetter() {
2078         var result = native.callSync('BluetoothAdapter_getName', {});
2079
2080         if (native.isFailure(result)) {
2081             return '';
2082         } else {
2083             return native.getResultObject(result);
2084         }
2085     }
2086
2087     function addressGetter() {
2088         var result = native.callSync('BluetoothAdapter_getAddress', {});
2089
2090         if (native.isFailure(result)) {
2091             return '';
2092         } else {
2093             return native.getResultObject(result);
2094         }
2095     }
2096
2097     function poweredGetter() {
2098         var result = native.callSync('BluetoothAdapter_getPowered', {});
2099
2100         if (native.isFailure(result)) {
2101             return false;
2102         } else {
2103             return native.getResultObject(result);
2104         }
2105     }
2106
2107     function visibleGetter() {
2108         var result = native.callSync('BluetoothAdapter_getVisible', {});
2109
2110         if (native.isFailure(result)) {
2111             return false;
2112         } else {
2113             return native.getResultObject(result);
2114         }
2115     }
2116
2117     Object.defineProperties(this, {
2118         name: {
2119             enumerable: true,
2120             set: function() {},
2121             get: nameGetter
2122         },
2123         address: {
2124             enumerable: true,
2125             set: function() {},
2126             get: addressGetter
2127         },
2128         powered: {
2129             enumerable: true,
2130             set: function() {},
2131             get: poweredGetter
2132         },
2133         visible: {
2134             enumerable: true,
2135             set: function() {},
2136             get: visibleGetter
2137         }
2138     });
2139 };
2140
2141 BluetoothAdapter.prototype.setName = function() {
2142     privUtils_.log('Entered BluetoothAdapter.setName()');
2143     var args = AV.validateMethod(arguments, [
2144         {
2145             name: 'name',
2146             type: AV.Types.STRING
2147         },
2148         {
2149             name: 'successCallback',
2150             type: AV.Types.FUNCTION,
2151             optional: true,
2152             nullable: true
2153         },
2154         {
2155             name: 'errorCallback',
2156             type: AV.Types.FUNCTION,
2157             optional: true,
2158             nullable: true
2159         }
2160     ]);
2161
2162     var callArgs = {
2163         name: args.name
2164     };
2165
2166     var callback = function(result) {
2167         if (native.isFailure(result)) {
2168             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
2169         } else {
2170             native.callIfPossible(args.successCallback);
2171         }
2172     };
2173
2174     var result = native.call('BluetoothAdapter_setName', callArgs, callback);
2175     if (native.isFailure(result)) {
2176         throw native.getErrorObject(result);
2177     }
2178 };
2179
2180 BluetoothAdapter.prototype.setPowered = function() {
2181     privUtils_.log('Entered BluetoothAdapter.setPowered()');
2182     privUtils_.warn(
2183         'DEPRECATION WARNING: setPowered() is deprecated and will be removed from ' +
2184             'next release. Let the user turn on/off Bluetooth through the Settings ' +
2185             'application instead.'
2186     );
2187
2188     var args = AV.validateMethod(arguments, [
2189         {
2190             name: 'powered',
2191             type: AV.Types.BOOLEAN
2192         },
2193         {
2194             name: 'successCallback',
2195             type: AV.Types.FUNCTION,
2196             optional: true,
2197             nullable: true
2198         },
2199         {
2200             name: 'errorCallback',
2201             type: AV.Types.FUNCTION,
2202             optional: true,
2203             nullable: true
2204         }
2205     ]);
2206
2207     var callArgs = {
2208         powered: args.powered
2209     };
2210
2211     var callback = function(result) {
2212         if (native.isFailure(result)) {
2213             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
2214         } else {
2215             native.callIfPossible(args.successCallback);
2216         }
2217     };
2218
2219     var result = native.call('BluetoothAdapter_setPowered', callArgs, callback);
2220     if (native.isFailure(result)) {
2221         throw native.getErrorObject(result);
2222     }
2223 };
2224
2225 // This method is deprecated since Tizen 2.3 and will be removed in Tizen 3.0.
2226 BluetoothAdapter.prototype.setVisible = function() {
2227     privUtils_.log('Entered BluetoothAdapter.setVisible()');
2228     privUtils_.warn(
2229         'DEPRECATION WARNING: setVisible() is deprecated and will be removed from ' +
2230             'next release. Let the user change the Bluetooth visibility through the ' +
2231             'Settings application instead.'
2232     );
2233
2234     var args = AV.validateMethod(arguments, [
2235         {
2236             name: 'visible',
2237             type: AV.Types.BOOLEAN
2238         },
2239         {
2240             name: 'successCallback',
2241             type: AV.Types.FUNCTION,
2242             optional: true,
2243             nullable: true
2244         },
2245         {
2246             name: 'errorCallback',
2247             type: AV.Types.FUNCTION,
2248             optional: true,
2249             nullable: true
2250         },
2251         {
2252             name: 'timeout',
2253             type: AV.Types.UNSIGNED_LONG,
2254             optional: true,
2255             nullable: true
2256         }
2257     ]);
2258
2259     var callArgs = {
2260         visible: args.visible
2261     };
2262
2263     if (args.visible === true) {
2264         if (T.isNullOrUndefined(args.timeout)) {
2265             callArgs.timeout = 0;
2266         } else {
2267             callArgs.timeout = args.timeout > 65535 ? 180 : args.timeout;
2268         }
2269     }
2270
2271     var callback = function(result) {
2272         if (native.isFailure(result)) {
2273             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
2274         } else {
2275             native.callIfPossible(args.successCallback);
2276         }
2277     };
2278
2279     var result = native.call('BluetoothAdapter_setVisible', callArgs, callback);
2280     if (native.isFailure(result)) {
2281         throw native.getErrorObject(result);
2282     }
2283 };
2284
2285 var _listener;
2286
2287 function _BluetoothAdapterChangeCallback(event) {
2288     privUtils_.log('_BluetoothAdapterChangeCallback');
2289
2290     var e = event;
2291     var d;
2292
2293     switch (e.action) {
2294     case 'onstatechanged':
2295         d = e.powered;
2296         break;
2297
2298     case 'onnamechanged':
2299         d = e.name;
2300         break;
2301
2302     case 'onvisibilitychanged':
2303         d = e.visible;
2304         break;
2305
2306     default:
2307         privUtils_.log('Unknown mode: ' + e.action);
2308         return;
2309     }
2310
2311     if (_listener[e.action]) {
2312         _listener[e.action](d);
2313     }
2314 }
2315
2316 BluetoothAdapter.prototype.setChangeListener = function() {
2317     privUtils_.log('Entered BluetoothAdapter.setChangeListener()');
2318     var args = AV.validateMethod(arguments, [
2319         {
2320             name: 'changeCallback',
2321             type: AV.Types.LISTENER,
2322             values: ['onstatechanged', 'onnamechanged', 'onvisibilitychanged']
2323         }
2324     ]);
2325
2326     if (T.isNullOrUndefined(_listener)) {
2327         native.addListener(
2328             'BluetoothAdapterChangeCallback',
2329             _BluetoothAdapterChangeCallback
2330         );
2331     }
2332     _listener = args.changeCallback;
2333 };
2334
2335 BluetoothAdapter.prototype.unsetChangeListener = function() {
2336     privUtils_.log('Entered BluetoothAdapter.unsetChangeListener()');
2337     if (!T.isNullOrUndefined(_listener)) {
2338         native.removeListener(
2339             'BluetoothAdapterChangeCallback',
2340             _BluetoothAdapterChangeCallback
2341         );
2342         _listener = undefined;
2343     }
2344 };
2345
2346 var _discoverDevicesSuccessCallback;
2347 var _discoverDevicesErrorCallback;
2348
2349 function _BluetoothDiscoverDevicesSuccessCallback(event) {
2350     var e = event;
2351     var d = null;
2352
2353     switch (e.action) {
2354     case 'onstarted':
2355         break;
2356
2357     case 'ondevicefound':
2358         d = new BluetoothDevice(e.data);
2359         break;
2360
2361     case 'ondevicedisappeared':
2362         d = e.data;
2363         break;
2364
2365     case 'onfinished':
2366         var result = e.data;
2367         d = [];
2368         result.forEach(function(data) {
2369             d.push(new BluetoothDevice(data));
2370         });
2371
2372         //remove listeners after discovering
2373         native.removeListener(
2374             'BluetoothDiscoverDevicesSuccessCallback',
2375             _BluetoothDiscoverDevicesSuccessCallback
2376         );
2377         native.removeListener(
2378             'BluetoothDiscoverDevicesErrorCallback',
2379             _BluetoothDiscoverDevicesErrorCallback
2380         );
2381         break;
2382
2383     default:
2384         privUtils_.log('Unknown mode: ' + e.action);
2385         return;
2386     }
2387
2388     if (_discoverDevicesSuccessCallback[e.action]) {
2389         _discoverDevicesSuccessCallback[e.action](d);
2390     }
2391 }
2392
2393 function _BluetoothDiscoverDevicesErrorCallback(event) {
2394     var e = event;
2395     setTimeout(function() {
2396         native.callIfPossible(_discoverDevicesErrorCallback, native.getErrorObject(e));
2397     }, 0);
2398 }
2399
2400 BluetoothAdapter.prototype.discoverDevices = function() {
2401     privUtils_.log('Entered BluetoothAdapter.discoverDevices()');
2402     var args = AV.validateMethod(arguments, [
2403         {
2404             name: 'successCallback',
2405             type: AV.Types.LISTENER,
2406             values: ['onstarted', 'ondevicefound', 'ondevicedisappeared', 'onfinished']
2407         },
2408         {
2409             name: 'errorCallback',
2410             type: AV.Types.FUNCTION,
2411             optional: true,
2412             nullable: true
2413         }
2414     ]);
2415
2416     _discoverDevicesSuccessCallback = args.successCallback;
2417     _discoverDevicesErrorCallback = args.errorCallback;
2418     native.addListener(
2419         'BluetoothDiscoverDevicesSuccessCallback',
2420         _BluetoothDiscoverDevicesSuccessCallback
2421     );
2422     native.addListener(
2423         'BluetoothDiscoverDevicesErrorCallback',
2424         _BluetoothDiscoverDevicesErrorCallback
2425     );
2426
2427     var result = native.callSync('BluetoothAdapter_discoverDevices', {});
2428
2429     if (native.isFailure(result)) {
2430         native.removeListener(
2431             'BluetoothDiscoverDevicesSuccessCallback',
2432             _BluetoothDiscoverDevicesSuccessCallback
2433         );
2434         native.removeListener(
2435             'BluetoothDiscoverDevicesErrorCallback',
2436             _BluetoothDiscoverDevicesErrorCallback
2437         );
2438         throw native.getErrorObject(result);
2439     }
2440 };
2441
2442 BluetoothAdapter.prototype.stopDiscovery = function() {
2443     privUtils_.log('Entered BluetoothAdapter.stopDiscovery()');
2444     var args = AV.validateMethod(arguments, [
2445         {
2446             name: 'successCallback',
2447             type: AV.Types.FUNCTION,
2448             optional: true,
2449             nullable: true
2450         },
2451         {
2452             name: 'errorCallback',
2453             type: AV.Types.FUNCTION,
2454             optional: true,
2455             nullable: true
2456         }
2457     ]);
2458
2459     var callback = function(result) {
2460         if (native.isFailure(result)) {
2461             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
2462         } else {
2463             native.callIfPossible(args.successCallback);
2464         }
2465     };
2466
2467     var result = native.call('BluetoothAdapter_stopDiscovery', {}, callback);
2468     if (native.isFailure(result)) {
2469         throw native.getErrorObject(result);
2470     }
2471 };
2472
2473 BluetoothAdapter.prototype.getKnownDevices = function() {
2474     privUtils_.log('Entered BluetoothAdapter.getKnownDevices()');
2475     var args = AV.validateMethod(arguments, [
2476         {
2477             name: 'successCallback',
2478             type: AV.Types.FUNCTION
2479         },
2480         {
2481             name: 'errorCallback',
2482             type: AV.Types.FUNCTION,
2483             optional: true,
2484             nullable: true
2485         }
2486     ]);
2487
2488     var callback = function(result) {
2489         if (native.isFailure(result)) {
2490             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
2491         } else {
2492             var r = native.getResultObject(result).devices;
2493             var devices = [];
2494             r.forEach(function(data) {
2495                 devices.push(new BluetoothDevice(data));
2496             });
2497             args.successCallback(devices);
2498         }
2499     };
2500
2501     var result = native.call('BluetoothAdapter_getKnownDevices', {}, callback);
2502     if (native.isFailure(result)) {
2503         throw native.getErrorObject(result);
2504     }
2505 };
2506
2507 BluetoothAdapter.prototype.getDevice = function() {
2508     privUtils_.log('Entered BluetoothAdapter.getDevice()');
2509     var args = AV.validateMethod(arguments, [
2510         {
2511             name: 'address',
2512             type: AV.Types.STRING
2513         },
2514         {
2515             name: 'successCallback',
2516             type: AV.Types.FUNCTION
2517         },
2518         {
2519             name: 'errorCallback',
2520             type: AV.Types.FUNCTION,
2521             optional: true,
2522             nullable: true
2523         }
2524     ]);
2525
2526     var callback = function(result) {
2527         if (native.isFailure(result)) {
2528             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
2529         } else {
2530             args.successCallback(new BluetoothDevice(native.getResultObject(result)));
2531         }
2532     };
2533
2534     var result = native.call(
2535         'BluetoothAdapter_getDevice',
2536         { address: args.address },
2537         callback
2538     );
2539     if (native.isFailure(result)) {
2540         throw native.getErrorObject(result);
2541     }
2542 };
2543
2544 BluetoothAdapter.prototype.createBonding = function() {
2545     privUtils_.log('Entered BluetoothAdapter.createBonding()');
2546     var args = AV.validateMethod(arguments, [
2547         {
2548             name: 'address',
2549             type: AV.Types.STRING
2550         },
2551         {
2552             name: 'successCallback',
2553             type: AV.Types.FUNCTION,
2554             optional: false,
2555             nullable: false
2556         },
2557         {
2558             name: 'errorCallback',
2559             type: AV.Types.FUNCTION,
2560             optional: true,
2561             nullable: true
2562         }
2563     ]);
2564
2565     var callArgs = {
2566         address: args.address
2567     };
2568
2569     var callback = function(result) {
2570         if (native.isFailure(result)) {
2571             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
2572         } else {
2573             args.successCallback(new BluetoothDevice(native.getResultObject(result)));
2574         }
2575     };
2576
2577     var result = native.call('BluetoothAdapter_createBonding', callArgs, callback);
2578     if (native.isFailure(result)) {
2579         throw native.getErrorObject(result);
2580     }
2581 };
2582
2583 BluetoothAdapter.prototype.destroyBonding = function() {
2584     privUtils_.log('Entered BluetoothAdapter.destroyBonding()');
2585     var args = AV.validateMethod(arguments, [
2586         {
2587             name: 'address',
2588             type: AV.Types.STRING
2589         },
2590         {
2591             name: 'successCallback',
2592             type: AV.Types.FUNCTION,
2593             optional: true,
2594             nullable: true
2595         },
2596         {
2597             name: 'errorCallback',
2598             type: AV.Types.FUNCTION,
2599             optional: true,
2600             nullable: true
2601         }
2602     ]);
2603
2604     var callArgs = {
2605         address: args.address
2606     };
2607
2608     var callback = function(result) {
2609         if (native.isFailure(result)) {
2610             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
2611         } else {
2612             native.callIfPossible(args.successCallback);
2613         }
2614     };
2615
2616     var result = native.call('BluetoothAdapter_destroyBonding', callArgs, callback);
2617     if (native.isFailure(result)) {
2618         throw native.getErrorObject(result);
2619     }
2620 };
2621
2622 BluetoothAdapter.prototype.registerRFCOMMServiceByUUID = function() {
2623     privUtils_.log('Entered BluetoothAdapter.registerRFCOMMServiceByUUID()');
2624     var args = AV.validateMethod(arguments, [
2625         {
2626             name: 'uuid',
2627             type: AV.Types.STRING
2628         },
2629         {
2630             name: 'name',
2631             type: AV.Types.STRING
2632         },
2633         {
2634             name: 'successCallback',
2635             type: AV.Types.FUNCTION
2636         },
2637         {
2638             name: 'errorCallback',
2639             type: AV.Types.FUNCTION,
2640             optional: true,
2641             nullable: true
2642         }
2643     ]);
2644
2645     var callArgs = {
2646         uuid: args.uuid,
2647         name: args.name
2648     };
2649
2650     var callback = function(result) {
2651         if (native.isFailure(result)) {
2652             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
2653         } else {
2654             // if registration was finished with success create BluetoothServiceHandler
2655             // with parameters passed to this function (uuid and name).
2656             args.successCallback(new BluetoothServiceHandler(callArgs));
2657         }
2658     };
2659
2660     var result = native.call(
2661         'BluetoothAdapter_registerRFCOMMServiceByUUID',
2662         callArgs,
2663         callback
2664     );
2665     if (native.isFailure(result)) {
2666         throw native.getErrorObject(result);
2667     }
2668 };
2669
2670 BluetoothAdapter.prototype.getBluetoothProfileHandler = function() {
2671     privUtils_.log('Entered BluetoothAdapter.getBluetoothProfileHandler()');
2672
2673     var args = AV.validateMethod(arguments, [
2674         {
2675             name: 'profileType',
2676             type: AV.Types.ENUM,
2677             values: T.getValues(_BluetoothProfileType)
2678         }
2679     ]);
2680
2681     var callArgs = { profileType: args.profileType };
2682
2683     var result = native.callSync('BluetoothAdapter_getBluetoothProfileHandler', callArgs);
2684
2685     if (native.isFailure(result)) {
2686         throw native.getErrorObject(result);
2687     } else {
2688         switch (args.profileType) {
2689         case _BluetoothProfileType.HEALTH:
2690             return new BluetoothHealthProfileHandler(callArgs);
2691
2692         default:
2693             throw new WebAPIException(
2694                 'NotSupportedError',
2695                 'Profile ' + args.profileType + ' is not supported.'
2696             );
2697         }
2698     }
2699 };
2700
2701 // class BluetoothManager //////////////////////////
2702 var BluetoothManager = function() {
2703     Object.defineProperties(this, {
2704         deviceMajor: {
2705             value: new BluetoothClassDeviceMajor(),
2706             writable: false,
2707             enumerable: true
2708         },
2709         deviceMinor: {
2710             value: new BluetoothClassDeviceMinor(),
2711             writable: false,
2712             enumerable: true
2713         },
2714         deviceService: {
2715             value: new BluetoothClassDeviceService(),
2716             writable: false,
2717             enumerable: true
2718         }
2719     });
2720 };
2721
2722 var BluetoothManager_getDefaultAdapter = function() {
2723     privUtils_.checkPrivilegeAccess4Ver(
2724         '2.4',
2725         Privilege.BLUETOOTH,
2726         Privilege.BLUETOOTH_GAP
2727     );
2728
2729     return new BluetoothAdapter();
2730 };
2731
2732 BluetoothManager.prototype.getDefaultAdapter = function() {
2733     privUtils_.log('Entered BluetoothManager.getDefaultAdapter()');
2734     return BluetoothManager_getDefaultAdapter();
2735 };
2736
2737 var BluetoothManager_getLEAdapter = function() {
2738     privUtils_.checkPrivilegeAccess4Ver(
2739         '2.4',
2740         Privilege.BLUETOOTH,
2741         Privilege.BLUETOOTH_ADMIN
2742     );
2743
2744     return new BluetoothLEAdapter();
2745 };
2746
2747 BluetoothManager.prototype.getLEAdapter = function() {
2748     privUtils_.log('Entered BluetoothManager.getLEAdapter()');
2749     return BluetoothManager_getLEAdapter();
2750 };
2751 // exports /////////////////////////////////////////
2752 exports = new BluetoothManager();