0564ba6cb578a96f67f952c236690fa76c0a6a15
[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                 try {
176                     data_ = BluetoothManager_toDOMString(v);
177                 }
178                 catch (err) {
179                     data_ = Converter.toString(v);
180                 }
181             }
182         }
183     });
184
185     if (arguments.length >= 2) {
186         // public constructor
187         this.uuid = arguments[0];
188         this.data = arguments[1];
189     } else if (d && T.isObject(d)) {
190         // internal constructor
191         this.uuid = d.uuid;
192         this.data = d.data;
193     } else {
194         uuid_ = undefined;
195         data_ = undefined;
196     }
197 };
198
199 //class BluetoothLEAdvertiseData ///////////////////////////
200 tizen.BluetoothLEAdvertiseData = function(dict) {
201     AV.isConstructorCall(this, tizen.BluetoothLEAdvertiseData);
202     var includeName_ = false;
203     var uuids_ = null;
204     var solicitationuuids_ = null;
205     var appearance_ = null;
206     var includeTxPowerLevel_ = false;
207     var serviceData_ = null;
208     var manufacturerData_ = null;
209
210     Object.defineProperties(this, {
211         includeName: {
212             enumerable: true,
213             get: function() {
214                 return includeName_;
215             },
216             set: function(v) {
217                 includeName_ = Converter.toBoolean(v, true);
218             }
219         },
220         uuids: {
221             enumerable: true,
222             get: function() {
223                 return uuids_;
224             },
225             set: function(v) {
226                 if (T.isNull(v)) {
227                     uuids_ = v;
228                 } else if (T.isArray(v)) {
229                     for (var i = 0; i < v.length; ++i) {
230                         if (!T.isString(v[i])) {
231                             v[i] = Converter.toString(v[i]);
232                         }
233                     }
234                     uuids_ = v;
235                 }
236             }
237         },
238         solicitationuuids: {
239             enumerable: true,
240             get: function() {
241                 return solicitationuuids_;
242             },
243             set: function(v) {
244                 if (T.isNull(v)) {
245                     solicitationuuids_ = v;
246                 } else if (T.isArray(v)) {
247                     for (var i = 0; i < v.length; ++i) {
248                         if (!T.isString(v[i])) {
249                             v[i] = Converter.toString(v[i]);
250                         }
251                     }
252                     solicitationuuids_ = v;
253                 }
254             }
255         },
256         appearance: {
257             enumerable: true,
258             get: function() {
259                 return appearance_;
260             },
261             set: function(v) {
262                 appearance_ = Converter.toUnsignedLong(v, true);
263             }
264         },
265         includeTxPowerLevel: {
266             enumerable: true,
267             get: function() {
268                 return includeTxPowerLevel_;
269             },
270             set: function(v) {
271                 includeTxPowerLevel_ = Converter.toBoolean(v, true);
272             }
273         },
274         serviceData: {
275             enumerable: true,
276             get: function() {
277                 return serviceData_;
278             },
279             set: function(v) {
280                 if (T.isNull(v) || v instanceof tizen.BluetoothLEServiceData) {
281                     serviceData_ = v;
282                 }
283             }
284         },
285         manufacturerData: {
286             enumerable: true,
287             get: function() {
288                 return manufacturerData_;
289             },
290             set: function(v) {
291                 if (T.isNull(v) || v instanceof tizen.BluetoothLEManufacturerData) {
292                     manufacturerData_ = v;
293                 }
294             }
295         }
296     });
297
298     if (T.isObject(dict)) {
299         var o = {};
300
301         // includeName
302         if (T.isNull(dict.includeName) || T.isBoolean(dict.includeName)) {
303             o.includeName = dict.includeName;
304         } else if (!T.isUndefined(dict.includeName)) {
305             return;
306         }
307
308         // uuids
309         if (T.isNull(dict.uuids)) {
310             o.uuids = dict.uuids;
311         } else if (T.isArray(dict.uuids)) {
312             for (var i = 0; i < dict.uuids.length; ++i) {
313                 if (!T.isString(dict.uuids[i])) {
314                     return;
315                 }
316             }
317             o.uuids = dict.uuids;
318         } else if (!T.isUndefined(dict.uuids)) {
319             return;
320         }
321
322         // solicitationuuids
323         if (T.isNull(dict.solicitationuuids)) {
324             o.solicitationuuids = dict.solicitationuuids;
325         } else if (T.isArray(dict.solicitationuuids)) {
326             for (var i = 0; i < dict.solicitationuuids.length; ++i) {
327                 if (!T.isString(dict.solicitationuuids[i])) {
328                     return;
329                 }
330             }
331             o.solicitationuuids = dict.solicitationuuids;
332         } else if (!T.isUndefined(dict.solicitationuuids)) {
333             return;
334         }
335
336         // appearance
337         if (T.isNull(dict.appearance) || T.isNumber(dict.appearance)) {
338             o.appearance = dict.appearance;
339         } else if (!T.isUndefined(dict.appearance)) {
340             return;
341         }
342
343         // includeTxPowerLevel
344         if (T.isNull(dict.includeTxPowerLevel) || T.isBoolean(dict.includeTxPowerLevel)) {
345             o.includeTxPowerLevel = dict.includeTxPowerLevel;
346         } else if (!T.isUndefined(dict.includeTxPowerLevel)) {
347             return;
348         }
349
350         // serviceData
351         if (
352             T.isNull(dict.serviceData) ||
353             dict.serviceData instanceof tizen.BluetoothLEServiceData
354         ) {
355             o.serviceData = dict.serviceData;
356         } else if (!T.isUndefined(dict.serviceData)) {
357             return;
358         }
359
360         // manufacturerData
361         if (
362             T.isNull(dict.manufacturerData) ||
363             dict.manufacturerData instanceof tizen.BluetoothLEManufacturerData
364         ) {
365             o.manufacturerData = dict.manufacturerData;
366         } else if (!T.isUndefined(dict.manufacturerData)) {
367             return;
368         }
369
370         for (var prop in o) {
371             if (o.hasOwnProperty(prop) && this.hasOwnProperty(prop)) {
372                 this[prop] = o[prop];
373             }
374         }
375     }
376 };
377
378 //class tizen.BluetoothLEManufacturerData ///////////////////////////
379 tizen.BluetoothLEManufacturerData = function(d) {
380     AV.isConstructorCall(this, tizen.BluetoothLEManufacturerData);
381     var id_ = '';
382     var data_ = '';
383
384     Object.defineProperties(this, {
385         id: {
386             enumerable: true,
387             get: function() {
388                 return id_;
389             },
390             set: function(v) {
391                 id_ = Converter.toString(v);
392             }
393         },
394         data: {
395             enumerable: true,
396             get: function() {
397                 return data_;
398             },
399             set: function(v) {
400                 try {
401                     data_ = BluetoothManager_toDOMString(v);
402                 } catch (err) {
403                     data_ = Converter.toString(v);
404                 }
405             }
406         }
407     });
408
409     if (arguments.length >= 2) {
410         // public constructor
411         this.id = arguments[0];
412         this.data = arguments[1];
413     } else if (d && T.isObject(d)) {
414         // internal constructor
415         this.id = d.id;
416         this.data = d.data;
417     } else {
418         id_ = undefined;
419         data_ = undefined;
420     }
421 };
422
423 // class BluetoothClass ///////////////////////////
424 var BluetoothClass = function(data) {
425     var services = [];
426     if (data) {
427         services = data.services;
428     }
429
430     Object.defineProperties(this, {
431         major: { value: data.major, writable: false, enumerable: true },
432         minor: { value: data.minor, writable: false, enumerable: true },
433         services: {
434             enumerable: true,
435             set: function() {},
436             get: function() {
437                 return services.slice();
438             }
439         }
440     });
441 };
442
443 var BluetoothClass_hasService = function() {
444     privUtils_.log('Entered BluetoothClass.hasService()');
445     privUtils_.checkPrivilegeAccess4Ver(
446         '2.4',
447         Privilege.BLUETOOTH,
448         Privilege.BLUETOOTH_GAP
449     );
450
451     var args = AV.validateArgs(arguments, [
452         {
453             name: 'service',
454             type: AV.Types.UNSIGNED_LONG
455         }
456     ]);
457
458     var size = this.services.length;
459     for (var i = 0; i < size; i++) {
460         if (this.services[i] === args.service) {
461             return true;
462         }
463     }
464     return false;
465 };
466
467 BluetoothClass.prototype.hasService = function() {
468     return BluetoothClass_hasService.apply(this, arguments);
469 };
470
471 // class BluetoothSocket ///////////////////////////
472 var _BLUETOOTH_SOCKET_STATE_CLOSED = 'CLOSED';
473
474 function BluetoothSocketListeners() {
475     var that = this;
476     this.socketCallback = function(data) {
477         var event = data;
478         var socket = that.sockets[event.id];
479
480         if (socket) {
481             if ('onclose' === event.event) {
482                 // no more events
483                 that.removeListener(event.id);
484                 // change state
485                 Object.defineProperty(socket, 'state', {
486                     value: _BLUETOOTH_SOCKET_STATE_CLOSED
487                 });
488             }
489
490             var callback = socket[event.event];
491             if (T.isFunction(callback)) {
492                 callback();
493             }
494         } else {
495             privUtils_.log('Received event for an unknown socket: ' + event.id);
496         }
497     };
498 }
499
500 BluetoothSocketListeners.prototype.sockets = {};
501
502 BluetoothSocketListeners.prototype.addListener = function(socket) {
503     if (T.isEmptyObject(this.sockets)) {
504         native.addListener('BLUETOOTH_SOCKET_STATE_CHANGED', this.socketCallback);
505     }
506
507     this.sockets[socket._id] = socket;
508 };
509
510 BluetoothSocketListeners.prototype.removeListener = function(id) {
511     delete this.sockets[id];
512
513     if (T.isEmptyObject(this.sockets)) {
514         native.removeListener('BLUETOOTH_SOCKET_STATE_CHANGED', this.socketCallback);
515     }
516 };
517
518 var _bluetoothSocketListeners = new BluetoothSocketListeners();
519
520 var BluetoothSocket = function(data) {
521     Object.defineProperties(this, {
522         uuid: { value: data.uuid, writable: false, enumerable: true },
523         state: {
524             value: data.state,
525             writable: false,
526             enumerable: true,
527             configurable: true
528         },
529         peer: {
530             value: new BluetoothDevice(data.peer),
531             writable: false,
532             enumerable: true
533         },
534         onmessage: { value: null, writable: true, enumerable: true },
535         onclose: { value: null, writable: true, enumerable: true },
536         _id: { value: data.id, writable: false, enumerable: false }
537     });
538
539     _bluetoothSocketListeners.addListener(this);
540 };
541
542 BluetoothSocket.prototype.writeData = function(data) {
543     privUtils_.log('Entered BluetoothSocket.writeData()');
544
545     var byteData = BluetoothManager_toByteArray(data);
546
547     var callArgs = {
548         id: this._id,
549         data: byteData
550     };
551
552     var result = native.callSync('BluetoothSocketWriteData', callArgs);
553
554     if (native.isFailure(result)) {
555         throw native.getErrorObject(result);
556     } else {
557         return native.getResultObject(result);
558     }
559 };
560
561 BluetoothSocket.prototype.readData = function() {
562     privUtils_.log('Entered BluetoothSocket.readData()');
563
564     var callArgs = {
565         id: this._id
566     };
567
568     var result = native.callSync('BluetoothSocketReadData', callArgs);
569
570     if (native.isFailure(result)) {
571         throw native.getErrorObject(result);
572     } else {
573         return native.getResultObject(result);
574     }
575 };
576
577 BluetoothSocket.prototype.close = function() {
578     privUtils_.log('Entered BluetoothSocket.close()');
579
580     if (_BLUETOOTH_SOCKET_STATE_CLOSED !== this.state) {
581         var callArgs = {
582             id: this._id
583         };
584
585         var result = native.callSync('BluetoothSocketClose', callArgs);
586
587         if (native.isFailure(result)) {
588             throw native.getErrorObject(result);
589         }
590
591         // change state
592         Object.defineProperty(this, 'state', { value: _BLUETOOTH_SOCKET_STATE_CLOSED });
593     }
594 };
595
596 //class BluetoothLEDevice ///////////////////////////
597 var BluetoothLEDevice = function(data) {
598     var address = '',
599         name = null,
600         txpowerlevel = null,
601         appearance = null,
602         uuids = null,
603         solicitationuuids = null,
604         serviceData = null,
605         manufacturerData = null,
606         rssi = null;
607
608     if (data) {
609         address = data.address;
610         name = data.name || null;
611         txpowerlevel = data.txpowerlevel || null;
612         appearance = data.appearance || null;
613         uuids = data.uuids || null;
614         solicitationuuids = data.solicitationuuids || null;
615         if (data.serviceData) {
616             serviceData = [];
617             data.serviceData.forEach(function(d) {
618                 serviceData.push(new tizen.BluetoothLEServiceData(d));
619             });
620         }
621         if (data.manufacturerData) {
622             manufacturerData = new tizen.BluetoothLEManufacturerData(
623                 data.manufacturerData
624             );
625         }
626         if (data.rssi) {
627             rssi = data.rssi;
628         }
629     }
630
631     Object.defineProperties(this, {
632         address: { value: address, writable: false, enumerable: true },
633         name: { value: name, writable: false, enumerable: true },
634         txpowerlevel: { value: txpowerlevel, writable: false, enumerable: true },
635         appearance: { value: appearance, writable: false, enumerable: true },
636         uuids: {
637             enumerable: true,
638             set: function() {},
639             get: function() {
640                 var service_uuids = uuids ? uuids.slice() : null;
641                 return service_uuids;
642             }
643         },
644         solicitationuuids: {
645             enumerable: true,
646             set: function() {},
647             get: function() {
648                 return solicitationuuids ? solicitationuuids.slice() : null;
649             }
650         },
651         serviceData: {
652             enumerable: true,
653             set: function() {},
654             get: function() {
655                 return serviceData ? serviceData.slice() : null;
656             }
657         },
658         manufacturerData: {
659             value: manufacturerData,
660             writable: false,
661             enumerable: true
662         },
663         rssi: { value: rssi, writable: false, enumerable: true }
664     });
665 };
666
667 BluetoothLEDevice.prototype.connect = function() {
668     privUtils_.log('Entered BluetoothLEDevice.connect()');
669     var args = AV.validateArgs(arguments, [
670         {
671             name: 'successCallback',
672             type: AV.Types.FUNCTION,
673             optional: true,
674             nullable: true
675         },
676         {
677             name: 'errorCallback',
678             type: AV.Types.FUNCTION,
679             optional: true,
680             nullable: true
681         }
682     ]);
683
684     var callback = function(result) {
685         if (native.isFailure(result)) {
686             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
687         } else {
688             native.callIfPossible(args.successCallback);
689         }
690     };
691     // Errors are handled by error callback
692     var result = native.call(
693         'BluetoothLEDeviceConnect',
694         { address: this.address },
695         callback
696     );
697     if (native.isFailure(result)) {
698         throw native.getErrorObject(result);
699     }
700 };
701
702 BluetoothLEDevice.prototype.disconnect = function() {
703     privUtils_.log('Entered BluetoothLEDevice.disconnect()');
704     var args = AV.validateArgs(arguments, [
705         {
706             name: 'successCallback',
707             type: AV.Types.FUNCTION,
708             optional: true,
709             nullable: true
710         },
711         {
712             name: 'errorCallback',
713             type: AV.Types.FUNCTION,
714             optional: true,
715             nullable: true
716         }
717     ]);
718     var callback = function(result) {
719         if (native.isFailure(result)) {
720             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
721         } else {
722             native.callIfPossible(args.successCallback);
723         }
724     };
725
726     var result = native.call(
727         'BluetoothLEDeviceDisconnect',
728         { address: this.address },
729         callback
730     );
731     if (native.isFailure(result)) {
732         throw native.getErrorObject(result);
733     }
734 };
735
736 BluetoothLEDevice.prototype.getService = function() {
737     privUtils_.log('Entered BluetoothLEDevice.getService()');
738     var args = AV.validateArgs(arguments, [
739         {
740             name: 'uuid',
741             type: AV.Types.STRING
742         }
743     ]);
744
745     var callArgs = {
746         uuid: args.uuid,
747         address: this.address
748     };
749
750     var result = native.callSync('BluetoothLEDeviceGetService', callArgs);
751     if (native.isFailure(result)) {
752         throw native.getErrorObject(result);
753     } else {
754         return new BluetoothGATTService(native.getResultObject(result));
755     }
756 };
757
758 BluetoothLEDevice.prototype.getServiceAllUuids = function() {
759     privUtils_.log('Entered BluetoothLEDevice.getServiceAllUuids()');
760
761     var callArgs = {
762         address: this.address
763     };
764
765     var result = native.callSync('BluetoothLEDeviceGetServiceAllUuids', callArgs);
766     if (native.isFailure(result)) {
767         throw native.getErrorObject(result);
768     } else {
769         var uuids = native.getResultObject(result);
770         return uuids;
771     }
772 };
773
774 BluetoothLEDevice.prototype.addConnectStateChangeListener = function() {
775     privUtils_.log('Entered BluetoothLEDevice.addConnectStateChangeListener()');
776     var args = AV.validateArgs(arguments, [
777         {
778             name: 'listener',
779             type: AV.Types.LISTENER,
780             values: ['onconnected', 'ondisconnected']
781         }
782     ]);
783
784     var that = this;
785
786     var func = function(event) {
787         if (event.address === that.address && args.listener[event.action]) {
788             args.listener[event.action](that);
789         }
790     };
791
792     var watchId = _bleConnectChangeListener.addListener(func);
793
794     return watchId;
795 };
796
797 BluetoothLEDevice.prototype.removeConnectStateChangeListener = function() {
798     privUtils_.log('Entered BluetoothLEDevice.removeConnectStateChangeListener()');
799
800     var args = AV.validateArgs(arguments, [
801         {
802             name: 'watchID',
803             type: AV.Types.LONG
804         }
805     ]);
806
807     _bleConnectChangeListener.removeListener(args.watchID);
808 };
809
810 // class BluetoothDevice ///////////////////////////
811 var BluetoothDevice = function(data) {
812     var self = this;
813     function _getter(field) {
814         var callArgs = {};
815
816         callArgs.address = self.address;
817         callArgs.field = field;
818
819         var result = native.callSync('BluetoothDeviceGetBoolValue', callArgs);
820
821         if (native.isFailure(result)) {
822             return false;
823         } else {
824             return native.getResultObject(result);
825         }
826     }
827
828     function isBondedGetter() {
829         return _getter('isBonded');
830     }
831
832     function isTrustedGetter() {
833         return _getter('isTrusted');
834     }
835
836     function isConnectedGetter() {
837         return _getter('isConnected');
838     }
839
840     var uuids = [];
841     if (data) {
842         uuids = data.uuids;
843     }
844
845     Object.defineProperties(this, {
846         name: { value: data.name, writable: false, enumerable: true },
847         address: { value: data.address, writable: false, enumerable: true },
848         deviceClass: {
849             value: new BluetoothClass(data.deviceClass),
850             writable: false,
851             enumerable: true
852         },
853         isBonded: {
854             enumerable: true,
855             set: function() {},
856             get: isBondedGetter
857         },
858         isTrusted: {
859             enumerable: true,
860             set: function() {},
861             get: isTrustedGetter
862         },
863         isConnected: {
864             enumerable: true,
865             set: function() {},
866             get: isConnectedGetter
867         },
868         uuids: {
869             enumerable: true,
870             set: function() {},
871             get: function() {
872                 return uuids.slice();
873             }
874         }
875     });
876 };
877
878 BluetoothDevice.prototype.connectToServiceByUUID = function() {
879     privUtils_.log('Entered BluetoothDevice.connectToServiceByUUID()');
880
881     var args = AV.validateArgs(arguments, [
882         {
883             name: 'uuid',
884             type: AV.Types.STRING
885         },
886         {
887             name: 'successCallback',
888             type: AV.Types.FUNCTION
889         },
890         {
891             name: 'errorCallback',
892             type: AV.Types.FUNCTION,
893             optional: true,
894             nullable: true
895         }
896     ]);
897
898     var callArgs = {
899         address: this.address,
900         uuid: args.uuid
901     };
902     var callback = function(result) {
903         if (native.isFailure(result)) {
904             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
905         } else {
906             args.successCallback(new BluetoothSocket(native.getResultObject(result)));
907         }
908     };
909
910     var result = native.call('BluetoothDeviceConnectToServiceByUUID', callArgs, callback);
911     if (native.isFailure(result)) {
912         throw native.getErrorObject(result);
913     }
914 };
915
916 // class BluetoothServiceHandler ///////////////////////////
917 function BluetoothServiceListeners() {
918     var that = this;
919     this.serviceCallback = function(data) {
920         var e = data;
921         var service = that.services[e.uuid];
922         var result = new BluetoothSocket(e);
923
924         if (service) {
925             privUtils_.log(service);
926             service.onconnect(result);
927         }
928     };
929 }
930
931 BluetoothServiceListeners.prototype.services = {};
932
933 BluetoothServiceListeners.prototype.addListener = function(service) {
934     if (T.isEmptyObject(this.services)) {
935         native.addListener('BLUETOOTH_SERVICE_ONCONNECT', this.serviceCallback);
936     }
937
938     this.services[service.uuid] = service;
939 };
940
941 BluetoothServiceListeners.prototype.removeListener = function(uuid) {
942     delete this.services[uuid];
943
944     if (T.isEmptyObject(this.services)) {
945         native.removeListener('BLUETOOTH_SERVICE_ONCONNECT', this.serviceCallback);
946     }
947 };
948
949 var _bluetoothServiceListeners = new BluetoothServiceListeners();
950
951 var BluetoothServiceHandler = function(data) {
952     function isConnectedGetter() {
953         var callArgs = {
954             uuid: this.uuid
955         };
956
957         var result = native.callSync('BluetoothAdapterIsServiceConnected', {
958             uuid: this.uuid
959         });
960
961         if (native.isFailure(result)) {
962             return false;
963         } else {
964             return native.getResultObject(result);
965         }
966     }
967
968     Object.defineProperties(this, {
969         uuid: { value: data.uuid, writable: false, enumerable: true },
970         name: { value: data.name, writable: false, enumerable: true },
971         isConnected: {
972             enumerable: true,
973             set: function() {},
974             get: isConnectedGetter
975         },
976         onconnect: { value: null, writable: true, enumerable: true }
977     });
978
979     _bluetoothServiceListeners.addListener(this);
980 };
981
982 BluetoothServiceHandler.prototype.unregister = function() {
983     privUtils_.log('Entered BluetoothServiceHandler.unregister()');
984     var args = AV.validateArgs(arguments, [
985         {
986             name: 'successCallback',
987             type: AV.Types.FUNCTION,
988             optional: true,
989             nullable: true
990         },
991         {
992             name: 'errorCallback',
993             type: AV.Types.FUNCTION,
994             optional: true,
995             nullable: true
996         }
997     ]);
998
999     var callArgs = {
1000         uuid: this.uuid
1001     };
1002
1003     var callback = function(result) {
1004         if (native.isFailure(result)) {
1005             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
1006         } else {
1007             native.callIfPossible(args.successCallback);
1008         }
1009     };
1010
1011     var result = native.call('BluetoothServiceHandlerUnregister', callArgs, callback);
1012     if (native.isFailure(result)) {
1013         throw native.getErrorObject(result);
1014     }
1015
1016     _bluetoothServiceListeners.removeListener(this.uuid);
1017 };
1018
1019 // class BluetoothHealthApplication ///////////////////////////
1020 function BluetoothHealthApplicationListeners() {
1021     var that = this;
1022     this.appCallback = function(data) {
1023         var event = data;
1024         var app = that.apps[event.id];
1025
1026         if (app) {
1027             var callback = app[event.event];
1028             if (T.isFunction(callback)) {
1029                 var param;
1030                 switch (event.event) {
1031                 case 'onconnect':
1032                     param = new BluetoothHealthChannel(native.getResultObject(event));
1033                     break;
1034
1035                 default:
1036                     privUtils_.log('Unknown event: ' + event.event);
1037                     break;
1038                 }
1039                 callback(param);
1040             }
1041         } else {
1042             privUtils_.log('Received event for an unknown application: ' + event.id);
1043         }
1044     };
1045 }
1046
1047 BluetoothHealthApplicationListeners.prototype.apps = {};
1048
1049 BluetoothHealthApplicationListeners.prototype.addListener = function(app) {
1050     if (T.isEmptyObject(this.apps)) {
1051         native.addListener('BLUETOOTH_HEALTH_APPLICATION_CHANGED', this.appCallback);
1052     }
1053
1054     this.apps[app._id] = app;
1055 };
1056
1057 BluetoothHealthApplicationListeners.prototype.removeListener = function(id) {
1058     delete this.apps[id];
1059
1060     if (T.isEmptyObject(this.apps)) {
1061         native.removeListener('BLUETOOTH_HEALTH_APPLICATION_CHANGED', this.appCallback);
1062     }
1063 };
1064
1065 var _bluetoothHealthApplicationListeners = new BluetoothHealthApplicationListeners();
1066
1067 var BluetoothHealthApplication = function(data) {
1068     Object.defineProperties(this, {
1069         dataType: { value: data.dataType, writable: false, enumerable: true },
1070         name: { value: data.name, writable: false, enumerable: true },
1071         onconnect: { value: null, writable: true, enumerable: true },
1072         _id: { value: data._id, writable: false, enumerable: false }
1073     });
1074
1075     _bluetoothHealthApplicationListeners.addListener(this);
1076 };
1077
1078 BluetoothHealthApplication.prototype.unregister = function() {
1079     privUtils_.log('Entered BluetoothHealthApplication.unregister()');
1080     privUtils_.printDeprecationWarningFor('BluetoothHealthApplication');
1081     var args = AV.validateArgs(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('BluetoothHealthApplicationUnregister', 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         var profileType = data.profileType;
1122         function profileTypeGetter() {
1123             privUtils_.printDeprecationWarningFor('profileType');
1124             return profileType;
1125         }
1126         Object.defineProperties(this, {
1127             profileType: { enumerable: true, set: function() {}, get: profileTypeGetter }
1128         });
1129     }
1130 };
1131
1132 // class BluetoothHealthProfileHandler ///////////////////////////
1133 var BluetoothHealthProfileHandler = function(data) {
1134     BluetoothProfileHandler.call(this, data);
1135 };
1136
1137 BluetoothHealthProfileHandler.prototype = new BluetoothProfileHandler();
1138
1139 BluetoothHealthProfileHandler.prototype.constructor = BluetoothProfileHandler;
1140
1141 BluetoothHealthProfileHandler.prototype.registerSinkApplication = function() {
1142     privUtils_.log('Entered BluetoothHealthProfileHandler.registerSinkApplication()');
1143     privUtils_.printDeprecationWarningFor('BluetoothHealthProfileHandler');
1144
1145     var args = AV.validateArgs(arguments, [
1146         {
1147             name: 'dataType',
1148             type: AV.Types.LONG // there's no short type
1149         },
1150         {
1151             name: 'name',
1152             type: AV.Types.STRING
1153         },
1154         {
1155             name: 'successCallback',
1156             type: AV.Types.FUNCTION
1157         },
1158         {
1159             name: 'errorCallback',
1160             type: AV.Types.FUNCTION,
1161             optional: true,
1162             nullable: true
1163         }
1164     ]);
1165
1166     var callArgs = {
1167         dataType: args.dataType,
1168         name: args.name
1169     };
1170
1171     var callback = function(result) {
1172         if (native.isFailure(result)) {
1173             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
1174         } else {
1175             args.successCallback(
1176                 new BluetoothHealthApplication(native.getResultObject(result))
1177             );
1178         }
1179     };
1180
1181     var result = native.call(
1182         'BluetoothHealthProfileHandlerRegisterSinkApp',
1183         callArgs,
1184         callback
1185     );
1186     if (native.isFailure(result)) {
1187         throw native.getErrorObject(result);
1188     }
1189 };
1190
1191 BluetoothHealthProfileHandler.prototype.connectToSource = function() {
1192     privUtils_.log('Entered BluetoothHealthProfileHandler.connectToSource()');
1193     privUtils_.printDeprecationWarningFor('BluetoothHealthProfileHandler');
1194
1195     var args = AV.validateArgs(arguments, [
1196         {
1197             name: 'peer',
1198             type: AV.Types.PLATFORM_OBJECT,
1199             values: BluetoothDevice
1200         },
1201         {
1202             name: 'application',
1203             type: AV.Types.PLATFORM_OBJECT,
1204             values: BluetoothHealthApplication
1205         },
1206         {
1207             name: 'successCallback',
1208             type: AV.Types.FUNCTION
1209         },
1210         {
1211             name: 'errorCallback',
1212             type: AV.Types.FUNCTION,
1213             optional: true,
1214             nullable: true
1215         }
1216     ]);
1217
1218     var callArgs = {
1219         address: args.peer.address,
1220         appId: args.application._id
1221     };
1222
1223     var callback = function(result) {
1224         if (native.isFailure(result)) {
1225             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
1226         } else {
1227             var channel = native.getResultObject(result);
1228             channel.peer = args.peer;
1229             channel.appId = args.application._id;
1230             args.successCallback(new BluetoothHealthChannel(channel));
1231         }
1232     };
1233
1234     var result = native.call(
1235         'BluetoothHealthProfileHandlerConnectToSource',
1236         callArgs,
1237         callback
1238     );
1239     if (native.isFailure(result)) {
1240         throw native.getErrorObject(result);
1241     }
1242 };
1243
1244 // class BluetoothHealthChannel ///////////////////////////
1245 var BluetoothHealthChannel = function(data) {
1246     Object.defineProperties(this, {
1247         peer: { value: data.peer, writable: false, enumerable: true },
1248         channelType: { value: data.channelType, writable: false, enumerable: true },
1249         application: {
1250             value: _bluetoothHealthApplicationListeners.apps[data.appId],
1251             writable: false,
1252             enumerable: true
1253         },
1254         isConnected: {
1255             value: data.isConnected,
1256             writable: false,
1257             enumerable: true,
1258             configurable: true
1259         },
1260         _id: { value: data._id, writable: false, enumerable: false }
1261     });
1262 };
1263
1264 BluetoothHealthChannel.prototype.close = function() {
1265     privUtils_.log('Entered BluetoothHealthChannel.close()');
1266     privUtils_.printDeprecationWarningFor('BluetoothHealthChannel');
1267
1268     if (this.isConnected) {
1269         var callArgs = {
1270             channel: this._id,
1271             address: this.peer.address
1272         };
1273
1274         var result = native.callSync('BluetoothHealthChannelClose', callArgs);
1275
1276         if (native.isFailure(result)) {
1277             throw native.getErrorObject(result);
1278         }
1279
1280         Object.defineProperty(this, 'isConnected', { value: false });
1281     }
1282 };
1283
1284 BluetoothHealthChannel.prototype.sendData = function() {
1285     privUtils_.log('Entered BluetoothHealthChannel.sendData()');
1286     privUtils_.printDeprecationWarningFor('BluetoothHealthChannel');
1287
1288     var args = AV.validateArgs(arguments, [
1289         {
1290             name: 'data',
1291             type: AV.Types.ARRAY,
1292             values: AV.Types.BYTE
1293         }
1294     ]);
1295
1296     var callArgs = {
1297         channel: this._id,
1298         data: args.data
1299     };
1300
1301     var result = native.callSync('BluetoothHealthChannelSendData', callArgs);
1302
1303     if (native.isFailure(result)) {
1304         throw native.getErrorObject(result);
1305     } else {
1306         return native.getResultObject(result);
1307     }
1308 };
1309
1310 var _healthListeners = {};
1311
1312 function _BluetoothHealthChannelChangeCallback(event) {
1313     var e = event;
1314     var callback = _healthListeners[e.id];
1315     var d;
1316
1317     switch (e.event) {
1318     case 'onmessage':
1319         d = e.data;
1320         break;
1321
1322     case 'onclose':
1323         break;
1324
1325     default:
1326         privUtils_.log('Unknown mode: ' + e.event);
1327         return;
1328     }
1329
1330     if (callback[e.event]) {
1331         callback[e.event](d);
1332     }
1333 }
1334
1335 var BluetoothHealthChannel_setListener = function() {
1336     privUtils_.log('Entered BluetoothHealthChannel.setListener()');
1337     privUtils_.printDeprecationWarningFor('BluetoothHealthChannel');
1338     privUtils_.checkPrivilegeAccess4Ver(
1339         '2.4',
1340         Privilege.BLUETOOTH,
1341         Privilege.BLUETOOTH_HEALTH
1342     );
1343     var args = AV.validateArgs(arguments, [
1344         {
1345             name: 'changeCallback',
1346             type: AV.Types.LISTENER,
1347             values: ['onmessage', 'onclose']
1348         }
1349     ]);
1350
1351     if (T.isEmptyObject(_healthListeners)) {
1352         native.addListener(
1353             'BluetoothHealthChannelChangeCallback',
1354             _BluetoothHealthChannelChangeCallback
1355         );
1356     }
1357     _healthListeners[this._id] = args.changeCallback;
1358 };
1359
1360 BluetoothHealthChannel.prototype.setListener = function() {
1361     BluetoothHealthChannel_setListener.apply(this, arguments);
1362 };
1363
1364 var BluetoothHealthChannel_unsetListener = function() {
1365     privUtils_.log('Entered BluetoothHealthChannel.unsetListener ()');
1366     privUtils_.printDeprecationWarningFor('BluetoothHealthChannel');
1367     if (T.isEmptyObject(_healthListeners)) {
1368         privUtils_.checkPrivilegeAccess4Ver(
1369             '2.4',
1370             Privilege.BLUETOOTH,
1371             Privilege.BLUETOOTH_HEALTH
1372         );
1373     }
1374
1375     delete _healthListeners[this._id];
1376
1377     if (T.isEmptyObject(_healthListeners)) {
1378         native.removeListener(
1379             'BluetoothHealthChannelChangeCallback',
1380             _BluetoothHealthChannelChangeCallback
1381         );
1382     }
1383 };
1384
1385 BluetoothHealthChannel.prototype.unsetListener = function() {
1386     privUtils_.printDeprecationWarningFor('BluetoothHealthChannel');
1387     BluetoothHealthChannel_unsetListener.apply(this, arguments);
1388 };
1389
1390 /**
1391  * Creates a manager for specified listener event.
1392  *
1393  * @param {string} name - name of the listener this manager handles
1394  * @param {function} callback - function to be invoked when event specified by the name
1395  *                              fires.
1396  *                              This function should return false if the callback
1397  *                              doesn't want to handle the event anymore, true otherwise.
1398  *                              This function should have following signature:
1399  *                              bool callback(event, successCallback, errorCallback);
1400  *
1401  * @return {object} object which allows to add or remove callbacks for specified listener
1402  */
1403 function _singleListenerBuilder(name, callback) {
1404     var listenerName = name;
1405     var successCallback;
1406     var errorCallback;
1407     var callbackFunction = callback;
1408     var listenerRegistered = false;
1409
1410     function innerCallback(event) {
1411         if (!callbackFunction(event, successCallback, errorCallback)) {
1412             removeListener();
1413         }
1414     }
1415
1416     function addListener(s, e) {
1417         successCallback = s;
1418         errorCallback = e;
1419
1420         if (!listenerRegistered) {
1421             native.addListener(listenerName, innerCallback);
1422             listenerRegistered = true;
1423         }
1424     }
1425
1426     function removeListener() {
1427         if (listenerRegistered) {
1428             native.removeListener(listenerName, innerCallback);
1429             listenerRegistered = false;
1430         }
1431
1432         successCallback = undefined;
1433         errorCallback = undefined;
1434     }
1435
1436     return {
1437         addListener: addListener,
1438         removeListener: removeListener
1439     };
1440 }
1441
1442 var _bleScanListener = _singleListenerBuilder('BluetoothLEScanCallback', function(
1443     event,
1444     successCallback,
1445     errorCallback
1446 ) {
1447     var d;
1448     var ret = true;
1449
1450     switch (event.action) {
1451     case 'onsuccess':
1452         d = new BluetoothLEDevice(event.data);
1453         break;
1454
1455     case 'onerror':
1456         if (errorCallback) {
1457             errorCallback(native.getErrorObject(event));
1458         }
1459         return ret;
1460
1461     default:
1462         privUtils_.log('Unknown mode: ' + event.action);
1463         return ret;
1464     }
1465     if (successCallback) {
1466         successCallback(d);
1467     }
1468
1469     return ret;
1470 });
1471
1472 var _bleAdvertiseListener = _singleListenerBuilder(
1473     'BluetoothLEAdvertiseCallback',
1474     function(event, successCallback, errorCallback) {
1475         var d;
1476         var ret = true;
1477
1478         switch (event.action) {
1479         case 'onstate':
1480             if (successCallback) {
1481                 successCallback(native.getResultObject(event));
1482                 if (native.getResultObject(event) == 'STOPPED') {
1483                     _bleAdvertiseListener.removeListener();
1484                 }
1485             }
1486             return ret;
1487
1488         case 'onerror':
1489             if (errorCallback) {
1490                 errorCallback(native.getErrorObject(event));
1491             }
1492             return ret;
1493
1494         default:
1495             privUtils_.log('Unknown mode: ' + event.action);
1496             return ret;
1497         }
1498     }
1499 );
1500
1501 //class BluetoothLEAdapter ///////////////////////////
1502 var BluetoothLEAdapter = function() {};
1503
1504 BluetoothLEAdapter.prototype.startScan = function() {
1505     privUtils_.log('Entered BluetoothLEAdapter.startScan()');
1506     var args = AV.validateArgs(arguments, [
1507         {
1508             name: 'successCallback',
1509             type: AV.Types.FUNCTION
1510         },
1511         {
1512             name: 'errorCallback',
1513             type: AV.Types.FUNCTION,
1514             optional: true,
1515             nullable: true
1516         }
1517     ]);
1518
1519     var result = native.callSync('BluetoothLEAdapterStartScan', {});
1520     if (native.isFailure(result)) {
1521         throw native.getErrorObject(result);
1522     }
1523
1524     _bleScanListener.addListener(args.successCallback, args.errorCallback);
1525 };
1526
1527 BluetoothLEAdapter.prototype.stopScan = function() {
1528     privUtils_.log('Entered BluetoothLEAdapter.stopScan()');
1529
1530     _bleScanListener.removeListener();
1531
1532     var result = native.callSync('BluetoothLEAdapterStopScan', {});
1533     if (native.isFailure(result)) {
1534         throw native.getErrorObject(result);
1535     }
1536 };
1537
1538 var _BluetoothAdvertisePacketType = {
1539     ADVERTISE: 'ADVERTISE',
1540     SCAN_RESPONSE: 'SCAN_RESPONSE'
1541 };
1542
1543 var _BluetoothAdvertisingMode = {
1544     BALANCED: 'BALANCED',
1545     LOW_LATENCY: 'LOW_LATENCY',
1546     LOW_ENERGY: 'LOW_ENERGY'
1547 };
1548
1549 BluetoothLEAdapter.prototype.startAdvertise = function() {
1550     privUtils_.log('Entered BluetoothLEAdapter.startAdvertise()');
1551     var args = AV.validateArgs(arguments, [
1552         {
1553             name: 'advertiseData',
1554             type: AV.Types.PLATFORM_OBJECT,
1555             values: tizen.BluetoothLEAdvertiseData
1556         },
1557         {
1558             name: 'packetType',
1559             type: AV.Types.ENUM,
1560             values: T.getValues(_BluetoothAdvertisePacketType)
1561         },
1562         {
1563             name: 'successCallback',
1564             type: AV.Types.FUNCTION
1565         },
1566         {
1567             name: 'errorCallback',
1568             type: AV.Types.FUNCTION,
1569             optional: true,
1570             nullable: true
1571         },
1572         {
1573             name: 'mode',
1574             type: AV.Types.ENUM,
1575             values: T.getValues(_BluetoothAdvertisingMode),
1576             optional: true,
1577             nullable: true
1578         },
1579         {
1580             name: 'connectable',
1581             type: AV.Types.BOOLEAN,
1582             optional: true,
1583             nullable: true
1584         }
1585     ]);
1586
1587     var callArgs = {
1588         advertiseData: args.advertiseData,
1589         packetType: args.packetType,
1590         mode: T.isNullOrUndefined(args.mode)
1591             ? _BluetoothAdvertisingMode.BALANCED
1592             : args.mode,
1593         connectable: T.isNullOrUndefined(args.connectable) ? true : args.connectable
1594     };
1595
1596     var result = native.callSync('BluetoothLEAdapterStartAdvertise', callArgs);
1597
1598     if (native.isFailure(result)) {
1599         throw native.getErrorObject(result);
1600     }
1601
1602     _bleAdvertiseListener.addListener(args.successCallback, args.errorCallback);
1603 };
1604
1605 BluetoothLEAdapter.prototype.stopAdvertise = function() {
1606     privUtils_.log('Entered BluetoothLEAdapter.stopAdvertise()');
1607
1608     var result = native.callSync('BluetoothLEAdapterStopAdvertise', {});
1609
1610     if (native.isFailure(result)) {
1611         throw native.getErrorObject(result);
1612     }
1613 };
1614
1615 BluetoothLEAdapter.prototype.addConnectStateChangeListener = function() {
1616     privUtils_.log('Entered BluetoothLEAdapter.addClientConnectStateChaneListener()');
1617     var args = AV.validateArgs(arguments, [
1618         {
1619             name: 'listener',
1620             type: AV.Types.LISTENER,
1621             values: ['onconnected', 'ondisconnected']
1622         }
1623     ]);
1624
1625     var func = function(event) {
1626         if (args.listener[event.action]) {
1627             args.listener[event.action](new BluetoothLEDevice(event.address));
1628         }
1629     };
1630
1631     return _bleConnectChangeListener.addListener(func);
1632 };
1633
1634 BluetoothLEAdapter.prototype.removeConnectStateChangeListener = function() {
1635     privUtils_.log('Entered BluetoothLEAdapter.removeConnectStateChangeListener()');
1636
1637     var args = AV.validateArgs(arguments, [
1638         {
1639             name: 'watchID',
1640             type: AV.Types.LONG
1641         }
1642     ]);
1643
1644     _bleConnectChangeListener.removeListener(args.watchID);
1645 };
1646
1647 //class BluetoothGATTService ///////////////////////////
1648 var BluetoothGATTService = function(data, address) {
1649     var handle_ = data.handle;
1650     var uuid_ = data.uuid;
1651     var serviceUuid_ = data.serviceUuid;
1652     //address_ is needed to control if device is still connected
1653     var address_ = address || data.address;
1654
1655     function servicesGetter() {
1656         var services = [];
1657         var result = native.callSync('BluetoothGATTClientServiceGetServices', {
1658             handle: handle_,
1659             address: address_
1660         });
1661         if (native.isSuccess(result)) {
1662             var resultObject = native.getResultObject(result);
1663             resultObject.forEach(function(s) {
1664                 services.push(new BluetoothGATTService(s, address_));
1665             });
1666         }
1667         return services;
1668     }
1669     function characteristicsGetter() {
1670         var characteristics = [];
1671         var result = native.callSync('BluetoothGATTClientServiceGetCharacteristics', {
1672             handle: handle_,
1673             uuid: serviceUuid_,
1674             address: address_
1675         });
1676         if (native.isSuccess(result)) {
1677             var resultObject = native.getResultObject(result);
1678             resultObject.forEach(function(c) {
1679               if (!T.isNullOrUndefined(c)) {
1680                 characteristics.push(new BluetoothGATTCharacteristic(c, address_));
1681               }
1682             });
1683         }
1684         return characteristics;
1685     }
1686
1687     /*
1688      * If this function is called as a constructor of BluetoothGATTServerService's
1689      * base class, some properties have to be left configurable, to enable redefinition.
1690      * Otherwise, if this function is called to create BluetoothGATTService,
1691      * they are left non-configurable.
1692      *
1693      * If BluetoothGATTService will be a base for any other class in the future,
1694      * the line below may have to be updated to take into account the new class.
1695      */
1696     var isConfigurable = this instanceof BluetoothGATTServerService;
1697
1698     Object.defineProperties(this, {
1699         uuid: { value: uuid_, writable: false, enumerable: true },
1700         serviceUuid: { value: serviceUuid_, writable: false, enumerable: true },
1701         services: {
1702             enumerable: true,
1703             configurable: isConfigurable,
1704             set: function() {},
1705             get: servicesGetter
1706         },
1707         characteristics: {
1708             enumerable: true,
1709             configurable: isConfigurable,
1710             set: function() {},
1711             get: characteristicsGetter
1712         }
1713     });
1714 };
1715
1716 var CurrentGATTServerEntityId = 0;
1717 function NextGattServerEntityID() {
1718     return ++CurrentGATTServerEntityId;
1719 }
1720
1721 function IsUuidValid(uuid) {
1722     return (
1723         BluetoothManager_UUIDIsValid16Bit(uuid) ||
1724         BluetoothManager_UUIDIsValid32Bit(uuid) ||
1725         BluetoothManager_UUIDIsValid128Bit(uuid)
1726     );
1727 }
1728
1729 var ValidateBluetoothGATTServerServiceInit = function(initData) {
1730     initData = AV.validateArgs(
1731         [
1732             initData['serviceUuid'],
1733             initData['isPrimary'] || true,
1734             initData['includedServices'] || [],
1735             initData['characteristics'] || []
1736         ],
1737         [
1738             {
1739                 name: 'serviceUuid',
1740                 type: AV.Types.STRING,
1741                 validator: IsUuidValid
1742             },
1743             {
1744                 name: 'isPrimary',
1745                 type: AV.Types.BOOLEAN
1746             },
1747             {
1748                 name: 'includedServices',
1749                 type: AV.Types.ARRAY
1750             },
1751             {
1752                 name: 'characteristics',
1753                 type: AV.Types.ARRAY
1754             }
1755         ]
1756     );
1757
1758     // "uuid" field is used to construct BluetoothGATTService, but it's not a part
1759     // of BluetoothGATTServerServiceInit dictionary.
1760     // In case of BluetoothGATTServerServices, its value is always the same as
1761     // serviceUuid's.
1762     initData.uuid = initData.serviceUuid;
1763     return initData;
1764 };
1765
1766 //class BluetoothGATTServerService ///////////////////////////
1767 var BluetoothGATTServerService = function(data) {
1768     data = ValidateBluetoothGATTServerServiceInit(data);
1769
1770     BluetoothGATTService.call(this, data, null);
1771
1772     var services_ = data.includedServices.map(function(serviceData) {
1773         return new BluetoothGATTServerService(serviceData, null);
1774     });
1775     var characteristics_ = data.characteristics.map(function(characteristicData) {
1776         return new BluetoothGATTServerCharacteristic(characteristicData, null);
1777     });
1778
1779     Object.defineProperties(this, {
1780         isPrimary: { value: data.isPrimary, writable: false, enumerable: true },
1781         services: {
1782             enumerable: true,
1783             get: function() {
1784                 return services_.slice();
1785             },
1786             set: function() {}
1787         },
1788         characteristics: {
1789             enumerable: true,
1790             get: function() {
1791                 return characteristics_.slice();
1792             },
1793             set: function() {}
1794         },
1795         // This property is "private" and meant not to be used by users
1796         _id: {
1797             // It has to be enumerable, to be serialized with JSON.stringify()
1798             enumerable: true,
1799             value: NextGattServerEntityID()
1800         }
1801     });
1802 };
1803
1804 BluetoothGATTServerService.prototype = Object.create(BluetoothGATTService.prototype);
1805
1806 Object.defineProperty(BluetoothGATTServerService.prototype, 'constructor', {
1807     value: BluetoothGATTServerService,
1808     enumerable: false,
1809     writable: true
1810 });
1811
1812 var numberArrayToByteArray = function(array) {
1813     var d = [];
1814
1815     array.forEach(function(b) {
1816         d.push(Converter.toOctet(b));
1817     });
1818     return d;
1819 };
1820
1821 //class BluetoothGATTCharacteristic ///////////////////////////
1822 var BluetoothGATTCharacteristic = function(data, address) {
1823     if (!T.isObject(data)) {
1824       return null;
1825     }
1826     var address_ = address;
1827     var handle_ = data.handle;
1828     var descriptors_ = data.descriptors.map(function(descriptor_data) {
1829         return new BluetoothGATTDescriptor(descriptor_data, address_);
1830       });
1831     var isBroadcast_ = data.isBroadcast;
1832     var hasExtendedProperties_ = data.hasExtendedProperties;
1833     var isNotify_ = data.isNotify;
1834     var isIndication_ = data.isIndication;
1835     var isReadable_ = data.isReadable;
1836     var isSignedWrite_ = data.isSignedWrite;
1837     var isWritable_ = data.isWritable;
1838     var isWriteNoResponse_ = data.isWriteNoResponse;
1839     var uuid_ = data.uuid;
1840
1841     /*
1842      * If this function is called as a constructor of BluetoothGATTServerCharacteristic's
1843      * base class, some properties have to be left configurable, to enable redefinition.
1844      * Otherwise, if this function is called to create BluetoothGATTCharacteristic,
1845      * they are left non-configurable.
1846      *
1847      * If BluetoothGATTCharacteristic will be a base for any other class in the future,
1848      * the line below may have to be updated to take into account the new class.
1849      */
1850     var isConfigurable = this instanceof BluetoothGATTServerCharacteristic;
1851
1852     Object.defineProperties(this, {
1853         descriptors: {
1854             enumerable: true,
1855             configurable: isConfigurable,
1856             get: function() {
1857                 return descriptors_.slice();
1858             },
1859             set: function() {}
1860         },
1861         isBroadcast: {
1862             enumerable: true,
1863             get: function() {
1864                 return isBroadcast_;
1865             },
1866             set: function() {}
1867         },
1868         hasExtendedProperties: {
1869             enumerable: true,
1870             get: function() {
1871                 return hasExtendedProperties_;
1872             },
1873             set: function() {}
1874         },
1875         isNotify: {
1876             enumerable: true,
1877             get: function() {
1878                 return isNotify_;
1879             },
1880             set: function() {}
1881         },
1882         isIndication: {
1883             enumerable: true,
1884             get: function() {
1885                 return isIndication_;
1886             },
1887             set: function() {}
1888         },
1889         isReadable: {
1890             enumerable: true,
1891             get: function() {
1892                 return isReadable_;
1893             },
1894             set: function() {}
1895         },
1896         isSignedWrite: {
1897             enumerable: true,
1898             get: function() {
1899                 return isSignedWrite_;
1900             },
1901             set: function() {}
1902         },
1903         isWritable: {
1904             enumerable: true,
1905             get: function() {
1906                 return isWritable_;
1907             },
1908             set: function() {}
1909         },
1910         isWriteNoResponse: {
1911             enumerable: true,
1912             get: function() {
1913                 return isWriteNoResponse_;
1914             },
1915             set: function() {}
1916         },
1917         uuid: {
1918             enumerable: true,
1919             get: function() {
1920                 return uuid_;
1921             },
1922             set: function() {}
1923         }
1924     });
1925
1926     this.readValue = function() {
1927         privUtils_.log('Entered BluetoothGATTCharacteristic.readValue()');
1928         var args = AV.validateArgs(arguments, [
1929             {
1930                 name: 'successCallback',
1931                 type: AV.Types.FUNCTION
1932             },
1933             {
1934                 name: 'errorCallback',
1935                 type: AV.Types.FUNCTION,
1936                 optional: true,
1937                 nullable: true
1938             }
1939         ]);
1940
1941         var callback = function(result) {
1942             if (native.isFailure(result)) {
1943                 native.callIfPossible(args.errorCallback, native.getErrorObject(result));
1944             } else {
1945                 var d = numberArrayToByteArray(native.getResultObject(result));
1946                 args.successCallback(d);
1947             }
1948         };
1949
1950         var callArgs = { handle: handle_, address: address_ };
1951
1952         var result = native.call('BluetoothGATTClientServiceReadValue', callArgs, callback);
1953
1954         if (native.isFailure(result)) {
1955             throw native.getErrorObject(result);
1956         }
1957     };
1958
1959     this.writeValue = function(value, successCallback, errorCallback) {
1960         privUtils_.log('Entered BluetoothGATTCharacteristic.writeValue()');
1961         var args = AV.validateArgs([successCallback, errorCallback], [
1962             {
1963                 name: 'successCallback',
1964                 type: AV.Types.FUNCTION,
1965                 optional: true,
1966                 nullable: true
1967             },
1968             {
1969                 name: 'errorCallback',
1970                 type: AV.Types.FUNCTION,
1971                 optional: true,
1972                 nullable: true
1973             }
1974         ]);
1975
1976         var callback = function(result) {
1977             if (native.isFailure(result)) {
1978                 native.callIfPossible(args.errorCallback, native.getErrorObject(result));
1979             } else {
1980                 native.callIfPossible(args.successCallback);
1981             }
1982         };
1983
1984         var callArgs = {
1985             handle: handle_,
1986             value: BluetoothManager_toByteArray(value),
1987             address: address_
1988         };
1989
1990         var result = native.call('BluetoothGATTClientServiceWriteValue', callArgs, callback);
1991
1992         if (native.isFailure(result)) {
1993             throw native.getErrorObject(result);
1994         }
1995     };
1996
1997     var addValueChangeListener = function() {
1998         privUtils_.log('Entered BluetoothGATTCharacteristic.addValueChangeListener()');
1999         privUtils_.checkPrivilegeAccess4Ver(
2000             '2.4',
2001             Privilege.BLUETOOTH,
2002             Privilege.BLUETOOTH_ADMIN
2003         );
2004         var args = AV.validateArgs(arguments, [
2005             {
2006                 name: 'callback',
2007                 type: AV.Types.FUNCTION
2008             }
2009         ]);
2010
2011         var callArgs = { handle: handle_, address: address_ };
2012
2013         var callback = function(event) {
2014             if (event.handle === handle_) {
2015                 args.callback(numberArrayToByteArray(native.getResultObject(event)));
2016             }
2017         };
2018
2019         return _bluetoothGATTCharacteristicListener.addListener(callback, callArgs);
2020     };
2021
2022     this.addValueChangeListener = function() {
2023         return addValueChangeListener.apply(this, arguments);
2024     };
2025
2026     this.removeValueChangeListener = function() {
2027         privUtils_.log('Entered BluetoothGATTCharacteristic.removeValueChangeListener()');
2028
2029         var args = AV.validateArgs(arguments, [
2030             {
2031                 name: 'watchID',
2032                 type: AV.Types.LONG
2033             }
2034         ]);
2035
2036         var callArgs = { handle: handle_, address: address_ };
2037
2038         return _bluetoothGATTCharacteristicListener.removeListener(
2039             args.watchID,
2040             callArgs
2041         );
2042     };
2043 };
2044
2045 var ValidateBluetoothGATTServerCharacteristicInit = function(initData) {
2046     return AV.validateArgs(
2047         [
2048             initData['uuid'],
2049             initData['descriptors'] || [],
2050             initData['isBroadcast'] || false,
2051             initData['hasExtendedProperties'] || false,
2052             initData['isNotify'] || false,
2053             initData['isIndication'] || false,
2054             initData['isReadable'] || false,
2055             initData['isSignedWrite'] || false,
2056             initData['isWritable'] || false,
2057             initData['isWriteNoResponse'] || false,
2058             initData['readPermission'] || false,
2059             initData['writePermission'] || false,
2060             initData['encryptedReadPermission'] || false,
2061             initData['encryptedWritePermission'] || false,
2062             initData['encryptedSignedReadPermission'] || false,
2063             initData['encryptedSignedWritePermission'] || false,
2064             initData['readValueRequestCallback'] || null,
2065             initData['writeValueRequestCallback'] || null
2066         ],
2067         [
2068             {
2069                 name: 'uuid',
2070                 type: AV.Types.STRING,
2071                 validator: IsUuidValid
2072             },
2073             {
2074                 name: 'descriptors',
2075                 type: AV.Types.ARRAY
2076             },
2077             {
2078                 name: 'isBroadcast',
2079                 type: AV.Types.BOOLEAN
2080             },
2081             {
2082                 name: 'hasExtendedProperties',
2083                 type: AV.Types.BOOLEAN
2084             },
2085             {
2086                 name: 'isNotify',
2087                 type: AV.Types.BOOLEAN
2088             },
2089             {
2090                 name: 'isIndication',
2091                 type: AV.Types.BOOLEAN
2092             },
2093             {
2094                 name: 'isReadable',
2095                 type: AV.Types.BOOLEAN
2096             },
2097             {
2098                 name: 'isSignedWrite',
2099                 type: AV.Types.BOOLEAN
2100             },
2101             {
2102                 name: 'isWritable',
2103                 type: AV.Types.BOOLEAN
2104             },
2105             {
2106                 name: 'isWriteNoResponse',
2107                 type: AV.Types.BOOLEAN
2108             },
2109             {
2110                 name: 'readPermission',
2111                 type: AV.Types.BOOLEAN
2112             },
2113             {
2114                 name: 'writePermission',
2115                 type: AV.Types.BOOLEAN
2116             },
2117             {
2118                 name: 'encryptedReadPermission',
2119                 type: AV.Types.BOOLEAN
2120             },
2121             {
2122                 name: 'encryptedWritePermission',
2123                 type: AV.Types.BOOLEAN
2124             },
2125             {
2126                 name: 'encryptedSignedReadPermission',
2127                 type: AV.Types.BOOLEAN
2128             },
2129             {
2130                 name: 'encryptedSignedWritePermission',
2131                 type: AV.Types.BOOLEAN
2132             },
2133             {
2134                 name: 'readValueRequestCallback',
2135                 type: AV.Types.FUNCTION,
2136                 nullable: true
2137             },
2138             {
2139                 name: 'writeValueRequestCallback',
2140                 type: AV.Types.FUNCTION,
2141                 nullable: true
2142             }
2143         ]
2144     );
2145 };
2146
2147 //class BluetoothGATTServerCharacteristic ///////////////////////////
2148 var BluetoothGATTServerCharacteristic = function(data) {
2149     data = ValidateBluetoothGATTServerCharacteristicInit(data);
2150
2151     BluetoothGATTCharacteristic.call(this, data, null);
2152
2153     var descriptors_ = data.descriptors.map(function(descriptor_data) {
2154         return new BluetoothGATTServerDescriptor(descriptor_data, null);
2155     });
2156
2157     Object.defineProperties(this, {
2158         descriptors: {
2159             enumerable: true,
2160             get: function() {
2161                 return descriptors_.slice();
2162             },
2163             set: function() {}
2164         },
2165         readPermission: {
2166             enumerable: true,
2167             get: function() {
2168                 return data.readPermission;
2169             },
2170             set: function() {}
2171         },
2172         writePermission: {
2173             enumerable: true,
2174             get: function() {
2175                 return data.writePermission;
2176             },
2177             set: function() {}
2178         },
2179         encryptedReadPermission: {
2180             enumerable: true,
2181             get: function() {
2182                 return data.encryptedReadPermission;
2183             },
2184             set: function() {}
2185         },
2186         encryptedWritePermission: {
2187             enumerable: true,
2188             get: function() {
2189                 return data.encryptedWritePermission;
2190             },
2191             set: function() {}
2192         },
2193         encryptedSignedReadPermission: {
2194             enumerable: true,
2195             get: function() {
2196                 return data.encryptedSignedReadPermission;
2197             },
2198             set: function() {}
2199         },
2200         encryptedSignedWritePermission: {
2201             enumerable: true,
2202             get: function() {
2203                 return data.encryptedSignedWritePermission;
2204             },
2205             set: function() {}
2206         },
2207         // This property is "private" and meant not to be used by users
2208         _id: {
2209             // It has to be enumerable, to be serialized with JSON.stringify()
2210             enumerable: true,
2211             value: NextGattServerEntityID()
2212         }
2213     });
2214
2215     this.readValue = function() {
2216         throw new WebAPIException(
2217             'NotSupportedError',
2218             'This method cannot be called on BluetoothGATTServerCharacteristic'
2219         );
2220     };
2221
2222     this.writeValue = function() {
2223         throw new WebAPIException(
2224             'NotSupportedError',
2225             'This method cannot be called on BluetoothGATTServerCharacteristic'
2226         );
2227     };
2228
2229     this.addValueChangeListener = function() {
2230         throw new WebAPIException(
2231             'NotSupportedError',
2232             'This method cannot be called on BluetoothGATTServerCharacteristic'
2233         );
2234     };
2235
2236     this.removeValueChangeListener = function() { /* Intended no operation */ };
2237 };
2238
2239 BluetoothGATTServerCharacteristic.prototype = Object.create(BluetoothGATTCharacteristic.prototype);
2240
2241 Object.defineProperty(BluetoothGATTServerCharacteristic.prototype, 'constructor', {
2242     value: BluetoothGATTServerCharacteristic,
2243     enumerable: false,
2244     writable: true
2245 });
2246
2247 /**
2248  * Creates a manager for specified listener event. Manager handles multiple
2249  * registered listeners
2250  *
2251  * @param {string} name - name of the listener this manager handles
2252  * @param {function} callback - function to be invoked when event specified by the name
2253  *                              fires.
2254  *                              This function should have following signature:
2255  *                              void callback(listener, event);
2256  * @param {string} addListenerId - optional parameter. If specified, this native
2257  *                                 method will be called synchronously when
2258  *                                 listener is added.
2259  * @param {string} removeListenerId - optional parameter. If specified, this native
2260  *                                 method will be called synchronously when
2261  *                                 listener is removed.
2262  * @param {bool} repeatNativeCall - optional parameter. If specified, the addListenerId
2263  *                                 and removeListenerId methods will be called
2264  *                                 synchronously each time listener is added/removed.
2265  *                                 Otherwise they are going to be called just once: when
2266  *                                 first listener is added and last listener is removed.
2267  *
2268  * @return {object} object which allows to add or remove callbacks for specified listener
2269  */
2270 function _multipleListenerBuilder(
2271     name,
2272     callback,
2273     addListenerId,
2274     removeListenerId,
2275     repeatNativeCall
2276 ) {
2277     var listenerName = name;
2278     var addId = addListenerId;
2279     var removeId = removeListenerId;
2280     var callbackFunction = callback;
2281     var listeners = {};
2282     var nextId = 1;
2283     var jsListenerRegistered = false;
2284     var nativeListenerRegistered = false;
2285     var repeatNativeListenerCall = repeatNativeCall;
2286
2287     function innerCallback(event) {
2288         for (var watchId in listeners) {
2289             if (listeners.hasOwnProperty(watchId)) {
2290                 callbackFunction(listeners[watchId], event);
2291             }
2292         }
2293     }
2294
2295     function addListener(callback, args) {
2296         var id = ++nextId;
2297
2298         if (addId && (!nativeListenerRegistered || repeatNativeListenerCall)) {
2299             var result = native.callSync(addId, args || {});
2300             if (native.isFailure(result)) {
2301                 throw native.getErrorObject(result);
2302             }
2303             nativeListenerRegistered = true;
2304         }
2305
2306         if (!jsListenerRegistered) {
2307             native.addListener(listenerName, innerCallback);
2308             jsListenerRegistered = true;
2309         }
2310
2311         listeners[id] = callback;
2312         return id;
2313     }
2314
2315     function removeListener(watchId, args) {
2316         if (listeners.hasOwnProperty(watchId)) {
2317             delete listeners[watchId];
2318         }
2319
2320         if (
2321             removeId &&
2322             ((nativeListenerRegistered && T.isEmptyObject(listeners)) ||
2323                 repeatNativeListenerCall)
2324         ) {
2325             var result = native.callSync(removeId, args || {});
2326             if (native.isFailure(result)) {
2327                 throw native.getErrorObject(result);
2328             }
2329             nativeListenerRegistered = false;
2330         }
2331
2332         if (jsListenerRegistered && T.isEmptyObject(listeners)) {
2333             native.removeListener(listenerName, innerCallback);
2334             jsListenerRegistered = false;
2335         }
2336     }
2337
2338     return {
2339         addListener: addListener,
2340         removeListener: removeListener
2341     };
2342 }
2343
2344 var _bluetoothGATTCharacteristicListener = _multipleListenerBuilder(
2345     'BluetoothGATTCharacteristicValueChangeListener',
2346     function(listener, event) {
2347         listener(event);
2348     },
2349     'BluetoothGATTClientServiceAddValueChangeListener',
2350     'BluetoothGATTClientServiceRemoveValueChangeListener',
2351     true
2352 );
2353
2354
2355 /*
2356  * This object is used by:
2357  * - BluetoothLEDevice.addConnectStateChangeListener()
2358  * - BluetoothLEAdapter.addConnectStateChangeListener()
2359  */
2360 var _bleConnectChangeListener = _multipleListenerBuilder(
2361     'BluetoothLEConnectChangeCallback',
2362     function(listener, event) {
2363         listener(event);
2364     },
2365     'BluetoothLEDeviceAddConnectStateChangeListener',
2366     'BluetoothLEDeviceRemoveConnectStateChangeListener'
2367 );
2368
2369 //class BluetoothGATTDescriptor ///////////////////////////
2370 var BluetoothGATTDescriptor = function(data, address) {
2371     var handle_ = data.handle;
2372     //address_ is needed to control if device is still connected
2373     var address_ = address;
2374     var uuid_ = data.uuid;
2375
2376     this.readValue = function() {
2377         privUtils_.log('Entered BluetoothGATTDescriptor.readValue()');
2378         var args = AV.validateArgs(arguments, [
2379             {
2380                 name: 'successCallback',
2381                 type: AV.Types.FUNCTION
2382             },
2383             {
2384                 name: 'errorCallback',
2385                 type: AV.Types.FUNCTION,
2386                 optional: true,
2387                 nullable: true
2388             }
2389         ]);
2390
2391         var callback = function(result) {
2392             if (native.isFailure(result)) {
2393                 native.callIfPossible(args.errorCallback, native.getErrorObject(result));
2394             } else {
2395                 var d = numberArrayToByteArray(native.getResultObject(result));
2396                 args.successCallback(d);
2397             }
2398         };
2399
2400         var callArgs = { handle: handle_, address: address_ };
2401
2402         var result = native.call('BluetoothGATTClientServiceReadValue', callArgs, callback);
2403
2404         if (native.isFailure(result)) {
2405             throw native.getErrorObject(result);
2406         }
2407     };
2408
2409     this.writeValue = function(value, successCallback, errorCallback) {
2410         privUtils_.log('Entered BluetoothGATTDescriptor.writeValue()');
2411         var args = AV.validateArgs([successCallback, errorCallback], [
2412             {
2413                 name: 'successCallback',
2414                 type: AV.Types.FUNCTION,
2415                 optional: true,
2416                 nullable: true
2417             },
2418             {
2419                 name: 'errorCallback',
2420                 type: AV.Types.FUNCTION,
2421                 optional: true,
2422                 nullable: true
2423             }
2424         ]);
2425
2426         var callback = function(result) {
2427             if (native.isFailure(result)) {
2428                 native.callIfPossible(args.errorCallback, native.getErrorObject(result));
2429             } else {
2430                 native.callIfPossible(args.successCallback);
2431             }
2432         };
2433
2434         var callArgs = {
2435             handle: handle_,
2436             value: BluetoothManager_toByteArray(value),
2437             address: address_
2438         };
2439
2440         var result = native.call('BluetoothGATTClientServiceWriteValue', callArgs, callback);
2441
2442         if (native.isFailure(result)) {
2443             throw native.getErrorObject(result);
2444         }
2445     };
2446
2447     Object.defineProperties(this, {
2448         uuid: {
2449             enumerable: true,
2450             get: function() {
2451                 return uuid_;
2452             },
2453             set: function() {}
2454         }
2455     });
2456 };
2457
2458 var ValidateBluetoothGATTServerDescriptorInit = function(initData) {
2459     return AV.validateArgs(
2460         [
2461             initData['uuid'],
2462             initData['readPermission'] || false,
2463             initData['writePermission'] || false,
2464             initData['encryptedReadPermission'] || false,
2465             initData['encryptedWritePermission'] || false,
2466             initData['encryptedSignedReadPermission'] || false,
2467             initData['encryptedSignedWritePermission'] || false,
2468             initData['readValueRequestCallback'] || null,
2469             initData['writeValueRequestCallback'] || null
2470         ],
2471         [
2472             {
2473                 name: 'uuid',
2474                 type: AV.Types.STRING,
2475                 validator: IsUuidValid
2476             },
2477             {
2478                 name: 'readPermission',
2479                 type: AV.Types.BOOLEAN
2480             },
2481             {
2482                 name: 'writePermission',
2483                 type: AV.Types.BOOLEAN
2484             },
2485             {
2486                 name: 'encryptedReadPermission',
2487                 type: AV.Types.BOOLEAN
2488             },
2489             {
2490                 name: 'encryptedWritePermission',
2491                 type: AV.Types.BOOLEAN
2492             },
2493             {
2494                 name: 'encryptedSignedReadPermission',
2495                 type: AV.Types.BOOLEAN
2496             },
2497             {
2498                 name: 'encryptedSignedWritePermission',
2499                 type: AV.Types.BOOLEAN
2500             },
2501             {
2502                 name: 'readValueRequestCallback',
2503                 type: AV.Types.FUNCTION,
2504                 nullable: true
2505             },
2506             {
2507                 name: 'writeValueRequestCallback',
2508                 type: AV.Types.FUNCTION,
2509                 nullable: true
2510             }
2511         ]
2512     );
2513 };
2514
2515 var BluetoothGATTServerDescriptor = function(data, address) {
2516     data = ValidateBluetoothGATTServerDescriptorInit(data);
2517
2518     BluetoothGATTDescriptor.call(this, data, null);
2519
2520     Object.defineProperties(this, {
2521         readPermission: {
2522             enumerable: true,
2523             get: function() {
2524                 return data.readPermission;
2525             },
2526             set: function() {}
2527         },
2528         writePermission: {
2529             enumerable: true,
2530             get: function() {
2531                 return data.writePermission;
2532             },
2533             set: function() {}
2534         },
2535         encryptedReadPermission: {
2536             enumerable: true,
2537             get: function() {
2538                 return data.encryptedReadPermission;
2539             },
2540             set: function() {}
2541         },
2542         encryptedWritePermission: {
2543             enumerable: true,
2544             get: function() {
2545                 return data.encryptedWritePermission;
2546             },
2547             set: function() {}
2548         },
2549         encryptedSignedReadPermission: {
2550             enumerable: true,
2551             get: function() {
2552                 return data.encryptedSignedReadPermission;
2553             },
2554             set: function() {}
2555         },
2556         encryptedSignedWritePermission: {
2557             enumerable: true,
2558             get: function() {
2559                 return data.encryptedSignedWritePermission;
2560             },
2561             set: function() {}
2562         },
2563         // This property is "private" and meant not to be used by users
2564         _id: {
2565             // It has to be enumerable, to be serialized with JSON.stringify()
2566             enumerable: true,
2567             value: NextGattServerEntityID()
2568         }
2569     });
2570
2571     this.readValue = function() {
2572         throw new WebAPIException(
2573             'NotSupportedError',
2574             'This method cannot be called on BluetoothGATTServerDescriptor'
2575         );
2576     };
2577
2578     this.writeValue = function() {
2579         throw new WebAPIException(
2580             'NotSupportedError',
2581             'This method cannot be called on BluetoothGATTServerDescriptor'
2582         );
2583     };
2584 };
2585
2586 BluetoothGATTServerDescriptor.prototype = Object.create(BluetoothGATTDescriptor.prototype);
2587
2588 Object.defineProperty(BluetoothGATTServerDescriptor.prototype, 'constructor', {
2589     value: BluetoothGATTServerDescriptor,
2590     enumerable: false,
2591     writable: true
2592 });
2593
2594 // class BluetoothAdapter ///////////////////////////
2595 var BluetoothAdapter = function() {
2596     function nameGetter() {
2597         var result = native.callSync('BluetoothAdapterGetName', {});
2598
2599         if (native.isFailure(result)) {
2600             return '';
2601         } else {
2602             return native.getResultObject(result);
2603         }
2604     }
2605
2606     function addressGetter() {
2607         var result = native.callSync('BluetoothAdapterGetAddress', {});
2608
2609         if (native.isFailure(result)) {
2610             return '';
2611         } else {
2612             return native.getResultObject(result);
2613         }
2614     }
2615
2616     function poweredGetter() {
2617         var result = native.callSync('BluetoothAdapterGetPowered', {});
2618
2619         if (native.isFailure(result)) {
2620             return false;
2621         } else {
2622             return native.getResultObject(result);
2623         }
2624     }
2625
2626     function visibleGetter() {
2627         var result = native.callSync('BluetoothAdapterGetVisible', {});
2628
2629         if (native.isFailure(result)) {
2630             return false;
2631         } else {
2632             return native.getResultObject(result);
2633         }
2634     }
2635
2636     Object.defineProperties(this, {
2637         name: {
2638             enumerable: true,
2639             set: function() {},
2640             get: nameGetter
2641         },
2642         address: {
2643             enumerable: true,
2644             set: function() {},
2645             get: addressGetter
2646         },
2647         powered: {
2648             enumerable: true,
2649             set: function() {},
2650             get: poweredGetter
2651         },
2652         visible: {
2653             enumerable: true,
2654             set: function() {},
2655             get: visibleGetter
2656         }
2657     });
2658 };
2659
2660 BluetoothAdapter.prototype.setName = function() {
2661     privUtils_.log('Entered BluetoothAdapter.setName()');
2662     var args = AV.validateArgs(arguments, [
2663         {
2664             name: 'name',
2665             type: AV.Types.STRING
2666         },
2667         {
2668             name: 'successCallback',
2669             type: AV.Types.FUNCTION,
2670             optional: true,
2671             nullable: true
2672         },
2673         {
2674             name: 'errorCallback',
2675             type: AV.Types.FUNCTION,
2676             optional: true,
2677             nullable: true
2678         }
2679     ]);
2680
2681     var callArgs = {
2682         name: args.name
2683     };
2684
2685     var callback = function(result) {
2686         if (native.isFailure(result)) {
2687             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
2688         } else {
2689             native.callIfPossible(args.successCallback);
2690         }
2691     };
2692
2693     var result = native.call('BluetoothAdapterSetName', callArgs, callback);
2694     if (native.isFailure(result)) {
2695         throw native.getErrorObject(result);
2696     }
2697 };
2698
2699 BluetoothAdapter.prototype.setPowered = function() {
2700     privUtils_.log('Entered BluetoothAdapter.setPowered()');
2701     privUtils_.printDeprecationWarningFor('setPowered()');
2702     privUtils_.warn(
2703         'Let the user turn on/off Bluetooth through the Settings application instead.'
2704     );
2705
2706     var args = AV.validateArgs(arguments, [
2707         {
2708             name: 'powered',
2709             type: AV.Types.BOOLEAN
2710         },
2711         {
2712             name: 'successCallback',
2713             type: AV.Types.FUNCTION,
2714             optional: true,
2715             nullable: true
2716         },
2717         {
2718             name: 'errorCallback',
2719             type: AV.Types.FUNCTION,
2720             optional: true,
2721             nullable: true
2722         }
2723     ]);
2724
2725     var callArgs = {
2726         powered: args.powered
2727     };
2728
2729     var callback = function(result) {
2730         if (native.isFailure(result)) {
2731             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
2732         } else {
2733             native.callIfPossible(args.successCallback);
2734         }
2735     };
2736
2737     var result = native.call('BluetoothAdapterSetPowered', callArgs, callback);
2738     if (native.isFailure(result)) {
2739         throw native.getErrorObject(result);
2740     }
2741 };
2742
2743 // This method is deprecated since Tizen 2.3.
2744 BluetoothAdapter.prototype.setVisible = function() {
2745     privUtils_.log('Entered BluetoothAdapter.setVisible()');
2746     privUtils_.printDeprecationWarningFor('setVisible()');
2747     privUtils_.warn(
2748         'Let the user change the Bluetooth visibility through the Settings ' +
2749             'application instead.'
2750     );
2751
2752     var args = AV.validateArgs(arguments, [
2753         {
2754             name: 'visible',
2755             type: AV.Types.BOOLEAN
2756         },
2757         {
2758             name: 'successCallback',
2759             type: AV.Types.FUNCTION,
2760             optional: true,
2761             nullable: true
2762         },
2763         {
2764             name: 'errorCallback',
2765             type: AV.Types.FUNCTION,
2766             optional: true,
2767             nullable: true
2768         },
2769         {
2770             name: 'timeout',
2771             type: AV.Types.UNSIGNED_LONG,
2772             optional: true,
2773             nullable: true
2774         }
2775     ]);
2776
2777     var callArgs = {
2778         visible: args.visible
2779     };
2780
2781     if (args.visible === true) {
2782         if (T.isNullOrUndefined(args.timeout)) {
2783             callArgs.timeout = 0;
2784         } else {
2785             callArgs.timeout = args.timeout > 65535 ? 180 : args.timeout;
2786         }
2787     }
2788
2789     var callback = function(result) {
2790         if (native.isFailure(result)) {
2791             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
2792         } else {
2793             native.callIfPossible(args.successCallback);
2794         }
2795     };
2796
2797     var result = native.call('BluetoothAdapterSetVisible', callArgs, callback);
2798     if (native.isFailure(result)) {
2799         throw native.getErrorObject(result);
2800     }
2801 };
2802
2803 var _listener;
2804
2805 function _BluetoothAdapterChangeCallback(event) {
2806     privUtils_.log('_BluetoothAdapterChangeCallback');
2807
2808     var e = event;
2809     var d;
2810
2811     switch (e.action) {
2812     case 'onstatechanged':
2813         d = e.powered;
2814         break;
2815
2816     case 'onnamechanged':
2817         d = e.name;
2818         break;
2819
2820     case 'onvisibilitychanged':
2821         d = e.visible;
2822         break;
2823
2824     default:
2825         privUtils_.log('Unknown mode: ' + e.action);
2826         return;
2827     }
2828
2829     if (_listener[e.action]) {
2830         _listener[e.action](d);
2831     }
2832 }
2833
2834 BluetoothAdapter.prototype.setChangeListener = function() {
2835     privUtils_.log('Entered BluetoothAdapter.setChangeListener()');
2836     var args = AV.validateArgs(arguments, [
2837         {
2838             name: 'changeCallback',
2839             type: AV.Types.LISTENER,
2840             values: ['onstatechanged', 'onnamechanged', 'onvisibilitychanged']
2841         }
2842     ]);
2843
2844     if (T.isNullOrUndefined(_listener)) {
2845         native.addListener(
2846             'BluetoothAdapterChangeCallback',
2847             _BluetoothAdapterChangeCallback
2848         );
2849         native.callSync('BluetoothAdapterSetChangeListener', {});
2850     }
2851     _listener = args.changeCallback;
2852 };
2853
2854 BluetoothAdapter.prototype.unsetChangeListener = function() {
2855     privUtils_.log('Entered BluetoothAdapter.unsetChangeListener()');
2856     if (!T.isNullOrUndefined(_listener)) {
2857         native.removeListener(
2858             'BluetoothAdapterChangeCallback',
2859             _BluetoothAdapterChangeCallback
2860         );
2861         native.callSync('BluetoothAdapterUnsetChangeListener', {});
2862         _listener = undefined;
2863     }
2864 };
2865
2866 var _discoverDevicesSuccessCallback;
2867 var _discoverDevicesErrorCallback;
2868
2869 function _BluetoothDiscoverDevicesSuccessCallback(event) {
2870     var e = event;
2871     var d = null;
2872
2873     switch (e.action) {
2874     case 'onstarted':
2875         break;
2876
2877     case 'ondevicefound':
2878         d = new BluetoothDevice(e.data);
2879         break;
2880
2881     case 'ondevicedisappeared':
2882         d = e.data;
2883         break;
2884
2885     case 'onfinished':
2886         var result = e.data;
2887         d = [];
2888         result.forEach(function(data) {
2889             d.push(new BluetoothDevice(data));
2890         });
2891
2892         //remove listeners after discovering
2893         native.removeListener(
2894             'BluetoothDiscoverDevicesSuccessCallback',
2895             _BluetoothDiscoverDevicesSuccessCallback
2896         );
2897         native.removeListener(
2898             'BluetoothDiscoverDevicesErrorCallback',
2899             _BluetoothDiscoverDevicesErrorCallback
2900         );
2901         break;
2902
2903     default:
2904         privUtils_.log('Unknown mode: ' + e.action);
2905         return;
2906     }
2907
2908     if (_discoverDevicesSuccessCallback[e.action]) {
2909         _discoverDevicesSuccessCallback[e.action](d);
2910     }
2911 }
2912
2913 function _BluetoothDiscoverDevicesErrorCallback(event) {
2914     var e = event;
2915     setTimeout(function() {
2916         native.callIfPossible(_discoverDevicesErrorCallback, native.getErrorObject(e));
2917     }, 0);
2918 }
2919
2920 BluetoothAdapter.prototype.discoverDevices = function() {
2921     privUtils_.log('Entered BluetoothAdapter.discoverDevices()');
2922     var args = AV.validateArgs(arguments, [
2923         {
2924             name: 'successCallback',
2925             type: AV.Types.LISTENER,
2926             values: ['onstarted', 'ondevicefound', 'ondevicedisappeared', 'onfinished']
2927         },
2928         {
2929             name: 'errorCallback',
2930             type: AV.Types.FUNCTION,
2931             optional: true,
2932             nullable: true
2933         }
2934     ]);
2935
2936     _discoverDevicesSuccessCallback = args.successCallback;
2937     _discoverDevicesErrorCallback = args.errorCallback;
2938     native.addListener(
2939         'BluetoothDiscoverDevicesSuccessCallback',
2940         _BluetoothDiscoverDevicesSuccessCallback
2941     );
2942     native.addListener(
2943         'BluetoothDiscoverDevicesErrorCallback',
2944         _BluetoothDiscoverDevicesErrorCallback
2945     );
2946
2947     var result = native.callSync('BluetoothAdapterDiscoverDevices', {});
2948
2949     if (native.isFailure(result)) {
2950         native.removeListener(
2951             'BluetoothDiscoverDevicesSuccessCallback',
2952             _BluetoothDiscoverDevicesSuccessCallback
2953         );
2954         native.removeListener(
2955             'BluetoothDiscoverDevicesErrorCallback',
2956             _BluetoothDiscoverDevicesErrorCallback
2957         );
2958         throw native.getErrorObject(result);
2959     }
2960 };
2961
2962 BluetoothAdapter.prototype.stopDiscovery = function() {
2963     privUtils_.log('Entered BluetoothAdapter.stopDiscovery()');
2964     var args = AV.validateArgs(arguments, [
2965         {
2966             name: 'successCallback',
2967             type: AV.Types.FUNCTION,
2968             optional: true,
2969             nullable: true
2970         },
2971         {
2972             name: 'errorCallback',
2973             type: AV.Types.FUNCTION,
2974             optional: true,
2975             nullable: true
2976         }
2977     ]);
2978
2979     var callback = function(result) {
2980         if (native.isFailure(result)) {
2981             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
2982         } else {
2983             native.callIfPossible(args.successCallback);
2984         }
2985     };
2986
2987     var result = native.call('BluetoothAdapterStopDiscovery', {}, callback);
2988     if (native.isFailure(result)) {
2989         throw native.getErrorObject(result);
2990     }
2991 };
2992
2993 BluetoothAdapter.prototype.getKnownDevices = function() {
2994     privUtils_.log('Entered BluetoothAdapter.getKnownDevices()');
2995     var args = AV.validateArgs(arguments, [
2996         {
2997             name: 'successCallback',
2998             type: AV.Types.FUNCTION
2999         },
3000         {
3001             name: 'errorCallback',
3002             type: AV.Types.FUNCTION,
3003             optional: true,
3004             nullable: true
3005         }
3006     ]);
3007
3008     var callback = function(result) {
3009         if (native.isFailure(result)) {
3010             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
3011         } else {
3012             var r = native.getResultObject(result).devices;
3013             var devices = [];
3014             r.forEach(function(data) {
3015                 devices.push(new BluetoothDevice(data));
3016             });
3017             args.successCallback(devices);
3018         }
3019     };
3020
3021     var result = native.call('BluetoothAdapterGetKnownDevices', {}, callback);
3022     if (native.isFailure(result)) {
3023         throw native.getErrorObject(result);
3024     }
3025 };
3026
3027 BluetoothAdapter.prototype.getDevice = function() {
3028     privUtils_.log('Entered BluetoothAdapter.getDevice()');
3029     var args = AV.validateArgs(arguments, [
3030         {
3031             name: 'address',
3032             type: AV.Types.STRING
3033         },
3034         {
3035             name: 'successCallback',
3036             type: AV.Types.FUNCTION
3037         },
3038         {
3039             name: 'errorCallback',
3040             type: AV.Types.FUNCTION,
3041             optional: true,
3042             nullable: true
3043         }
3044     ]);
3045
3046     var callback = function(result) {
3047         if (native.isFailure(result)) {
3048             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
3049         } else {
3050             args.successCallback(new BluetoothDevice(native.getResultObject(result)));
3051         }
3052     };
3053
3054     var result = native.call(
3055         'BluetoothAdapterGetDevice',
3056         { address: args.address },
3057         callback
3058     );
3059     if (native.isFailure(result)) {
3060         throw native.getErrorObject(result);
3061     }
3062 };
3063
3064 BluetoothAdapter.prototype.createBonding = function() {
3065     privUtils_.log('Entered BluetoothAdapter.createBonding()');
3066     var args = AV.validateArgs(arguments, [
3067         {
3068             name: 'address',
3069             type: AV.Types.STRING
3070         },
3071         {
3072             name: 'successCallback',
3073             type: AV.Types.FUNCTION,
3074             optional: false,
3075             nullable: false
3076         },
3077         {
3078             name: 'errorCallback',
3079             type: AV.Types.FUNCTION,
3080             optional: true,
3081             nullable: true
3082         }
3083     ]);
3084
3085     var callArgs = {
3086         address: args.address
3087     };
3088
3089     var callback = function(result) {
3090         if (native.isFailure(result)) {
3091             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
3092         } else {
3093             args.successCallback(new BluetoothDevice(native.getResultObject(result)));
3094         }
3095     };
3096
3097     var result = native.call('BluetoothAdapterCreateBonding', callArgs, callback);
3098     if (native.isFailure(result)) {
3099         throw native.getErrorObject(result);
3100     }
3101 };
3102
3103 BluetoothAdapter.prototype.destroyBonding = function() {
3104     privUtils_.log('Entered BluetoothAdapter.destroyBonding()');
3105     var args = AV.validateArgs(arguments, [
3106         {
3107             name: 'address',
3108             type: AV.Types.STRING
3109         },
3110         {
3111             name: 'successCallback',
3112             type: AV.Types.FUNCTION,
3113             optional: true,
3114             nullable: true
3115         },
3116         {
3117             name: 'errorCallback',
3118             type: AV.Types.FUNCTION,
3119             optional: true,
3120             nullable: true
3121         }
3122     ]);
3123
3124     var callArgs = {
3125         address: args.address
3126     };
3127
3128     var callback = function(result) {
3129         if (native.isFailure(result)) {
3130             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
3131         } else {
3132             native.callIfPossible(args.successCallback);
3133         }
3134     };
3135
3136     var result = native.call('BluetoothAdapterDestroyBonding', callArgs, callback);
3137     if (native.isFailure(result)) {
3138         throw native.getErrorObject(result);
3139     }
3140 };
3141
3142 BluetoothAdapter.prototype.registerRFCOMMServiceByUUID = function() {
3143     privUtils_.log('Entered BluetoothAdapter.registerRFCOMMServiceByUUID()');
3144     var args = AV.validateArgs(arguments, [
3145         {
3146             name: 'uuid',
3147             type: AV.Types.STRING
3148         },
3149         {
3150             name: 'name',
3151             type: AV.Types.STRING
3152         },
3153         {
3154             name: 'successCallback',
3155             type: AV.Types.FUNCTION
3156         },
3157         {
3158             name: 'errorCallback',
3159             type: AV.Types.FUNCTION,
3160             optional: true,
3161             nullable: true
3162         }
3163     ]);
3164
3165     var callArgs = {
3166         uuid: args.uuid,
3167         name: args.name
3168     };
3169
3170     var callback = function(result) {
3171         if (native.isFailure(result)) {
3172             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
3173         } else {
3174             // if registration was finished with success create BluetoothServiceHandler
3175             // with parameters passed to this function (uuid and name).
3176             args.successCallback(new BluetoothServiceHandler(callArgs));
3177         }
3178     };
3179
3180     var result = native.call(
3181         'BluetoothAdapterRegisterRFCOMMServiceByUUID',
3182         callArgs,
3183         callback
3184     );
3185     if (native.isFailure(result)) {
3186         throw native.getErrorObject(result);
3187     }
3188 };
3189
3190 BluetoothAdapter.prototype.getBluetoothProfileHandler = function() {
3191     privUtils_.log('Entered BluetoothAdapter.getBluetoothProfileHandler()');
3192     privUtils_.printDeprecationWarningFor('getBluetoothProfileHandler');
3193     var args = AV.validateArgs(arguments, [
3194         {
3195             name: 'profileType',
3196             type: AV.Types.ENUM,
3197             values: T.getValues(_BluetoothProfileType)
3198         }
3199     ]);
3200
3201     var callArgs = { profileType: args.profileType };
3202
3203     var result = native.callSync('BluetoothAdapterGetBluetoothProfileHandler', callArgs);
3204
3205     if (native.isFailure(result)) {
3206         throw native.getErrorObject(result);
3207     } else {
3208         switch (args.profileType) {
3209         case _BluetoothProfileType.HEALTH:
3210             return new BluetoothHealthProfileHandler(callArgs);
3211
3212         default:
3213             throw new WebAPIException(
3214                 'NotSupportedError',
3215                 'Profile ' + args.profileType + ' is not supported.'
3216             );
3217         }
3218     }
3219 };
3220
3221 // class BluetoothGATTServer ////////////////////////
3222 var _BluetoothGATTServerServices = [];
3223
3224 /*
3225  * This set is used in BluetoothGATTServer::start() to check which services
3226  * from BluetoothGATTServer::services have already been registered in native
3227  * layer and which have to be registered.
3228  */
3229 var _BluetoothGATTServerServicesRegisteredInNativeLayer = {};
3230
3231 var BluetoothGATTServer = function() {
3232     Object.defineProperties(this, {
3233         services: {
3234             enumerable: true,
3235             get: function() {
3236                 return _BluetoothGATTServerServices;
3237             },
3238             set: function() {}
3239         }
3240     });
3241 };
3242
3243 var AbortError = new WebAPIException('AbortError', 'An unknown error occurred');
3244
3245 var BluetoothGATTServer_valid_registerService_errors = [
3246     'InvalidStateError',
3247     'NotSupportedError',
3248     'InvalidValuesError',
3249     'AbortError'
3250 ];
3251 var BluetoothGATTServer_valid_registerService_exceptions = [
3252     'InvalidStateError',
3253     'TypeMismatchError',
3254     'SecurityError'
3255 ];
3256
3257 BluetoothGATTServer.prototype.registerService = function() {
3258     var args = AV.validateArgs(arguments, [
3259         {
3260             name: 'service',
3261             type: AV.Types.DICTIONARY
3262         },
3263         {
3264             name: 'successCallback',
3265             type: AV.Types.FUNCTION,
3266             optional: true,
3267             nullable: true
3268         },
3269         {
3270             name: 'errorCallback',
3271             type: AV.Types.FUNCTION,
3272             optional: true,
3273             nullable: true
3274         }
3275     ]);
3276
3277     var service = new BluetoothGATTServerService(args.service);
3278
3279     var callback = function(result) {
3280         if (native.isFailure(result)) {
3281             native.callIfPossible(
3282                 args.errorCallback,
3283                 native.getErrorObjectAndValidate(
3284                     result,
3285                     BluetoothGATTServer_valid_registerService_errors,
3286                     AbortError
3287                 )
3288             );
3289         } else {
3290             _BluetoothGATTServerServicesRegisteredInNativeLayer[service._id] = true;
3291             _BluetoothGATTServerServices.push(service);
3292             native.callIfPossible(args.successCallback);
3293         }
3294     };
3295
3296     var result = native.call('BluetoothGATTServerRegisterService', service, callback);
3297     if (native.isFailure(result)) {
3298         throw native.getErrorObjectAndValidate(
3299             result,
3300             BluetoothGATTServer_valid_registerService_exceptions,
3301             AbortError
3302         );
3303     }
3304 };
3305
3306 /*
3307  * Objects of this class are used to wait for multiple callbacks results.
3308  *
3309  * Its successCallback and errorCallback members should be passed as
3310  * the success and error callbacks, respectively, to the functions we wait for.
3311  * When the functions we wait for are called callbacksNum times, either
3312  * onAllSucceeded or onFailure is called, depending on whether only
3313  * successCallback was called or not.
3314  *
3315  * For the usage example, take a look at BluetoothGATTServer.prototype.start,
3316  * where it's used to wait for registration of multiple services.
3317  */
3318 var ResultCallbacksAggregator = function(callbacksNum, onAllSucceeded, onFailure) {
3319     var _callbacksNum = callbacksNum;
3320     var _allSucceeded = true;
3321     var _error;
3322
3323     this.successCallback = function (){
3324       _callbacksNum--;
3325
3326       if (!_callbacksNum) {
3327         if (_allSucceeded) {
3328           onAllSucceeded();
3329         } else {
3330           onFailure(_error);
3331         }
3332       }
3333     };
3334
3335     this.errorCallback = function(error) {
3336       _callbacksNum--;
3337       _allSucceeded = false;
3338       _error = error;
3339
3340       if (!_callbacksNum) {
3341           onFailure(_error);
3342       }
3343     };
3344 };
3345
3346 var BluetoothGATTServer_valid_start_errors = ['InvalidStateError', 'NotSupportedError', 'AbortError'];
3347 var BluetoothGATTServer_valid_start_exceptions = ['InvalidStateError', 'TypeMismatchError', 'SecurityError'];
3348
3349 BluetoothGATTServer.prototype.start = function() {
3350     privUtils_.log('Entered BluetoothGATTServer.start()');
3351     var args = AV.validateArgs(arguments, [
3352         {
3353             name: 'successCallback',
3354             type: AV.Types.FUNCTION,
3355             optional: true,
3356             nullable: true
3357         },
3358         {
3359             name: 'errorCallback',
3360             type: AV.Types.FUNCTION,
3361             optional: true,
3362             nullable: true
3363         }
3364     ]);
3365
3366     var servicesUnregisteredInNativeLayer = [];
3367     for (var i = 0; i < this.services.length; ++i) {
3368         if(this.services[i]) {
3369             if (!_BluetoothGATTServerServicesRegisteredInNativeLayer[this.services[i]._id]) {
3370                 servicesUnregisteredInNativeLayer.push(this.services[i]);
3371             }
3372         }
3373     }
3374
3375     var startServerCallback = function(result) {
3376         if (native.isFailure(result)) {
3377             native.callIfPossible(args.errorCallback,
3378                                   native.getErrorObjectAndValidate(result,
3379                                     BluetoothGATTServer_valid_start_errors, AbortError));
3380         } else {
3381             native.callIfPossible(args.successCallback);
3382         }
3383     };
3384
3385     if (servicesUnregisteredInNativeLayer.length) {
3386       var registerServiceCallbacksAggregator = new ResultCallbacksAggregator(servicesUnregisteredInNativeLayer.length,
3387           function onAllSucceeded() {
3388
3389               var result = native.call('BluetoothGATTServerStart', {}, startServerCallback);
3390
3391               if (native.isFailure(result)) {
3392                   throw native.getErrorObjectAndValidate(result, BluetoothGATTServer_valid_start_exceptions, AbortError);
3393               }
3394           },
3395           function onFailure(error) {
3396               native.callIfPossible(args.errorCallback,
3397                                     native.getErrorObjectAndValidate(error,
3398                                       BluetoothGATTServer_valid_start_errors, AbortError));
3399           });
3400
3401       var registerServicesCallback = function(result) {
3402           if (native.isFailure(result)) {
3403               registerServiceCallbacksAggregator.errorCallback(
3404                                     native.getErrorObjectAndValidate(result,
3405                                       BluetoothGATTServer_valid_start_errors, AbortError));
3406           } else {
3407               registerServiceCallbacksAggregator.successCallback();
3408           }
3409       };
3410
3411       for (var i = 0; i < servicesUnregisteredInNativeLayer.length; ++i) {
3412         var result = native.call('BluetoothGATTServerRegisterService', servicesUnregisteredInNativeLayer[i], registerServicesCallback);
3413         if (native.isFailure(result)) {
3414             throw native.getErrorObjectAndValidate(
3415                 result,
3416                 BluetoothGATTServer_valid_registerService_exceptions,
3417                 AbortError
3418             );
3419         }
3420       }
3421     } else {
3422         var result = native.call('BluetoothGATTServerStart', {}, startServerCallback);
3423
3424         if (native.isFailure(result)) {
3425             throw native.getErrorObjectAndValidate(result, BluetoothGATTServer_valid_start_exceptions, AbortError);
3426         }
3427     }
3428 }
3429
3430 var BluetoothGATTServer_valid_stop_errors = ['InvalidStateError', 'NotSupportedError', 'AbortError'];
3431 var BluetoothGATTServer_valid_stop_exceptions = ['InvalidStateError', 'TypeMismatchError', 'SecurityError'];
3432
3433 BluetoothGATTServer.prototype.stop = function() {
3434     privUtils_.log('Entered BluetoothGATTServer.stop()');
3435     var args = AV.validateArgs(arguments, [
3436         {
3437             name: 'successCallback',
3438             type: AV.Types.FUNCTION,
3439             optional: true,
3440             nullable: true
3441         },
3442         {
3443             name: 'errorCallback',
3444             type: AV.Types.FUNCTION,
3445             optional: true,
3446             nullable: true
3447         }
3448     ]);
3449
3450     var callback = function(result) {
3451         if (native.isFailure(result)) {
3452             native.callIfPossible(args.errorCallback,
3453                                   native.getErrorObjectAndValidate(result,
3454                                     BluetoothGATTServer_valid_stop_errors, AbortError));
3455         } else {
3456             _BluetoothGATTServerServicesRegisteredInNativeLayer = {};
3457             native.callIfPossible(args.successCallback);
3458         }
3459     };
3460
3461     var result = native.call('BluetoothGATTServerStop', {}, callback);
3462     if (native.isFailure(result)) {
3463         throw native.getErrorObjectAndValidate(result, BluetoothGATTServer_valid_stop_exceptions, AbortError);
3464     }
3465 }
3466
3467 var BluetoothGATTServer_valid_getConnectionMtu_errors = ['InvalidStateError', 'NotSupportedError', 'UnknownError'];
3468 var BluetoothGATTServer_valid_getConnectionMtu_exceptions = ['TypeMismatchError', 'SecurityError'];
3469
3470 BluetoothGATTServer.prototype.getConnectionMtu = function() {
3471     privUtils_.log('Entered BluetoothGATTServer.getConnectionMtu()');
3472     var args = AV.validateArgs(arguments, [
3473         {
3474             name: 'clientAddress',
3475             type: AV.Types.STRING
3476         },
3477         {
3478             name: 'successCallback',
3479             type: AV.Types.FUNCTION
3480         },
3481         {
3482             name: 'errorCallback',
3483             type: AV.Types.FUNCTION,
3484             optional: true
3485         }
3486     ]);
3487
3488     var callback = function(result) {
3489         if (native.isFailure(result)) {
3490             native.callIfPossible(args.errorCallback,
3491                                   native.getErrorObjectAndValidate(result,
3492                                     BluetoothGATTServer_valid_getConnectionMtu_errors, UnknownError));
3493         } else {
3494             args.successCallback(native.getResultObject(result));
3495         }
3496     };
3497
3498     var result = native.call('BluetoothGATTServerGetConnectionMtu',
3499                              {clientAddress: args.clientAddress}, callback);
3500     if (native.isFailure(result)) {
3501         throw native.getErrorObjectAndValidate(result, BluetoothGATTServer_valid_getConnectionMtu_exceptions,
3502                                                UnknownError);
3503     }
3504 }
3505
3506 var GATTServer = new BluetoothGATTServer();
3507
3508 // class BluetoothManager ///////////////////////////
3509 var BluetoothManager = function() {
3510     Object.defineProperties(this, {
3511         deviceMajor: {
3512             value: new BluetoothClassDeviceMajor(),
3513             writable: false,
3514             enumerable: true
3515         },
3516         deviceMinor: {
3517             value: new BluetoothClassDeviceMinor(),
3518             writable: false,
3519             enumerable: true
3520         },
3521         deviceService: {
3522             value: new BluetoothClassDeviceService(),
3523             writable: false,
3524             enumerable: true
3525         },
3526         BASE_UUID: {
3527             value: '00000000-0000-1000-8000-00805F9B34FB',
3528             writable: false,
3529             enumerable: true
3530         }
3531     });
3532 };
3533
3534 var BluetoothManager_getDefaultAdapter = function() {
3535     privUtils_.checkPrivilegeAccess4Ver(
3536         '2.4',
3537         Privilege.BLUETOOTH,
3538         Privilege.BLUETOOTH_GAP
3539     );
3540
3541     return new BluetoothAdapter();
3542 };
3543
3544 BluetoothManager.prototype.getDefaultAdapter = function() {
3545     privUtils_.log('Entered BluetoothManager.getDefaultAdapter()');
3546     return BluetoothManager_getDefaultAdapter();
3547 };
3548
3549 var BluetoothManager_getLEAdapter = function() {
3550     privUtils_.checkPrivilegeAccess4Ver(
3551         '2.4',
3552         Privilege.BLUETOOTH,
3553         Privilege.BLUETOOTH_ADMIN
3554     );
3555
3556     return new BluetoothLEAdapter();
3557 };
3558
3559 BluetoothManager.prototype.getLEAdapter = function() {
3560     privUtils_.log('Entered BluetoothManager.getLEAdapter()');
3561     return BluetoothManager_getLEAdapter();
3562 };
3563
3564 var BluetoothManager_checkAndNormalizeHexString = function(hexString) {
3565     hexString = hexString.toLowerCase();
3566     if (hexString.startsWith('0x')) {
3567         hexString = hexString.substring(2);
3568     }
3569     if (!(/^[0-9a-f]+$/.test(hexString))) {
3570         throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR, 'Given string is not hexadecimal value');
3571     }
3572     if ('0' === hexString) {
3573         return  '00';
3574     }
3575     if (1 === hexString.length%2) {
3576         // to save consistency with BluetoothLEManufacturerData, last character is omitted
3577         hexString = hexString.replace(/.$/,'');
3578     }
3579     return hexString;
3580 }
3581
3582 var BluetoothManager_HexStringToUint8Array = function(hexString) {
3583     hexString = BluetoothManager_checkAndNormalizeHexString(hexString);
3584     if (0 === hexString.length) {
3585         return new Uint8Array([]);
3586     }
3587     var data = hexString.match(/[0-9a-f]{2}/g).map(function(byte) {return parseInt(byte, 16)});
3588     return new Uint8Array(data);
3589 }
3590
3591 var BluetoothManager_byteArrayToHexString = function(bytes) {
3592     if (0 == bytes.length) {
3593         return '';
3594     }
3595     return '0x' + Array.prototype.map.call(bytes, function(byte) {
3596         return ('0' + (byte & 0xFF).toString(16)).slice(-2);
3597     }).join('');
3598 }
3599
3600 var BluetoothManager_toByteArray = function(data) {
3601     if (data && String === data.constructor) {
3602         return numberArrayToByteArray(BluetoothManager_HexStringToUint8Array(data));
3603     } else {
3604         try {
3605             data = numberArrayToByteArray(data);
3606         }
3607         catch(err) {
3608             throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR, 'argument is not a valid Bytes type');
3609         }
3610     }
3611     return data;
3612 };
3613
3614 BluetoothManager.prototype.toByteArray = function(data) {
3615     privUtils_.log('Entered BluetoothManager.toByteArray()');
3616     return BluetoothManager_toByteArray(data);
3617 };
3618
3619 var BluetoothManager_toDOMString = function(data) {
3620     if (String === data.constructor) {
3621         data = BluetoothManager_checkAndNormalizeHexString(data);
3622         if (0 !== data.length) {
3623             data = '0x' + data;
3624         }
3625         return data;
3626     } else {
3627         return BluetoothManager_byteArrayToHexString(numberArrayToByteArray(data));
3628     }
3629 };
3630
3631 BluetoothManager.prototype.toDOMString = function(data) {
3632     privUtils_.log('Entered BluetoothManager.toDOMString()');
3633     return BluetoothManager_toDOMString(data);
3634 };
3635
3636 var BluetoothManager_toUint8Array = function(data) {
3637     if (String === data.constructor) {
3638         return BluetoothManager_HexStringToUint8Array(data);
3639     } else {
3640         try {
3641             data =  new Uint8Array(numberArrayToByteArray(data));
3642         }
3643         catch(err) {
3644             throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR, 'argument is not a valid Bytes type');
3645         }
3646     }
3647     return data;
3648 };
3649
3650 BluetoothManager.prototype.toUint8Array = function(data) {
3651     privUtils_.log('Entered BluetoothManager.toUint8Array()');
3652     return BluetoothManager_toUint8Array(data);
3653 };
3654
3655 var BluetoothManager_UUIDIsValid128Bit = function(uuid) {
3656     var re128BitFormat = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
3657     if (re128BitFormat.test(uuid)) {
3658         return true;
3659     }
3660     return false;
3661 };
3662
3663 var BluetoothManager_UUIDIsValid32Bit = function(uuid) {
3664     var re32BitFormat = /^[0-9a-f]{8}$/;
3665     if (re32BitFormat.test(uuid)) {
3666         return true;
3667     }
3668     return false;
3669 };
3670
3671 var BluetoothManager_UUIDIsValid16Bit = function(uuid) {
3672     var re16BitFormat = /^[0-9a-f]{4}$/;
3673     if (re16BitFormat.test(uuid)) {
3674         return true;
3675     }
3676     return false;
3677 };
3678
3679 var BluetoothManager_UUIDIsConvertibleTo16Bit = function(uuid) {
3680     var re128BitFormat = /^0000[0-9a-f]{4}-0000-1000-8000-00805f9b34fb$/;
3681     if (re128BitFormat.test(uuid)) {
3682         return true;
3683     }
3684     var re32BitFormat = /^0000[0-9a-f]{4}$/;
3685     if (re32BitFormat.test(uuid)) {
3686         return true;
3687     }
3688     return false;
3689 };
3690
3691 var BluetoothManager_UUIDIsConvertibleTo32Bit = function(uuid) {
3692     var re = /^[0-9a-f]{8}-0000-1000-8000-00805f9b34fb$/;
3693     if (re.test(uuid)) {
3694         return true;
3695     }
3696     return false;
3697 };
3698
3699 var BluetoothManager_UUIDTo128bit = function(uuid) {
3700     uuid = Converter.toString(uuid).toLowerCase();
3701     if (BluetoothManager_UUIDIsValid128Bit(uuid)) {
3702         return uuid;
3703     }
3704     var baseUuidLast96Bits = '-0000-1000-8000-00805f9b34fb';
3705     if (BluetoothManager_UUIDIsValid16Bit(uuid)) {
3706         return '0000' + uuid + baseUuidLast96Bits;
3707     }
3708     if (BluetoothManager_UUIDIsValid32Bit(uuid)) {
3709         return uuid + baseUuidLast96Bits;
3710     }
3711     throw new WebAPIException(
3712         WebAPIException.INVALID_VALUES_ERR,
3713         'Given parameter is not a supported uuid format: ' + uuid
3714     );
3715 };
3716
3717 var BluetoothManager_UUIDToShortestPossible = function(uuid) {
3718     uuid = uuid.toLowerCase();
3719     if (BluetoothManager_UUIDIsValid16Bit(uuid)) {
3720         return uuid;
3721     }
3722     if (BluetoothManager_UUIDIsValid32Bit(uuid)) {
3723         if (BluetoothManager_UUIDIsConvertibleTo16Bit(uuid)) {
3724             return uuid.substring(4, 8);
3725         }
3726     }
3727     if (!BluetoothManager_UUIDIsValid128Bit(uuid)) {
3728         throw new WebAPIException(
3729             WebAPIException.INVALID_VALUES_ERR,
3730             'Given parameter is not a supported uuid format: ' + uuid
3731         );
3732     }
3733     if (BluetoothManager_UUIDIsConvertibleTo16Bit(uuid)) {
3734         return uuid.substring(4, 8);
3735     }
3736     if (BluetoothManager_UUIDIsConvertibleTo32Bit(uuid)) {
3737         return uuid.substring(0, 8);
3738     }
3739     return uuid;
3740 };
3741
3742 var BluetoothManager_UUIDsEqual = function(uuid1, uuid2) {
3743     uuid1 = Converter.toString(uuid1).toLowerCase();
3744     uuid1 = BluetoothManager_UUIDTo128bit(uuid1);
3745     uuid2 = Converter.toString(uuid2).toLowerCase();
3746     uuid2 = BluetoothManager_UUIDTo128bit(uuid2);
3747     return uuid1 === uuid2;
3748 };
3749
3750 BluetoothManager.prototype.uuidTo128bit = function(uuid) {
3751     privUtils_.log('Entered BluetoothManager.uuidTo128bit()');
3752     var args = AV.validateArgs(arguments, [
3753         {
3754             name: 'uuid',
3755             type: AV.Types.STRING
3756         }
3757     ]);
3758     return BluetoothManager_UUIDTo128bit(args.uuid);
3759 };
3760
3761 BluetoothManager.prototype.uuidToShortestPossible = function(uuid) {
3762     privUtils_.log('Entered BluetoothManager.uuidToShortestPossible()');
3763     var args = AV.validateArgs(arguments, [
3764         {
3765             name: 'uuid',
3766             type: AV.Types.STRING
3767         }
3768     ]);
3769     return BluetoothManager_UUIDToShortestPossible(args.uuid);
3770 };
3771
3772 BluetoothManager.prototype.uuidsEqual = function(uuid1, uuid2) {
3773     privUtils_.log('Entered BluetoothManager.uuidsEqual()');
3774     var args = AV.validateArgs(arguments, [
3775         {
3776             name: 'uuid1',
3777             type: AV.Types.STRING
3778         },
3779         {
3780             name: 'uuid2',
3781             type: AV.Types.STRING
3782         }
3783     ]);
3784     return BluetoothManager_UUIDsEqual(args.uuid1, args.uuid2);
3785 };
3786 BluetoothManager.prototype.getGATTServer = function() {
3787     privUtils_.log('Entered BluetoothManager.getGATTServer()');
3788     return BluetoothManager_getGATTServer();
3789 };
3790
3791 var BluetoothManager_getGATTServer = function() {
3792     return GATTServer;
3793 };
3794
3795 // exports //////////////////////////////////////////
3796 exports = new BluetoothManager();