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