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