[Bluetooth] Implement BluetoothGATTServerService::unregister() 09/241209/10
authorPawel Wasowski <p.wasowski2@samsung.com>
Tue, 18 Aug 2020 03:08:55 +0000 (05:08 +0200)
committerPawel Wasowski <p.wasowski2@samsung.com>
Mon, 14 Sep 2020 17:25:43 +0000 (19:25 +0200)
This commit implements Implement BluetoothGATTServerService::unregister()
in JS and native layers.

[Verification] Basic tests in Chrome Dev Tools have shown that it works.
               More thorough tests may be needed.

Change-Id: I0d0cd99ea2e11337ac19970d07ec92730c1cb2a0
Signed-off-by: Pawel Wasowski <p.wasowski2@samsung.com>
src/bluetooth/bluetooth_api.js
src/bluetooth/bluetooth_gatt_server.cc
src/bluetooth/bluetooth_gatt_server.h
src/bluetooth/bluetooth_gatt_server_service.cc
src/bluetooth/bluetooth_instance.cc
src/bluetooth/bluetooth_instance.h

index f6f4061..5fe9939 100755 (executable)
@@ -174,8 +174,7 @@ tizen.BluetoothLEServiceData = function(d) {
             set: function(v) {
                 try {
                     data_ = BluetoothManager_toDOMString(v);
-                }
-                catch (err) {
+                } catch (err) {
                     data_ = Converter.toString(v);
                 }
             }
@@ -1676,9 +1675,9 @@ var BluetoothGATTService = function(data, address) {
         if (native.isSuccess(result)) {
             var resultObject = native.getResultObject(result);
             resultObject.forEach(function(c) {
-              if (!T.isNullOrUndefined(c)) {
-                characteristics.push(new BluetoothGATTCharacteristic(c, address_));
-              }
+                if (!T.isNullOrUndefined(c)) {
+                    characteristics.push(new BluetoothGATTCharacteristic(c, address_));
+                }
             });
         }
         return characteristics;
@@ -1809,6 +1808,122 @@ Object.defineProperty(BluetoothGATTServerService.prototype, 'constructor', {
     writable: true
 });
 
+function _getIncludedServicesAndItsComponentsIdsRecursively(service) {
+    var ids = [];
+
+    for (var serviceIndex = 0; serviceIndex < service.services.length; ++serviceIndex) {
+        ids.push(service.services[serviceIndex]._id);
+        ids = ids.concat(
+            _getIncludedServicesAndItsComponentsIdsRecursively(
+                service.services[serviceIndex]
+            )
+        );
+    }
+
+    for (
+        var characteristicIndex = 0;
+        characteristicIndex < service.characteristics.length;
+        ++characteristicIndex
+    ) {
+        ids.push(service.characteristics[characteristicIndex]._id);
+
+        for (
+            var descriptorIndex = 0;
+            descriptorIndex <
+            service.characteristics[characteristicIndex].descriptors.length;
+            ++descriptorIndex
+        ) {
+            ids.push(
+                service.characteristics[characteristicIndex].descriptors[descriptorIndex]
+                    ._id
+            );
+        }
+    }
+
+    return ids;
+}
+
+var BluetoothGATTServer_valid_unregisterService_errors = [
+    'InvalidStateError',
+    'AbortError'
+];
+var BluetoothGATTServer_valid_unregisterService_exceptions = [
+    'TypeMismatchError',
+    'SecurityError'
+];
+
+BluetoothGATTServerService.prototype.unregister = function() {
+    var args = AV.validateArgs(arguments, [
+        {
+            name: 'successCallback',
+            type: AV.Types.FUNCTION,
+            optional: true,
+            nullable: true
+        },
+        {
+            name: 'errorCallback',
+            type: AV.Types.FUNCTION,
+            optional: true,
+            nullable: true
+        }
+    ]);
+
+    var serviceIndex = _BluetoothGATTServerServices.findIndex(
+        function(service) {
+            return service._id === this._id;
+        }.bind(this)
+    );
+
+    if (serviceIndex === -1) {
+        throw new WebAPIException(
+            'AbortError',
+            'The service is not registered in the local GATT server'
+        );
+    }
+
+    function removeFromJSArrayAndCallSuccessCb() {
+        _BluetoothGATTServerServices.splice(serviceIndex, 1);
+        native.callIfPossible(args.successCallback);
+    }
+
+    if (!_BluetoothGATTServerServicesRegisteredInNativeLayer[this._id]) {
+        removeFromJSArrayAndCallSuccessCb();
+        return;
+    }
+
+    var callback = function(result) {
+        if (native.isFailure(result)) {
+            native.callIfPossible(
+                args.errorCallback,
+                native.getErrorObjectAndValidate(
+                    result,
+                    BluetoothGATTServer_valid_unregisterService_errors,
+                    AbortError
+                )
+            );
+        } else {
+            delete _BluetoothGATTServerServicesRegisteredInNativeLayer[this._id];
+            removeFromJSArrayAndCallSuccessCb();
+        }
+    }.bind(this);
+
+    var callArgs = {
+        _id: this._id,
+        idsToRemoveFromNativeLayer: _getIncludedServicesAndItsComponentsIdsRecursively(
+            this
+        )
+    };
+    var result = native.call('BluetoothGATTServerUnregisterService', callArgs, callback);
+
+    if (native.isFailure(result)) {
+        throw native.getErrorObjectAndValidate(
+            result,
+            BluetoothGATTServer_valid_unregisterService_errors,
+            AbortError
+        );
+    }
+};
+
 var numberArrayToByteArray = function(array) {
     var d = [];
 
@@ -1821,13 +1936,13 @@ var numberArrayToByteArray = function(array) {
 //class BluetoothGATTCharacteristic ///////////////////////////
 var BluetoothGATTCharacteristic = function(data, address) {
     if (!T.isObject(data)) {
-      return null;
+        return null;
     }
     var address_ = address;
     var handle_ = data.handle;
     var descriptors_ = data.descriptors.map(function(descriptor_data) {
         return new BluetoothGATTDescriptor(descriptor_data, address_);
-      });
+    });
     var isBroadcast_ = data.isBroadcast;
     var hasExtendedProperties_ = data.hasExtendedProperties;
     var isNotify_ = data.isNotify;
@@ -1949,7 +2064,11 @@ var BluetoothGATTCharacteristic = function(data, address) {
 
         var callArgs = { handle: handle_, address: address_ };
 
-        var result = native.call('BluetoothGATTClientServiceReadValue', callArgs, callback);
+        var result = native.call(
+            'BluetoothGATTClientServiceReadValue',
+            callArgs,
+            callback
+        );
 
         if (native.isFailure(result)) {
             throw native.getErrorObject(result);
@@ -1958,20 +2077,23 @@ var BluetoothGATTCharacteristic = function(data, address) {
 
     this.writeValue = function(value, successCallback, errorCallback) {
         privUtils_.log('Entered BluetoothGATTCharacteristic.writeValue()');
-        var args = AV.validateArgs([successCallback, errorCallback], [
-            {
-                name: 'successCallback',
-                type: AV.Types.FUNCTION,
-                optional: true,
-                nullable: true
-            },
-            {
-                name: 'errorCallback',
-                type: AV.Types.FUNCTION,
-                optional: true,
-                nullable: true
-            }
-        ]);
+        var args = AV.validateArgs(
+            [successCallback, errorCallback],
+            [
+                {
+                    name: 'successCallback',
+                    type: AV.Types.FUNCTION,
+                    optional: true,
+                    nullable: true
+                },
+                {
+                    name: 'errorCallback',
+                    type: AV.Types.FUNCTION,
+                    optional: true,
+                    nullable: true
+                }
+            ]
+        );
 
         var callback = function(result) {
             if (native.isFailure(result)) {
@@ -1987,7 +2109,11 @@ var BluetoothGATTCharacteristic = function(data, address) {
             address: address_
         };
 
-        var result = native.call('BluetoothGATTClientServiceWriteValue', callArgs, callback);
+        var result = native.call(
+            'BluetoothGATTClientServiceWriteValue',
+            callArgs,
+            callback
+        );
 
         if (native.isFailure(result)) {
             throw native.getErrorObject(result);
@@ -2233,10 +2359,14 @@ var BluetoothGATTServerCharacteristic = function(data) {
         );
     };
 
-    this.removeValueChangeListener = function() { /* Intended no operation */ };
+    this.removeValueChangeListener = function() {
+        /* Intended no operation */
+    };
 };
 
-BluetoothGATTServerCharacteristic.prototype = Object.create(BluetoothGATTCharacteristic.prototype);
+BluetoothGATTServerCharacteristic.prototype = Object.create(
+    BluetoothGATTCharacteristic.prototype
+);
 
 Object.defineProperty(BluetoothGATTServerCharacteristic.prototype, 'constructor', {
     value: BluetoothGATTServerCharacteristic,
@@ -2399,7 +2529,11 @@ var BluetoothGATTDescriptor = function(data, address) {
 
         var callArgs = { handle: handle_, address: address_ };
 
-        var result = native.call('BluetoothGATTClientServiceReadValue', callArgs, callback);
+        var result = native.call(
+            'BluetoothGATTClientServiceReadValue',
+            callArgs,
+            callback
+        );
 
         if (native.isFailure(result)) {
             throw native.getErrorObject(result);
@@ -2408,20 +2542,23 @@ var BluetoothGATTDescriptor = function(data, address) {
 
     this.writeValue = function(value, successCallback, errorCallback) {
         privUtils_.log('Entered BluetoothGATTDescriptor.writeValue()');
-        var args = AV.validateArgs([successCallback, errorCallback], [
-            {
-                name: 'successCallback',
-                type: AV.Types.FUNCTION,
-                optional: true,
-                nullable: true
-            },
-            {
-                name: 'errorCallback',
-                type: AV.Types.FUNCTION,
-                optional: true,
-                nullable: true
-            }
-        ]);
+        var args = AV.validateArgs(
+            [successCallback, errorCallback],
+            [
+                {
+                    name: 'successCallback',
+                    type: AV.Types.FUNCTION,
+                    optional: true,
+                    nullable: true
+                },
+                {
+                    name: 'errorCallback',
+                    type: AV.Types.FUNCTION,
+                    optional: true,
+                    nullable: true
+                }
+            ]
+        );
 
         var callback = function(result) {
             if (native.isFailure(result)) {
@@ -2437,7 +2574,11 @@ var BluetoothGATTDescriptor = function(data, address) {
             address: address_
         };
 
-        var result = native.call('BluetoothGATTClientServiceWriteValue', callArgs, callback);
+        var result = native.call(
+            'BluetoothGATTClientServiceWriteValue',
+            callArgs,
+            callback
+        );
 
         if (native.isFailure(result)) {
             throw native.getErrorObject(result);
@@ -2583,7 +2724,9 @@ var BluetoothGATTServerDescriptor = function(data, address) {
     };
 };
 
-BluetoothGATTServerDescriptor.prototype = Object.create(BluetoothGATTDescriptor.prototype);
+BluetoothGATTServerDescriptor.prototype = Object.create(
+    BluetoothGATTDescriptor.prototype
+);
 
 Object.defineProperty(BluetoothGATTServerDescriptor.prototype, 'constructor', {
     value: BluetoothGATTServerDescriptor,
@@ -3320,31 +3463,39 @@ var ResultCallbacksAggregator = function(callbacksNum, onAllSucceeded, onFailure
     var _allSucceeded = true;
     var _error;
 
-    this.successCallback = function (){
-      _callbacksNum--;
+    this.successCallback = function() {
+        _callbacksNum--;
 
-      if (!_callbacksNum) {
-        if (_allSucceeded) {
-          onAllSucceeded();
-        } else {
-          onFailure(_error);
+        if (!_callbacksNum) {
+            if (_allSucceeded) {
+                onAllSucceeded();
+            } else {
+                onFailure(_error);
+            }
         }
-      }
     };
 
     this.errorCallback = function(error) {
-      _callbacksNum--;
-      _allSucceeded = false;
-      _error = error;
+        _callbacksNum--;
+        _allSucceeded = false;
+        _error = error;
 
-      if (!_callbacksNum) {
-          onFailure(_error);
-      }
+        if (!_callbacksNum) {
+            onFailure(_error);
+        }
     };
 };
 
-var BluetoothGATTServer_valid_start_errors = ['InvalidStateError', 'NotSupportedError', 'AbortError'];
-var BluetoothGATTServer_valid_start_exceptions = ['InvalidStateError', 'TypeMismatchError', 'SecurityError'];
+var BluetoothGATTServer_valid_start_errors = [
+    'InvalidStateError',
+    'NotSupportedError',
+    'AbortError'
+];
+var BluetoothGATTServer_valid_start_exceptions = [
+    'InvalidStateError',
+    'TypeMismatchError',
+    'SecurityError'
+];
 
 BluetoothGATTServer.prototype.start = function() {
     privUtils_.log('Entered BluetoothGATTServer.start()');
@@ -3374,61 +3525,100 @@ BluetoothGATTServer.prototype.start = function() {
 
     var startServerCallback = function(result) {
         if (native.isFailure(result)) {
-            native.callIfPossible(args.errorCallback,
-                                  native.getErrorObjectAndValidate(result,
-                                    BluetoothGATTServer_valid_start_errors, AbortError));
+            native.callIfPossible(
+                args.errorCallback,
+                native.getErrorObjectAndValidate(
+                    result,
+                    BluetoothGATTServer_valid_start_errors,
+                    AbortError
+                )
+            );
         } else {
             native.callIfPossible(args.successCallback);
         }
     };
 
     if (servicesUnregisteredInNativeLayer.length) {
-      var registerServiceCallbacksAggregator = new ResultCallbacksAggregator(servicesUnregisteredInNativeLayer.length,
-          function onAllSucceeded() {
-
-              var result = native.call('BluetoothGATTServerStart', {}, startServerCallback);
-
-              if (native.isFailure(result)) {
-                  throw native.getErrorObjectAndValidate(result, BluetoothGATTServer_valid_start_exceptions, AbortError);
-              }
-          },
-          function onFailure(error) {
-              native.callIfPossible(args.errorCallback,
-                                    native.getErrorObjectAndValidate(error,
-                                      BluetoothGATTServer_valid_start_errors, AbortError));
-          });
-
-      var registerServicesCallback = function(result) {
-          if (native.isFailure(result)) {
-              registerServiceCallbacksAggregator.errorCallback(
-                                    native.getErrorObjectAndValidate(result,
-                                      BluetoothGATTServer_valid_start_errors, AbortError));
-          } else {
-              registerServiceCallbacksAggregator.successCallback();
-          }
-      };
-
-      for (var i = 0; i < servicesUnregisteredInNativeLayer.length; ++i) {
-        var result = native.call('BluetoothGATTServerRegisterService', servicesUnregisteredInNativeLayer[i], registerServicesCallback);
-        if (native.isFailure(result)) {
-            throw native.getErrorObjectAndValidate(
-                result,
-                BluetoothGATTServer_valid_registerService_exceptions,
-                AbortError
+        var registerServiceCallbacksAggregator = new ResultCallbacksAggregator(
+            servicesUnregisteredInNativeLayer.length,
+            function onAllSucceeded() {
+                var result = native.call(
+                    'BluetoothGATTServerStart',
+                    {},
+                    startServerCallback
+                );
+
+                if (native.isFailure(result)) {
+                    throw native.getErrorObjectAndValidate(
+                        result,
+                        BluetoothGATTServer_valid_start_exceptions,
+                        AbortError
+                    );
+                }
+            },
+            function onFailure(error) {
+                native.callIfPossible(
+                    args.errorCallback,
+                    native.getErrorObjectAndValidate(
+                        error,
+                        BluetoothGATTServer_valid_start_errors,
+                        AbortError
+                    )
+                );
+            }
+        );
+
+        var registerServicesCallback = function(result) {
+            if (native.isFailure(result)) {
+                registerServiceCallbacksAggregator.errorCallback(
+                    native.getErrorObjectAndValidate(
+                        result,
+                        BluetoothGATTServer_valid_start_errors,
+                        AbortError
+                    )
+                );
+            } else {
+                registerServiceCallbacksAggregator.successCallback();
+            }
+        };
+
+        for (var i = 0; i < servicesUnregisteredInNativeLayer.length; ++i) {
+            var result = native.call(
+                'BluetoothGATTServerRegisterService',
+                servicesUnregisteredInNativeLayer[i],
+                registerServicesCallback
             );
+            if (native.isFailure(result)) {
+                throw native.getErrorObjectAndValidate(
+                    result,
+                    BluetoothGATTServer_valid_registerService_exceptions,
+                    AbortError
+                );
+            }
         }
-      }
     } else {
         var result = native.call('BluetoothGATTServerStart', {}, startServerCallback);
 
         if (native.isFailure(result)) {
-            throw native.getErrorObjectAndValidate(result, BluetoothGATTServer_valid_start_exceptions, AbortError);
+            throw native.getErrorObjectAndValidate(
+                result,
+                BluetoothGATTServer_valid_start_exceptions,
+                AbortError
+            );
         }
     }
-}
+};
 
-var BluetoothGATTServer_valid_stop_errors = ['InvalidStateError', 'NotSupportedError', 'AbortError'];
-var BluetoothGATTServer_valid_stop_exceptions = ['InvalidStateError', 'TypeMismatchError', 'SecurityError'];
+var BluetoothGATTServer_valid_stop_errors = [
+    'InvalidStateError',
+    'NotSupportedError',
+    'AbortError'
+];
+var BluetoothGATTServer_valid_stop_exceptions = [
+    'InvalidStateError',
+    'TypeMismatchError',
+    'SecurityError'
+];
 
 BluetoothGATTServer.prototype.stop = function() {
     privUtils_.log('Entered BluetoothGATTServer.stop()');
@@ -3449,9 +3639,14 @@ BluetoothGATTServer.prototype.stop = function() {
 
     var callback = function(result) {
         if (native.isFailure(result)) {
-            native.callIfPossible(args.errorCallback,
-                                  native.getErrorObjectAndValidate(result,
-                                    BluetoothGATTServer_valid_stop_errors, AbortError));
+            native.callIfPossible(
+                args.errorCallback,
+                native.getErrorObjectAndValidate(
+                    result,
+                    BluetoothGATTServer_valid_stop_errors,
+                    AbortError
+                )
+            );
         } else {
             _BluetoothGATTServerServicesRegisteredInNativeLayer = {};
             native.callIfPossible(args.successCallback);
@@ -3460,9 +3655,13 @@ BluetoothGATTServer.prototype.stop = function() {
 
     var result = native.call('BluetoothGATTServerStop', {}, callback);
     if (native.isFailure(result)) {
-        throw native.getErrorObjectAndValidate(result, BluetoothGATTServer_valid_stop_exceptions, AbortError);
+        throw native.getErrorObjectAndValidate(
+            result,
+            BluetoothGATTServer_valid_stop_exceptions,
+            AbortError
+        );
     }
-}
+};
 
 var BluetoothGATTServer_valid_getConnectionMtu_errors = ['InvalidStateError', 'NotSupportedError', 'UnknownError'];
 var BluetoothGATTServer_valid_getConnectionMtu_exceptions = ['TypeMismatchError', 'SecurityError'];
@@ -3566,36 +3765,46 @@ var BluetoothManager_checkAndNormalizeHexString = function(hexString) {
     if (hexString.startsWith('0x')) {
         hexString = hexString.substring(2);
     }
-    if (!(/^[0-9a-f]+$/.test(hexString))) {
-        throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR, 'Given string is not hexadecimal value');
+    if (!/^[0-9a-f]+$/.test(hexString)) {
+        throw new WebAPIException(
+            WebAPIException.TYPE_MISMATCH_ERR,
+            'Given string is not hexadecimal value'
+        );
     }
     if ('0' === hexString) {
-        return  '00';
+        return '00';
     }
-    if (1 === hexString.length%2) {
+    if (1 === hexString.length % 2) {
         // to save consistency with BluetoothLEManufacturerData, last character is omitted
-        hexString = hexString.replace(/.$/,'');
+        hexString = hexString.replace(/.$/, '');
     }
     return hexString;
-}
+};
 
 var BluetoothManager_HexStringToUint8Array = function(hexString) {
     hexString = BluetoothManager_checkAndNormalizeHexString(hexString);
     if (0 === hexString.length) {
         return new Uint8Array([]);
     }
-    var data = hexString.match(/[0-9a-f]{2}/g).map(function(byte) {return parseInt(byte, 16)});
+    var data = hexString.match(/[0-9a-f]{2}/g).map(function(byte) {
+        return parseInt(byte, 16);
+    });
     return new Uint8Array(data);
-}
+};
 
 var BluetoothManager_byteArrayToHexString = function(bytes) {
     if (0 == bytes.length) {
         return '';
     }
-    return '0x' + Array.prototype.map.call(bytes, function(byte) {
-        return ('0' + (byte & 0xFF).toString(16)).slice(-2);
-    }).join('');
-}
+    return (
+        '0x' +
+        Array.prototype.map
+            .call(bytes, function(byte) {
+                return ('0' + (byte & 0xff).toString(16)).slice(-2);
+            })
+            .join('')
+    );
+};
 
 var BluetoothManager_toByteArray = function(data) {
     if (data && String === data.constructor) {
@@ -3603,9 +3812,11 @@ var BluetoothManager_toByteArray = function(data) {
     } else {
         try {
             data = numberArrayToByteArray(data);
-        }
-        catch(err) {
-            throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR, 'argument is not a valid Bytes type');
+        } catch (err) {
+            throw new WebAPIException(
+                WebAPIException.TYPE_MISMATCH_ERR,
+                'argument is not a valid Bytes type'
+            );
         }
     }
     return data;
@@ -3638,10 +3849,12 @@ var BluetoothManager_toUint8Array = function(data) {
         return BluetoothManager_HexStringToUint8Array(data);
     } else {
         try {
-            data =  new Uint8Array(numberArrayToByteArray(data));
-        }
-        catch(err) {
-            throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR, 'argument is not a valid Bytes type');
+            data = new Uint8Array(numberArrayToByteArray(data));
+        } catch (err) {
+            throw new WebAPIException(
+                WebAPIException.TYPE_MISMATCH_ERR,
+                'argument is not a valid Bytes type'
+            );
         }
     }
     return data;
index 0d131a0..d33c275 100644 (file)
@@ -178,6 +178,19 @@ void BluetoothGATTServer::RegisterService(const picojson::value& args, picojson:
   ReportSuccess(out);
 }
 
+void BluetoothGATTServer::UnregisterService(const picojson::value& args, picojson::object& out) {
+  ScopeLogger();
+
+  auto result = service_.UnregisterService(args, handle_);
+
+  if (result.IsError()) {
+    ReportError(result, &out);
+    return;
+  }
+
+  ReportSuccess(out);
+}
+
 bool BluetoothGATTServer::IsRunning() {
   return running_;
 }
index 82185f4..525d921 100644 (file)
@@ -39,6 +39,7 @@ class BluetoothGATTServer {
   void Stop(picojson::object& out);
   void GetConnectionMtu(const picojson::value& args, picojson::object& out);
   void RegisterService(const picojson::value& args, picojson::object& out);
+  void UnregisterService(const picojson::value& args, picojson::object& out);
   bool IsRunning();
 
   static bool DestroyService(int total, int index, bt_gatt_h handle, void* user_data);
index dcf0e6d..8568821 100644 (file)
@@ -56,6 +56,7 @@ const std::string kEncryptedSignedReadPermission = "encryptedSignedReadPermissio
 const std::string kEncryptedSignedWritePermission = "encryptedSignedWritePermission";
 
 const std::string kId = "_id";
+const std::string kIdsToRemoveFromNativeLayer = "idsToRemoveFromNativeLayer";
 
 int GetPermissionsInt(const picojson::value& permissions) {
   ScopeLogger("permissions: %s", permissions.serialize().c_str());
@@ -119,7 +120,8 @@ int GetPropertiesInt(const picojson::value& properties) {
 
 }  // namespace
 
-bool BluetoothGATTServerService::DestroyService(int total, int index, bt_gatt_h handle, void* user_data) {
+bool BluetoothGATTServerService::DestroyService(int total, int index, bt_gatt_h handle,
+                                                void* user_data) {
   ScopeLogger("total: %d, index: %d, handle: %p", total, index, handle);
 
   /*
@@ -138,7 +140,7 @@ bool BluetoothGATTServerService::DestroyService(int total, int index, bt_gatt_h
 }
 
 bool BluetoothGATTServerService::DestroyCharacteristic(int total, int index, bt_gatt_h handle,
-                                                void* user_data) {
+                                                       void* user_data) {
   ScopeLogger("total: %d, index: %d, handle: %p", total, index, handle);
 
   auto ret = bt_gatt_characteristic_destroy(handle);
@@ -151,7 +153,7 @@ bool BluetoothGATTServerService::DestroyCharacteristic(int total, int index, bt_
 }
 
 bool BluetoothGATTServerService::DestroyDescriptor(int total, int index, bt_gatt_h handle,
-                                            void* user_data) {
+                                                   void* user_data) {
   ScopeLogger("total: %d, index: %d, handle: %p", total, index, handle);
 
   auto ret = bt_gatt_descriptor_destroy(handle);
@@ -381,8 +383,8 @@ common::PlatformResult BluetoothGATTServerService::RegisterService(
   return common::PlatformResult();
 }
 
-
-common::PlatformResult BluetoothGATTServerService::UnregisterService(const picojson::value& args, bt_gatt_server_h server) {
+common::PlatformResult BluetoothGATTServerService::UnregisterService(const picojson::value& args,
+                                                                     bt_gatt_server_h server) {
   ScopeLogger();
 
   auto _id = static_cast<int>(args.get(kId).get<double>());
@@ -390,6 +392,13 @@ common::PlatformResult BluetoothGATTServerService::UnregisterService(const picoj
 
   auto service_handle = gatt_objects_[_id];
 
+  /*
+   * Undocumented behavior of native Bluetooth API:
+   * bt_gatt_server_unregister_service() not only unregisters the service,
+   * but also calls bt_gatt_destroy_service() on its handle, destroying
+   * the corresponding service object and all its components (included services,
+   * characteristics, descriptors).
+   */
   auto ret = bt_gatt_server_unregister_service(server, service_handle);
   if (BT_ERROR_NONE != ret) {
     LoggerE("bt_gatt_server_unregister_service() failed: %d (%s)", ret, get_error_message(ret));
@@ -398,8 +407,15 @@ common::PlatformResult BluetoothGATTServerService::UnregisterService(const picoj
   LoggerD("bt_gatt_server_unregister_service(): SUCCESS");
 
   gatt_objects_.erase(_id);
-  return common::PlatformResult{};
 
+  auto ids_to_remove = args.get(kIdsToRemoveFromNativeLayer).get<picojson::array>();
+  for (const auto& to_remove : ids_to_remove) {
+    int id_to_remove = static_cast<int>(to_remove.get<double>());
+    LoggerD("Erasing gatt object with _id: %d", id_to_remove);
+    gatt_objects_.erase(id_to_remove);
+  }
+
+  return common::PlatformResult{};
 }
 
 }  // namespace bluetooth
index a7575ea..70a70b7 100644 (file)
@@ -105,6 +105,7 @@ BluetoothInstance::BluetoothInstance()
   REGISTER_METHOD(BluetoothGATTServerStop);
   REGISTER_METHOD(BluetoothGATTServerGetConnectionMtu);
   REGISTER_METHOD(BluetoothGATTServerRegisterService);
+  REGISTER_METHOD(BluetoothGATTServerUnregisterService);
 
 #undef REGISTER_METHOD
 }
@@ -410,37 +411,37 @@ void BluetoothInstance::BluetoothLEDeviceGetServiceAllUuids(const picojson::valu
 }
 
 void BluetoothInstance::BluetoothGATTClientServiceGetServices(const picojson::value& args,
-                                                        picojson::object& out) {
+                                                              picojson::object& out) {
   ScopeLogger();
   bluetooth_gatt_client_service_.GetServices(args, out);
 }
 
 void BluetoothInstance::BluetoothGATTClientServiceGetCharacteristics(const picojson::value& args,
-                                                               picojson::object& out) {
+                                                                     picojson::object& out) {
   ScopeLogger();
   bluetooth_gatt_client_service_.GetCharacteristics(args, out);
 }
 
 void BluetoothInstance::BluetoothGATTClientServiceReadValue(const picojson::value& args,
-                                                      picojson::object& out) {
+                                                            picojson::object& out) {
   ScopeLogger();
   bluetooth_gatt_client_service_.ReadValue(args, out);
 }
 
 void BluetoothInstance::BluetoothGATTClientServiceWriteValue(const picojson::value& args,
-                                                       picojson::object& out) {
+                                                             picojson::object& out) {
   ScopeLogger();
   bluetooth_gatt_client_service_.WriteValue(args, out);
 }
 
-void BluetoothInstance::BluetoothGATTClientServiceAddValueChangeListener(const picojson::value& args,
-                                                                   picojson::object& out) {
+void BluetoothInstance::BluetoothGATTClientServiceAddValueChangeListener(
+    const picojson::value& args, picojson::object& out) {
   ScopeLogger();
   bluetooth_gatt_client_service_.AddValueChangeListener(args, out);
 }
 
-void BluetoothInstance::BluetoothGATTClientServiceRemoveValueChangeListener(const picojson::value& args,
-                                                                      picojson::object& out) {
+void BluetoothInstance::BluetoothGATTClientServiceRemoveValueChangeListener(
+    const picojson::value& args, picojson::object& out) {
   ScopeLogger();
   bluetooth_gatt_client_service_.RemoveValueChangeListener(args, out);
 }
@@ -520,5 +521,23 @@ void BluetoothInstance::BluetoothGATTServerRegisterService(const picojson::value
   ReportSuccess(out);
 }
 
+void BluetoothInstance::BluetoothGATTServerUnregisterService(const picojson::value& args,
+                                                             picojson::object& out) {
+  ScopeLogger();
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeBluetooth, &out);
+
+  worker.add_job([this, args] {
+    ScopeLogger("Async call: BluetoothGATTServerUnregisterService");
+    picojson::value response = picojson::value(picojson::object());
+    picojson::object& async_out = response.get<picojson::object>();
+    double callback_id = args.get("callbackId").get<double>();
+    async_out["callbackId"] = picojson::value(callback_id);
+    this->bluetooth_gatt_server_.UnregisterService(args, async_out);
+    this->PostMessage(response.serialize().c_str());
+  });
+
+  ReportSuccess(out);
+}
+
 }  // namespace bluetooth
 }  // namespace extension
index ed12227..e3e31c2 100644 (file)
@@ -21,8 +21,8 @@
 
 #include "bluetooth/bluetooth_adapter.h"
 #include "bluetooth/bluetooth_device.h"
-#include "bluetooth/bluetooth_gatt_server.h"
 #include "bluetooth/bluetooth_gatt_client_service.h"
+#include "bluetooth/bluetooth_gatt_server.h"
 #include "bluetooth/bluetooth_gatt_server_service.h"
 #include "bluetooth/bluetooth_health_application.h"
 #include "bluetooth/bluetooth_health_channel.h"
@@ -102,17 +102,19 @@ class BluetoothInstance : public common::ParsedInstance {
   void BluetoothLEDeviceGetServiceAllUuids(const picojson::value& args, picojson::object& out);
 
   void BluetoothGATTClientServiceGetServices(const picojson::value& args, picojson::object& out);
-  void BluetoothGATTClientServiceGetCharacteristics(const picojson::value& args, picojson::object& out);
+  void BluetoothGATTClientServiceGetCharacteristics(const picojson::value& args,
+                                                    picojson::object& out);
   void BluetoothGATTClientServiceReadValue(const picojson::value& args, picojson::object& out);
   void BluetoothGATTClientServiceWriteValue(const picojson::value& args, picojson::object& out);
   void BluetoothGATTClientServiceAddValueChangeListener(const picojson::value& args,
-                                                  picojson::object& out);
+                                                        picojson::object& out);
   void BluetoothGATTClientServiceRemoveValueChangeListener(const picojson::value& args,
-                                                     picojson::object& out);
+                                                           picojson::object& out);
   void BluetoothGATTServerStart(const picojson::value& args, picojson::object& out);
   void BluetoothGATTServerStop(const picojson::value& args, picojson::object& out);
   void BluetoothGATTServerGetConnectionMtu(const picojson::value& args, picojson::object& out);
   void BluetoothGATTServerRegisterService(const picojson::value& args, picojson::object& out);
+  void BluetoothGATTServerUnregisterService(const picojson::value& args, picojson::object& out);
 
   BluetoothAdapter bluetooth_adapter_;
   BluetoothDevice bluetooth_device_;