/ app / packages / ble_peripheral / windows / ble_peripheral_plugin.cpp
ble_peripheral_plugin.cpp
  1  #include "ble_peripheral_plugin.h"
  2  #include <windows.h>
  3  #include <flutter/plugin_registrar_windows.h>
  4  #include <map>
  5  #include <memory>
  6  #include <sstream>
  7  #include <algorithm>
  8  #include <iomanip>
  9  #include <thread>
 10  #include <regex>
 11  #include "Utils.h"
 12  
 13  #include "BlePeripheral.g.h"
 14  
 15  namespace ble_peripheral
 16  {
 17    using ble_peripheral::BleCallback;
 18    using ble_peripheral::BlePeripheralChannel;
 19    using ble_peripheral::ErrorOr;
 20    std::unique_ptr<BleCallback> bleCallback;
 21    std::map<std::string, GattServiceProviderObject *> serviceProviderMap;
 22    std::mutex cout_mutex;
 23  
 24    // static
 25    void BlePeripheralPlugin::RegisterWithRegistrar(flutter::PluginRegistrarWindows *registrar)
 26    {
 27      auto plugin = std::make_unique<BlePeripheralPlugin>(registrar);
 28      BlePeripheralChannel::SetUp(registrar->messenger(), plugin.get());
 29      bleCallback = std::make_unique<BleCallback>(registrar->messenger());
 30      registrar->AddPlugin(std::move(plugin));
 31    }
 32  
 33    BlePeripheralPlugin::BlePeripheralPlugin(flutter::PluginRegistrarWindows *registrar) : uiThreadHandler_(registrar) {}
 34  
 35    BlePeripheralPlugin::~BlePeripheralPlugin() {}
 36  
 37    winrt::fire_and_forget BlePeripheralPlugin::InitializeAdapter()
 38    {
 39      auto radios = co_await Radio::GetRadiosAsync();
 40      for (auto &&radio : radios)
 41      {
 42        if (radio.Kind() == RadioKind::Bluetooth)
 43        {
 44          bluetoothRadio = radio;
 45          radioStateChangedRevoker = bluetoothRadio.StateChanged(winrt::auto_revoke, {this, &BlePeripheralPlugin::Radio_StateChanged});
 46          bool isOn = bluetoothRadio.State() == RadioState::On;
 47          uiThreadHandler_.Post([isOn]
 48                                { bleCallback->OnBleStateChange(isOn, SuccessCallback, ErrorCallback); });
 49  
 50          break;
 51        }
 52      }
 53      if (!bluetoothRadio)
 54      {
 55        std::cout << "Bluetooth is not available" << std::endl;
 56      }
 57    }
 58  
 59    std::optional<FlutterError> BlePeripheralPlugin::Initialize()
 60    {
 61      InitializeAdapter();
 62      return std::nullopt;
 63    };
 64  
 65    ErrorOr<std::optional<bool>> BlePeripheralPlugin::IsAdvertising()
 66    {
 67      // Check is any service is advertising, or if services list is empty
 68      // Get serviceProviderMap length
 69      if (serviceProviderMap.size() == 0)
 70        return ErrorOr<std::optional<bool>>(std::optional<bool>(false));
 71      bool advertising = AreAllServicesStarted();
 72      return ErrorOr<std::optional<bool>>(std::optional<bool>(advertising));
 73    };
 74  
 75    ErrorOr<bool> BlePeripheralPlugin::IsSupported()
 76    {
 77      return bluetoothRadio != nullptr;
 78    };
 79  
 80    ErrorOr<bool> BlePeripheralPlugin::AskBlePermission()
 81    {
 82      // No need to ask permission on Windows
 83      return true;
 84    };
 85  
 86    std::optional<FlutterError> BlePeripheralPlugin::AddService(const BleService &service)
 87    {
 88      AddServiceAsync(service);
 89      return std::nullopt;
 90    };
 91  
 92    std::optional<FlutterError> BlePeripheralPlugin::RemoveService(const std::string &service_id)
 93    {
 94      // lower case the service_id
 95      std::string serviceId = to_lower_case(service_id);
 96      if (serviceProviderMap.find(serviceId) == serviceProviderMap.end())
 97      {
 98        std::cout << "Service not found in map" << std::endl;
 99        return FlutterError("Service not found");
100      }
101      auto gattServiceObject = serviceProviderMap[serviceId];
102      disposeGattServiceObject(gattServiceObject);
103      serviceProviderMap.erase(serviceId);
104      return std::nullopt;
105    }
106  
107    std::optional<FlutterError> BlePeripheralPlugin::ClearServices()
108    {
109      for (auto const &[key, gattServiceObject] : serviceProviderMap)
110      {
111        disposeGattServiceObject(gattServiceObject);
112      }
113      // Clear map
114      serviceProviderMap.clear();
115      return std::nullopt;
116    }
117  
118    ErrorOr<flutter::EncodableList> BlePeripheralPlugin::GetServices()
119    {
120      flutter::EncodableList services = flutter::EncodableList();
121      for (auto const &[key, gattServiceObject] : serviceProviderMap)
122      {
123        services.push_back(flutter::EncodableValue(key));
124      }
125      return services;
126    }
127  
128    std::optional<FlutterError> BlePeripheralPlugin::StartAdvertising(
129        const flutter::EncodableList &services,
130        const std::string *local_name,
131        const int64_t *timeout,
132        const ManufacturerData *manufacturer_data,
133        bool add_manufacturer_data_in_scan_response)
134    {
135      try
136      {
137        // check if services are empty
138        if (serviceProviderMap.size() == 0)
139          return FlutterError("No services added to advertise");
140  
141        if (AreAllServicesStarted())
142        {
143          std::cout << "All services already advertising" << std::endl;
144          uiThreadHandler_.Post([]
145                                { bleCallback->OnAdvertisingStatusUpdate(true, nullptr, SuccessCallback, ErrorCallback); });
146          return std::nullopt;
147        }
148  
149        auto advertisementParameter = GattServiceProviderAdvertisingParameters();
150        advertisementParameter.IsDiscoverable(true);
151        advertisementParameter.IsConnectable(true);
152  
153        for (auto const &[key, gattServiceObject] : serviceProviderMap)
154        {
155          if (gattServiceObject->obj.AdvertisementStatus() == GattServiceProviderAdvertisementStatus::Started)
156          {
157            std::cout << "Service " << key << " is already advertising, skipping" << std::endl;
158            continue;
159          }
160          gattServiceObject->obj.StartAdvertising(advertisementParameter);
161        }
162  
163        return std::nullopt;
164      }
165      catch (...)
166      {
167        std::cout << "Error: "
168                  << "Unknown error" << std::endl;
169        return std::nullopt;
170      }
171    }
172  
173    std::optional<FlutterError> BlePeripheralPlugin::StopAdvertising()
174    {
175      for (auto const &[key, gattServiceObject] : serviceProviderMap)
176      {
177        try
178        {
179          gattServiceObject->obj.StopAdvertising();
180          std::cout << "Stopped advertising for service: " << key << std::endl;
181        }
182        catch (const winrt::hresult_error &e)
183        {
184          std::wcerr << "Failed to stop service: " << key.c_str() << ", Error: " << e.message().c_str() << std::endl;
185        }
186        catch (...)
187        {
188          std::cout << "Error: Unknown error" << std::endl;
189        }
190      }
191      uiThreadHandler_.Post([]
192                            { bleCallback->OnAdvertisingStatusUpdate(false, nullptr, SuccessCallback, ErrorCallback); });
193      return std::nullopt;
194    }
195  
196    std::optional<FlutterError> BlePeripheralPlugin::UpdateCharacteristic(
197        const std::string &characteristic_id,
198        const std::vector<uint8_t> &value,
199        const std::string *device_id)
200    {
201      GattCharacteristicObject *gattCharacteristicObject = FindGattCharacteristicObject(characteristic_id);
202      if (gattCharacteristicObject == nullptr)
203        return FlutterError("Failed to get this characteristic");
204  
205      IBuffer bytes = from_bytevc(value);
206      DataWriter writer;
207      writer.ByteOrder(ByteOrder::LittleEndian);
208      writer.WriteBuffer(bytes);
209      gattCharacteristicObject->obj.NotifyValueAsync(writer.DetachBuffer());
210      return std::nullopt;
211    }
212  
213    // Helpers
214    winrt::fire_and_forget BlePeripheralPlugin::AddServiceAsync(const BleService &service)
215    {
216      auto serviceUuid = service.uuid();
217      try
218      {
219        // Build Service
220        auto characteristics = service.characteristics();
221        auto gattCharacteristicObjList = std::map<std::string, GattCharacteristicObject *>();
222  
223        auto serviceProviderResult = co_await GattServiceProvider::CreateAsync(uuid_to_guid(serviceUuid));
224        if (serviceProviderResult.Error() != BluetoothError::Success)
225        {
226          std::string bleError = ParseBluetoothError(serviceProviderResult.Error());
227          std::string err = "Failed to create service provider: " + serviceUuid + ", errorCode: " + bleError;
228          std::cout << err << std::endl;
229          bleCallback->OnServiceAdded(serviceUuid, &err, SuccessCallback, ErrorCallback);
230          co_return;
231        }
232  
233        GattServiceProvider serviceProvider = serviceProviderResult.ServiceProvider();
234  
235        // Build Characteristic
236        for (auto characteristicEncoded : characteristics)
237        {
238          BleCharacteristic characteristic = std::any_cast<BleCharacteristic>(std::get<flutter::CustomEncodableValue>(characteristicEncoded));
239          flutter::EncodableList descriptors = characteristic.descriptors() == nullptr ? flutter::EncodableList() : *characteristic.descriptors();
240  
241          auto charParameters = GattLocalCharacteristicParameters();
242          auto characteristicUuid = characteristic.uuid();
243  
244          // Add characteristic properties
245          auto charProperties = characteristic.properties();
246          for (flutter::EncodableValue propertyEncoded : charProperties)
247          {
248            int property = static_cast<int>(std::get<int64_t>(propertyEncoded));
249            charParameters.CharacteristicProperties(charParameters.CharacteristicProperties() | toGattCharacteristicProperties(property));
250          }
251  
252          // Add characteristic permissions
253          auto charPermissions = characteristic.permissions();
254          for (flutter::EncodableValue permissionEncoded : charPermissions)
255          {
256            auto blePermission = toBlePermission(static_cast<int>(std::get<int64_t>(permissionEncoded)));
257            switch (blePermission)
258            {
259            case BlePermission::readable:
260              charParameters.ReadProtectionLevel(GattProtectionLevel::Plain);
261              break;
262            case BlePermission::writeable:
263              charParameters.WriteProtectionLevel(GattProtectionLevel::Plain);
264              break;
265            case BlePermission::readEncryptionRequired:
266              charParameters.ReadProtectionLevel(GattProtectionLevel::EncryptionRequired);
267              break;
268            case BlePermission::writeEncryptionRequired:
269              charParameters.WriteProtectionLevel(GattProtectionLevel::EncryptionRequired);
270              break;
271            }
272          }
273  
274          const std::vector<uint8_t> *characteristicValue = characteristic.value();
275          if (characteristicValue != nullptr)
276          {
277            auto characteristicBytes = from_bytevc(*characteristicValue);
278            charParameters.StaticValue(characteristicBytes);
279          }
280  
281          auto characteristicResult = co_await serviceProvider.Service().CreateCharacteristicAsync(uuid_to_guid(characteristicUuid), charParameters);
282          if (characteristicResult.Error() != BluetoothError::Success)
283          {
284            std::wcerr << "Failed to create Char Provider: " << std::endl;
285            co_return;
286          }
287          auto gattCharacteristic = characteristicResult.Characteristic();
288  
289          auto gattCharacteristicObject = new GattCharacteristicObject();
290          gattCharacteristicObject->obj = gattCharacteristic;
291          gattCharacteristicObject->stored_clients = gattCharacteristic.SubscribedClients();
292  
293          gattCharacteristicObject->read_requested_token = gattCharacteristic.ReadRequested({this, &BlePeripheralPlugin::ReadRequestedAsync});
294          gattCharacteristicObject->write_requested_token = gattCharacteristic.WriteRequested({this, &BlePeripheralPlugin::WriteRequestedAsync});
295          gattCharacteristicObject->value_changed_token = gattCharacteristic.SubscribedClientsChanged({this, &BlePeripheralPlugin::SubscribedClientsChanged});
296  
297          // Build Descriptors
298          for (flutter::EncodableValue descriptorEncoded : descriptors)
299          {
300            BleDescriptor descriptor = std::any_cast<BleDescriptor>(std::get<flutter::CustomEncodableValue>(descriptorEncoded));
301            auto descriptorUuid = descriptor.uuid();
302            auto descriptorParameters = GattLocalDescriptorParameters();
303  
304            // Add descriptor permissions
305            flutter::EncodableList descriptorPermissions = descriptor.permissions() == nullptr ? flutter::EncodableList() : *descriptor.permissions();
306            for (flutter::EncodableValue permissionsEncoded : descriptorPermissions)
307            {
308              auto blePermission = toBlePermission(static_cast<int>(std::get<int64_t>(permissionsEncoded)));
309              switch (blePermission)
310              {
311              case BlePermission::readable:
312                descriptorParameters.ReadProtectionLevel(GattProtectionLevel::Plain);
313                break;
314              case BlePermission::writeable:
315                descriptorParameters.WriteProtectionLevel(GattProtectionLevel::Plain);
316                break;
317              case BlePermission::readEncryptionRequired:
318                descriptorParameters.ReadProtectionLevel(GattProtectionLevel::EncryptionRequired);
319                break;
320              case BlePermission::writeEncryptionRequired:
321                descriptorParameters.WriteProtectionLevel(GattProtectionLevel::EncryptionRequired);
322                break;
323              }
324            }
325            const std::vector<uint8_t> *descriptorValue = descriptor.value();
326            if (descriptorValue != nullptr)
327            {
328              auto descriptorBytes = from_bytevc(*descriptorValue);
329              descriptorParameters.StaticValue(descriptorBytes);
330            }
331            auto descriptorResult = co_await gattCharacteristic.CreateDescriptorAsync(uuid_to_guid(descriptorUuid), descriptorParameters);
332            if (descriptorResult.Error() != BluetoothError::Success)
333            {
334              std::wcerr << "Failed to create Descriptor Provider: " << std::endl;
335              co_return;
336            }
337            GattLocalDescriptor gattDescriptor = descriptorResult.Descriptor();
338          }
339  
340          gattCharacteristicObjList.insert_or_assign(guid_to_uuid(gattCharacteristic.Uuid()), gattCharacteristicObject);
341        }
342  
343        GattServiceProviderObject *gattServiceProviderObject = new GattServiceProviderObject();
344        gattServiceProviderObject->obj = serviceProvider;
345        gattServiceProviderObject->characteristics = gattCharacteristicObjList;
346        gattServiceProviderObject->advertisement_status_changed_token = serviceProvider.AdvertisementStatusChanged({this, &BlePeripheralPlugin::ServiceProvider_AdvertisementStatusChanged});
347        serviceProviderMap.insert_or_assign(guid_to_uuid(serviceProvider.Service().Uuid()), gattServiceProviderObject);
348  
349        uiThreadHandler_.Post([serviceUuid]
350                              { bleCallback->OnServiceAdded(serviceUuid, nullptr, SuccessCallback, ErrorCallback); });
351      }
352      catch (const winrt::hresult_error &e)
353      {
354        std::wcerr << "Failed with error: Code: " << e.code() << "Message: " << e.message().c_str() << std::endl;
355        std::string errorMessage = winrt::to_string(e.message());
356  
357        uiThreadHandler_.Post([serviceUuid, errorMessage]
358                              { bleCallback->OnServiceAdded(serviceUuid, &errorMessage, SuccessCallback, ErrorCallback); });
359      }
360      catch (const std::exception &e)
361      {
362        std::cout << "Error: " << e.what() << std::endl;
363        std::wstring errorMessage = winrt::to_hstring(e.what()).c_str();
364        std::string *err = new std::string(winrt::to_string(errorMessage));
365        uiThreadHandler_.Post([serviceUuid, err]
366                              { bleCallback->OnServiceAdded(serviceUuid, err, SuccessCallback, ErrorCallback); });
367      }
368      catch (...)
369      {
370        std::cout << "Error: Unknown error" << std::endl;
371        std::string *err = new std::string(winrt::to_string(L"Unknown error"));
372        uiThreadHandler_.Post([serviceUuid, err]
373                              { bleCallback->OnServiceAdded(serviceUuid, err, SuccessCallback, ErrorCallback); });
374      }
375    }
376  
377    /// Handlers
378  
379    /// Advertisements Listener
380    void BlePeripheralPlugin::ServiceProvider_AdvertisementStatusChanged(GattServiceProvider const &sender, GattServiceProviderAdvertisementStatusChangedEventArgs const &args)
381    {
382      std::lock_guard<std::mutex> lock(cout_mutex);
383      auto serviceUuid = guid_to_uuid(sender.Service().Uuid());
384      if (args.Error() != BluetoothError::Success)
385      {
386        std::string errorStr = ParseBluetoothError(args.Error());
387        std::cout << "AdvertisementStatusChanged " << serviceUuid << ", Error " << errorStr << std::endl;
388  
389        uiThreadHandler_.Post([errorStr]
390                              { bleCallback->OnAdvertisingStatusUpdate(false, &errorStr, SuccessCallback, ErrorCallback); });
391        return;
392      }
393  
394      auto argStatus = args.Status();
395      auto statusStr = AdvertisementStatusToString(argStatus);
396      std::cout << "AdvertisingStatus of service " << serviceUuid << ", changed to " << statusStr << " " << std::endl;
397  
398      // Check if all services started
399      if (AreAllServicesStarted())
400      {
401        uiThreadHandler_.Post([]
402                              { bleCallback->OnAdvertisingStatusUpdate(true, nullptr, SuccessCallback, ErrorCallback); });
403      }
404    }
405  
406    /// Characteristic Listeners
407    winrt::fire_and_forget BlePeripheralPlugin::SubscribedClientsChanged(GattLocalCharacteristic const &localChar, IInspectable const &)
408    {
409      auto characteristicId = guid_to_uuid(localChar.Uuid());
410  
411      // Find GattCharacteristicObject
412      GattCharacteristicObject *gattCharacteristicObject = FindGattCharacteristicObject(characteristicId);
413  
414      if (gattCharacteristicObject == nullptr)
415      {
416        std::cout << "Failed to get char " << characteristicId << std::endl;
417        co_return;
418      }
419  
420      // Compare Stored clients and New clients
421      IVectorView<GattSubscribedClient> currentClients = localChar.SubscribedClients();
422      IVectorView<GattSubscribedClient> oldClients = gattCharacteristicObject->stored_clients;
423  
424      // Check if any client removed
425      for (unsigned int i = 0; i < oldClients.Size(); ++i)
426      {
427        auto oldClient = oldClients.GetAt(i);
428        bool found = false;
429        for (unsigned int j = 0; j < currentClients.Size(); ++j)
430        {
431          if (currentClients.GetAt(j) == oldClient)
432          {
433            found = true;
434            break;
435          }
436        }
437        if (!found)
438        {
439          // oldClient is not in currentClients, so it was removed
440          std::string deviceIdArg = ParseBluetoothClientId(oldClient.Session().DeviceId().Id());
441          try
442          {
443            auto deviceInfo = co_await DeviceInformation::CreateFromIdAsync(oldClient.Session().DeviceId().Id());
444            auto deviceName = winrt::to_string(deviceInfo.Name());
445            uiThreadHandler_.Post([deviceName, deviceIdArg, characteristicId]
446                                  {
447                                    bleCallback->OnCharacteristicSubscriptionChange(
448                                        deviceIdArg, characteristicId, false, &deviceName,
449                                        SuccessCallback, ErrorCallback);
450                                    // Notify subscription change
451                                  });
452            // Point to the local variable
453          }
454          catch (...)
455          {
456            std::cerr << "Failed to retrieve device name" << std::endl;
457          }
458        }
459      }
460  
461      // Check if any client added
462      for (unsigned int i = 0; i < currentClients.Size(); ++i)
463      {
464        auto currentClient = currentClients.GetAt(i);
465        bool found = false;
466        for (unsigned int j = 0; j < oldClients.Size(); ++j)
467        {
468          if (oldClients.GetAt(j) == currentClient)
469          {
470            found = true;
471            break;
472          }
473        }
474        if (!found)
475        {
476          // currentClient is not in oldClients, so it was added
477          std::string deviceIdArg = ParseBluetoothClientId(currentClient.Session().DeviceId().Id());
478  
479          try
480          {
481            auto deviceInfo = co_await DeviceInformation::CreateFromIdAsync(currentClient.Session().DeviceId().Id());
482            auto deviceName = winrt::to_string(deviceInfo.Name());
483            uiThreadHandler_.Post([deviceName, deviceIdArg, characteristicId]
484                                  {
485                                    bleCallback->OnCharacteristicSubscriptionChange(
486                                        deviceIdArg, characteristicId, true, &deviceName,
487                                        SuccessCallback, ErrorCallback);
488                                    // Notify subscription change
489                                  });
490            // Point to the local variable
491          }
492          catch (...)
493          {
494            std::cerr << "Failed to retrieve device name" << std::endl;
495          }
496  
497          int64_t maxPuid = currentClient.Session().MaxPduSize();
498          uiThreadHandler_.Post([deviceIdArg, maxPuid]
499                                {
500                                  bleCallback->OnMtuChange(deviceIdArg, maxPuid,
501                                                           SuccessCallback, ErrorCallback);
502                                  // Notify added device MTU change
503                                });
504        }
505      }
506  
507      // Update stored clients in stored char
508      gattCharacteristicObject->stored_clients = currentClients;
509    }
510  
511    winrt::fire_and_forget BlePeripheralPlugin::ReadRequestedAsync(GattLocalCharacteristic const &localChar, GattReadRequestedEventArgs args)
512    {
513      std::string characteristicId = to_uuidstr(localChar.Uuid());
514      std::vector<uint8_t> *value_arg = nullptr;
515      IBuffer charValue = localChar.StaticValue();
516      // IBuffer charValue = nullptr;
517      if (charValue != nullptr)
518      {
519        auto bytevc = to_bytevc(charValue);
520        value_arg = &bytevc;
521      }
522      
523      auto deferral = args.GetDeferral();
524      auto request = co_await args.GetRequestAsync();
525      if (request == nullptr)
526      {
527        // No access allowed to the device.  Application should indicate this to the user.
528        std::cout << "No access allowed to the device" << std::endl;
529        deferral.Complete();
530        co_return;
531      }
532  
533      std::string deviceId = ParseBluetoothClientId(args.Session().DeviceId().Id());
534      int64_t offset = request.Offset();
535    
536      uiThreadHandler_.Post([deviceId, characteristicId, offset, value_arg, deferral, request]
537                            {
538                              bleCallback->OnReadRequest(
539                                  deviceId, characteristicId, offset, value_arg,
540                                  // SuccessCallback,
541                                  [deferral, request](const ReadRequestResult *readResult)
542                                  {
543                                    if (readResult == nullptr)
544                                    {
545                                      std::cout << "ReadRequestResult is null" << std::endl;
546                                      // request.RespondWithProtocolError(GattProtocolError::InvalidHandle());
547                                    }
548                                    else
549                                    {
550                                      // FIXME: use offset as well
551                                      std::vector<uint8_t> resultVal = readResult->value();
552                                      IBuffer result = from_bytevc(resultVal);
553  
554                                      // Send response
555                                      DataWriter writer;
556                                      writer.ByteOrder(ByteOrder::LittleEndian);
557                                      writer.WriteBuffer(result);
558                                      request.RespondWithValue(writer.DetachBuffer());
559                                    }
560                                    deferral.Complete();
561                                  },
562                                  // ErrorCallback
563                                  [deferral](const FlutterError &error)
564                                  {
565                                    std::cout << "ErrorCallback: " << error.message() << std::endl;
566                                    deferral.Complete();
567                                  });
568                              // Handle readRequest result
569                            });
570    }
571  
572    winrt::fire_and_forget BlePeripheralPlugin::WriteRequestedAsync(GattLocalCharacteristic const &localChar, GattWriteRequestedEventArgs args)
573    {
574      auto deferral = args.GetDeferral();
575      GattWriteRequest request = co_await args.GetRequestAsync();
576      if (request == nullptr)
577      {
578        std::cout << "No access allowed to the device" << std::endl;
579        deferral.Complete();
580        co_return;
581      }
582  
583      std::string deviceId = ParseBluetoothClientId(args.Session().DeviceId().Id());
584  
585      uiThreadHandler_.Post([localChar, request, deferral, deviceId]
586                            {
587                              auto characteristicId = guid_to_uuid(localChar.Uuid());
588                              int64_t offset = request.Offset();
589                              auto bytevc = to_bytevc(request.Value());
590                              std::vector<uint8_t> *value_arg = &bytevc;
591  
592                              bleCallback->OnWriteRequest(
593                                  deviceId, characteristicId, offset, value_arg,
594                                  // SuccessCallback
595                                  [deferral, request, localChar](const WriteRequestResult *writeResult)
596                                  {
597                                    // respond with error if status is not null,
598                                    // FIXME: parse proper error
599                                    if (writeResult->status() != nullptr)
600                                      // request.RespondWithProtocolError(GattProtocolError::InvalidHandle());
601                                      std::cout << "WriteRequestResult should throw error" << std::endl;
602                                    else
603                                      request.Respond();
604                                    deferral.Complete();
605                                  },
606                                  // ErrorCallback
607                                  [deferral](const FlutterError &error)
608                                  {
609                                    std::cout << "ErrorCallback: " << error.message() << std::endl;
610                                    deferral.Complete();
611                                  });
612  
613                              // Write Request
614                            });
615    }
616  
617    void BlePeripheralPlugin::disposeGattServiceObject(GattServiceProviderObject *gattServiceObject)
618    {
619      auto serviceId = guid_to_uuid(gattServiceObject->obj.Service().Uuid());
620      try
621      {
622        // check if serviceMap have this uuid
623        if (serviceProviderMap.find(serviceId) == serviceProviderMap.end())
624        {
625          std::cout << "Service not found in map" << std::endl;
626          return;
627        }
628        std::cout << "Cleaning service: " << serviceId << std::endl;
629        // clean resources for this service
630        gattServiceObject->obj.AdvertisementStatusChanged(gattServiceObject->advertisement_status_changed_token);
631  
632        // Stop advertising if started
633  
634        try
635        {
636          if (gattServiceObject->obj.AdvertisementStatus() == GattServiceProviderAdvertisementStatus::Started)
637          {
638            gattServiceObject->obj.StopAdvertising();
639          }
640        }
641        catch (...)
642        {
643          std::cout << "Warning: Failed to stop advertisement of " << serviceId << std::endl;
644        }
645  
646        // clean resources for characteristics
647        for (auto const &[chatKey, gattCharacteristicObject] : gattServiceObject->characteristics)
648        {
649          gattCharacteristicObject->obj.ReadRequested(gattCharacteristicObject->read_requested_token);
650          gattCharacteristicObject->obj.WriteRequested(gattCharacteristicObject->write_requested_token);
651          gattCharacteristicObject->obj.SubscribedClientsChanged(gattCharacteristicObject->value_changed_token);
652        }
653      }
654      catch (const winrt::hresult_error &e)
655      {
656        std::wcerr << "Failed to clear service: " << serviceId.c_str() << ", Error: " << e.message().c_str() << std::endl;
657      }
658      catch (...)
659      {
660        std::cout << "Error: Unknown error" << std::endl;
661      }
662    }
663  
664    void BlePeripheralPlugin::Radio_StateChanged(Radio radio, IInspectable args)
665    {
666      auto radioState = !radio ? RadioState::Disabled : radio.State();
667      if (oldRadioState == radioState)
668      {
669        return;
670      }
671      oldRadioState = radioState;
672      bleCallback->OnBleStateChange(radioState == RadioState::On, SuccessCallback, ErrorCallback);
673    }
674  
675    GattCharacteristicProperties BlePeripheralPlugin::toGattCharacteristicProperties(int property)
676    {
677      switch (property)
678      {
679      case 0:
680        return GattCharacteristicProperties::Broadcast;
681      case 1:
682        return GattCharacteristicProperties::Read;
683      case 2:
684        return GattCharacteristicProperties::WriteWithoutResponse;
685      case 3:
686        return GattCharacteristicProperties::Write;
687      case 4:
688        return GattCharacteristicProperties::Notify;
689      case 5:
690        return GattCharacteristicProperties::Indicate;
691      case 6:
692        return GattCharacteristicProperties::AuthenticatedSignedWrites;
693      case 7:
694        return GattCharacteristicProperties::ExtendedProperties;
695      case 8:
696        return GattCharacteristicProperties::Notify;
697      case 9:
698        return GattCharacteristicProperties::Indicate;
699      default:
700        return GattCharacteristicProperties::None;
701      }
702    }
703  
704    BlePermission BlePeripheralPlugin::toBlePermission(int permission)
705    {
706      switch (permission)
707      {
708      case 0:
709        return BlePermission::readable;
710      case 1:
711        return BlePermission::writeable;
712      case 2:
713        return BlePermission::readEncryptionRequired;
714      case 3:
715        return BlePermission::writeEncryptionRequired;
716      default:
717        return BlePermission::none;
718      }
719    }
720  
721    std::string BlePeripheralPlugin::AdvertisementStatusToString(GattServiceProviderAdvertisementStatus status)
722    {
723      switch (status)
724      {
725      case GattServiceProviderAdvertisementStatus::Created:
726        return "Created";
727      case GattServiceProviderAdvertisementStatus::Started:
728        return "Started";
729      case GattServiceProviderAdvertisementStatus::Stopped:
730        return "Stopped";
731      case GattServiceProviderAdvertisementStatus::Aborted:
732        return "Aborted";
733      case GattServiceProviderAdvertisementStatus::StartedWithoutAllAdvertisementData:
734        return "StartedWithoutAllAdvertisementData";
735      default:
736        return "Unknown";
737      }
738    }
739  
740    std::string BlePeripheralPlugin::ParseBluetoothClientId(hstring clientId)
741    {
742      std::string deviceIdString = winrt::to_string(clientId);
743      size_t pos = deviceIdString.find_last_of('-');
744      if (pos != std::string::npos)
745      {
746        return deviceIdString.substr(pos + 1);
747      }
748      return deviceIdString;
749    }
750  
751    std::string BlePeripheralPlugin::ParseBluetoothError(BluetoothError error)
752    {
753      switch (error)
754      {
755      case BluetoothError::Success:
756        return "Success";
757      case BluetoothError::RadioNotAvailable:
758        return "RadioNotAvailable";
759      case BluetoothError::ResourceInUse:
760        return "ResourceInUse";
761      case BluetoothError::DeviceNotConnected:
762        return "DeviceNotConnected";
763      case BluetoothError::OtherError:
764        return "OtherError";
765      case BluetoothError::DisabledByPolicy:
766        return "DisabledByPolicy";
767      case BluetoothError::NotSupported:
768        return "NotSupported";
769      case BluetoothError::DisabledByUser:
770        return "DisabledByUser";
771      case BluetoothError::ConsentRequired:
772        return "ConsentRequired";
773      case BluetoothError::TransportNotSupported:
774        return "TransportNotSupported";
775      default:
776        return "Unknown";
777      }
778    }
779  
780    GattCharacteristicObject *BlePeripheralPlugin::FindGattCharacteristicObject(std::string characteristicId)
781    {
782      // This might return wrong result if multiple services have same characteristic Id
783      std::string loweCaseCharId = to_lower_case(characteristicId);
784      for (auto const &[key, gattServiceObject] : serviceProviderMap)
785      {
786        for (auto const &[charKey, gattChar] : gattServiceObject->characteristics)
787        {
788          if (charKey == loweCaseCharId)
789            return gattChar;
790        }
791      }
792      return nullptr;
793    }
794  
795    bool BlePeripheralPlugin::AreAllServicesStarted()
796    {
797      for (auto const &[key, gattServiceObject] : serviceProviderMap)
798      {
799        if (gattServiceObject->obj.AdvertisementStatus() != GattServiceProviderAdvertisementStatus::Started)
800        {
801          return false;
802        }
803      }
804      return true;
805    }
806  
807  } // namespace ble_peripheral