26ad947423968e8f68a6aa67c4ccf6bef953d21c
[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 var AbortError = new WebAPIException('AbortError', 'An unknown error occurred');
25
26 // class BluetoothClassDeviceMajor /////////////////////////////////////////
27 var BluetoothClassDeviceMajor = function() {
28     Object.defineProperties(this, {
29         MISC: { value: 0x00, writable: false, enumerable: true },
30         COMPUTER: { value: 0x01, writable: false, enumerable: true },
31         PHONE: { value: 0x02, writable: false, enumerable: true },
32         NETWORK: { value: 0x03, writable: false, enumerable: true },
33         AUDIO_VIDEO: { value: 0x04, writable: false, enumerable: true },
34         PERIPHERAL: { value: 0x05, writable: false, enumerable: true },
35         IMAGING: { value: 0x06, writable: false, enumerable: true },
36         WEARABLE: { value: 0x07, writable: false, enumerable: true },
37         TOY: { value: 0x08, writable: false, enumerable: true },
38         HEALTH: { value: 0x09, writable: false, enumerable: true },
39         UNCATEGORIZED: { value: 0x1f, writable: false, enumerable: true }
40     });
41 };
42
43 // class BluetoothClassDeviceMinor /////////////////////////////////////////
44 var BluetoothClassDeviceMinor = function() {
45     Object.defineProperties(this, {
46         COMPUTER_UNCATEGORIZED: { value: 0x00, writable: false, enumerable: true },
47         COMPUTER_DESKTOP: { value: 0x01, writable: false, enumerable: true },
48         COMPUTER_SERVER: { value: 0x02, writable: false, enumerable: true },
49         COMPUTER_LAPTOP: { value: 0x03, writable: false, enumerable: true },
50         COMPUTER_HANDHELD_PC_OR_PDA: { value: 0x04, writable: false, enumerable: true },
51         COMPUTER_PALM_PC_OR_PDA: { value: 0x05, writable: false, enumerable: true },
52         COMPUTER_WEARABLE: { value: 0x06, writable: false, enumerable: true },
53
54         PHONE_UNCATEGORIZED: { value: 0x00, writable: false, enumerable: true },
55         PHONE_CELLULAR: { value: 0x01, writable: false, enumerable: true },
56         PHONE_CORDLESS: { value: 0x02, writable: false, enumerable: true },
57         PHONE_SMARTPHONE: { value: 0x03, writable: false, enumerable: true },
58         PHONE_MODEM_OR_GATEWAY: { value: 0x04, writable: false, enumerable: true },
59         PHONE_ISDN: { value: 0x05, writable: false, enumerable: true },
60
61         AV_UNRECOGNIZED: { value: 0x00, writable: false, enumerable: true },
62         AV_WEARABLE_HEADSET: { value: 0x01, writable: false, enumerable: true },
63         AV_HANDSFREE: { value: 0x02, writable: false, enumerable: true },
64         AV_MICROPHONE: { value: 0x04, writable: false, enumerable: true },
65         AV_LOUDSPEAKER: { value: 0x05, writable: false, enumerable: true },
66         AV_HEADPHONES: { value: 0x06, writable: false, enumerable: true },
67         AV_PORTABLE_AUDIO: { value: 0x07, writable: false, enumerable: true },
68         AV_CAR_AUDIO: { value: 0x08, writable: false, enumerable: true },
69         AV_SETTOP_BOX: { value: 0x09, writable: false, enumerable: true },
70         AV_HIFI: { value: 0x0a, writable: false, enumerable: true },
71         AV_VCR: { value: 0x0b, writable: false, enumerable: true },
72         AV_VIDEO_CAMERA: { value: 0x0c, writable: false, enumerable: true },
73         AV_CAMCORDER: { value: 0x0d, writable: false, enumerable: true },
74         AV_MONITOR: { value: 0x0e, writable: false, enumerable: true },
75         AV_DISPLAY_AND_LOUDSPEAKER: { value: 0x0f, writable: false, enumerable: true },
76         AV_VIDEO_CONFERENCING: { value: 0x10, writable: false, enumerable: true },
77         AV_GAMING_TOY: { value: 0x12, writable: false, enumerable: true },
78
79         PERIPHERAL_UNCATEGORIZED: { value: 0x00, writable: false, enumerable: true },
80         PERIPHERAL_KEYBOARD: { value: 0x10, writable: false, enumerable: true },
81         PERIPHERAL_POINTING_DEVICE: { value: 0x20, writable: false, enumerable: true },
82         PERIPHERAL_KEYBOARD_AND_POINTING_DEVICE: {
83             value: 0x30,
84             writable: false,
85             enumerable: true
86         },
87         PERIPHERAL_JOYSTICK: { value: 0x01, writable: false, enumerable: true },
88         PERIPHERAL_GAMEPAD: { value: 0x02, writable: false, enumerable: true },
89         PERIPHERAL_REMOTE_CONTROL: { value: 0x03, writable: false, enumerable: true },
90         PERIPHERAL_SENSING_DEVICE: { value: 0x04, writable: false, enumerable: true },
91         PERIPHERAL_DEGITIZER_TABLET: { value: 0x05, writable: false, enumerable: true },
92         PERIPHERAL_CARD_READER: { value: 0x06, writable: false, enumerable: true },
93         PERIPHERAL_DIGITAL_PEN: { value: 0x07, writable: false, enumerable: true },
94         PERIPHERAL_HANDHELD_SCANNER: { value: 0x08, writable: false, enumerable: true },
95         PERIPHERAL_HANDHELD_INPUT_DEVICE: {
96             value: 0x09,
97             writable: false,
98             enumerable: true
99         },
100
101         IMAGING_UNCATEGORIZED: { value: 0x00, writable: false, enumerable: true },
102         IMAGING_DISPLAY: { value: 0x04, writable: false, enumerable: true },
103         IMAGING_CAMERA: { value: 0x08, writable: false, enumerable: true },
104         IMAGING_SCANNER: { value: 0x10, writable: false, enumerable: true },
105         IMAGING_PRINTER: { value: 0x20, writable: false, enumerable: true },
106
107         WEARABLE_WRITST_WATCH: { value: 0x01, writable: false, enumerable: true },
108         WEARABLE_PAGER: { value: 0x02, writable: false, enumerable: true },
109         WEARABLE_JACKET: { value: 0x03, writable: false, enumerable: true },
110         WEARABLE_HELMET: { value: 0x04, writable: false, enumerable: true },
111         WEARABLE_GLASSES: { value: 0x05, writable: false, enumerable: true },
112
113         TOY_ROBOT: { value: 0x01, writable: false, enumerable: true },
114         TOY_VEHICLE: { value: 0x02, writable: false, enumerable: true },
115         TOY_DOLL: { value: 0x03, writable: false, enumerable: true },
116         TOY_CONTROLLER: { value: 0x04, writable: false, enumerable: true },
117         TOY_GAME: { value: 0x05, writable: false, enumerable: true },
118
119         HEALTH_UNDEFINED: { value: 0x00, writable: false, enumerable: true },
120         HEALTH_BLOOD_PRESSURE_MONITOR: { value: 0x01, writable: false, enumerable: true },
121         HEALTH_THERMOMETER: { value: 0x02, writable: false, enumerable: true },
122         HEALTH_WEIGHING_SCALE: { value: 0x03, writable: false, enumerable: true },
123         HEALTH_GLUCOSE_METER: { value: 0x04, writable: false, enumerable: true },
124         HEALTH_PULSE_OXIMETER: { value: 0x05, writable: false, enumerable: true },
125         HEALTH_PULSE_RATE_MONITOR: { value: 0x06, writable: false, enumerable: true },
126         HEALTH_DATA_DISPLAY: { value: 0x07, writable: false, enumerable: true },
127         HEALTH_STEP_COUNTER: { value: 0x08, writable: false, enumerable: true },
128         HEALTH_BODY_COMPOSITION_ANALYZER: {
129             value: 0x09,
130             writable: false,
131             enumerable: true
132         },
133         HEALTH_PEAK_FLOW_MONITOR: { value: 0x0a, writable: false, enumerable: true },
134         HEALTH_MEDICATION_MONITOR: { value: 0x0b, writable: false, enumerable: true },
135         HEALTH_KNEE_PROSTHESIS: { value: 0x0c, writable: false, enumerable: true },
136         HEALTH_ANKLE_PROSTHESIS: { value: 0x0d, writable: false, enumerable: true }
137     });
138 };
139
140 // class BluetoothClassDeviceService ///////////////////////////////////////
141 var BluetoothClassDeviceService = function() {
142     Object.defineProperties(this, {
143         LIMITED_DISCOVERABILITY: { value: 0x0001, writable: false, enumerable: true },
144         POSITIONING: { value: 0x0008, writable: false, enumerable: true },
145         NETWORKING: { value: 0x0010, writable: false, enumerable: true },
146         RENDERING: { value: 0x0020, writable: false, enumerable: true },
147         CAPTURING: { value: 0x0040, writable: false, enumerable: true },
148         OBJECT_TRANSFER: { value: 0x0080, writable: false, enumerable: true },
149         AUDIO: { value: 0x0100, writable: false, enumerable: true },
150         TELEPHONY: { value: 0x0200, writable: false, enumerable: true },
151         INFORMATION: { value: 0x0400, writable: false, enumerable: true }
152     });
153 };
154
155 //class tizen.BluetoothLEServiceData ///////////////////////////
156 tizen.BluetoothLEServiceData = function(d) {
157     AV.isConstructorCall(this, tizen.BluetoothLEServiceData);
158     var uuid_ = '';
159     var data_ = '';
160
161     Object.defineProperties(this, {
162         uuid: {
163             enumerable: true,
164             get: function() {
165                 return uuid_;
166             },
167             set: function(v) {
168                 uuid_ = Converter.toString(v);
169             }
170         },
171         data: {
172             enumerable: true,
173             get: function() {
174                 return data_;
175             },
176             set: function(v) {
177                 try {
178                     data_ = BluetoothManager_toDOMString(v);
179                 } catch (err) {
180                     data_ = Converter.toString(v);
181                 }
182             }
183         }
184     });
185
186     if (arguments.length >= 2) {
187         // public constructor
188         this.uuid = arguments[0];
189         this.data = arguments[1];
190     } else if (d && T.isObject(d)) {
191         // internal constructor
192         this.uuid = d.uuid;
193         this.data = d.data;
194     } else {
195         uuid_ = undefined;
196         data_ = undefined;
197     }
198 };
199
200 //class BluetoothLEAdvertiseData ///////////////////////////
201 tizen.BluetoothLEAdvertiseData = function(dict) {
202     AV.isConstructorCall(this, tizen.BluetoothLEAdvertiseData);
203     var includeName_ = false;
204     var uuids_ = null;
205     var solicitationuuids_ = null;
206     var appearance_ = null;
207     var includeTxPowerLevel_ = false;
208     var serviceData_ = null;
209     var servicesData_ = null;
210     var manufacturerData_ = null;
211
212     Object.defineProperties(this, {
213         includeName: {
214             enumerable: true,
215             get: function() {
216                 return includeName_;
217             },
218             set: function(v) {
219                 includeName_ = Converter.toBoolean(v, true);
220             }
221         },
222         uuids: {
223             enumerable: true,
224             get: function() {
225                 return uuids_;
226             },
227             set: function(v) {
228                 if (T.isNull(v)) {
229                     uuids_ = v;
230                 } else if (T.isArray(v)) {
231                     for (var i = 0; i < v.length; ++i) {
232                         if (!T.isString(v[i])) {
233                             v[i] = Converter.toString(v[i]);
234                         }
235                     }
236                     uuids_ = v;
237                 }
238             }
239         },
240         solicitationuuids: {
241             enumerable: true,
242             get: function() {
243                 return solicitationuuids_;
244             },
245             set: function(v) {
246                 if (T.isNull(v)) {
247                     solicitationuuids_ = v;
248                 } else if (T.isArray(v)) {
249                     for (var i = 0; i < v.length; ++i) {
250                         if (!T.isString(v[i])) {
251                             v[i] = Converter.toString(v[i]);
252                         }
253                     }
254                     solicitationuuids_ = v;
255                 }
256             }
257         },
258         appearance: {
259             enumerable: true,
260             get: function() {
261                 return appearance_;
262             },
263             set: function(v) {
264                 appearance_ = Converter.toUnsignedLong(v, true);
265             }
266         },
267         includeTxPowerLevel: {
268             enumerable: true,
269             get: function() {
270                 return includeTxPowerLevel_;
271             },
272             set: function(v) {
273                 includeTxPowerLevel_ = Converter.toBoolean(v, true);
274             }
275         },
276         serviceData: {
277             enumerable: true,
278             get: function() {
279                 return serviceData_;
280             },
281             set: function(v) {
282                 if (T.isNull(v) || v instanceof tizen.BluetoothLEServiceData) {
283                     serviceData_ = v;
284                 }
285             }
286         },
287         servicesData: {
288             enumerable: true,
289             get: function() {
290                 return servicesData_;
291             },
292             set: function(v) {
293                 if (T.isNull(v)) {
294                     servicesData_ = v;
295                 } else if (T.isArray(v)) {
296                     var tmpArray = [];
297                     for (var i = 0; i < v.length; ++i) {
298                         if (v[i] instanceof tizen.BluetoothLEServiceData) {
299                             tmpArray.push(v[i]);
300                         }
301                     }
302                     servicesData_ = tmpArray;
303                 }
304             }
305         },
306         manufacturerData: {
307             enumerable: true,
308             get: function() {
309                 return manufacturerData_;
310             },
311             set: function(v) {
312                 if (T.isNull(v) || v instanceof tizen.BluetoothLEManufacturerData) {
313                     manufacturerData_ = v;
314                 }
315             }
316         }
317     });
318
319     if (T.isObject(dict)) {
320         var o = {};
321
322         // includeName
323         if (T.isNull(dict.includeName) || T.isBoolean(dict.includeName)) {
324             o.includeName = dict.includeName;
325         } else if (!T.isUndefined(dict.includeName)) {
326             return;
327         }
328
329         // uuids
330         if (T.isNull(dict.uuids)) {
331             o.uuids = dict.uuids;
332         } else if (T.isArray(dict.uuids)) {
333             for (var i = 0; i < dict.uuids.length; ++i) {
334                 if (!T.isString(dict.uuids[i])) {
335                     return;
336                 }
337             }
338             o.uuids = dict.uuids;
339         } else if (!T.isUndefined(dict.uuids)) {
340             return;
341         }
342
343         // solicitationuuids
344         if (T.isNull(dict.solicitationuuids)) {
345             o.solicitationuuids = dict.solicitationuuids;
346         } else if (T.isArray(dict.solicitationuuids)) {
347             for (var i = 0; i < dict.solicitationuuids.length; ++i) {
348                 if (!T.isString(dict.solicitationuuids[i])) {
349                     return;
350                 }
351             }
352             o.solicitationuuids = dict.solicitationuuids;
353         } else if (!T.isUndefined(dict.solicitationuuids)) {
354             return;
355         }
356
357         // appearance
358         if (T.isNull(dict.appearance) || T.isNumber(dict.appearance)) {
359             o.appearance = dict.appearance;
360         } else if (!T.isUndefined(dict.appearance)) {
361             return;
362         }
363
364         // includeTxPowerLevel
365         if (T.isNull(dict.includeTxPowerLevel) || T.isBoolean(dict.includeTxPowerLevel)) {
366             o.includeTxPowerLevel = dict.includeTxPowerLevel;
367         } else if (!T.isUndefined(dict.includeTxPowerLevel)) {
368             return;
369         }
370
371         // serviceData
372         if (
373             T.isNull(dict.serviceData) ||
374             dict.serviceData instanceof tizen.BluetoothLEServiceData
375         ) {
376             o.serviceData = dict.serviceData;
377         } else if (!T.isUndefined(dict.serviceData)) {
378             return;
379         }
380
381         // servicesData
382         if (T.isNull(dict.servicesData) || T.isArray(dict.servicesData)) {
383             // additional validation is done in setter of BluetoothLEAdvertiseData.servicesData
384             o.servicesData = dict.servicesData;
385         }
386
387         // manufacturerData
388         if (
389             T.isNull(dict.manufacturerData) ||
390             dict.manufacturerData instanceof tizen.BluetoothLEManufacturerData
391         ) {
392             o.manufacturerData = dict.manufacturerData;
393         } else if (!T.isUndefined(dict.manufacturerData)) {
394             return;
395         }
396
397         for (var prop in o) {
398             if (o.hasOwnProperty(prop) && this.hasOwnProperty(prop)) {
399                 this[prop] = o[prop];
400             }
401         }
402     }
403 };
404
405 //class tizen.BluetoothLEManufacturerData ///////////////////////////
406 tizen.BluetoothLEManufacturerData = function(d) {
407     AV.isConstructorCall(this, tizen.BluetoothLEManufacturerData);
408     var id_ = '';
409     var data_ = '';
410
411     Object.defineProperties(this, {
412         id: {
413             enumerable: true,
414             get: function() {
415                 return id_;
416             },
417             set: function(v) {
418                 id_ = Converter.toString(v);
419             }
420         },
421         data: {
422             enumerable: true,
423             get: function() {
424                 return data_;
425             },
426             set: function(v) {
427                 try {
428                     data_ = BluetoothManager_toDOMString(v);
429                 } catch (err) {
430                     data_ = Converter.toString(v);
431                 }
432             }
433         }
434     });
435
436     if (arguments.length >= 2) {
437         // public constructor
438         this.id = arguments[0];
439         this.data = arguments[1];
440     } else if (d && T.isObject(d)) {
441         // internal constructor
442         this.id = d.id;
443         this.data = d.data;
444     } else {
445         id_ = undefined;
446         data_ = undefined;
447     }
448 };
449
450 // class BluetoothClass ///////////////////////////
451 var BluetoothClass = function(data) {
452     var services = [];
453     if (data) {
454         services = data.services;
455     }
456
457     Object.defineProperties(this, {
458         major: { value: data.major, writable: false, enumerable: true },
459         minor: { value: data.minor, writable: false, enumerable: true },
460         services: {
461             enumerable: true,
462             set: function() {},
463             get: function() {
464                 return services.slice();
465             }
466         }
467     });
468 };
469
470 var BluetoothClass_hasService = function() {
471     privUtils_.log('Entered BluetoothClass.hasService()');
472     privUtils_.checkPrivilegeAccess4Ver(
473         '2.4',
474         Privilege.BLUETOOTH,
475         Privilege.BLUETOOTH_GAP
476     );
477
478     var args = AV.validateArgs(arguments, [
479         {
480             name: 'service',
481             type: AV.Types.UNSIGNED_LONG
482         }
483     ]);
484
485     var size = this.services.length;
486     for (var i = 0; i < size; i++) {
487         if (this.services[i] === args.service) {
488             return true;
489         }
490     }
491     return false;
492 };
493
494 BluetoothClass.prototype.hasService = function() {
495     return BluetoothClass_hasService.apply(this, arguments);
496 };
497
498 // class BluetoothSocket ///////////////////////////
499 var _BLUETOOTH_SOCKET_STATE_CLOSED = 'CLOSED';
500
501 function BluetoothSocketListeners() {
502     var that = this;
503     this.socketCallback = function(data) {
504         var event = data;
505         var socket = that.sockets[event.id];
506
507         if (socket) {
508             if ('onclose' === event.event) {
509                 // no more events
510                 that.removeListener(event.id);
511                 // change state
512                 Object.defineProperty(socket, 'state', {
513                     value: _BLUETOOTH_SOCKET_STATE_CLOSED
514                 });
515             }
516
517             var callback = socket[event.event];
518             if (T.isFunction(callback)) {
519                 callback();
520             }
521         } else {
522             privUtils_.log('Received event for an unknown socket: ' + event.id);
523         }
524     };
525 }
526
527 BluetoothSocketListeners.prototype.sockets = {};
528
529 BluetoothSocketListeners.prototype.addListener = function(socket) {
530     if (T.isEmptyObject(this.sockets)) {
531         native.addListener('BLUETOOTH_SOCKET_STATE_CHANGED', this.socketCallback);
532     }
533
534     this.sockets[socket._id] = socket;
535 };
536
537 BluetoothSocketListeners.prototype.removeListener = function(id) {
538     delete this.sockets[id];
539
540     if (T.isEmptyObject(this.sockets)) {
541         native.removeListener('BLUETOOTH_SOCKET_STATE_CHANGED', this.socketCallback);
542     }
543 };
544
545 var _bluetoothSocketListeners = new BluetoothSocketListeners();
546
547 var BluetoothSocket = function(data) {
548     Object.defineProperties(this, {
549         uuid: { value: data.uuid, writable: false, enumerable: true },
550         state: {
551             value: data.state,
552             writable: false,
553             enumerable: true,
554             configurable: true
555         },
556         peer: {
557             value: new BluetoothDevice(data.peer),
558             writable: false,
559             enumerable: true
560         },
561         onmessage: { value: null, writable: true, enumerable: true },
562         onclose: { value: null, writable: true, enumerable: true },
563         _id: { value: data.id, writable: false, enumerable: false }
564     });
565
566     _bluetoothSocketListeners.addListener(this);
567 };
568
569 BluetoothSocket.prototype.writeData = function(data) {
570     privUtils_.log('Entered BluetoothSocket.writeData()');
571
572     var byteData = BluetoothManager_toByteArray(data);
573
574     var callArgs = {
575         id: this._id,
576         data: byteData
577     };
578
579     var result = native.callSync('BluetoothSocketWriteData', callArgs);
580
581     if (native.isFailure(result)) {
582         throw native.getErrorObject(result);
583     } else {
584         return native.getResultObject(result);
585     }
586 };
587
588 BluetoothSocket.prototype.readData = function() {
589     privUtils_.log('Entered BluetoothSocket.readData()');
590
591     var callArgs = {
592         id: this._id
593     };
594
595     var result = native.callSync('BluetoothSocketReadData', callArgs);
596
597     if (native.isFailure(result)) {
598         throw native.getErrorObject(result);
599     } else {
600         return native.getResultObject(result);
601     }
602 };
603
604 BluetoothSocket.prototype.close = function() {
605     privUtils_.log('Entered BluetoothSocket.close()');
606
607     if (_BLUETOOTH_SOCKET_STATE_CLOSED !== this.state) {
608         var callArgs = {
609             id: this._id
610         };
611
612         var result = native.callSync('BluetoothSocketClose', callArgs);
613
614         if (native.isFailure(result)) {
615             throw native.getErrorObject(result);
616         }
617
618         // change state
619         Object.defineProperty(this, 'state', { value: _BLUETOOTH_SOCKET_STATE_CLOSED });
620     }
621 };
622
623 //class BluetoothLEDevice ///////////////////////////
624 var BluetoothLEDevice = function(data) {
625     var address = '',
626         name = null,
627         txpowerlevel = null,
628         appearance = null,
629         uuids = null,
630         solicitationuuids = null,
631         serviceData = null,
632         manufacturerData = null,
633         rssi = null;
634
635     if (data) {
636         address = data.address;
637         name = data.name || null;
638         txpowerlevel = data.txpowerlevel || null;
639         appearance = data.appearance || null;
640         uuids = data.uuids || null;
641         solicitationuuids = data.solicitationuuids || null;
642         if (data.serviceData) {
643             serviceData = [];
644             data.serviceData.forEach(function(d) {
645                 serviceData.push(new tizen.BluetoothLEServiceData(d));
646             });
647         }
648         if (data.manufacturerData) {
649             manufacturerData = new tizen.BluetoothLEManufacturerData(
650                 data.manufacturerData
651             );
652         }
653         if (data.rssi) {
654             rssi = data.rssi;
655         }
656     }
657
658     Object.defineProperties(this, {
659         address: { value: address, writable: false, enumerable: true },
660         name: { value: name, writable: false, enumerable: true },
661         txpowerlevel: { value: txpowerlevel, writable: false, enumerable: true },
662         appearance: { value: appearance, writable: false, enumerable: true },
663         uuids: {
664             enumerable: true,
665             set: function() {},
666             get: function() {
667                 var service_uuids = uuids ? uuids.slice() : null;
668                 return service_uuids;
669             }
670         },
671         solicitationuuids: {
672             enumerable: true,
673             set: function() {},
674             get: function() {
675                 return solicitationuuids ? solicitationuuids.slice() : null;
676             }
677         },
678         serviceData: {
679             enumerable: true,
680             set: function() {},
681             get: function() {
682                 return serviceData ? serviceData.slice() : null;
683             }
684         },
685         manufacturerData: {
686             value: manufacturerData,
687             writable: false,
688             enumerable: true
689         },
690         rssi: { value: rssi, writable: false, enumerable: true }
691     });
692 };
693
694 BluetoothLEDevice.prototype.connect = function() {
695     privUtils_.log('Entered BluetoothLEDevice.connect()');
696     var args = AV.validateArgs(arguments, [
697         {
698             name: 'successCallback',
699             type: AV.Types.FUNCTION,
700             optional: true,
701             nullable: true
702         },
703         {
704             name: 'errorCallback',
705             type: AV.Types.FUNCTION,
706             optional: true,
707             nullable: true
708         }
709     ]);
710
711     var callback = function(result) {
712         if (native.isFailure(result)) {
713             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
714         } else {
715             native.callIfPossible(args.successCallback);
716         }
717     };
718     // Errors are handled by error callback
719     var result = native.call(
720         'BluetoothLEDeviceConnect',
721         { address: this.address },
722         callback
723     );
724     if (native.isFailure(result)) {
725         throw native.getErrorObject(result);
726     }
727 };
728
729 BluetoothLEDevice.prototype.disconnect = function() {
730     privUtils_.log('Entered BluetoothLEDevice.disconnect()');
731     var args = AV.validateArgs(arguments, [
732         {
733             name: 'successCallback',
734             type: AV.Types.FUNCTION,
735             optional: true,
736             nullable: true
737         },
738         {
739             name: 'errorCallback',
740             type: AV.Types.FUNCTION,
741             optional: true,
742             nullable: true
743         }
744     ]);
745     var callback = function(result) {
746         if (native.isFailure(result)) {
747             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
748         } else {
749             native.callIfPossible(args.successCallback);
750         }
751     };
752
753     var result = native.call(
754         'BluetoothLEDeviceDisconnect',
755         { address: this.address },
756         callback
757     );
758     if (native.isFailure(result)) {
759         throw native.getErrorObject(result);
760     }
761 };
762
763 BluetoothLEDevice.prototype.getService = function() {
764     privUtils_.log('Entered BluetoothLEDevice.getService()');
765     var args = AV.validateArgs(arguments, [
766         {
767             name: 'uuid',
768             type: AV.Types.STRING
769         }
770     ]);
771
772     var callArgs = {
773         uuid: args.uuid,
774         address: this.address
775     };
776
777     var result = native.callSync('BluetoothLEDeviceGetService', callArgs);
778     if (native.isFailure(result)) {
779         throw native.getErrorObject(result);
780     } else {
781         return new BluetoothGATTService(native.getResultObject(result));
782     }
783 };
784
785 BluetoothLEDevice.prototype.getServiceAllUuids = function() {
786     privUtils_.log('Entered BluetoothLEDevice.getServiceAllUuids()');
787
788     var callArgs = {
789         address: this.address
790     };
791
792     var result = native.callSync('BluetoothLEDeviceGetServiceAllUuids', callArgs);
793     if (native.isFailure(result)) {
794         throw native.getErrorObject(result);
795     } else {
796         var uuids = native.getResultObject(result);
797         return uuids;
798     }
799 };
800
801 BluetoothLEDevice.prototype.isConnected = function() {
802     privUtils_.log('Entered BluetoothLEDevice.isConnected()');
803
804     var callArgs = {
805         address: this.address
806     };
807
808     var result = native.callSync('BluetoothLEDeviceIsConnected', callArgs);
809     if (native.isFailure(result)) {
810         throw native.getErrorObject(result);
811     }
812     return native.getResultObject(result);
813 };
814
815 BluetoothLEDevice.prototype.addConnectStateChangeListener = function() {
816     privUtils_.log('Entered BluetoothLEDevice.addConnectStateChangeListener()');
817     var args = AV.validateArgs(arguments, [
818         {
819             name: 'listener',
820             type: AV.Types.LISTENER,
821             values: ['onconnected', 'ondisconnected']
822         }
823     ]);
824
825     var that = this;
826
827     var func = function(event) {
828         if (event.address === that.address && args.listener[event.action]) {
829             args.listener[event.action](that);
830         }
831     };
832
833     var watchId = _bleConnectChangeListener.addListener(func);
834
835     return watchId;
836 };
837
838 BluetoothLEDevice.prototype.removeConnectStateChangeListener = function() {
839     privUtils_.log('Entered BluetoothLEDevice.removeConnectStateChangeListener()');
840
841     var args = AV.validateArgs(arguments, [
842         {
843             name: 'watchID',
844             type: AV.Types.LONG
845         }
846     ]);
847
848     _bleConnectChangeListener.removeListener(args.watchID);
849 };
850
851 BluetoothLEDevice.prototype.getAttMtu = function() {
852     privUtils_.log('Entered BluetoothLEDevice.getAttMtu()');
853
854     var callArgs = {
855         address: this.address
856     };
857
858     var result = native.callSync('BluetoothLEDeviceGetAttMtu', callArgs);
859     if (native.isFailure(result)) {
860         throw native.getErrorObject(result);
861     }
862     return native.getResultObject(result);
863 };
864
865 BluetoothLEDevice.prototype.requestAttMtuChange = function() {
866     privUtils_.log('Entered BluetoothLEDevice.requestAttMtuChange()');
867
868     var args = AV.validateArgs(arguments, [
869         {
870             name: 'mtu',
871             type: AV.Types.LONG
872         }
873     ]);
874
875     var callArgs = {
876         address: this.address,
877         mtu: args.mtu
878     };
879
880     var result = native.callSync('BluetoothLEDeviceRequestAttMtuChange', callArgs);
881     if (native.isFailure(result)) {
882         throw native.getErrorObject(result);
883     }
884     return native.getResultObject(result);
885 };
886
887 BluetoothLEDevice.prototype.addAttMtuChangeListener = function() {
888     privUtils_.log('Entered BluetoothLEDevice.addAttMtuChangeListener()');
889     var args = AV.validateArgs(arguments, [
890         {
891             name: 'callback',
892             type: AV.Types.FUNCTION
893         }
894     ]);
895
896     var callArgs = { address: this.address };
897
898     var callback = function(result) {
899         privUtils_.log('Entered BluetoothLEDevice.addAttMtuChangeListener() callback');
900         args.callback(result.attMtuValue);
901     };
902
903     var watchId = _bleAttMtuChangeListener.addListener(callback, callArgs);
904
905     return watchId;
906 };
907
908 BluetoothLEDevice.prototype.removeAttMtuChangeListener = function() {
909     privUtils_.log('Entered BluetoothLEDevice.removeAttMtuChangeListener()');
910
911     var args = AV.validateArgs(arguments, [
912         {
913             name: 'watchID',
914             type: AV.Types.LONG
915         }
916     ]);
917
918     var callArgs = { address: this.address };
919
920     _bleAttMtuChangeListener.removeListener(args.watchID, callArgs);
921 };
922
923 // class BluetoothDevice ///////////////////////////
924 var BluetoothDevice = function(data) {
925     var self = this;
926     function _getter(field) {
927         var callArgs = {};
928
929         callArgs.address = self.address;
930         callArgs.field = field;
931
932         var result = native.callSync('BluetoothDeviceGetBoolValue', callArgs);
933
934         if (native.isFailure(result)) {
935             return false;
936         } else {
937             return native.getResultObject(result);
938         }
939     }
940
941     function isBondedGetter() {
942         return _getter('isBonded');
943     }
944
945     function isTrustedGetter() {
946         return _getter('isTrusted');
947     }
948
949     function isConnectedGetter() {
950         return _getter('isConnected');
951     }
952
953     var uuids = [];
954     if (data) {
955         uuids = data.uuids;
956     }
957
958     Object.defineProperties(this, {
959         name: { value: data.name, writable: false, enumerable: true },
960         address: { value: data.address, writable: false, enumerable: true },
961         deviceClass: {
962             value: new BluetoothClass(data.deviceClass),
963             writable: false,
964             enumerable: true
965         },
966         isBonded: {
967             enumerable: true,
968             set: function() {},
969             get: isBondedGetter
970         },
971         isTrusted: {
972             enumerable: true,
973             set: function() {},
974             get: isTrustedGetter
975         },
976         isConnected: {
977             enumerable: true,
978             set: function() {},
979             get: isConnectedGetter
980         },
981         uuids: {
982             enumerable: true,
983             set: function() {},
984             get: function() {
985                 return uuids.slice();
986             }
987         }
988     });
989 };
990
991 BluetoothDevice.prototype.connectToServiceByUUID = function() {
992     privUtils_.log('Entered BluetoothDevice.connectToServiceByUUID()');
993
994     var args = AV.validateArgs(arguments, [
995         {
996             name: 'uuid',
997             type: AV.Types.STRING
998         },
999         {
1000             name: 'successCallback',
1001             type: AV.Types.FUNCTION
1002         },
1003         {
1004             name: 'errorCallback',
1005             type: AV.Types.FUNCTION,
1006             optional: true,
1007             nullable: true
1008         }
1009     ]);
1010
1011     var callArgs = {
1012         address: this.address,
1013         uuid: args.uuid
1014     };
1015     var callback = function(result) {
1016         if (native.isFailure(result)) {
1017             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
1018         } else {
1019             args.successCallback(new BluetoothSocket(native.getResultObject(result)));
1020         }
1021     };
1022
1023     var result = native.call('BluetoothDeviceConnectToServiceByUUID', callArgs, callback);
1024     if (native.isFailure(result)) {
1025         throw native.getErrorObject(result);
1026     }
1027 };
1028
1029 // class BluetoothServiceHandler ///////////////////////////
1030 function BluetoothServiceListeners() {
1031     var that = this;
1032     this.serviceCallback = function(data) {
1033         var e = data;
1034         var service = that.services[e.uuid];
1035         var result = new BluetoothSocket(e);
1036
1037         if (service) {
1038             privUtils_.log(service);
1039             service.onconnect(result);
1040         }
1041     };
1042 }
1043
1044 BluetoothServiceListeners.prototype.services = {};
1045
1046 BluetoothServiceListeners.prototype.addListener = function(service) {
1047     if (T.isEmptyObject(this.services)) {
1048         native.addListener('BLUETOOTH_SERVICE_ONCONNECT', this.serviceCallback);
1049     }
1050
1051     this.services[service.uuid] = service;
1052 };
1053
1054 BluetoothServiceListeners.prototype.removeListener = function(uuid) {
1055     delete this.services[uuid];
1056
1057     if (T.isEmptyObject(this.services)) {
1058         native.removeListener('BLUETOOTH_SERVICE_ONCONNECT', this.serviceCallback);
1059     }
1060 };
1061
1062 var _bluetoothServiceListeners = new BluetoothServiceListeners();
1063
1064 var BluetoothServiceHandler = function(data) {
1065     function isConnectedGetter() {
1066         var callArgs = {
1067             uuid: this.uuid
1068         };
1069
1070         var result = native.callSync('BluetoothAdapterIsServiceConnected', {
1071             uuid: this.uuid
1072         });
1073
1074         if (native.isFailure(result)) {
1075             return false;
1076         } else {
1077             return native.getResultObject(result);
1078         }
1079     }
1080
1081     Object.defineProperties(this, {
1082         uuid: { value: data.uuid, writable: false, enumerable: true },
1083         name: { value: data.name, writable: false, enumerable: true },
1084         isConnected: {
1085             enumerable: true,
1086             set: function() {},
1087             get: isConnectedGetter
1088         },
1089         onconnect: { value: null, writable: true, enumerable: true }
1090     });
1091
1092     _bluetoothServiceListeners.addListener(this);
1093 };
1094
1095 BluetoothServiceHandler.prototype.unregister = function() {
1096     privUtils_.log('Entered BluetoothServiceHandler.unregister()');
1097     var args = AV.validateArgs(arguments, [
1098         {
1099             name: 'successCallback',
1100             type: AV.Types.FUNCTION,
1101             optional: true,
1102             nullable: true
1103         },
1104         {
1105             name: 'errorCallback',
1106             type: AV.Types.FUNCTION,
1107             optional: true,
1108             nullable: true
1109         }
1110     ]);
1111
1112     var callArgs = {
1113         uuid: this.uuid
1114     };
1115
1116     var callback = function(result) {
1117         if (native.isFailure(result)) {
1118             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
1119         } else {
1120             native.callIfPossible(args.successCallback);
1121         }
1122     };
1123
1124     var result = native.call('BluetoothServiceHandlerUnregister', callArgs, callback);
1125     if (native.isFailure(result)) {
1126         throw native.getErrorObject(result);
1127     }
1128
1129     _bluetoothServiceListeners.removeListener(this.uuid);
1130 };
1131
1132 // class BluetoothHealthApplication ///////////////////////////
1133 function BluetoothHealthApplicationListeners() {
1134     var that = this;
1135     this.appCallback = function(data) {
1136         var event = data;
1137         var app = that.apps[event.id];
1138
1139         if (app) {
1140             var callback = app[event.event];
1141             if (T.isFunction(callback)) {
1142                 var param;
1143                 switch (event.event) {
1144                 case 'onconnect':
1145                     param = new BluetoothHealthChannel(native.getResultObject(event));
1146                     break;
1147
1148                 default:
1149                     privUtils_.log('Unknown event: ' + event.event);
1150                     break;
1151                 }
1152                 callback(param);
1153             }
1154         } else {
1155             privUtils_.log('Received event for an unknown application: ' + event.id);
1156         }
1157     };
1158 }
1159
1160 BluetoothHealthApplicationListeners.prototype.apps = {};
1161
1162 BluetoothHealthApplicationListeners.prototype.addListener = function(app) {
1163     if (T.isEmptyObject(this.apps)) {
1164         native.addListener('BLUETOOTH_HEALTH_APPLICATION_CHANGED', this.appCallback);
1165     }
1166
1167     this.apps[app._id] = app;
1168 };
1169
1170 BluetoothHealthApplicationListeners.prototype.removeListener = function(id) {
1171     delete this.apps[id];
1172
1173     if (T.isEmptyObject(this.apps)) {
1174         native.removeListener('BLUETOOTH_HEALTH_APPLICATION_CHANGED', this.appCallback);
1175     }
1176 };
1177
1178 var _bluetoothHealthApplicationListeners = new BluetoothHealthApplicationListeners();
1179
1180 var BluetoothHealthApplication = function(data) {
1181     Object.defineProperties(this, {
1182         dataType: { value: data.dataType, writable: false, enumerable: true },
1183         name: { value: data.name, writable: false, enumerable: true },
1184         onconnect: { value: null, writable: true, enumerable: true },
1185         _id: { value: data._id, writable: false, enumerable: false }
1186     });
1187
1188     _bluetoothHealthApplicationListeners.addListener(this);
1189 };
1190
1191 BluetoothHealthApplication.prototype.unregister = function() {
1192     privUtils_.log('Entered BluetoothHealthApplication.unregister()');
1193     privUtils_.printDeprecationWarningFor('BluetoothHealthApplication');
1194     var args = AV.validateArgs(arguments, [
1195         {
1196             name: 'successCallback',
1197             type: AV.Types.FUNCTION,
1198             optional: true,
1199             nullable: true
1200         },
1201         {
1202             name: 'errorCallback',
1203             type: AV.Types.FUNCTION,
1204             optional: true,
1205             nullable: true
1206         }
1207     ]);
1208
1209     var callArgs = { id: this._id };
1210
1211     var callback = function(result) {
1212         if (native.isFailure(result)) {
1213             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
1214         } else {
1215             native.callIfPossible(args.successCallback);
1216         }
1217     };
1218
1219     var result = native.call('BluetoothHealthApplicationUnregister', callArgs, callback);
1220     if (native.isFailure(result)) {
1221         throw native.getErrorObject(result);
1222     }
1223
1224     _bluetoothHealthApplicationListeners.removeListener(this._id);
1225 };
1226
1227 // class BluetoothProfileHandler ///////////////////////////
1228 var _BluetoothProfileType = {
1229     HEALTH: 'HEALTH'
1230 };
1231
1232 var BluetoothProfileHandler = function(data) {
1233     if (data) {
1234         var profileType = data.profileType;
1235         function profileTypeGetter() {
1236             privUtils_.printDeprecationWarningFor('profileType');
1237             return profileType;
1238         }
1239         Object.defineProperties(this, {
1240             profileType: { enumerable: true, set: function() {}, get: profileTypeGetter }
1241         });
1242     }
1243 };
1244
1245 // class BluetoothHealthProfileHandler ///////////////////////////
1246 var BluetoothHealthProfileHandler = function(data) {
1247     BluetoothProfileHandler.call(this, data);
1248 };
1249
1250 BluetoothHealthProfileHandler.prototype = new BluetoothProfileHandler();
1251
1252 BluetoothHealthProfileHandler.prototype.constructor = BluetoothProfileHandler;
1253
1254 BluetoothHealthProfileHandler.prototype.registerSinkApplication = function() {
1255     privUtils_.log('Entered BluetoothHealthProfileHandler.registerSinkApplication()');
1256     privUtils_.printDeprecationWarningFor('BluetoothHealthProfileHandler');
1257
1258     var args = AV.validateArgs(arguments, [
1259         {
1260             name: 'dataType',
1261             type: AV.Types.LONG // there's no short type
1262         },
1263         {
1264             name: 'name',
1265             type: AV.Types.STRING
1266         },
1267         {
1268             name: 'successCallback',
1269             type: AV.Types.FUNCTION
1270         },
1271         {
1272             name: 'errorCallback',
1273             type: AV.Types.FUNCTION,
1274             optional: true,
1275             nullable: true
1276         }
1277     ]);
1278
1279     var callArgs = {
1280         dataType: args.dataType,
1281         name: args.name
1282     };
1283
1284     var callback = function(result) {
1285         if (native.isFailure(result)) {
1286             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
1287         } else {
1288             args.successCallback(
1289                 new BluetoothHealthApplication(native.getResultObject(result))
1290             );
1291         }
1292     };
1293
1294     var result = native.call(
1295         'BluetoothHealthProfileHandlerRegisterSinkApp',
1296         callArgs,
1297         callback
1298     );
1299     if (native.isFailure(result)) {
1300         throw native.getErrorObject(result);
1301     }
1302 };
1303
1304 BluetoothHealthProfileHandler.prototype.connectToSource = function() {
1305     privUtils_.log('Entered BluetoothHealthProfileHandler.connectToSource()');
1306     privUtils_.printDeprecationWarningFor('BluetoothHealthProfileHandler');
1307
1308     var args = AV.validateArgs(arguments, [
1309         {
1310             name: 'peer',
1311             type: AV.Types.PLATFORM_OBJECT,
1312             values: BluetoothDevice
1313         },
1314         {
1315             name: 'application',
1316             type: AV.Types.PLATFORM_OBJECT,
1317             values: BluetoothHealthApplication
1318         },
1319         {
1320             name: 'successCallback',
1321             type: AV.Types.FUNCTION
1322         },
1323         {
1324             name: 'errorCallback',
1325             type: AV.Types.FUNCTION,
1326             optional: true,
1327             nullable: true
1328         }
1329     ]);
1330
1331     var callArgs = {
1332         address: args.peer.address,
1333         appId: args.application._id
1334     };
1335
1336     var callback = function(result) {
1337         if (native.isFailure(result)) {
1338             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
1339         } else {
1340             var channel = native.getResultObject(result);
1341             channel.peer = args.peer;
1342             channel.appId = args.application._id;
1343             args.successCallback(new BluetoothHealthChannel(channel));
1344         }
1345     };
1346
1347     var result = native.call(
1348         'BluetoothHealthProfileHandlerConnectToSource',
1349         callArgs,
1350         callback
1351     );
1352     if (native.isFailure(result)) {
1353         throw native.getErrorObject(result);
1354     }
1355 };
1356
1357 // class BluetoothHealthChannel ///////////////////////////
1358 var BluetoothHealthChannel = function(data) {
1359     Object.defineProperties(this, {
1360         peer: { value: data.peer, writable: false, enumerable: true },
1361         channelType: { value: data.channelType, writable: false, enumerable: true },
1362         application: {
1363             value: _bluetoothHealthApplicationListeners.apps[data.appId],
1364             writable: false,
1365             enumerable: true
1366         },
1367         isConnected: {
1368             value: data.isConnected,
1369             writable: false,
1370             enumerable: true,
1371             configurable: true
1372         },
1373         _id: { value: data._id, writable: false, enumerable: false }
1374     });
1375 };
1376
1377 BluetoothHealthChannel.prototype.close = function() {
1378     privUtils_.log('Entered BluetoothHealthChannel.close()');
1379     privUtils_.printDeprecationWarningFor('BluetoothHealthChannel');
1380
1381     if (this.isConnected) {
1382         var callArgs = {
1383             channel: this._id,
1384             address: this.peer.address
1385         };
1386
1387         var result = native.callSync('BluetoothHealthChannelClose', callArgs);
1388
1389         if (native.isFailure(result)) {
1390             throw native.getErrorObject(result);
1391         }
1392
1393         Object.defineProperty(this, 'isConnected', { value: false });
1394     }
1395 };
1396
1397 BluetoothHealthChannel.prototype.sendData = function() {
1398     privUtils_.log('Entered BluetoothHealthChannel.sendData()');
1399     privUtils_.printDeprecationWarningFor('BluetoothHealthChannel');
1400
1401     var args = AV.validateArgs(arguments, [
1402         {
1403             name: 'data',
1404             type: AV.Types.ARRAY,
1405             values: AV.Types.BYTE
1406         }
1407     ]);
1408
1409     var callArgs = {
1410         channel: this._id,
1411         data: args.data
1412     };
1413
1414     var result = native.callSync('BluetoothHealthChannelSendData', callArgs);
1415
1416     if (native.isFailure(result)) {
1417         throw native.getErrorObject(result);
1418     } else {
1419         return native.getResultObject(result);
1420     }
1421 };
1422
1423 var _healthListeners = {};
1424
1425 function _BluetoothHealthChannelChangeCallback(event) {
1426     var e = event;
1427     var callback = _healthListeners[e.id];
1428     var d;
1429
1430     switch (e.event) {
1431     case 'onmessage':
1432         d = e.data;
1433         break;
1434
1435     case 'onclose':
1436         break;
1437
1438     default:
1439         privUtils_.log('Unknown mode: ' + e.event);
1440         return;
1441     }
1442
1443     if (callback[e.event]) {
1444         callback[e.event](d);
1445     }
1446 }
1447
1448 var BluetoothHealthChannel_setListener = function() {
1449     privUtils_.log('Entered BluetoothHealthChannel.setListener()');
1450     privUtils_.printDeprecationWarningFor('BluetoothHealthChannel');
1451     privUtils_.checkPrivilegeAccess4Ver(
1452         '2.4',
1453         Privilege.BLUETOOTH,
1454         Privilege.BLUETOOTH_HEALTH
1455     );
1456     var args = AV.validateArgs(arguments, [
1457         {
1458             name: 'changeCallback',
1459             type: AV.Types.LISTENER,
1460             values: ['onmessage', 'onclose']
1461         }
1462     ]);
1463
1464     if (T.isEmptyObject(_healthListeners)) {
1465         native.addListener(
1466             'BluetoothHealthChannelChangeCallback',
1467             _BluetoothHealthChannelChangeCallback
1468         );
1469     }
1470     _healthListeners[this._id] = args.changeCallback;
1471 };
1472
1473 BluetoothHealthChannel.prototype.setListener = function() {
1474     BluetoothHealthChannel_setListener.apply(this, arguments);
1475 };
1476
1477 var BluetoothHealthChannel_unsetListener = function() {
1478     privUtils_.log('Entered BluetoothHealthChannel.unsetListener ()');
1479     privUtils_.printDeprecationWarningFor('BluetoothHealthChannel');
1480     if (T.isEmptyObject(_healthListeners)) {
1481         privUtils_.checkPrivilegeAccess4Ver(
1482             '2.4',
1483             Privilege.BLUETOOTH,
1484             Privilege.BLUETOOTH_HEALTH
1485         );
1486     }
1487
1488     delete _healthListeners[this._id];
1489
1490     if (T.isEmptyObject(_healthListeners)) {
1491         native.removeListener(
1492             'BluetoothHealthChannelChangeCallback',
1493             _BluetoothHealthChannelChangeCallback
1494         );
1495     }
1496 };
1497
1498 BluetoothHealthChannel.prototype.unsetListener = function() {
1499     privUtils_.printDeprecationWarningFor('BluetoothHealthChannel');
1500     BluetoothHealthChannel_unsetListener.apply(this, arguments);
1501 };
1502
1503 /**
1504  * Creates a manager for specified listener event.
1505  *
1506  * @param {string} name - name of the listener this manager handles
1507  * @param {function} callback - function to be invoked when event specified by the name
1508  *                              fires.
1509  *                              This function should return false if the callback
1510  *                              doesn't want to handle the event anymore, true otherwise.
1511  *                              This function should have following signature:
1512  *                              bool callback(event, successCallback, errorCallback);
1513  *
1514  * @return {object} object which allows to add or remove callbacks for specified listener
1515  */
1516 function _singleListenerBuilder(name, callback) {
1517     var listenerName = name;
1518     var successCallback;
1519     var errorCallback;
1520     var callbackFunction = callback;
1521     var listenerRegistered = false;
1522
1523     function innerCallback(event) {
1524         if (!callbackFunction(event, successCallback, errorCallback)) {
1525             removeListener();
1526         }
1527     }
1528
1529     function addListener(s, e) {
1530         successCallback = s;
1531         errorCallback = e;
1532
1533         if (!listenerRegistered) {
1534             native.addListener(listenerName, innerCallback);
1535             listenerRegistered = true;
1536         }
1537     }
1538
1539     function removeListener() {
1540         if (listenerRegistered) {
1541             native.removeListener(listenerName, innerCallback);
1542             listenerRegistered = false;
1543         }
1544
1545         successCallback = undefined;
1546         errorCallback = undefined;
1547     }
1548
1549     return {
1550         addListener: addListener,
1551         removeListener: removeListener
1552     };
1553 }
1554
1555 var _bleScanListener = _singleListenerBuilder('BluetoothLEScanCallback', function(
1556     event,
1557     successCallback,
1558     errorCallback
1559 ) {
1560     var d;
1561     var ret = true;
1562
1563     switch (event.action) {
1564     case 'onsuccess':
1565         d = new BluetoothLEDevice(event.data);
1566         break;
1567
1568     case 'onerror':
1569         if (errorCallback) {
1570             errorCallback(native.getErrorObject(event));
1571         }
1572         return ret;
1573
1574     default:
1575         privUtils_.log('Unknown mode: ' + event.action);
1576         return ret;
1577     }
1578     if (successCallback) {
1579         successCallback(d);
1580     }
1581
1582     return ret;
1583 });
1584
1585 var _bleAdvertiseListener = _singleListenerBuilder(
1586     'BluetoothLEAdvertiseCallback',
1587     function(event, successCallback, errorCallback) {
1588         var d;
1589         var ret = true;
1590
1591         switch (event.action) {
1592         case 'onstate':
1593             if (successCallback) {
1594                 successCallback(native.getResultObject(event));
1595                 if (native.getResultObject(event) == 'STOPPED') {
1596                     _bleAdvertiseListener.removeListener();
1597                 }
1598             }
1599             return ret;
1600
1601         case 'onerror':
1602             if (errorCallback) {
1603                 errorCallback(native.getErrorObject(event));
1604             }
1605             return ret;
1606
1607         default:
1608             privUtils_.log('Unknown mode: ' + event.action);
1609             return ret;
1610         }
1611     }
1612 );
1613
1614 //class BluetoothLEAdapter ///////////////////////////
1615 var BluetoothLEAdapter = function() {};
1616
1617 BluetoothLEAdapter.prototype.startScan = function() {
1618     privUtils_.log('Entered BluetoothLEAdapter.startScan()');
1619     var args = AV.validateArgs(arguments, [
1620         {
1621             name: 'successCallback',
1622             type: AV.Types.FUNCTION
1623         },
1624         {
1625             name: 'errorCallback',
1626             type: AV.Types.FUNCTION,
1627             optional: true,
1628             nullable: true
1629         }
1630     ]);
1631
1632     var result = native.callSync('BluetoothLEAdapterStartScan', {});
1633     if (native.isFailure(result)) {
1634         throw native.getErrorObject(result);
1635     }
1636
1637     _bleScanListener.addListener(args.successCallback, args.errorCallback);
1638 };
1639
1640 BluetoothLEAdapter.prototype.stopScan = function() {
1641     privUtils_.log('Entered BluetoothLEAdapter.stopScan()');
1642
1643     _bleScanListener.removeListener();
1644
1645     var result = native.callSync('BluetoothLEAdapterStopScan', {});
1646     if (native.isFailure(result)) {
1647         throw native.getErrorObject(result);
1648     }
1649 };
1650
1651 BluetoothLEAdapter.prototype.isScanning = function() {
1652     privUtils_.log('Entered BluetoothLEAdapter.isScanning()');
1653
1654     var result = native.callSync('BluetoothLEAdapterIsScanning', {});
1655     if (native.isFailure(result)) {
1656         throw native.getErrorObject(result);
1657     }
1658     return native.getResultObject(result);
1659 };
1660
1661 var _BluetoothAdvertisePacketType = {
1662     ADVERTISE: 'ADVERTISE',
1663     SCAN_RESPONSE: 'SCAN_RESPONSE'
1664 };
1665
1666 var _BluetoothAdvertisingMode = {
1667     BALANCED: 'BALANCED',
1668     LOW_LATENCY: 'LOW_LATENCY',
1669     LOW_ENERGY: 'LOW_ENERGY'
1670 };
1671
1672 BluetoothLEAdapter.prototype.startAdvertise = function() {
1673     privUtils_.log('Entered BluetoothLEAdapter.startAdvertise()');
1674     var args = AV.validateArgs(arguments, [
1675         {
1676             name: 'advertiseData',
1677             type: AV.Types.PLATFORM_OBJECT,
1678             values: tizen.BluetoothLEAdvertiseData
1679         },
1680         {
1681             name: 'packetType',
1682             type: AV.Types.ENUM,
1683             values: T.getValues(_BluetoothAdvertisePacketType)
1684         },
1685         {
1686             name: 'successCallback',
1687             type: AV.Types.FUNCTION
1688         },
1689         {
1690             name: 'errorCallback',
1691             type: AV.Types.FUNCTION,
1692             optional: true,
1693             nullable: true
1694         },
1695         {
1696             name: 'mode',
1697             type: AV.Types.ENUM,
1698             values: T.getValues(_BluetoothAdvertisingMode),
1699             optional: true,
1700             nullable: true
1701         },
1702         {
1703             name: 'connectable',
1704             type: AV.Types.BOOLEAN,
1705             optional: true,
1706             nullable: true
1707         }
1708     ]);
1709
1710     var callArgs = {
1711         advertiseData: args.advertiseData,
1712         packetType: args.packetType,
1713         mode: T.isNullOrUndefined(args.mode)
1714             ? _BluetoothAdvertisingMode.BALANCED
1715             : args.mode,
1716         connectable: T.isNullOrUndefined(args.connectable) ? true : args.connectable
1717     };
1718
1719     var result = native.callSync('BluetoothLEAdapterStartAdvertise', callArgs);
1720
1721     if (native.isFailure(result)) {
1722         throw native.getErrorObject(result);
1723     }
1724
1725     _bleAdvertiseListener.addListener(args.successCallback, args.errorCallback);
1726 };
1727
1728 BluetoothLEAdapter.prototype.stopAdvertise = function() {
1729     privUtils_.log('Entered BluetoothLEAdapter.stopAdvertise()');
1730
1731     var result = native.callSync('BluetoothLEAdapterStopAdvertise', {});
1732
1733     if (native.isFailure(result)) {
1734         throw native.getErrorObject(result);
1735     }
1736 };
1737
1738 BluetoothLEAdapter.prototype.addConnectStateChangeListener = function() {
1739     privUtils_.log('Entered BluetoothLEAdapter.addClientConnectStateChaneListener()');
1740     var args = AV.validateArgs(arguments, [
1741         {
1742             name: 'listener',
1743             type: AV.Types.LISTENER,
1744             values: ['onconnected', 'ondisconnected']
1745         }
1746     ]);
1747
1748     var func = function(event) {
1749         if (args.listener[event.action]) {
1750             args.listener[event.action](new BluetoothLEDevice(event.address));
1751         }
1752     };
1753
1754     return _bleConnectChangeListener.addListener(func);
1755 };
1756
1757 BluetoothLEAdapter.prototype.removeConnectStateChangeListener = function() {
1758     privUtils_.log('Entered BluetoothLEAdapter.removeConnectStateChangeListener()');
1759
1760     var args = AV.validateArgs(arguments, [
1761         {
1762             name: 'watchID',
1763             type: AV.Types.LONG
1764         }
1765     ]);
1766
1767     _bleConnectChangeListener.removeListener(args.watchID);
1768 };
1769
1770 //class BluetoothGATTService ///////////////////////////
1771 var BluetoothGATTService = function(data, address) {
1772     var handle_ = data.handle;
1773     var uuid_ = data.uuid;
1774     var serviceUuid_ = data.serviceUuid;
1775     //address_ is needed to control if device is still connected
1776     var address_ = address || data.address;
1777
1778     function servicesGetter() {
1779         var services = [];
1780         var result = native.callSync('BluetoothGATTClientServiceGetServices', {
1781             handle: handle_,
1782             address: address_
1783         });
1784         if (native.isSuccess(result)) {
1785             var resultObject = native.getResultObject(result);
1786             resultObject.forEach(function(s) {
1787                 services.push(new BluetoothGATTService(s, address_));
1788             });
1789         }
1790         return services;
1791     }
1792     function characteristicsGetter() {
1793         var characteristics = [];
1794         var result = native.callSync('BluetoothGATTClientServiceGetCharacteristics', {
1795             handle: handle_,
1796             uuid: serviceUuid_,
1797             address: address_
1798         });
1799         if (native.isSuccess(result)) {
1800             var resultObject = native.getResultObject(result);
1801             resultObject.forEach(function(c) {
1802                 if (!T.isNullOrUndefined(c)) {
1803                     characteristics.push(new BluetoothGATTCharacteristic(c, address_));
1804                 }
1805             });
1806         }
1807         return characteristics;
1808     }
1809
1810     /*
1811      * If this function is called as a constructor of BluetoothGATTServerService's
1812      * base class, some properties have to be left configurable, to enable redefinition.
1813      * Otherwise, if this function is called to create BluetoothGATTService,
1814      * they are left non-configurable.
1815      *
1816      * If BluetoothGATTService will be a base for any other class in the future,
1817      * the line below may have to be updated to take into account the new class.
1818      */
1819     var isConfigurable = this instanceof BluetoothGATTServerService;
1820
1821     Object.defineProperties(this, {
1822         uuid: { value: uuid_, writable: false, enumerable: true },
1823         serviceUuid: { value: serviceUuid_, writable: false, enumerable: true },
1824         services: {
1825             enumerable: true,
1826             configurable: isConfigurable,
1827             set: function() {},
1828             get: servicesGetter
1829         },
1830         characteristics: {
1831             enumerable: true,
1832             configurable: isConfigurable,
1833             set: function() {},
1834             get: characteristicsGetter
1835         }
1836     });
1837 };
1838
1839 var CurrentGATTServerEntityId = 0;
1840 function NextGattServerEntityID() {
1841     return ++CurrentGATTServerEntityId;
1842 }
1843
1844 function IsUuidValid(uuid) {
1845     return (
1846         BluetoothManager_UUIDIsValid16Bit(uuid) ||
1847         BluetoothManager_UUIDIsValid32Bit(uuid) ||
1848         BluetoothManager_UUIDIsValid128Bit(uuid)
1849     );
1850 }
1851
1852 var ValidateBluetoothGATTServerServiceInit = function(initData) {
1853     initData = AV.validateArgs(
1854         [
1855             initData['serviceUuid'],
1856             initData['isPrimary'] || true,
1857             initData['includedServices'] || [],
1858             initData['characteristics'] || []
1859         ],
1860         [
1861             {
1862                 name: 'serviceUuid',
1863                 type: AV.Types.STRING,
1864                 validator: IsUuidValid
1865             },
1866             {
1867                 name: 'isPrimary',
1868                 type: AV.Types.BOOLEAN
1869             },
1870             {
1871                 name: 'includedServices',
1872                 type: AV.Types.ARRAY
1873             },
1874             {
1875                 name: 'characteristics',
1876                 type: AV.Types.ARRAY
1877             }
1878         ]
1879     );
1880
1881     // "uuid" field is used to construct BluetoothGATTService, but it's not a part
1882     // of BluetoothGATTServerServiceInit dictionary.
1883     // In case of BluetoothGATTServerServices, its value is always the same as
1884     // serviceUuid's.
1885     initData.uuid = initData.serviceUuid;
1886     return initData;
1887 };
1888
1889 //class BluetoothGATTServerService ///////////////////////////
1890 var BluetoothGATTServerService = function(data) {
1891     data = ValidateBluetoothGATTServerServiceInit(data);
1892
1893     BluetoothGATTService.call(this, data, null);
1894
1895     var services_ = data.includedServices.map(function(serviceData) {
1896         return new BluetoothGATTServerService(serviceData, null);
1897     });
1898     var characteristics_ = data.characteristics.map(function(characteristicData) {
1899         return new BluetoothGATTServerCharacteristic(characteristicData, null);
1900     });
1901
1902     Object.defineProperties(this, {
1903         isPrimary: { value: data.isPrimary, writable: false, enumerable: true },
1904         services: {
1905             enumerable: true,
1906             get: function() {
1907                 return services_.slice();
1908             },
1909             set: function() {}
1910         },
1911         characteristics: {
1912             enumerable: true,
1913             get: function() {
1914                 return characteristics_.slice();
1915             },
1916             set: function() {}
1917         },
1918         // This property is "private" and meant not to be used by users
1919         _id: {
1920             // It has to be enumerable, to be serialized with JSON.stringify()
1921             enumerable: true,
1922             value: NextGattServerEntityID()
1923         }
1924     });
1925 };
1926
1927 BluetoothGATTServerService.prototype = Object.create(BluetoothGATTService.prototype);
1928
1929 Object.defineProperty(BluetoothGATTServerService.prototype, 'constructor', {
1930     value: BluetoothGATTServerService,
1931     enumerable: false,
1932     writable: true
1933 });
1934
1935 function _getIncludedServicesAndItsComponentsIdsRecursively(service) {
1936     var ids = [];
1937
1938     for (var serviceIndex = 0; serviceIndex < service.services.length; ++serviceIndex) {
1939         ids.push(service.services[serviceIndex]._id);
1940         ids = ids.concat(
1941             _getIncludedServicesAndItsComponentsIdsRecursively(
1942                 service.services[serviceIndex]
1943             )
1944         );
1945     }
1946
1947     for (
1948         var characteristicIndex = 0;
1949         characteristicIndex < service.characteristics.length;
1950         ++characteristicIndex
1951     ) {
1952         ids.push(service.characteristics[characteristicIndex]._id);
1953
1954         for (
1955             var descriptorIndex = 0;
1956             descriptorIndex <
1957             service.characteristics[characteristicIndex].descriptors.length;
1958             ++descriptorIndex
1959         ) {
1960             ids.push(
1961                 service.characteristics[characteristicIndex].descriptors[descriptorIndex]
1962                     ._id
1963             );
1964         }
1965     }
1966
1967     return ids;
1968 }
1969
1970 var BluetoothGATTServer_valid_unregisterService_errors = [
1971     'InvalidStateError',
1972     'AbortError'
1973 ];
1974 var BluetoothGATTServer_valid_unregisterService_exceptions = [
1975     'TypeMismatchError',
1976     'SecurityError'
1977 ];
1978
1979 BluetoothGATTServerService.prototype.unregister = function() {
1980     var args = AV.validateArgs(arguments, [
1981         {
1982             name: 'successCallback',
1983             type: AV.Types.FUNCTION,
1984             optional: true,
1985             nullable: true
1986         },
1987         {
1988             name: 'errorCallback',
1989             type: AV.Types.FUNCTION,
1990             optional: true,
1991             nullable: true
1992         }
1993     ]);
1994
1995     var serviceIndex = _BluetoothGATTServerServices.findIndex(
1996         function(service) {
1997             return service._id === this._id;
1998         }.bind(this)
1999     );
2000
2001     if (serviceIndex === -1) {
2002         throw new WebAPIException(
2003             'AbortError',
2004             'The service is not registered in the local GATT server or is included in another service'
2005         );
2006     }
2007
2008     function removeFromJSArrayAndCallSuccessCb() {
2009         _BluetoothGATTServerServices.splice(serviceIndex, 1);
2010         native.callIfPossible(args.successCallback);
2011     }
2012
2013     if (!_BluetoothGATTServerServicesRegisteredInNativeLayer[this._id]) {
2014         removeFromJSArrayAndCallSuccessCb();
2015         return;
2016     }
2017
2018     var callback = function(result) {
2019         if (native.isFailure(result)) {
2020             native.callIfPossible(
2021                 args.errorCallback,
2022                 native.getErrorObjectAndValidate(
2023                     result,
2024                     BluetoothGATTServer_valid_unregisterService_errors,
2025                     AbortError
2026                 )
2027             );
2028         } else {
2029             delete _BluetoothGATTServerServicesRegisteredInNativeLayer[this._id];
2030             removeFromJSArrayAndCallSuccessCb();
2031         }
2032     }.bind(this);
2033
2034     var callArgs = {
2035         _id: this._id,
2036         idsToRemoveFromNativeLayer: _getIncludedServicesAndItsComponentsIdsRecursively(
2037             this
2038         )
2039     };
2040     var result = native.call('BluetoothGATTServerUnregisterService', callArgs, callback);
2041
2042     if (native.isFailure(result)) {
2043         throw native.getErrorObjectAndValidate(
2044             result,
2045             BluetoothGATTServer_valid_unregisterService_errors,
2046             AbortError
2047         );
2048     }
2049 };
2050
2051 function _setReadWriteValueRequestCallbacksIfPresent(
2052     object,
2053     readValueRequestCallback,
2054     readValueSendResponseSuccessCallback,
2055     readValueSendResponseErrorCallback,
2056     writeValueRequestCallback,
2057     writeValueSendResponseSuccessCallback,
2058     writeValueSendResponseErrorCallback,
2059     successCallback,
2060     errorCallback
2061 ) {
2062     if (
2063         object instanceof BluetoothGATTServerCharacteristic ||
2064         object instanceof BluetoothGATTServerDescriptor
2065     ) {
2066         if (readValueRequestCallback) {
2067             object.setReadValueRequestCallback(
2068                 readValueRequestCallback,
2069                 successCallback,
2070                 errorCallback,
2071                 readValueSendResponseSuccessCallback,
2072                 readValueSendResponseErrorCallback
2073             );
2074         }
2075         if (writeValueRequestCallback) {
2076             object.setWriteValueRequestCallback(
2077                 writeValueRequestCallback,
2078                 successCallback,
2079                 errorCallback,
2080                 writeValueSendResponseSuccessCallback,
2081                 writeValueSendResponseErrorCallback
2082             );
2083         }
2084     }
2085 }
2086
2087 function _countCallbackToRegisterInService(service) {
2088     var count = 0;
2089     for (var serviceIndex = 0; serviceIndex < service.services.length; ++serviceIndex) {
2090         count = count + _countCallbackToRegisterInService(service.services[serviceIndex]);
2091     }
2092
2093     for (
2094         var characteristicIndex = 0;
2095         characteristicIndex < service.characteristics.length;
2096         ++characteristicIndex
2097     ) {
2098         var characteristic = service.characteristics[characteristicIndex];
2099         if (characteristic['_readValueRequestCallback']) {
2100             count++;
2101         }
2102         if (characteristic['_writeValueRequestCallback']) {
2103             count++;
2104         }
2105         for (
2106             var descriptorIndex = 0;
2107             descriptorIndex < characteristic.descriptors.length;
2108             ++descriptorIndex
2109         ) {
2110             var descriptor = characteristic.descriptors[descriptorIndex];
2111             if (descriptor['_readValueRequestCallback']) {
2112                 count++;
2113             }
2114             if (descriptor['_writeValueRequestCallback']) {
2115                 count++;
2116             }
2117         }
2118     }
2119     return count;
2120 }
2121
2122 function _registerReadWriteValueRequestCallbacksInGATTServiceRecursively(
2123     service,
2124     callbacksAggregator
2125 ) {
2126     for (var serviceIndex = 0; serviceIndex < service.services.length; ++serviceIndex) {
2127         _registerReadWriteValueRequestCallbacksInGATTServiceRecursively(
2128             service.services[serviceIndex],
2129             callbacksAggregator
2130         );
2131     }
2132
2133     for (
2134         var characteristicIndex = 0;
2135         characteristicIndex < service.characteristics.length;
2136         ++characteristicIndex
2137     ) {
2138         var characteristic = service.characteristics[characteristicIndex];
2139         // Callbacks for characteristics
2140         _setReadWriteValueRequestCallbacksIfPresent(
2141             characteristic,
2142             characteristic['_readValueRequestCallback'],
2143             characteristic['_readValueSendResponseSuccessCallback'],
2144             characteristic['_readValueSendResponseErrorCallback'],
2145             characteristic['_writeValueRequestCallback'],
2146             characteristic['_writeValueSendResponseSuccessCallback'],
2147             characteristic['_writeValueSendResponseErrorCallback'],
2148             callbacksAggregator.successCallback,
2149             callbacksAggregator.errorCallback
2150         );
2151         for (
2152             var descriptorIndex = 0;
2153             descriptorIndex < characteristic.descriptors.length;
2154             ++descriptorIndex
2155         ) {
2156             // Callbacks for descriptors
2157             var descriptor = characteristic.descriptors[descriptorIndex];
2158             _setReadWriteValueRequestCallbacksIfPresent(
2159                 descriptor,
2160                 descriptor['_readValueRequestCallback'],
2161                 descriptor['_readValueSendResponseSuccessCallback'],
2162                 descriptor['_readValueSendResponseErrorCallback'],
2163                 descriptor['_writeValueRequestCallback'],
2164                 descriptor['_writeValueSendResponseSuccessCallback'],
2165                 descriptor['_writeValueSendResponseErrorCallback'],
2166                 callbacksAggregator.successCallback,
2167                 callbacksAggregator.errorCallback
2168             );
2169         }
2170     }
2171 }
2172
2173 function _registerReadWriteValueRequestCallbacksInGATTService(
2174     service,
2175     successCallback,
2176     errorCallback
2177 ) {
2178     var count = _countCallbackToRegisterInService(service);
2179     var callbacksAggregator = new ResultCallbacksAggregator(
2180         count,
2181         function onAllSucceeded() {
2182             successCallback();
2183         },
2184         function onFailure(error) {
2185             errorCallback(error);
2186         }
2187     );
2188
2189     _registerReadWriteValueRequestCallbacksInGATTServiceRecursively(
2190         service,
2191         callbacksAggregator
2192     );
2193 }
2194
2195 var numberArrayToByteArray = function(array) {
2196     var d = [];
2197
2198     array.forEach(function(b) {
2199         d.push(Converter.toOctet(b));
2200     });
2201     return d;
2202 };
2203
2204 //class BluetoothGATTCharacteristic ///////////////////////////
2205 var BluetoothGATTCharacteristic = function(data, address) {
2206     if (!T.isObject(data)) {
2207         return null;
2208     }
2209     var address_ = address;
2210     var handle_ = data.handle;
2211     var descriptors_ = data.descriptors.map(function(descriptor_data) {
2212         return new BluetoothGATTDescriptor(descriptor_data, address_);
2213     });
2214     var isBroadcast_ = data.isBroadcast;
2215     var hasExtendedProperties_ = data.hasExtendedProperties;
2216     var isNotify_ = data.isNotify;
2217     var isIndication_ = data.isIndication;
2218     var isReadable_ = data.isReadable;
2219     var isSignedWrite_ = data.isSignedWrite;
2220     var isWritable_ = data.isWritable;
2221     var isWriteNoResponse_ = data.isWriteNoResponse;
2222     var uuid_ = data.uuid;
2223
2224     /*
2225      * If this function is called as a constructor of BluetoothGATTServerCharacteristic's
2226      * base class, some properties have to be left configurable, to enable redefinition.
2227      * Otherwise, if this function is called to create BluetoothGATTCharacteristic,
2228      * they are left non-configurable.
2229      *
2230      * If BluetoothGATTCharacteristic will be a base for any other class in the future,
2231      * the line below may have to be updated to take into account the new class.
2232      */
2233     var isConfigurable = this instanceof BluetoothGATTServerCharacteristic;
2234
2235     Object.defineProperties(this, {
2236         descriptors: {
2237             enumerable: true,
2238             configurable: isConfigurable,
2239             get: function() {
2240                 return descriptors_.slice();
2241             },
2242             set: function() {}
2243         },
2244         isBroadcast: {
2245             enumerable: true,
2246             get: function() {
2247                 return isBroadcast_;
2248             },
2249             set: function() {}
2250         },
2251         hasExtendedProperties: {
2252             enumerable: true,
2253             get: function() {
2254                 return hasExtendedProperties_;
2255             },
2256             set: function() {}
2257         },
2258         isNotify: {
2259             enumerable: true,
2260             get: function() {
2261                 return isNotify_;
2262             },
2263             set: function() {}
2264         },
2265         isIndication: {
2266             enumerable: true,
2267             get: function() {
2268                 return isIndication_;
2269             },
2270             set: function() {}
2271         },
2272         isReadable: {
2273             enumerable: true,
2274             get: function() {
2275                 return isReadable_;
2276             },
2277             set: function() {}
2278         },
2279         isSignedWrite: {
2280             enumerable: true,
2281             get: function() {
2282                 return isSignedWrite_;
2283             },
2284             set: function() {}
2285         },
2286         isWritable: {
2287             enumerable: true,
2288             get: function() {
2289                 return isWritable_;
2290             },
2291             set: function() {}
2292         },
2293         isWriteNoResponse: {
2294             enumerable: true,
2295             get: function() {
2296                 return isWriteNoResponse_;
2297             },
2298             set: function() {}
2299         },
2300         uuid: {
2301             enumerable: true,
2302             get: function() {
2303                 return uuid_;
2304             },
2305             set: function() {}
2306         }
2307     });
2308
2309     this.readValue = function() {
2310         privUtils_.log('Entered BluetoothGATTCharacteristic.readValue()');
2311         var args = AV.validateArgs(arguments, [
2312             {
2313                 name: 'successCallback',
2314                 type: AV.Types.FUNCTION
2315             },
2316             {
2317                 name: 'errorCallback',
2318                 type: AV.Types.FUNCTION,
2319                 optional: true,
2320                 nullable: true
2321             }
2322         ]);
2323
2324         var callback = function(result) {
2325             if (native.isFailure(result)) {
2326                 native.callIfPossible(args.errorCallback, native.getErrorObject(result));
2327             } else {
2328                 var d = numberArrayToByteArray(native.getResultObject(result));
2329                 args.successCallback(d);
2330             }
2331         };
2332
2333         var callArgs = { handle: handle_, address: address_ };
2334
2335         var result = native.call(
2336             'BluetoothGATTClientServiceReadValue',
2337             callArgs,
2338             callback
2339         );
2340
2341         if (native.isFailure(result)) {
2342             throw native.getErrorObject(result);
2343         }
2344     };
2345
2346     this.writeValue = function(value, successCallback, errorCallback) {
2347         privUtils_.log('Entered BluetoothGATTCharacteristic.writeValue()');
2348         var args = AV.validateArgs(Array.prototype.slice.call(arguments, 1), [
2349             {
2350                 name: 'successCallback',
2351                 type: AV.Types.FUNCTION,
2352                 optional: true,
2353                 nullable: true
2354             },
2355             {
2356                 name: 'errorCallback',
2357                 type: AV.Types.FUNCTION,
2358                 optional: true,
2359                 nullable: true
2360             }
2361         ]);
2362
2363         var callback = function(result) {
2364             if (native.isFailure(result)) {
2365                 native.callIfPossible(args.errorCallback, native.getErrorObject(result));
2366             } else {
2367                 native.callIfPossible(args.successCallback);
2368             }
2369         };
2370
2371         var callArgs = {
2372             handle: handle_,
2373             value: BluetoothManager_toByteArray(value),
2374             address: address_
2375         };
2376
2377         var result = native.call(
2378             'BluetoothGATTClientServiceWriteValue',
2379             callArgs,
2380             callback
2381         );
2382
2383         if (native.isFailure(result)) {
2384             throw native.getErrorObject(result);
2385         }
2386     };
2387
2388     var addValueChangeListener = function() {
2389         privUtils_.log('Entered BluetoothGATTCharacteristic.addValueChangeListener()');
2390         privUtils_.checkPrivilegeAccess4Ver(
2391             '2.4',
2392             Privilege.BLUETOOTH,
2393             Privilege.BLUETOOTH_ADMIN
2394         );
2395         var args = AV.validateArgs(arguments, [
2396             {
2397                 name: 'callback',
2398                 type: AV.Types.FUNCTION
2399             }
2400         ]);
2401
2402         var callArgs = { handle: handle_, address: address_ };
2403
2404         var callback = function(event) {
2405             if (event.handle === handle_) {
2406                 args.callback(numberArrayToByteArray(native.getResultObject(event)));
2407             }
2408         };
2409
2410         return _bluetoothGATTCharacteristicListener.addListener(callback, callArgs);
2411     };
2412
2413     this.addValueChangeListener = function() {
2414         return addValueChangeListener.apply(this, arguments);
2415     };
2416
2417     this.removeValueChangeListener = function() {
2418         privUtils_.log('Entered BluetoothGATTCharacteristic.removeValueChangeListener()');
2419
2420         var args = AV.validateArgs(arguments, [
2421             {
2422                 name: 'watchID',
2423                 type: AV.Types.LONG
2424             }
2425         ]);
2426
2427         var callArgs = { handle: handle_, address: address_ };
2428
2429         return _bluetoothGATTCharacteristicListener.removeListener(
2430             args.watchID,
2431             callArgs
2432         );
2433     };
2434 };
2435
2436 var ValidateBluetoothGATTServerCharacteristicInit = function(initData) {
2437     return AV.validateArgs(
2438         [
2439             initData['uuid'],
2440             initData['descriptors'] || [],
2441             initData['isBroadcast'] || false,
2442             initData['hasExtendedProperties'] || false,
2443             initData['isNotify'] || false,
2444             initData['isIndication'] || false,
2445             initData['isReadable'] || false,
2446             initData['isSignedWrite'] || false,
2447             initData['isWritable'] || false,
2448             initData['isWriteNoResponse'] || false,
2449             initData['readPermission'] || false,
2450             initData['writePermission'] || false,
2451             initData['encryptedReadPermission'] || false,
2452             initData['encryptedWritePermission'] || false,
2453             initData['encryptedSignedReadPermission'] || false,
2454             initData['encryptedSignedWritePermission'] || false,
2455             initData['readValueRequestCallback'] || null,
2456             initData['readValueSendResponseSuccessCallback'] || null,
2457             initData['readValueSendResponseErrorCallback'] || null,
2458             initData['writeValueRequestCallback'] || null,
2459             initData['writeValueSendResponseSuccessCallback'] || null,
2460             initData['writeValueSendResponseErrorCallback'] || null
2461         ],
2462         [
2463             {
2464                 name: 'uuid',
2465                 type: AV.Types.STRING,
2466                 validator: IsUuidValid
2467             },
2468             {
2469                 name: 'descriptors',
2470                 type: AV.Types.ARRAY
2471             },
2472             {
2473                 name: 'isBroadcast',
2474                 type: AV.Types.BOOLEAN
2475             },
2476             {
2477                 name: 'hasExtendedProperties',
2478                 type: AV.Types.BOOLEAN
2479             },
2480             {
2481                 name: 'isNotify',
2482                 type: AV.Types.BOOLEAN
2483             },
2484             {
2485                 name: 'isIndication',
2486                 type: AV.Types.BOOLEAN
2487             },
2488             {
2489                 name: 'isReadable',
2490                 type: AV.Types.BOOLEAN
2491             },
2492             {
2493                 name: 'isSignedWrite',
2494                 type: AV.Types.BOOLEAN
2495             },
2496             {
2497                 name: 'isWritable',
2498                 type: AV.Types.BOOLEAN
2499             },
2500             {
2501                 name: 'isWriteNoResponse',
2502                 type: AV.Types.BOOLEAN
2503             },
2504             {
2505                 name: 'readPermission',
2506                 type: AV.Types.BOOLEAN
2507             },
2508             {
2509                 name: 'writePermission',
2510                 type: AV.Types.BOOLEAN
2511             },
2512             {
2513                 name: 'encryptedReadPermission',
2514                 type: AV.Types.BOOLEAN
2515             },
2516             {
2517                 name: 'encryptedWritePermission',
2518                 type: AV.Types.BOOLEAN
2519             },
2520             {
2521                 name: 'encryptedSignedReadPermission',
2522                 type: AV.Types.BOOLEAN
2523             },
2524             {
2525                 name: 'encryptedSignedWritePermission',
2526                 type: AV.Types.BOOLEAN
2527             },
2528             {
2529                 name: 'readValueRequestCallback',
2530                 type: AV.Types.FUNCTION,
2531                 nullable: true
2532             },
2533             {
2534                 name: 'readValueSendResponseSuccessCallback',
2535                 type: AV.Types.FUNCTION,
2536                 nullable: true
2537             },
2538             {
2539                 name: 'readValueSendResponseErrorCallback',
2540                 type: AV.Types.FUNCTION,
2541                 nullable: true
2542             },
2543             {
2544                 name: 'writeValueRequestCallback',
2545                 type: AV.Types.FUNCTION,
2546                 nullable: true
2547             },
2548             {
2549                 name: 'writeValueSendResponseErrorCallback',
2550                 type: AV.Types.FUNCTION,
2551                 nullable: true
2552             },
2553             {
2554                 name: 'writeValueSendResponseSuccessCallback',
2555                 type: AV.Types.FUNCTION,
2556                 nullable: true
2557             }
2558         ]
2559     );
2560 };
2561
2562 //class BluetoothGATTServerCharacteristic ///////////////////////////
2563 var BluetoothGATTServerCharacteristic = function(data) {
2564     data = ValidateBluetoothGATTServerCharacteristicInit(data);
2565
2566     BluetoothGATTCharacteristic.call(this, data, null);
2567
2568     var descriptors_ = data.descriptors.map(function(descriptor_data) {
2569         return new BluetoothGATTServerDescriptor(descriptor_data, null);
2570     });
2571
2572     Object.defineProperties(this, {
2573         descriptors: {
2574             enumerable: true,
2575             get: function() {
2576                 return descriptors_.slice();
2577             },
2578             set: function() {}
2579         },
2580         readPermission: {
2581             enumerable: true,
2582             get: function() {
2583                 return data.readPermission;
2584             },
2585             set: function() {}
2586         },
2587         writePermission: {
2588             enumerable: true,
2589             get: function() {
2590                 return data.writePermission;
2591             },
2592             set: function() {}
2593         },
2594         encryptedReadPermission: {
2595             enumerable: true,
2596             get: function() {
2597                 return data.encryptedReadPermission;
2598             },
2599             set: function() {}
2600         },
2601         encryptedWritePermission: {
2602             enumerable: true,
2603             get: function() {
2604                 return data.encryptedWritePermission;
2605             },
2606             set: function() {}
2607         },
2608         encryptedSignedReadPermission: {
2609             enumerable: true,
2610             get: function() {
2611                 return data.encryptedSignedReadPermission;
2612             },
2613             set: function() {}
2614         },
2615         encryptedSignedWritePermission: {
2616             enumerable: true,
2617             get: function() {
2618                 return data.encryptedSignedWritePermission;
2619             },
2620             set: function() {}
2621         },
2622         // This properties are "private" and meant not to be used by users
2623         _readValueRequestCallback: {
2624             enumerable: true,
2625             get: function() {
2626                 return data.readValueRequestCallback;
2627             },
2628             set: function() {}
2629         },
2630         _readValueSendResponseSuccessCallback: {
2631             enumerable: false,
2632             get: function() {
2633                 return data.readValueSendResponseSuccessCallback;
2634             },
2635             set: function() {}
2636         },
2637         _readValueSendResponseErrorCallback: {
2638             enumerable: false,
2639             get: function() {
2640                 return data.readValueSendResponseErrorCallback;
2641             },
2642             set: function() {}
2643         },
2644         _writeValueRequestCallback: {
2645             enumerable: false,
2646             get: function() {
2647                 return data.writeValueRequestCallback;
2648             },
2649             set: function() {}
2650         },
2651         _writeValueSendResponseSuccessCallback: {
2652             enumerable: false,
2653             get: function() {
2654                 return data.writeValueSendResponseSuccessCallback;
2655             },
2656             set: function() {}
2657         },
2658         _writeValueSendResponseErrorCallback: {
2659             enumerable: false,
2660             get: function() {
2661                 return data.writeValueSendResponseErrorCallback;
2662             },
2663             set: function() {}
2664         },
2665         _id: {
2666             // It has to be enumerable, to be serialized with JSON.stringify()
2667             enumerable: true,
2668             value: NextGattServerEntityID()
2669         }
2670     });
2671
2672     this.readValue = function() {
2673         throw new WebAPIException(
2674             'NotSupportedError',
2675             'This method cannot be called on BluetoothGATTServerCharacteristic'
2676         );
2677     };
2678
2679     this.writeValue = function() {
2680         throw new WebAPIException(
2681             'NotSupportedError',
2682             'This method cannot be called on BluetoothGATTServerCharacteristic'
2683         );
2684     };
2685
2686     this.addValueChangeListener = function() {
2687         throw new WebAPIException(
2688             'NotSupportedError',
2689             'This method cannot be called on BluetoothGATTServerCharacteristic'
2690         );
2691     };
2692
2693     this.removeValueChangeListener = function() {
2694         /* Intended no operation */
2695     };
2696
2697     this.notifyAboutValueChange = function(
2698         value,
2699         clientAddress,
2700         notificationCB,
2701         errorCB
2702     ) {
2703         var args = AV.validateArgs(Array.prototype.slice.call(arguments, 1), [
2704             {
2705                 name: 'clientAddress',
2706                 type: AV.Types.STRING,
2707                 optional: true,
2708                 nullable: true
2709             },
2710             {
2711                 name: 'notificationCB',
2712                 type: AV.Types.LISTENER,
2713                 values: [
2714                     'onnotificationsuccess',
2715                     'onnotificationfail',
2716                     'onnotificationfinish'
2717                 ],
2718                 optional: true,
2719                 nullable: true
2720             },
2721             {
2722                 name: 'errorCB',
2723                 type: AV.Types.FUNCTION,
2724                 optional: true,
2725                 nullable: true
2726             }
2727         ]);
2728
2729         var callArgs = {
2730             value: BluetoothManager_toByteArray(value),
2731             client: args.clientAddress || null,
2732             _id: this._id,
2733             notifyId: _BluetoothGATTServerCharacteristicNotifyId++
2734         };
2735
2736         var callback = function(result) {
2737             if (native.isFailure(result)) {
2738                 native.callIfPossible(args.errorCB, native.getErrorObject(result));
2739             }
2740         };
2741
2742         var result = native.call(
2743             'BluetoothGATTServerCharacteristicNotifyAboutValueChange',
2744             callArgs,
2745             callback
2746         );
2747
2748         if (native.isFailure(result)) {
2749             throw native.getErrorObject(result);
2750         }
2751
2752         native.addListener(
2753             'BluetoothGATTServerCharacteristicNotifyCallback_' + callArgs.notifyId,
2754             _BluetoothGATTServerCharacteristicNotifyCallback
2755         );
2756
2757         _BluetoothGATTServerCharacteristicNotifyListeners[callArgs.notifyId] =
2758             args.notificationCB;
2759     };
2760 };
2761
2762 BluetoothGATTServerCharacteristic.prototype = Object.create(
2763     BluetoothGATTCharacteristic.prototype
2764 );
2765
2766 Object.defineProperty(BluetoothGATTServerCharacteristic.prototype, 'constructor', {
2767     value: BluetoothGATTServerCharacteristic,
2768     enumerable: false,
2769     writable: true
2770 });
2771
2772 tizen.GATTRequestReply = function(statusCode, data) {
2773     AV.isConstructorCall(this, tizen.GATTRequestReply);
2774
2775     var statusCode_ = Converter.toLong(statusCode);
2776     var data_ = T.isNullOrUndefined(data) ? null : BluetoothManager_toByteArray(data);
2777
2778     Object.defineProperties(this, {
2779         statusCode: {
2780             enumerable: true,
2781             get: function() {
2782                 return statusCode_;
2783             },
2784             set: function(v) {
2785                 statusCode_ = Converter.toLong(v);
2786             }
2787         },
2788         data: {
2789             enumerable: true,
2790             get: function() {
2791                 return data_;
2792             },
2793             set: function(v) {
2794                 data_ = T.isNullOrUndefined(v) ? null : numberArrayToByteArray(v);
2795             }
2796         }
2797     });
2798 };
2799
2800 function _createReadValueRequestCallback(
2801     _id,
2802     sendResponseSuccessCallback,
2803     sendResponseErrorCallback
2804 ) {
2805     return _singleListenerBuilder(
2806         'ReadValueRequestCallback_' + _id,
2807         /*
2808          * _singleListenerBuilder requires 2 callbacks, the second of which
2809          * is an error callback.
2810          * Read value request events coming from the native layer
2811          * are never errors.
2812          * Hence, we don't use the second callback here.
2813          */
2814         function(event, readValueRequestCallback, unusedErrorCallback) {
2815             var clientAddress = event.clientAddress;
2816             var offset = event.offset;
2817
2818             if (readValueRequestCallback) {
2819                 var requestReply = readValueRequestCallback(clientAddress, offset);
2820                 var response = {
2821                     _id: _id,
2822                     requestId: event.requestId,
2823                     requestType: event.requestType,
2824                     offset: offset,
2825                     statusCode: requestReply.statusCode,
2826                     data: requestReply.data
2827                 };
2828                 var callback = function(result) {
2829                     if (native.isFailure(result)) {
2830                         native.callIfPossible(
2831                             sendResponseErrorCallback,
2832                             native.getErrorObject(result)
2833                         );
2834                     } else {
2835                         native.callIfPossible(sendResponseSuccessCallback);
2836                     }
2837                 };
2838                 var result = native.call(
2839                     'BluetoothGATTServerSendResponse',
2840                     response,
2841                     callback
2842                 );
2843             }
2844             return true;
2845         }
2846     );
2847 }
2848
2849 function _createWriteValueRequestCallback(
2850     _id,
2851     sendResponseSuccessCallback,
2852     sendResponseErrorCallback
2853 ) {
2854     return _singleListenerBuilder(
2855         'WriteValueRequestCallback_' + _id,
2856         /*
2857          * _singleListenerBuilder requires 2 callbacks, the second of which
2858          * is an error callback.
2859          * Write value request events coming from the native layer
2860          * are never errors.
2861          * Hence, we don't use the second callback here.
2862          */
2863         function(event, writeValueRequestCallback, unusedErrorCallback) {
2864             var clientAddress = event.clientAddress;
2865             var value = event.value;
2866             var offset = event.offset;
2867             var replyRequired = event.replyRequired;
2868
2869             if (writeValueRequestCallback) {
2870                 var requestReply = writeValueRequestCallback(
2871                     clientAddress,
2872                     BluetoothManager_toByteArray(value),
2873                     offset,
2874                     replyRequired
2875                 );
2876                 var response = {
2877                     _id: _id,
2878                     requestId: event.requestId,
2879                     requestType: event.requestType,
2880                     value: null, // Responses to write requests don't contain value
2881                     offset: offset,
2882                     statusCode: requestReply.statusCode,
2883                     data: requestReply.data
2884                 };
2885                 var callback = function(result) {
2886                     if (native.isFailure(result)) {
2887                         native.callIfPossible(
2888                             sendResponseErrorCallback,
2889                             native.getErrorObject(result)
2890                         );
2891                     } else {
2892                         native.callIfPossible(sendResponseSuccessCallback);
2893                     }
2894                 };
2895                 var result = native.call(
2896                     'BluetoothGATTServerSendResponse',
2897                     response,
2898                     callback
2899                 );
2900             }
2901             return true;
2902         }
2903     );
2904 }
2905
2906 var _BluetoothGATTServerReadWriteValueRequestCallbacks = {};
2907
2908 var _setReadValueRequestCallbackCommon = function() {
2909     var args = AV.validateArgs(arguments, [
2910         {
2911             name: 'readValueRequestCallback',
2912             type: AV.Types.FUNCTION
2913         },
2914         {
2915             name: 'successCallback',
2916             type: AV.Types.FUNCTION,
2917             optional: true,
2918             nullable: true
2919         },
2920         {
2921             name: 'errorCallback',
2922             type: AV.Types.FUNCTION,
2923             optional: true,
2924             nullable: true
2925         },
2926         {
2927             name: 'sendResponseSuccessCallback',
2928             type: AV.Types.FUNCTION,
2929             optional: true,
2930             nullable: true
2931         },
2932         {
2933             name: 'sendResponseErrorCallback',
2934             type: AV.Types.FUNCTION,
2935             optional: true,
2936             nullable: true
2937         }
2938     ]);
2939
2940     var entityId = this._id;
2941
2942     var callback = function(result) {
2943         if (native.isFailure(result)) {
2944             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
2945         } else {
2946             var readValueRequestCallback = _createReadValueRequestCallback(
2947                 entityId,
2948                 args.sendResponseSuccessCallback,
2949                 args.sendResponseErrorCallback
2950             );
2951             readValueRequestCallback.addListener(
2952                 args.readValueRequestCallback,
2953                 function unusedErrorCallback() {
2954                     /* Intentionally no operation */
2955                 }
2956             );
2957             _BluetoothGATTServerReadWriteValueRequestCallbacks[
2958                 'ReadValueCallback' + entityId
2959             ] = readValueRequestCallback;
2960             native.callIfPossible(args.successCallback, native.getErrorObject(result));
2961         }
2962     };
2963
2964     var callArgs = { _id: this._id };
2965     var result = native.call(
2966         'BluetoothGATTServerSetReadValueRequestCallback',
2967         callArgs,
2968         callback
2969     );
2970
2971     if (native.isFailure(result)) {
2972         throw native.getErrorObject(result);
2973     }
2974 };
2975
2976 var _setWriteValueRequestCallbackCommon = function() {
2977     var args = AV.validateArgs(arguments, [
2978         {
2979             name: 'writeValueRequestCallback',
2980             type: AV.Types.FUNCTION
2981         },
2982         {
2983             name: 'successCallback',
2984             type: AV.Types.FUNCTION,
2985             optional: true,
2986             nullable: true
2987         },
2988         {
2989             name: 'errorCallback',
2990             type: AV.Types.FUNCTION,
2991             optional: true,
2992             nullable: true
2993         },
2994         {
2995             name: 'sendResponseSuccessCallback',
2996             type: AV.Types.FUNCTION,
2997             optional: true,
2998             nullable: true
2999         },
3000         {
3001             name: 'sendResponseErrorCallback',
3002             type: AV.Types.FUNCTION,
3003             optional: true,
3004             nullable: true
3005         }
3006     ]);
3007
3008     var entityId = this._id;
3009
3010     var callback = function(result) {
3011         if (native.isFailure(result)) {
3012             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
3013         } else {
3014             var writeValueRequestCallback = _createWriteValueRequestCallback(
3015                 entityId,
3016                 args.sendResponseSuccessCallback,
3017                 args.sendResponseErrorCallback
3018             );
3019             writeValueRequestCallback.addListener(
3020                 args.writeValueRequestCallback,
3021                 function unusedErrorCallback() {
3022                     /* Intentionally no operation */
3023                 }
3024             );
3025             _BluetoothGATTServerReadWriteValueRequestCallbacks[
3026                 'WriteValueCallback' + entityId
3027             ] = writeValueRequestCallback;
3028             native.callIfPossible(args.successCallback, native.getErrorObject(result));
3029         }
3030     };
3031
3032     var callArgs = { _id: this._id };
3033     var result = native.call(
3034         'BluetoothGATTServerSetWriteValueRequestCallback',
3035         callArgs,
3036         callback
3037     );
3038
3039     if (native.isFailure(result)) {
3040         throw native.getErrorObject(result);
3041     }
3042 };
3043
3044 BluetoothGATTServerCharacteristic.prototype.setReadValueRequestCallback = _setReadValueRequestCallbackCommon;
3045 BluetoothGATTServerCharacteristic.prototype.setWriteValueRequestCallback = _setWriteValueRequestCallbackCommon;
3046
3047 var _BluetoothGATTServerCharacteristicNotifyId = 0;
3048 var _BluetoothGATTServerCharacteristicNotifyListeners = {};
3049 var _BluetoothGATTServerCharacteristicNotifyCallback = function(event) {
3050     privUtils_.log('Got notification about characteristic\'s value change');
3051     if (
3052         T.isNullOrUndefined(
3053             _BluetoothGATTServerCharacteristicNotifyListeners[event.notifyId]
3054         )
3055     ) {
3056         privUtils_.log('Notification callback is not set, skipping');
3057         return;
3058     }
3059
3060     var callback = _BluetoothGATTServerCharacteristicNotifyListeners[event.notifyId];
3061
3062     if (event.type === 'onnotificationsuccess') {
3063         native.callIfPossible(callback.onnotificationsuccess, event.clientAddress);
3064         return;
3065     }
3066
3067     if (event.type === 'onnotificationfail') {
3068         native.callIfPossible(
3069             callback.onnotificationfail,
3070             event.clientAddress,
3071             native.getErrorObject(event)
3072         );
3073         return;
3074     }
3075
3076     if (event.type === 'onnotificationfinish') {
3077         native.callIfPossible(callback.onnotificationfinish, event.clientAddress);
3078     }
3079
3080     native.removeListener(
3081         'BluetoothGATTServerCharacteristicNotifyCallback_' + event.notifyId
3082     );
3083 };
3084
3085 /**
3086  * Creates a manager for specified listener event. Manager handles multiple
3087  * registered listeners
3088  *
3089  * @param {string} name - name of the listener this manager handles
3090  * @param {function} callback - function to be invoked when event specified by the name
3091  *                              fires.
3092  *                              This function should have following signature:
3093  *                              void callback(listener, event);
3094  * @param {string} addListenerId - optional parameter. If specified, this native
3095  *                                 method will be called synchronously when
3096  *                                 listener is added.
3097  * @param {string} removeListenerId - optional parameter. If specified, this native
3098  *                                 method will be called synchronously when
3099  *                                 listener is removed.
3100  * @param {bool} repeatNativeCall - optional parameter. If specified, the addListenerId
3101  *                                 and removeListenerId methods will be called
3102  *                                 synchronously each time listener is added/removed.
3103  *                                 Otherwise they are going to be called just once: when
3104  *                                 first listener is added and last listener is removed.
3105  *
3106  * @return {object} object which allows to add or remove callbacks for specified listener
3107  */
3108 function _multipleListenerBuilder(
3109     name,
3110     callback,
3111     addListenerId,
3112     removeListenerId,
3113     repeatNativeCall
3114 ) {
3115     var listenerName = name;
3116     var addId = addListenerId;
3117     var removeId = removeListenerId;
3118     var callbackFunction = callback;
3119     var listeners = {};
3120     var nextId = 1;
3121     var jsListenerRegistered = false;
3122     var nativeListenerRegistered = false;
3123     var repeatNativeListenerCall = repeatNativeCall;
3124
3125     function innerCallback(event) {
3126         for (var watchId in listeners) {
3127             if (listeners.hasOwnProperty(watchId)) {
3128                 callbackFunction(listeners[watchId], event);
3129             }
3130         }
3131     }
3132
3133     function addListener(callback, args) {
3134         var id = ++nextId;
3135
3136         if (addId && (!nativeListenerRegistered || repeatNativeListenerCall)) {
3137             var result = native.callSync(addId, args || {});
3138             if (native.isFailure(result)) {
3139                 throw native.getErrorObject(result);
3140             }
3141             nativeListenerRegistered = true;
3142         }
3143
3144         if (!jsListenerRegistered) {
3145             native.addListener(listenerName, innerCallback);
3146             jsListenerRegistered = true;
3147         }
3148
3149         listeners[id] = callback;
3150         return id;
3151     }
3152
3153     function removeListener(watchId, args) {
3154         if (listeners.hasOwnProperty(watchId)) {
3155             delete listeners[watchId];
3156         }
3157
3158         if (
3159             removeId &&
3160             ((nativeListenerRegistered && T.isEmptyObject(listeners)) ||
3161                 repeatNativeListenerCall)
3162         ) {
3163             var result = native.callSync(removeId, args || {});
3164             if (native.isFailure(result)) {
3165                 throw native.getErrorObject(result);
3166             }
3167             nativeListenerRegistered = false;
3168         }
3169
3170         if (jsListenerRegistered && T.isEmptyObject(listeners)) {
3171             native.removeListener(listenerName, innerCallback);
3172             jsListenerRegistered = false;
3173         }
3174     }
3175
3176     return {
3177         addListener: addListener,
3178         removeListener: removeListener
3179     };
3180 }
3181
3182 var _bluetoothGATTCharacteristicListener = _multipleListenerBuilder(
3183     'BluetoothGATTCharacteristicValueChangeListener',
3184     function(listener, event) {
3185         listener(event);
3186     },
3187     'BluetoothGATTClientServiceAddValueChangeListener',
3188     'BluetoothGATTClientServiceRemoveValueChangeListener',
3189     true
3190 );
3191
3192 /*
3193  * This object is used by:
3194  * - BluetoothLEDevice.addConnectStateChangeListener()
3195  * - BluetoothLEAdapter.addConnectStateChangeListener()
3196  */
3197 var _bleConnectChangeListener = _multipleListenerBuilder(
3198     'BluetoothLEConnectChangeCallback',
3199     function(listener, event) {
3200         listener(event);
3201     },
3202     'BluetoothLEDeviceAddConnectStateChangeListener',
3203     'BluetoothLEDeviceRemoveConnectStateChangeListener'
3204 );
3205
3206 var _bleAttMtuChangeListener = _multipleListenerBuilder(
3207     'BluetoothLEAttMtuChangeCallback',
3208     function(listener, event) {
3209         listener(event);
3210     },
3211     'BluetoothLEDeviceAddAttMtuChangeListener',
3212     'BluetoothLEDeviceRemoveAttMtuChangeListener'
3213 );
3214
3215 //class BluetoothGATTDescriptor ///////////////////////////
3216 var BluetoothGATTDescriptor = function(data, address) {
3217     var handle_ = data.handle;
3218     //address_ is needed to control if device is still connected
3219     var address_ = address;
3220     var uuid_ = data.uuid;
3221
3222     this.readValue = function() {
3223         privUtils_.log('Entered BluetoothGATTDescriptor.readValue()');
3224         var args = AV.validateArgs(arguments, [
3225             {
3226                 name: 'successCallback',
3227                 type: AV.Types.FUNCTION
3228             },
3229             {
3230                 name: 'errorCallback',
3231                 type: AV.Types.FUNCTION,
3232                 optional: true,
3233                 nullable: true
3234             }
3235         ]);
3236
3237         var callback = function(result) {
3238             if (native.isFailure(result)) {
3239                 native.callIfPossible(args.errorCallback, native.getErrorObject(result));
3240             } else {
3241                 var d = numberArrayToByteArray(native.getResultObject(result));
3242                 args.successCallback(d);
3243             }
3244         };
3245
3246         var callArgs = { handle: handle_, address: address_ };
3247
3248         var result = native.call(
3249             'BluetoothGATTClientServiceReadValue',
3250             callArgs,
3251             callback
3252         );
3253
3254         if (native.isFailure(result)) {
3255             throw native.getErrorObject(result);
3256         }
3257     };
3258
3259     this.writeValue = function(value, successCallback, errorCallback) {
3260         privUtils_.log('Entered BluetoothGATTDescriptor.writeValue()');
3261         var args = AV.validateArgs(Array.prototype.slice.call(arguments, 1), [
3262             {
3263                 name: 'successCallback',
3264                 type: AV.Types.FUNCTION,
3265                 optional: true,
3266                 nullable: true
3267             },
3268             {
3269                 name: 'errorCallback',
3270                 type: AV.Types.FUNCTION,
3271                 optional: true,
3272                 nullable: true
3273             }
3274         ]);
3275
3276         var callback = function(result) {
3277             if (native.isFailure(result)) {
3278                 native.callIfPossible(args.errorCallback, native.getErrorObject(result));
3279             } else {
3280                 native.callIfPossible(args.successCallback);
3281             }
3282         };
3283
3284         var callArgs = {
3285             handle: handle_,
3286             value: BluetoothManager_toByteArray(value),
3287             address: address_
3288         };
3289
3290         var result = native.call(
3291             'BluetoothGATTClientServiceWriteValue',
3292             callArgs,
3293             callback
3294         );
3295
3296         if (native.isFailure(result)) {
3297             throw native.getErrorObject(result);
3298         }
3299     };
3300
3301     Object.defineProperties(this, {
3302         uuid: {
3303             enumerable: true,
3304             get: function() {
3305                 return uuid_;
3306             },
3307             set: function() {}
3308         }
3309     });
3310 };
3311
3312 var ValidateBluetoothGATTServerDescriptorInit = function(initData) {
3313     return AV.validateArgs(
3314         [
3315             initData['uuid'],
3316             initData['readPermission'] || false,
3317             initData['writePermission'] || false,
3318             initData['encryptedReadPermission'] || false,
3319             initData['encryptedWritePermission'] || false,
3320             initData['encryptedSignedReadPermission'] || false,
3321             initData['encryptedSignedWritePermission'] || false,
3322             initData['readValueRequestCallback'] || null,
3323             initData['readValueSendResponseSuccessCallback'] || null,
3324             initData['readValueSendResponseErrorCallback'] || null,
3325             initData['writeValueRequestCallback'] || null,
3326             initData['writeValueSendResponseSuccessCallback'] || null,
3327             initData['writeValueSendResponseErrorCallback'] || null
3328         ],
3329         [
3330             {
3331                 name: 'uuid',
3332                 type: AV.Types.STRING,
3333                 validator: IsUuidValid
3334             },
3335             {
3336                 name: 'readPermission',
3337                 type: AV.Types.BOOLEAN
3338             },
3339             {
3340                 name: 'writePermission',
3341                 type: AV.Types.BOOLEAN
3342             },
3343             {
3344                 name: 'encryptedReadPermission',
3345                 type: AV.Types.BOOLEAN
3346             },
3347             {
3348                 name: 'encryptedWritePermission',
3349                 type: AV.Types.BOOLEAN
3350             },
3351             {
3352                 name: 'encryptedSignedReadPermission',
3353                 type: AV.Types.BOOLEAN
3354             },
3355             {
3356                 name: 'encryptedSignedWritePermission',
3357                 type: AV.Types.BOOLEAN
3358             },
3359             {
3360                 name: 'readValueRequestCallback',
3361                 type: AV.Types.FUNCTION,
3362                 nullable: true
3363             },
3364             {
3365                 name: 'readValueSendResponseSuccessCallback',
3366                 type: AV.Types.FUNCTION,
3367                 nullable: true
3368             },
3369             {
3370                 name: 'readValueSendResponseErrorCallback',
3371                 type: AV.Types.FUNCTION,
3372                 nullable: true
3373             },
3374             {
3375                 name: 'writeValueRequestCallback',
3376                 type: AV.Types.FUNCTION,
3377                 nullable: true
3378             },
3379             {
3380                 name: 'writeValueSendResponseErrorCallback',
3381                 type: AV.Types.FUNCTION,
3382                 nullable: true
3383             },
3384             {
3385                 name: 'writeValueSendResponseSuccessCallback',
3386                 type: AV.Types.FUNCTION,
3387                 nullable: true
3388             }
3389         ]
3390     );
3391 };
3392
3393 var BluetoothGATTServerDescriptor = function(data, address) {
3394     data = ValidateBluetoothGATTServerDescriptorInit(data);
3395
3396     BluetoothGATTDescriptor.call(this, data, null);
3397
3398     Object.defineProperties(this, {
3399         readPermission: {
3400             enumerable: true,
3401             get: function() {
3402                 return data.readPermission;
3403             },
3404             set: function() {}
3405         },
3406         writePermission: {
3407             enumerable: true,
3408             get: function() {
3409                 return data.writePermission;
3410             },
3411             set: function() {}
3412         },
3413         encryptedReadPermission: {
3414             enumerable: true,
3415             get: function() {
3416                 return data.encryptedReadPermission;
3417             },
3418             set: function() {}
3419         },
3420         encryptedWritePermission: {
3421             enumerable: true,
3422             get: function() {
3423                 return data.encryptedWritePermission;
3424             },
3425             set: function() {}
3426         },
3427         encryptedSignedReadPermission: {
3428             enumerable: true,
3429             get: function() {
3430                 return data.encryptedSignedReadPermission;
3431             },
3432             set: function() {}
3433         },
3434         encryptedSignedWritePermission: {
3435             enumerable: true,
3436             get: function() {
3437                 return data.encryptedSignedWritePermission;
3438             },
3439             set: function() {}
3440         },
3441         // This properties are "private" and meant not to be used by users
3442         _readValueRequestCallback: {
3443             enumerable: false,
3444             get: function() {
3445                 return data.readValueRequestCallback;
3446             },
3447             set: function() {}
3448         },
3449         _readValueSendResponseSuccessCallback: {
3450             enumerable: false,
3451             get: function() {
3452                 return data.readValueSendResponseSuccessCallback;
3453             },
3454             set: function() {}
3455         },
3456         _readValueSendResponseErrorCallback: {
3457             enumerable: false,
3458             get: function() {
3459                 return data.readValueSendResponseErrorCallback;
3460             },
3461             set: function() {}
3462         },
3463         _writeValueRequestCallback: {
3464             enumerable: false,
3465             get: function() {
3466                 return data.writeValueRequestCallback;
3467             },
3468             set: function() {}
3469         },
3470         _writeValueSendResponseSuccessCallback: {
3471             enumerable: false,
3472             get: function() {
3473                 return data.writeValueSendResponseSuccessCallback;
3474             },
3475             set: function() {}
3476         },
3477         _writeValueSendResponseErrorCallback: {
3478             enumerable: false,
3479             get: function() {
3480                 return data.writeValueSendResponseErrorCallback;
3481             },
3482             set: function() {}
3483         },
3484         _id: {
3485             // It has to be enumerable, to be serialized with JSON.stringify()
3486             enumerable: true,
3487             value: NextGattServerEntityID()
3488         }
3489     });
3490
3491     this.readValue = function() {
3492         throw new WebAPIException(
3493             'NotSupportedError',
3494             'This method cannot be called on BluetoothGATTServerDescriptor'
3495         );
3496     };
3497
3498     this.writeValue = function() {
3499         throw new WebAPIException(
3500             'NotSupportedError',
3501             'This method cannot be called on BluetoothGATTServerDescriptor'
3502         );
3503     };
3504 };
3505
3506 BluetoothGATTServerDescriptor.prototype = Object.create(
3507     BluetoothGATTDescriptor.prototype
3508 );
3509
3510 Object.defineProperty(BluetoothGATTServerDescriptor.prototype, 'constructor', {
3511     value: BluetoothGATTServerDescriptor,
3512     enumerable: false,
3513     writable: true
3514 });
3515
3516 BluetoothGATTServerDescriptor.prototype.setReadValueRequestCallback = _setReadValueRequestCallbackCommon;
3517 BluetoothGATTServerDescriptor.prototype.setWriteValueRequestCallback = _setWriteValueRequestCallbackCommon;
3518
3519 // class BluetoothAdapter ///////////////////////////
3520 var BluetoothAdapter = function() {
3521     function nameGetter() {
3522         var result = native.callSync('BluetoothAdapterGetName', {});
3523
3524         if (native.isFailure(result)) {
3525             return '';
3526         } else {
3527             return native.getResultObject(result);
3528         }
3529     }
3530
3531     function addressGetter() {
3532         var result = native.callSync('BluetoothAdapterGetAddress', {});
3533
3534         if (native.isFailure(result)) {
3535             return '';
3536         } else {
3537             return native.getResultObject(result);
3538         }
3539     }
3540
3541     function poweredGetter() {
3542         var result = native.callSync('BluetoothAdapterGetPowered', {});
3543
3544         if (native.isFailure(result)) {
3545             return false;
3546         } else {
3547             return native.getResultObject(result);
3548         }
3549     }
3550
3551     function visibleGetter() {
3552         var result = native.callSync('BluetoothAdapterGetVisible', {});
3553
3554         if (native.isFailure(result)) {
3555             return false;
3556         } else {
3557             return native.getResultObject(result);
3558         }
3559     }
3560
3561     Object.defineProperties(this, {
3562         name: {
3563             enumerable: true,
3564             set: function() {},
3565             get: nameGetter
3566         },
3567         address: {
3568             enumerable: true,
3569             set: function() {},
3570             get: addressGetter
3571         },
3572         powered: {
3573             enumerable: true,
3574             set: function() {},
3575             get: poweredGetter
3576         },
3577         visible: {
3578             enumerable: true,
3579             set: function() {},
3580             get: visibleGetter
3581         }
3582     });
3583 };
3584
3585 BluetoothAdapter.prototype.setName = function() {
3586     privUtils_.log('Entered BluetoothAdapter.setName()');
3587     var args = AV.validateArgs(arguments, [
3588         {
3589             name: 'name',
3590             type: AV.Types.STRING
3591         },
3592         {
3593             name: 'successCallback',
3594             type: AV.Types.FUNCTION,
3595             optional: true,
3596             nullable: true
3597         },
3598         {
3599             name: 'errorCallback',
3600             type: AV.Types.FUNCTION,
3601             optional: true,
3602             nullable: true
3603         }
3604     ]);
3605
3606     var callArgs = {
3607         name: args.name
3608     };
3609
3610     var callback = function(result) {
3611         if (native.isFailure(result)) {
3612             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
3613         } else {
3614             native.callIfPossible(args.successCallback);
3615         }
3616     };
3617
3618     var result = native.call('BluetoothAdapterSetName', callArgs, callback);
3619     if (native.isFailure(result)) {
3620         throw native.getErrorObject(result);
3621     }
3622 };
3623
3624 BluetoothAdapter.prototype.setPowered = function() {
3625     privUtils_.log('Entered BluetoothAdapter.setPowered()');
3626     privUtils_.printDeprecationWarningFor('setPowered()');
3627     privUtils_.warn(
3628         'Let the user turn on/off Bluetooth through the Settings application instead.'
3629     );
3630
3631     var args = AV.validateArgs(arguments, [
3632         {
3633             name: 'powered',
3634             type: AV.Types.BOOLEAN
3635         },
3636         {
3637             name: 'successCallback',
3638             type: AV.Types.FUNCTION,
3639             optional: true,
3640             nullable: true
3641         },
3642         {
3643             name: 'errorCallback',
3644             type: AV.Types.FUNCTION,
3645             optional: true,
3646             nullable: true
3647         }
3648     ]);
3649
3650     var callArgs = {
3651         powered: args.powered
3652     };
3653
3654     var callback = function(result) {
3655         if (native.isFailure(result)) {
3656             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
3657         } else {
3658             native.callIfPossible(args.successCallback);
3659         }
3660     };
3661
3662     var result = native.call('BluetoothAdapterSetPowered', callArgs, callback);
3663     if (native.isFailure(result)) {
3664         throw native.getErrorObject(result);
3665     }
3666 };
3667
3668 // This method is deprecated since Tizen 2.3.
3669 BluetoothAdapter.prototype.setVisible = function() {
3670     privUtils_.log('Entered BluetoothAdapter.setVisible()');
3671     privUtils_.printDeprecationWarningFor('setVisible()');
3672     privUtils_.warn(
3673         'Let the user change the Bluetooth visibility through the Settings ' +
3674             'application instead.'
3675     );
3676
3677     var args = AV.validateArgs(arguments, [
3678         {
3679             name: 'visible',
3680             type: AV.Types.BOOLEAN
3681         },
3682         {
3683             name: 'successCallback',
3684             type: AV.Types.FUNCTION,
3685             optional: true,
3686             nullable: true
3687         },
3688         {
3689             name: 'errorCallback',
3690             type: AV.Types.FUNCTION,
3691             optional: true,
3692             nullable: true
3693         },
3694         {
3695             name: 'timeout',
3696             type: AV.Types.UNSIGNED_LONG,
3697             optional: true,
3698             nullable: true
3699         }
3700     ]);
3701
3702     var callArgs = {
3703         visible: args.visible
3704     };
3705
3706     if (args.visible === true) {
3707         if (T.isNullOrUndefined(args.timeout)) {
3708             callArgs.timeout = 0;
3709         } else {
3710             callArgs.timeout = args.timeout > 65535 ? 180 : args.timeout;
3711         }
3712     }
3713
3714     var callback = function(result) {
3715         if (native.isFailure(result)) {
3716             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
3717         } else {
3718             native.callIfPossible(args.successCallback);
3719         }
3720     };
3721
3722     var result = native.call('BluetoothAdapterSetVisible', callArgs, callback);
3723     if (native.isFailure(result)) {
3724         throw native.getErrorObject(result);
3725     }
3726 };
3727
3728 var _listener;
3729
3730 function _BluetoothAdapterChangeCallback(event) {
3731     privUtils_.log('_BluetoothAdapterChangeCallback');
3732
3733     var e = event;
3734     var d;
3735
3736     switch (e.action) {
3737     case 'onstatechanged':
3738         d = e.powered;
3739         break;
3740
3741     case 'onnamechanged':
3742         d = e.name;
3743         break;
3744
3745     case 'onvisibilitychanged':
3746         d = e.visible;
3747         break;
3748
3749     default:
3750         privUtils_.log('Unknown mode: ' + e.action);
3751         return;
3752     }
3753
3754     if (_listener[e.action]) {
3755         _listener[e.action](d);
3756     }
3757 }
3758
3759 BluetoothAdapter.prototype.setChangeListener = function() {
3760     privUtils_.log('Entered BluetoothAdapter.setChangeListener()');
3761     var args = AV.validateArgs(arguments, [
3762         {
3763             name: 'changeCallback',
3764             type: AV.Types.LISTENER,
3765             values: ['onstatechanged', 'onnamechanged', 'onvisibilitychanged']
3766         }
3767     ]);
3768
3769     if (T.isNullOrUndefined(_listener)) {
3770         native.addListener(
3771             'BluetoothAdapterChangeCallback',
3772             _BluetoothAdapterChangeCallback
3773         );
3774         native.callSync('BluetoothAdapterSetChangeListener', {});
3775     }
3776     _listener = args.changeCallback;
3777 };
3778
3779 BluetoothAdapter.prototype.unsetChangeListener = function() {
3780     privUtils_.log('Entered BluetoothAdapter.unsetChangeListener()');
3781     if (!T.isNullOrUndefined(_listener)) {
3782         native.removeListener(
3783             'BluetoothAdapterChangeCallback',
3784             _BluetoothAdapterChangeCallback
3785         );
3786         native.callSync('BluetoothAdapterUnsetChangeListener', {});
3787         _listener = undefined;
3788     }
3789 };
3790
3791 var _discoverDevicesSuccessCallback;
3792 var _discoverDevicesErrorCallback;
3793
3794 function _BluetoothDiscoverDevicesSuccessCallback(event) {
3795     var e = event;
3796     var d = null;
3797
3798     switch (e.action) {
3799     case 'onstarted':
3800         break;
3801
3802     case 'ondevicefound':
3803         d = new BluetoothDevice(e.data);
3804         break;
3805
3806     case 'ondevicedisappeared':
3807         d = e.data;
3808         break;
3809
3810     case 'onfinished':
3811         var result = e.data;
3812         d = [];
3813         result.forEach(function(data) {
3814             d.push(new BluetoothDevice(data));
3815         });
3816
3817         //remove listeners after discovering
3818         native.removeListener(
3819             'BluetoothDiscoverDevicesSuccessCallback',
3820             _BluetoothDiscoverDevicesSuccessCallback
3821         );
3822         native.removeListener(
3823             'BluetoothDiscoverDevicesErrorCallback',
3824             _BluetoothDiscoverDevicesErrorCallback
3825         );
3826         break;
3827
3828     default:
3829         privUtils_.log('Unknown mode: ' + e.action);
3830         return;
3831     }
3832
3833     if (_discoverDevicesSuccessCallback[e.action]) {
3834         _discoverDevicesSuccessCallback[e.action](d);
3835     }
3836 }
3837
3838 function _BluetoothDiscoverDevicesErrorCallback(event) {
3839     var e = event;
3840     setTimeout(function() {
3841         native.callIfPossible(_discoverDevicesErrorCallback, native.getErrorObject(e));
3842     }, 0);
3843 }
3844
3845 BluetoothAdapter.prototype.discoverDevices = function() {
3846     privUtils_.log('Entered BluetoothAdapter.discoverDevices()');
3847     var args = AV.validateArgs(arguments, [
3848         {
3849             name: 'successCallback',
3850             type: AV.Types.LISTENER,
3851             values: ['onstarted', 'ondevicefound', 'ondevicedisappeared', 'onfinished']
3852         },
3853         {
3854             name: 'errorCallback',
3855             type: AV.Types.FUNCTION,
3856             optional: true,
3857             nullable: true
3858         }
3859     ]);
3860
3861     _discoverDevicesSuccessCallback = args.successCallback;
3862     _discoverDevicesErrorCallback = args.errorCallback;
3863     native.addListener(
3864         'BluetoothDiscoverDevicesSuccessCallback',
3865         _BluetoothDiscoverDevicesSuccessCallback
3866     );
3867     native.addListener(
3868         'BluetoothDiscoverDevicesErrorCallback',
3869         _BluetoothDiscoverDevicesErrorCallback
3870     );
3871
3872     var result = native.callSync('BluetoothAdapterDiscoverDevices', {});
3873
3874     if (native.isFailure(result)) {
3875         native.removeListener(
3876             'BluetoothDiscoverDevicesSuccessCallback',
3877             _BluetoothDiscoverDevicesSuccessCallback
3878         );
3879         native.removeListener(
3880             'BluetoothDiscoverDevicesErrorCallback',
3881             _BluetoothDiscoverDevicesErrorCallback
3882         );
3883         throw native.getErrorObject(result);
3884     }
3885 };
3886
3887 BluetoothAdapter.prototype.stopDiscovery = function() {
3888     privUtils_.log('Entered BluetoothAdapter.stopDiscovery()');
3889     var args = AV.validateArgs(arguments, [
3890         {
3891             name: 'successCallback',
3892             type: AV.Types.FUNCTION,
3893             optional: true,
3894             nullable: true
3895         },
3896         {
3897             name: 'errorCallback',
3898             type: AV.Types.FUNCTION,
3899             optional: true,
3900             nullable: true
3901         }
3902     ]);
3903
3904     var callback = function(result) {
3905         if (native.isFailure(result)) {
3906             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
3907         } else {
3908             native.callIfPossible(args.successCallback);
3909         }
3910     };
3911
3912     var result = native.call('BluetoothAdapterStopDiscovery', {}, callback);
3913     if (native.isFailure(result)) {
3914         throw native.getErrorObject(result);
3915     }
3916 };
3917
3918 BluetoothAdapter.prototype.getKnownDevices = function() {
3919     privUtils_.log('Entered BluetoothAdapter.getKnownDevices()');
3920     var args = AV.validateArgs(arguments, [
3921         {
3922             name: 'successCallback',
3923             type: AV.Types.FUNCTION
3924         },
3925         {
3926             name: 'errorCallback',
3927             type: AV.Types.FUNCTION,
3928             optional: true,
3929             nullable: true
3930         }
3931     ]);
3932
3933     var callback = function(result) {
3934         if (native.isFailure(result)) {
3935             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
3936         } else {
3937             var r = native.getResultObject(result).devices;
3938             var devices = [];
3939             r.forEach(function(data) {
3940                 devices.push(new BluetoothDevice(data));
3941             });
3942             args.successCallback(devices);
3943         }
3944     };
3945
3946     var result = native.call('BluetoothAdapterGetKnownDevices', {}, callback);
3947     if (native.isFailure(result)) {
3948         throw native.getErrorObject(result);
3949     }
3950 };
3951
3952 BluetoothAdapter.prototype.getDevice = function() {
3953     privUtils_.log('Entered BluetoothAdapter.getDevice()');
3954     var args = AV.validateArgs(arguments, [
3955         {
3956             name: 'address',
3957             type: AV.Types.STRING
3958         },
3959         {
3960             name: 'successCallback',
3961             type: AV.Types.FUNCTION
3962         },
3963         {
3964             name: 'errorCallback',
3965             type: AV.Types.FUNCTION,
3966             optional: true,
3967             nullable: true
3968         }
3969     ]);
3970
3971     var callback = function(result) {
3972         if (native.isFailure(result)) {
3973             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
3974         } else {
3975             args.successCallback(new BluetoothDevice(native.getResultObject(result)));
3976         }
3977     };
3978
3979     var result = native.call(
3980         'BluetoothAdapterGetDevice',
3981         { address: args.address },
3982         callback
3983     );
3984     if (native.isFailure(result)) {
3985         throw native.getErrorObject(result);
3986     }
3987 };
3988
3989 BluetoothAdapter.prototype.createBonding = function() {
3990     privUtils_.log('Entered BluetoothAdapter.createBonding()');
3991     var args = AV.validateArgs(arguments, [
3992         {
3993             name: 'address',
3994             type: AV.Types.STRING
3995         },
3996         {
3997             name: 'successCallback',
3998             type: AV.Types.FUNCTION,
3999             optional: false,
4000             nullable: false
4001         },
4002         {
4003             name: 'errorCallback',
4004             type: AV.Types.FUNCTION,
4005             optional: true,
4006             nullable: true
4007         }
4008     ]);
4009
4010     var callArgs = {
4011         address: args.address
4012     };
4013
4014     var callback = function(result) {
4015         if (native.isFailure(result)) {
4016             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
4017         } else {
4018             args.successCallback(new BluetoothDevice(native.getResultObject(result)));
4019         }
4020     };
4021
4022     var result = native.call('BluetoothAdapterCreateBonding', callArgs, callback);
4023     if (native.isFailure(result)) {
4024         throw native.getErrorObject(result);
4025     }
4026 };
4027
4028 BluetoothAdapter.prototype.destroyBonding = function() {
4029     privUtils_.log('Entered BluetoothAdapter.destroyBonding()');
4030     var args = AV.validateArgs(arguments, [
4031         {
4032             name: 'address',
4033             type: AV.Types.STRING
4034         },
4035         {
4036             name: 'successCallback',
4037             type: AV.Types.FUNCTION,
4038             optional: true,
4039             nullable: true
4040         },
4041         {
4042             name: 'errorCallback',
4043             type: AV.Types.FUNCTION,
4044             optional: true,
4045             nullable: true
4046         }
4047     ]);
4048
4049     var callArgs = {
4050         address: args.address
4051     };
4052
4053     var callback = function(result) {
4054         if (native.isFailure(result)) {
4055             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
4056         } else {
4057             native.callIfPossible(args.successCallback);
4058         }
4059     };
4060
4061     var result = native.call('BluetoothAdapterDestroyBonding', callArgs, callback);
4062     if (native.isFailure(result)) {
4063         throw native.getErrorObject(result);
4064     }
4065 };
4066
4067 BluetoothAdapter.prototype.registerRFCOMMServiceByUUID = function() {
4068     privUtils_.log('Entered BluetoothAdapter.registerRFCOMMServiceByUUID()');
4069     var args = AV.validateArgs(arguments, [
4070         {
4071             name: 'uuid',
4072             type: AV.Types.STRING
4073         },
4074         {
4075             name: 'name',
4076             type: AV.Types.STRING
4077         },
4078         {
4079             name: 'successCallback',
4080             type: AV.Types.FUNCTION
4081         },
4082         {
4083             name: 'errorCallback',
4084             type: AV.Types.FUNCTION,
4085             optional: true,
4086             nullable: true
4087         }
4088     ]);
4089
4090     var callArgs = {
4091         uuid: args.uuid,
4092         name: args.name
4093     };
4094
4095     var callback = function(result) {
4096         if (native.isFailure(result)) {
4097             native.callIfPossible(args.errorCallback, native.getErrorObject(result));
4098         } else {
4099             // if registration was finished with success create BluetoothServiceHandler
4100             // with parameters passed to this function (uuid and name).
4101             args.successCallback(new BluetoothServiceHandler(callArgs));
4102         }
4103     };
4104
4105     var result = native.call(
4106         'BluetoothAdapterRegisterRFCOMMServiceByUUID',
4107         callArgs,
4108         callback
4109     );
4110     if (native.isFailure(result)) {
4111         throw native.getErrorObject(result);
4112     }
4113 };
4114
4115 BluetoothAdapter.prototype.getBluetoothProfileHandler = function() {
4116     privUtils_.log('Entered BluetoothAdapter.getBluetoothProfileHandler()');
4117     privUtils_.printDeprecationWarningFor('getBluetoothProfileHandler');
4118     var args = AV.validateArgs(arguments, [
4119         {
4120             name: 'profileType',
4121             type: AV.Types.ENUM,
4122             values: T.getValues(_BluetoothProfileType)
4123         }
4124     ]);
4125
4126     var callArgs = { profileType: args.profileType };
4127
4128     var result = native.callSync('BluetoothAdapterGetBluetoothProfileHandler', callArgs);
4129
4130     if (native.isFailure(result)) {
4131         throw native.getErrorObject(result);
4132     } else {
4133         switch (args.profileType) {
4134         case _BluetoothProfileType.HEALTH:
4135             return new BluetoothHealthProfileHandler(callArgs);
4136
4137         default:
4138             throw new WebAPIException(
4139                 'NotSupportedError',
4140                 'Profile ' + args.profileType + ' is not supported.'
4141             );
4142         }
4143     }
4144 };
4145
4146 // class BluetoothGATTServer ////////////////////////
4147 var _BluetoothGATTServerServices = [];
4148 var _isBluetoothGATTServerRunning = false;
4149
4150 function _BluetoothGattServerIsRunningChangeListener(result) {
4151     _isBluetoothGATTServerRunning = result.state;
4152 }
4153
4154 function _BluetoothGattServerBluetoothAdapterStateChangeListener(result) {
4155     if (_isBluetoothGATTServerRunning && false === result.state) {
4156         _BluetoothGATTServerServicesRegisteredInNativeLayer = {};
4157     }
4158 }
4159
4160 /*
4161  * This set is used in BluetoothGATTServer::start() to check which services
4162  * from BluetoothGATTServer::services have already been registered in native
4163  * layer and which have to be registered.
4164  */
4165 var _BluetoothGATTServerServicesRegisteredInNativeLayer = {};
4166
4167 var BluetoothGATTServer = function() {
4168     Object.defineProperties(this, {
4169         services: {
4170             enumerable: true,
4171             get: function() {
4172                 return _BluetoothGATTServerServices;
4173             },
4174             set: function() {}
4175         },
4176         isRunning: {
4177             enumerable: true,
4178             get: function() {
4179                 return _isBluetoothGATTServerRunning;
4180             },
4181             set: function() {}
4182         }
4183     });
4184
4185     // Register listener for managing GATTServer start / stop
4186     native.addListener(
4187         'BluetoothGattServerIsRunningChangeListener',
4188         _BluetoothGattServerIsRunningChangeListener
4189     );
4190
4191     // Register listener for managing BluetoothAdapter power off
4192     native.addListener(
4193         'BluetoothGattServerBluetoothAdapterStateChangeListener',
4194         _BluetoothGattServerBluetoothAdapterStateChangeListener
4195     );
4196 };
4197
4198 var BluetoothGATTServer_valid_registerService_errors = [
4199     'InvalidStateError',
4200     'NotSupportedError',
4201     'InvalidValuesError',
4202     'AbortError'
4203 ];
4204 var BluetoothGATTServer_valid_registerService_exceptions = [
4205     'InvalidStateError',
4206     'TypeMismatchError',
4207     'SecurityError'
4208 ];
4209
4210 BluetoothGATTServer.prototype.registerService = function() {
4211     var args = AV.validateArgs(arguments, [
4212         {
4213             name: 'service',
4214             type: AV.Types.DICTIONARY
4215         },
4216         {
4217             name: 'successCallback',
4218             type: AV.Types.FUNCTION,
4219             optional: true,
4220             nullable: true
4221         },
4222         {
4223             name: 'errorCallback',
4224             type: AV.Types.FUNCTION,
4225             optional: true,
4226             nullable: true
4227         }
4228     ]);
4229
4230     var service = new BluetoothGATTServerService(args.service);
4231
4232     // Callback when registering characteristics and descriptors callbacks is successful
4233     var successCallback = function() {
4234         _BluetoothGATTServerServicesRegisteredInNativeLayer[service._id] = true;
4235         _BluetoothGATTServerServices.push(service);
4236         native.callIfPossible(args.successCallback);
4237     };
4238
4239     // Callback when registering characteristics and descriptors callbacks fails,
4240     // unregister service
4241     var errorCallback = function() {
4242         var unregisterServiceCallback = function() {
4243             native.callIfPossible(args.errorCallback, AbortError);
4244         };
4245
4246         var callArgs = {
4247             _id: service._id,
4248             idsToRemoveFromNativeLayer: _getIncludedServicesAndItsComponentsIdsRecursively(
4249                 service
4250             )
4251         };
4252
4253         native.call(
4254             'BluetoothGATTServerUnregisterService',
4255             callArgs,
4256             unregisterServiceCallback
4257         );
4258     };
4259
4260     var callback = function(result) {
4261         if (native.isFailure(result)) {
4262             native.callIfPossible(
4263                 args.errorCallback,
4264                 native.getErrorObjectAndValidate(
4265                     result,
4266                     BluetoothGATTServer_valid_registerService_errors,
4267                     AbortError
4268                 )
4269             );
4270         } else {
4271             _registerReadWriteValueRequestCallbacksInGATTService(
4272                 service,
4273                 successCallback,
4274                 errorCallback
4275             );
4276         }
4277     };
4278
4279     var result = native.call('BluetoothGATTServerRegisterService', service, callback);
4280     if (native.isFailure(result)) {
4281         throw native.getErrorObjectAndValidate(
4282             result,
4283             BluetoothGATTServer_valid_registerService_exceptions,
4284             AbortError
4285         );
4286     }
4287 };
4288
4289 var BluetoothGATTServer_valid_unregisterAllServices_errors = [
4290     'InvalidStateError',
4291     'AbortError'
4292 ];
4293
4294 var BluetoothGATTServer_valid_unregisterAllServices_exceptions = [
4295     'TypeMismatchError',
4296     'SecurityError'
4297 ];
4298
4299 BluetoothGATTServer.prototype.unregisterAllServices = function() {
4300     privUtils_.log('Entered BluetoothGATTServer.unregisterAllServices()');
4301     var args = AV.validateArgs(arguments, [
4302         {
4303             name: 'successCallback',
4304             type: AV.Types.FUNCTION,
4305             optional: true,
4306             nullable: true
4307         },
4308         {
4309             name: 'errorCallback',
4310             type: AV.Types.FUNCTION,
4311             optional: true,
4312             nullable: true
4313         }
4314     ]);
4315
4316     var servicesToUnregister = [];
4317     for (var i = 0; i < _BluetoothGATTServerServices.length; ++i) {
4318         if (!T.isNullOrUndefined(_BluetoothGATTServerServices[i]._id)) {
4319             servicesToUnregister.push(_BluetoothGATTServerServices[i]);
4320         }
4321     }
4322
4323     if (servicesToUnregister.length) {
4324         privUtils_.log(
4325             'Number of services to unregister: ' + servicesToUnregister.length
4326         );
4327         var unregisterServiceCallbacksAggregator = new ResultCallbacksAggregator(
4328             servicesToUnregister.length,
4329             function onAllSucceeded() {
4330                 _BluetoothGATTServerServices = [];
4331                 _BluetoothGATTServerServicesRegisteredInNativeLayer = [];
4332                 native.callIfPossible(args.successCallback);
4333             },
4334             function onFailure(error) {
4335                 native.callIfPossible(
4336                     args.errorCallback,
4337                     native.getErrorObjectAndValidate(
4338                         error,
4339                         BluetoothGATTServer_valid_unregisterAllServices_errors,
4340                         AbortError
4341                     )
4342                 );
4343             }
4344         );
4345
4346         for (var i = 0; i < servicesToUnregister.length; ++i) {
4347             var serviceIndex = _BluetoothGATTServerServices.findIndex(function(service) {
4348                 return service._id === servicesToUnregister[i]._id;
4349             });
4350
4351             var serviceId = servicesToUnregister[i]._id;
4352
4353             var unregisterServiceCallback = function(result) {
4354                 if (native.isFailure(result)) {
4355                     unregisterServiceCallbacksAggregator.errorCallback(
4356                         native.getErrorObjectAndValidate(
4357                             result,
4358                             BluetoothGATTServer_valid_unregisterAllServices_errors,
4359                             AbortError
4360                         )
4361                     );
4362                 } else {
4363                     delete _BluetoothGATTServerServicesRegisteredInNativeLayer[serviceId];
4364                     _BluetoothGATTServerServices.splice(serviceIndex, 1);
4365                     unregisterServiceCallbacksAggregator.successCallback();
4366                 }
4367             };
4368
4369             var callArgs = {
4370                 _id: servicesToUnregister[i]._id,
4371                 idsToRemoveFromNativeLayer: _getIncludedServicesAndItsComponentsIdsRecursively(
4372                     servicesToUnregister[i]
4373                 )
4374             };
4375
4376             var result = native.call(
4377                 'BluetoothGATTServerUnregisterService',
4378                 callArgs,
4379                 unregisterServiceCallback
4380             );
4381
4382             if (native.isFailure(result)) {
4383                 throw native.getErrorObjectAndValidate(
4384                     result,
4385                     BluetoothGATTServer_valid_unregisterAllServices_exceptions,
4386                     AbortError
4387                 );
4388             }
4389         }
4390     } else {
4391         privUtils_.log(
4392             'Nothing registered in native layer, calling BluetoothGATTServer.stop()'
4393         );
4394
4395         var callback = function(result) {
4396             if (native.isFailure(result)) {
4397                 native.callIfPossible(
4398                     args.errorCallback,
4399                     native.getErrorObjectAndValidate(
4400                         result,
4401                         BluetoothGATTServer_valid_unregisterAllServices_errors,
4402                         AbortError
4403                     )
4404                 );
4405             } else {
4406                 _BluetoothGATTServerServices = [];
4407                 _BluetoothGATTServerServicesRegisteredInNativeLayer = {};
4408                 native.callIfPossible(args.successCallback);
4409             }
4410         };
4411
4412         var result = native.call('BluetoothGATTServerStop', {}, callback);
4413         if (native.isFailure(result)) {
4414             throw native.getErrorObjectAndValidate(
4415                 result,
4416                 BluetoothGATTServer_valid_unregisterAllServices_exceptions,
4417                 AbortError
4418             );
4419         }
4420     }
4421 };
4422
4423 /*
4424  * Objects of this class are used to wait for multiple callbacks results.
4425  *
4426  * Its successCallback and errorCallback members should be passed as
4427  * the success and error callbacks, respectively, to the functions we wait for.
4428  * When the functions we wait for are called callbacksNum times, either
4429  * onAllSucceeded or onFailure is called, depending on whether only
4430  * successCallback was called or not.
4431  *
4432  * For the usage example, take a look at BluetoothGATTServer.prototype.start,
4433  * where it's used to wait for registration of multiple services.
4434  */
4435 var ResultCallbacksAggregator = function(callbacksNum, onAllSucceeded, onFailure) {
4436     var _callbacksNum = callbacksNum;
4437     var _allSucceeded = true;
4438     var _error;
4439
4440     this.successCallback = function() {
4441         _callbacksNum--;
4442
4443         if (!_callbacksNum) {
4444             if (_allSucceeded) {
4445                 onAllSucceeded();
4446             } else {
4447                 onFailure(_error);
4448             }
4449         }
4450     };
4451
4452     this.errorCallback = function(error) {
4453         _callbacksNum--;
4454         _allSucceeded = false;
4455         _error = error;
4456
4457         if (!_callbacksNum) {
4458             onFailure(_error);
4459         }
4460     };
4461 };
4462
4463 function _getServicesUnregisteredInNativeLayer() {
4464     var servicesUnregisteredInNativeLayer = [];
4465     for (var i = 0; i < _BluetoothGATTServerServices.length; ++i) {
4466         if (
4467             !_BluetoothGATTServerServicesRegisteredInNativeLayer[
4468                 _BluetoothGATTServerServices[i]._id
4469             ]
4470         ) {
4471             servicesUnregisteredInNativeLayer.push(_BluetoothGATTServerServices[i]);
4472         }
4473     }
4474     return servicesUnregisteredInNativeLayer;
4475 }
4476
4477 function _getIdsToReregisterReadWriteCallbacks(servicesUnregisteredInNativeLayer) {
4478     var idsToRegisterWriteValueCallbacks = [];
4479     var idsToRegisterReadValueCallbacks = [];
4480
4481     for (var i = 0; i < servicesUnregisteredInNativeLayer.length; ++i) {
4482         var serviceComponentIds = _getIncludedServicesAndItsComponentsIdsRecursively(
4483             servicesUnregisteredInNativeLayer[i]
4484         );
4485
4486         for (var j = 0; j < serviceComponentIds.length; ++j) {
4487             var _id = serviceComponentIds[j];
4488             var callbackName = 'ReadValueCallback' + _id;
4489
4490             /*
4491              * Only some of the serviceComponentIds are characteristics and descriptors
4492              * and within them, only some have read/write callbacks registered.
4493              * We have to check if the callbacks were registered for each id.
4494              */
4495             if (callbackName in _BluetoothGATTServerReadWriteValueRequestCallbacks) {
4496                 idsToRegisterReadValueCallbacks.push(_id);
4497             }
4498
4499             callbackName = 'WriteValueCallback' + _id;
4500             if (callbackName in _BluetoothGATTServerReadWriteValueRequestCallbacks) {
4501                 idsToRegisterWriteValueCallbacks.push(_id);
4502             }
4503         }
4504     }
4505
4506     return [idsToRegisterReadValueCallbacks, idsToRegisterWriteValueCallbacks];
4507 }
4508
4509 var BluetoothGATTServer_valid_start_errors = [
4510     'InvalidStateError',
4511     'NotSupportedError',
4512     'AbortError'
4513 ];
4514 var BluetoothGATTServer_valid_start_exceptions = [
4515     'InvalidStateError',
4516     'TypeMismatchError',
4517     'SecurityError'
4518 ];
4519
4520 function _reregisterCallback(_id, nativeFunction, successCallback, errorCallback) {
4521     /*
4522      * This function should only be used to reregister read/write value request
4523      * callbacks in native layer for characteristics/descriptors, that have
4524      * had such callbacks registered before the last server's stop().
4525      * As JS listeners corresponding to these callbacks already exist in JS,
4526      * only C++ callbacks have to be reregistered.
4527      */
4528     var callback = function(result) {
4529         if (native.isFailure(result)) {
4530             errorCallback(native.getErrorObject(result));
4531         } else {
4532             successCallback();
4533         }
4534     };
4535
4536     var result = native.call(nativeFunction, { _id: _id }, callback);
4537
4538     if (native.isFailure(result)) {
4539         throw native.getErrorObjectAndValidate(
4540             result,
4541             BluetoothGATTServer_valid_start_exceptions,
4542             AbortError
4543         );
4544     }
4545 }
4546
4547 BluetoothGATTServer.prototype.start = function() {
4548     privUtils_.log('Entered BluetoothGATTServer.start()');
4549     var args = AV.validateArgs(arguments, [
4550         {
4551             name: 'successCallback',
4552             type: AV.Types.FUNCTION,
4553             optional: true,
4554             nullable: true
4555         },
4556         {
4557             name: 'errorCallback',
4558             type: AV.Types.FUNCTION,
4559             optional: true,
4560             nullable: true
4561         }
4562     ]);
4563
4564     var servicesUnregisteredInNativeLayer = _getServicesUnregisteredInNativeLayer();
4565     /*
4566      * Characteristics and descriptors that were registered in the server before the last stop() call
4567      * could have had read/write value request callbacks registered.
4568      * These callbacks have to be reregistered (only) in native layer.
4569      * These arrays contain _ids of characteristics and descriptors, that will have their callbacks
4570      * reregistered.
4571      */
4572     var idsToReregisterReadWriteValueCallbacks = _getIdsToReregisterReadWriteCallbacks(
4573         servicesUnregisteredInNativeLayer
4574     );
4575     var idsToReregisterReadValueCallbacks = idsToReregisterReadWriteValueCallbacks[0];
4576     var idsToReregisterWriteValueCallbacks = idsToReregisterReadWriteValueCallbacks[1];
4577
4578     var startServerCallback = function(result) {
4579         if (native.isFailure(result)) {
4580             native.callIfPossible(
4581                 args.errorCallback,
4582                 native.getErrorObjectAndValidate(
4583                     result,
4584                     BluetoothGATTServer_valid_start_errors,
4585                     AbortError
4586                 )
4587             );
4588         } else {
4589             native.callIfPossible(args.successCallback);
4590         }
4591     };
4592
4593     if (servicesUnregisteredInNativeLayer.length) {
4594         var numberOfCallbacksToWaitFor =
4595             servicesUnregisteredInNativeLayer.length +
4596             idsToReregisterReadValueCallbacks.length +
4597             idsToReregisterWriteValueCallbacks.length;
4598         var registerServiceCallbacksAggregator = new ResultCallbacksAggregator(
4599             numberOfCallbacksToWaitFor,
4600             function onAllSucceeded() {
4601                 var result = native.call(
4602                     'BluetoothGATTServerStart',
4603                     {},
4604                     startServerCallback
4605                 );
4606
4607                 if (native.isFailure(result)) {
4608                     throw native.getErrorObjectAndValidate(
4609                         result,
4610                         BluetoothGATTServer_valid_start_exceptions,
4611                         AbortError
4612                     );
4613                 }
4614             },
4615             function onFailure(error) {
4616                 native.callIfPossible(
4617                     args.errorCallback,
4618                     native.getErrorObjectAndValidate(
4619                         error,
4620                         BluetoothGATTServer_valid_start_errors,
4621                         AbortError
4622                     )
4623                 );
4624             }
4625         );
4626
4627         var registerServicesCallback = function(result) {
4628             if (native.isFailure(result)) {
4629                 registerServiceCallbacksAggregator.errorCallback(result);
4630             } else {
4631                 registerServiceCallbacksAggregator.successCallback();
4632             }
4633         };
4634
4635         for (var i = 0; i < servicesUnregisteredInNativeLayer.length; ++i) {
4636             var result = native.call(
4637                 'BluetoothGATTServerRegisterService',
4638                 servicesUnregisteredInNativeLayer[i],
4639                 registerServicesCallback
4640             );
4641             if (native.isFailure(result)) {
4642                 throw native.getErrorObjectAndValidate(
4643                     result,
4644                     BluetoothGATTServer_valid_registerService_exceptions,
4645                     AbortError
4646                 );
4647             }
4648         }
4649
4650         for (var i = 0; i < idsToReregisterReadValueCallbacks.length; ++i) {
4651             _reregisterCallback(
4652                 idsToReregisterReadValueCallbacks[i],
4653                 'BluetoothGATTServerSetReadValueRequestCallback',
4654                 registerServiceCallbacksAggregator.successCallback,
4655                 registerServiceCallbacksAggregator.errorCallback
4656             );
4657         }
4658
4659         for (var i = 0; i < idsToReregisterWriteValueCallbacks.length; ++i) {
4660             _reregisterCallback(
4661                 idsToReregisterWriteValueCallbacks[i],
4662                 'BluetoothGATTServerSetWriteValueRequestCallback',
4663                 registerServiceCallbacksAggregator.successCallback,
4664                 registerServiceCallbacksAggregator.errorCallback
4665             );
4666         }
4667     } else {
4668         var result = native.call('BluetoothGATTServerStart', {}, startServerCallback);
4669
4670         if (native.isFailure(result)) {
4671             throw native.getErrorObjectAndValidate(
4672                 result,
4673                 BluetoothGATTServer_valid_start_exceptions,
4674                 AbortError
4675             );
4676         }
4677     }
4678 };
4679
4680 var BluetoothGATTServer_valid_stop_errors = [
4681     'InvalidStateError',
4682     'NotSupportedError',
4683     'AbortError'
4684 ];
4685 var BluetoothGATTServer_valid_stop_exceptions = [
4686     'InvalidStateError',
4687     'TypeMismatchError',
4688     'SecurityError'
4689 ];
4690
4691 BluetoothGATTServer.prototype.stop = function() {
4692     privUtils_.log('Entered BluetoothGATTServer.stop()');
4693     var args = AV.validateArgs(arguments, [
4694         {
4695             name: 'successCallback',
4696             type: AV.Types.FUNCTION,
4697             optional: true,
4698             nullable: true
4699         },
4700         {
4701             name: 'errorCallback',
4702             type: AV.Types.FUNCTION,
4703             optional: true,
4704             nullable: true
4705         }
4706     ]);
4707
4708     var callback = function(result) {
4709         if (native.isFailure(result)) {
4710             native.callIfPossible(
4711                 args.errorCallback,
4712                 native.getErrorObjectAndValidate(
4713                     result,
4714                     BluetoothGATTServer_valid_stop_errors,
4715                     AbortError
4716                 )
4717             );
4718         } else {
4719             _BluetoothGATTServerServicesRegisteredInNativeLayer = {};
4720             native.callIfPossible(args.successCallback);
4721         }
4722     };
4723
4724     var result = native.call('BluetoothGATTServerStop', {}, callback);
4725     if (native.isFailure(result)) {
4726         throw native.getErrorObjectAndValidate(
4727             result,
4728             BluetoothGATTServer_valid_stop_exceptions,
4729             AbortError
4730         );
4731     }
4732 };
4733
4734 var BluetoothGATTServer_valid_getConnectionMtu_errors = [
4735     'InvalidStateError',
4736     'NotSupportedError',
4737     'AbortError'
4738 ];
4739 var BluetoothGATTServer_valid_getConnectionMtu_exceptions = [
4740     'TypeMismatchError',
4741     'SecurityError'
4742 ];
4743
4744 BluetoothGATTServer.prototype.getConnectionMtu = function() {
4745     privUtils_.log('Entered BluetoothGATTServer.getConnectionMtu()');
4746     var args = AV.validateArgs(arguments, [
4747         {
4748             name: 'clientAddress',
4749             type: AV.Types.STRING
4750         },
4751         {
4752             name: 'successCallback',
4753             type: AV.Types.FUNCTION
4754         },
4755         {
4756             name: 'errorCallback',
4757             type: AV.Types.FUNCTION,
4758             optional: true
4759         }
4760     ]);
4761
4762     var callback = function(result) {
4763         if (native.isFailure(result)) {
4764             native.callIfPossible(
4765                 args.errorCallback,
4766                 native.getErrorObjectAndValidate(
4767                     result,
4768                     BluetoothGATTServer_valid_getConnectionMtu_errors,
4769                     AbortError
4770                 )
4771             );
4772         } else {
4773             args.successCallback(native.getResultObject(result));
4774         }
4775     };
4776
4777     var result = native.call(
4778         'BluetoothGATTServerGetConnectionMtu',
4779         { clientAddress: args.clientAddress },
4780         callback
4781     );
4782     if (native.isFailure(result)) {
4783         throw native.getErrorObjectAndValidate(
4784             result,
4785             BluetoothGATTServer_valid_getConnectionMtu_exceptions,
4786             AbortError
4787         );
4788     }
4789 };
4790
4791 var GATTServer = new BluetoothGATTServer();
4792
4793 // class BluetoothManager ///////////////////////////
4794 var BluetoothManager = function() {
4795     Object.defineProperties(this, {
4796         deviceMajor: {
4797             value: new BluetoothClassDeviceMajor(),
4798             writable: false,
4799             enumerable: true
4800         },
4801         deviceMinor: {
4802             value: new BluetoothClassDeviceMinor(),
4803             writable: false,
4804             enumerable: true
4805         },
4806         deviceService: {
4807             value: new BluetoothClassDeviceService(),
4808             writable: false,
4809             enumerable: true
4810         },
4811         BASE_UUID: {
4812             value: '00000000-0000-1000-8000-00805F9B34FB',
4813             writable: false,
4814             enumerable: true
4815         }
4816     });
4817 };
4818
4819 var BluetoothManager_getDefaultAdapter = function() {
4820     privUtils_.checkPrivilegeAccess4Ver(
4821         '2.4',
4822         Privilege.BLUETOOTH,
4823         Privilege.BLUETOOTH_GAP
4824     );
4825
4826     return new BluetoothAdapter();
4827 };
4828
4829 BluetoothManager.prototype.getDefaultAdapter = function() {
4830     privUtils_.log('Entered BluetoothManager.getDefaultAdapter()');
4831     return BluetoothManager_getDefaultAdapter();
4832 };
4833
4834 var BluetoothManager_getLEAdapter = function() {
4835     privUtils_.checkPrivilegeAccess4Ver(
4836         '2.4',
4837         Privilege.BLUETOOTH,
4838         Privilege.BLUETOOTH_ADMIN
4839     );
4840
4841     return new BluetoothLEAdapter();
4842 };
4843
4844 BluetoothManager.prototype.getLEAdapter = function() {
4845     privUtils_.log('Entered BluetoothManager.getLEAdapter()');
4846     return BluetoothManager_getLEAdapter();
4847 };
4848
4849 var BluetoothManager_checkAndNormalizeHexString = function(hexString) {
4850     hexString = hexString.toLowerCase();
4851     if (hexString.startsWith('0x')) {
4852         hexString = hexString.substring(2);
4853     }
4854     if (!/^[0-9a-f]+$/.test(hexString)) {
4855         throw new WebAPIException(
4856             WebAPIException.TYPE_MISMATCH_ERR,
4857             'Given string is not hexadecimal value'
4858         );
4859     }
4860     if ('0' === hexString) {
4861         return '00';
4862     }
4863     if (1 === hexString.length % 2) {
4864         // to save consistency with BluetoothLEManufacturerData, last character is omitted
4865         hexString = hexString.replace(/.$/, '');
4866     }
4867     return hexString;
4868 };
4869
4870 var BluetoothManager_HexStringToUint8Array = function(hexString) {
4871     hexString = BluetoothManager_checkAndNormalizeHexString(hexString);
4872     if (0 === hexString.length) {
4873         return new Uint8Array([]);
4874     }
4875     var data = hexString.match(/[0-9a-f]{2}/g).map(function(byte) {
4876         return parseInt(byte, 16);
4877     });
4878     return new Uint8Array(data);
4879 };
4880
4881 var BluetoothManager_byteArrayToHexString = function(bytes) {
4882     if (0 == bytes.length) {
4883         return '';
4884     }
4885     return (
4886         '0x' +
4887         Array.prototype.map
4888             .call(bytes, function(byte) {
4889                 return ('0' + (byte & 0xff).toString(16)).slice(-2);
4890             })
4891             .join('')
4892     );
4893 };
4894
4895 var BluetoothManager_toByteArray = function(data) {
4896     if (data && String === data.constructor) {
4897         return numberArrayToByteArray(BluetoothManager_HexStringToUint8Array(data));
4898     } else {
4899         try {
4900             data = numberArrayToByteArray(data);
4901         } catch (err) {
4902             throw new WebAPIException(
4903                 WebAPIException.TYPE_MISMATCH_ERR,
4904                 'argument is not a valid Bytes type'
4905             );
4906         }
4907     }
4908     return data;
4909 };
4910
4911 BluetoothManager.prototype.toByteArray = function(data) {
4912     privUtils_.log('Entered BluetoothManager.toByteArray()');
4913     return BluetoothManager_toByteArray(data);
4914 };
4915
4916 var BluetoothManager_toDOMString = function(data) {
4917     if (data && String === data.constructor) {
4918         data = BluetoothManager_checkAndNormalizeHexString(data);
4919         if (0 !== data.length) {
4920             data = '0x' + data;
4921         }
4922         return data;
4923     }
4924     try {
4925         data = numberArrayToByteArray(data);
4926     } catch (err) {
4927         throw new WebAPIException(
4928             WebAPIException.TYPE_MISMATCH_ERR,
4929             'argument is not a valid Bytes type'
4930         );
4931     }
4932     return BluetoothManager_byteArrayToHexString(data);
4933 };
4934
4935 BluetoothManager.prototype.toDOMString = function(data) {
4936     privUtils_.log('Entered BluetoothManager.toDOMString()');
4937     return BluetoothManager_toDOMString(data);
4938 };
4939
4940 var BluetoothManager_toUint8Array = function(data) {
4941     if (data && String === data.constructor) {
4942         return BluetoothManager_HexStringToUint8Array(data);
4943     } else {
4944         try {
4945             data = new Uint8Array(numberArrayToByteArray(data));
4946         } catch (err) {
4947             throw new WebAPIException(
4948                 WebAPIException.TYPE_MISMATCH_ERR,
4949                 'argument is not a valid Bytes type'
4950             );
4951         }
4952     }
4953     return data;
4954 };
4955
4956 BluetoothManager.prototype.toUint8Array = function(data) {
4957     privUtils_.log('Entered BluetoothManager.toUint8Array()');
4958     return BluetoothManager_toUint8Array(data);
4959 };
4960
4961 var BluetoothManager_UUIDIsValid128Bit = function(uuid) {
4962     var re128BitFormat = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
4963     if (re128BitFormat.test(uuid)) {
4964         return true;
4965     }
4966     return false;
4967 };
4968
4969 var BluetoothManager_UUIDIsValid32Bit = function(uuid) {
4970     var re32BitFormat = /^[0-9a-f]{8}$/;
4971     if (re32BitFormat.test(uuid)) {
4972         return true;
4973     }
4974     return false;
4975 };
4976
4977 var BluetoothManager_UUIDIsValid16Bit = function(uuid) {
4978     var re16BitFormat = /^[0-9a-f]{4}$/;
4979     if (re16BitFormat.test(uuid)) {
4980         return true;
4981     }
4982     return false;
4983 };
4984
4985 var BluetoothManager_UUIDIsConvertibleTo16Bit = function(uuid) {
4986     var re128BitFormat = /^0000[0-9a-f]{4}-0000-1000-8000-00805f9b34fb$/;
4987     if (re128BitFormat.test(uuid)) {
4988         return true;
4989     }
4990     var re32BitFormat = /^0000[0-9a-f]{4}$/;
4991     if (re32BitFormat.test(uuid)) {
4992         return true;
4993     }
4994     return false;
4995 };
4996
4997 var BluetoothManager_UUIDIsConvertibleTo32Bit = function(uuid) {
4998     var re = /^[0-9a-f]{8}-0000-1000-8000-00805f9b34fb$/;
4999     if (re.test(uuid)) {
5000         return true;
5001     }
5002     return false;
5003 };
5004
5005 var BluetoothManager_UUIDTo128bit = function(uuid) {
5006     uuid = Converter.toString(uuid).toLowerCase();
5007     if (BluetoothManager_UUIDIsValid128Bit(uuid)) {
5008         return uuid;
5009     }
5010     var baseUuidLast96Bits = '-0000-1000-8000-00805f9b34fb';
5011     if (BluetoothManager_UUIDIsValid16Bit(uuid)) {
5012         return '0000' + uuid + baseUuidLast96Bits;
5013     }
5014     if (BluetoothManager_UUIDIsValid32Bit(uuid)) {
5015         return uuid + baseUuidLast96Bits;
5016     }
5017     throw new WebAPIException(
5018         WebAPIException.INVALID_VALUES_ERR,
5019         'Given parameter is not a supported uuid format: ' + uuid
5020     );
5021 };
5022
5023 var BluetoothManager_UUIDToShortestPossible = function(uuid) {
5024     uuid = uuid.toLowerCase();
5025     if (BluetoothManager_UUIDIsValid16Bit(uuid)) {
5026         return uuid;
5027     }
5028     if (BluetoothManager_UUIDIsValid32Bit(uuid)) {
5029         if (BluetoothManager_UUIDIsConvertibleTo16Bit(uuid)) {
5030             return uuid.substring(4, 8);
5031         }
5032     }
5033     if (!BluetoothManager_UUIDIsValid128Bit(uuid)) {
5034         throw new WebAPIException(
5035             WebAPIException.INVALID_VALUES_ERR,
5036             'Given parameter is not a supported uuid format: ' + uuid
5037         );
5038     }
5039     if (BluetoothManager_UUIDIsConvertibleTo16Bit(uuid)) {
5040         return uuid.substring(4, 8);
5041     }
5042     if (BluetoothManager_UUIDIsConvertibleTo32Bit(uuid)) {
5043         return uuid.substring(0, 8);
5044     }
5045     return uuid;
5046 };
5047
5048 var BluetoothManager_UUIDsEqual = function(uuid1, uuid2) {
5049     uuid1 = Converter.toString(uuid1).toLowerCase();
5050     uuid1 = BluetoothManager_UUIDTo128bit(uuid1);
5051     uuid2 = Converter.toString(uuid2).toLowerCase();
5052     uuid2 = BluetoothManager_UUIDTo128bit(uuid2);
5053     return uuid1 === uuid2;
5054 };
5055
5056 BluetoothManager.prototype.uuidTo128bit = function(uuid) {
5057     privUtils_.log('Entered BluetoothManager.uuidTo128bit()');
5058     var args = AV.validateArgs(arguments, [
5059         {
5060             name: 'uuid',
5061             type: AV.Types.STRING
5062         }
5063     ]);
5064     return BluetoothManager_UUIDTo128bit(args.uuid);
5065 };
5066
5067 BluetoothManager.prototype.uuidToShortestPossible = function(uuid) {
5068     privUtils_.log('Entered BluetoothManager.uuidToShortestPossible()');
5069     var args = AV.validateArgs(arguments, [
5070         {
5071             name: 'uuid',
5072             type: AV.Types.STRING
5073         }
5074     ]);
5075     return BluetoothManager_UUIDToShortestPossible(args.uuid);
5076 };
5077
5078 BluetoothManager.prototype.uuidsEqual = function(uuid1, uuid2) {
5079     privUtils_.log('Entered BluetoothManager.uuidsEqual()');
5080     var args = AV.validateArgs(arguments, [
5081         {
5082             name: 'uuid1',
5083             type: AV.Types.STRING
5084         },
5085         {
5086             name: 'uuid2',
5087             type: AV.Types.STRING
5088         }
5089     ]);
5090     return BluetoothManager_UUIDsEqual(args.uuid1, args.uuid2);
5091 };
5092 BluetoothManager.prototype.getGATTServer = function() {
5093     privUtils_.log('Entered BluetoothManager.getGATTServer()');
5094     return BluetoothManager_getGATTServer();
5095 };
5096
5097 var BluetoothManager_getGATTServer = function() {
5098     return GATTServer;
5099 };
5100
5101 // exports //////////////////////////////////////////
5102 exports = new BluetoothManager();