bluetooth_low_energy_advertisement_manager_mac.mm 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "device/bluetooth/bluetooth_low_energy_advertisement_manager_mac.h"
  5. #include "base/bind.h"
  6. #include "base/logging.h"
  7. #include "base/mac/scoped_nsobject.h"
  8. #include "base/strings/sys_string_conversions.h"
  9. #include "device/bluetooth/bluetooth_advertisement.h"
  10. #include "third_party/abseil-cpp/absl/types/optional.h"
  11. namespace device {
  12. BluetoothLowEnergyAdvertisementManagerMac::
  13. BluetoothLowEnergyAdvertisementManagerMac() {}
  14. BluetoothLowEnergyAdvertisementManagerMac::
  15. ~BluetoothLowEnergyAdvertisementManagerMac() {}
  16. void BluetoothLowEnergyAdvertisementManagerMac::Init(
  17. scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
  18. CBPeripheralManager* peripheral_manager) {
  19. ui_task_runner_ = ui_task_runner;
  20. peripheral_manager_ = peripheral_manager;
  21. }
  22. void BluetoothLowEnergyAdvertisementManagerMac::
  23. OnPeripheralManagerStateChanged() {
  24. // This function handles several cases:
  25. // 1. Error out when registering an advertisement, but the adapter is not
  26. // powered on.
  27. // 2. Start advertising a registered advertisement if the adapter is powered
  28. // on.
  29. // 3. Stop the advertisement when the adapter is powered off.
  30. // Note that if the adapter is powered off while advertising, macOS will
  31. // automatically restart advertising when the adapter is powered back on,
  32. // so we need to explicitly stop advertising in this case.
  33. if (!active_advertisement_) {
  34. return;
  35. }
  36. CBManagerState adapter_state = [peripheral_manager_ state];
  37. if (adapter_state == CBManagerStateUnknown) {
  38. // Wait for the adapter to initialize.
  39. return;
  40. }
  41. if (active_advertisement_->is_waiting_for_adapter() &&
  42. adapter_state < CBManagerStatePoweredOn) {
  43. DVLOG(1)
  44. << "Registration failed. Adapter changed to invalid adapter_state.";
  45. BluetoothAdvertisement::ErrorCode error_code =
  46. adapter_state == CBManagerStatePoweredOff
  47. ? BluetoothAdvertisement::ERROR_ADAPTER_POWERED_OFF
  48. : BluetoothAdvertisement::ERROR_UNSUPPORTED_PLATFORM;
  49. active_advertisement_->OnAdvertisementError(ui_task_runner_.get(),
  50. error_code);
  51. active_advertisement_ = nullptr;
  52. return;
  53. }
  54. if (active_advertisement_->is_advertising() &&
  55. adapter_state == CBManagerStateResetting) {
  56. DVLOG(1) << "Adapter resetting. Invalidating advertisement.";
  57. active_advertisement_->OnAdapterReset();
  58. active_advertisement_ = nullptr;
  59. return;
  60. }
  61. if (active_advertisement_->is_advertising() &&
  62. adapter_state == CBManagerStatePoweredOff) {
  63. DVLOG(1) << "Adapter powered off. Stopping advertisement.";
  64. // Note: we purposefully don't unregister the active advertisement for
  65. // consistency with ChromeOS. The caller must manually unregister
  66. // the advertisement themselves.
  67. [peripheral_manager_ stopAdvertising];
  68. return;
  69. }
  70. if (active_advertisement_->is_waiting_for_adapter()) {
  71. StartAdvertising();
  72. }
  73. }
  74. void BluetoothLowEnergyAdvertisementManagerMac::StartAdvertising() {
  75. base::scoped_nsobject<NSMutableArray> service_uuid_array(
  76. [[NSMutableArray alloc]
  77. initWithCapacity:active_advertisement_->service_uuids().size()]);
  78. for (const std::string& service_uuid :
  79. active_advertisement_->service_uuids()) {
  80. NSString* uuid_string = base::SysUTF8ToNSString(service_uuid);
  81. [service_uuid_array addObject:[CBUUID UUIDWithString:uuid_string]];
  82. }
  83. active_advertisement_->OnAdvertisementPending();
  84. [peripheral_manager_ startAdvertising:@{
  85. CBAdvertisementDataServiceUUIDsKey : service_uuid_array
  86. }];
  87. }
  88. void BluetoothLowEnergyAdvertisementManagerMac::RegisterAdvertisement(
  89. std::unique_ptr<BluetoothAdvertisement::Data> advertisement_data,
  90. BluetoothAdapter::CreateAdvertisementCallback callback,
  91. BluetoothAdapter::AdvertisementErrorCallback error_callback) {
  92. absl::optional<BluetoothAdvertisement::ErrorCode> error_code;
  93. std::unique_ptr<BluetoothAdvertisement::UUIDList> service_uuids =
  94. advertisement_data->service_uuids();
  95. if (!service_uuids || advertisement_data->manufacturer_data() ||
  96. advertisement_data->solicit_uuids() ||
  97. advertisement_data->service_data()) {
  98. DVLOG(1) << "macOS only supports advertising service UUIDs.";
  99. error_code = BluetoothAdvertisement::ERROR_UNSUPPORTED_PLATFORM;
  100. }
  101. if (active_advertisement_ && active_advertisement_->status() !=
  102. BluetoothAdvertisementMac::UNREGISTERED) {
  103. DVLOG(1) << "Only one active BLE advertisement is currently supported.";
  104. error_code = BluetoothAdvertisement::ERROR_ADVERTISEMENT_ALREADY_EXISTS;
  105. }
  106. if (error_code) {
  107. ui_task_runner_->PostTask(
  108. FROM_HERE, base::BindOnce(std::move(error_callback), *error_code));
  109. return;
  110. }
  111. active_advertisement_ = base::MakeRefCounted<BluetoothAdvertisementMac>(
  112. std::move(service_uuids), std::move(callback), std::move(error_callback),
  113. this);
  114. OnPeripheralManagerStateChanged();
  115. }
  116. void BluetoothLowEnergyAdvertisementManagerMac::UnregisterAdvertisement(
  117. BluetoothAdvertisementMac* advertisement,
  118. BluetoothAdvertisement::SuccessCallback success_callback,
  119. BluetoothAdvertisement::ErrorCallback error_callback) {
  120. if (advertisement != active_advertisement_.get()) {
  121. DVLOG(1) << "Cannot unregister none-active advertisement.";
  122. ui_task_runner_->PostTask(
  123. FROM_HERE,
  124. base::BindOnce(std::move(error_callback),
  125. BluetoothAdvertisement::ERROR_RESET_ADVERTISING));
  126. return;
  127. }
  128. active_advertisement_ = nullptr;
  129. [peripheral_manager_ stopAdvertising];
  130. ui_task_runner_->PostTask(FROM_HERE, std::move(success_callback));
  131. }
  132. void BluetoothLowEnergyAdvertisementManagerMac::DidStartAdvertising(
  133. NSError* error) {
  134. DCHECK(active_advertisement_ &&
  135. active_advertisement_->is_advertisement_pending());
  136. if (!active_advertisement_ ||
  137. !active_advertisement_->is_advertisement_pending()) {
  138. return;
  139. }
  140. if (error != nil) {
  141. DVLOG(1) << "Error advertising: "
  142. << base::SysNSStringToUTF8(error.localizedDescription);
  143. active_advertisement_->OnAdvertisementError(
  144. ui_task_runner_.get(),
  145. BluetoothAdvertisement::ERROR_STARTING_ADVERTISEMENT);
  146. active_advertisement_ = nullptr;
  147. return;
  148. }
  149. active_advertisement_->OnAdvertisementSuccess(ui_task_runner_.get());
  150. }
  151. } // device