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