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