bluetooth_adapter_mac.mm 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933
  1. // Copyright 2013 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_adapter_mac.h"
  5. #import <CoreBluetooth/CBManager.h>
  6. #include <CoreFoundation/CFNumber.h>
  7. #import <IOBluetooth/objc/IOBluetoothDevice.h>
  8. #import <IOBluetooth/objc/IOBluetoothHostController.h>
  9. #include <IOKit/IOKitLib.h>
  10. #include <stddef.h>
  11. #include <memory>
  12. #include <string>
  13. #include <utility>
  14. #include "base/bind.h"
  15. #include "base/compiler_specific.h"
  16. #include "base/location.h"
  17. #include "base/logging.h"
  18. #include "base/mac/foundation_util.h"
  19. #include "base/mac/mac_util.h"
  20. #include "base/mac/scoped_ioobject.h"
  21. #include "base/memory/ptr_util.h"
  22. #include "base/numerics/safe_conversions.h"
  23. #include "base/strings/sys_string_conversions.h"
  24. #include "base/task/single_thread_task_runner.h"
  25. #include "base/task/task_traits.h"
  26. #include "base/threading/thread_task_runner_handle.h"
  27. #include "base/time/time.h"
  28. #include "components/device_event_log/device_event_log.h"
  29. #include "device/bluetooth/bluetooth_adapter_mac_metrics.h"
  30. #include "device/bluetooth/bluetooth_advertisement_mac.h"
  31. #include "device/bluetooth/bluetooth_classic_device_mac.h"
  32. #include "device/bluetooth/bluetooth_common.h"
  33. #include "device/bluetooth/bluetooth_discovery_session.h"
  34. #include "device/bluetooth/bluetooth_discovery_session_outcome.h"
  35. #include "device/bluetooth/bluetooth_low_energy_central_manager_delegate.h"
  36. #include "device/bluetooth/bluetooth_low_energy_device_watcher_mac.h"
  37. #include "device/bluetooth/bluetooth_low_energy_peripheral_manager_delegate.h"
  38. #include "device/bluetooth/bluetooth_socket_mac.h"
  39. #include "device/bluetooth/public/cpp/bluetooth_address.h"
  40. extern "C" {
  41. // Undocumented IOBluetooth Preference API [1]. Used by `blueutil` [2] and
  42. // `Karabiner` [3] to programmatically control the Bluetooth state. Calling the
  43. // method with `1` powers the adapter on, calling it with `0` powers it off.
  44. // Using this API has the same effect as turning Bluetooth on or off using the
  45. // macOS System Preferences [4], and will effect all adapters.
  46. //
  47. // [1] https://goo.gl/Gbjm1x
  48. // [2] http://www.frederikseiffert.de/blueutil/
  49. // [3] https://pqrs.org/osx/karabiner/
  50. // [4] https://support.apple.com/kb/PH25091
  51. void IOBluetoothPreferenceSetControllerPowerState(int state);
  52. }
  53. namespace {
  54. // The frequency with which to poll the adapter for updates.
  55. const int kPollIntervalMs = 500;
  56. bool IsDeviceSystemPaired(const std::string& device_address) {
  57. IOBluetoothDevice* device = [IOBluetoothDevice
  58. deviceWithAddressString:base::SysUTF8ToNSString(device_address)];
  59. return device && [device isPaired];
  60. }
  61. } // namespace
  62. namespace device {
  63. // static
  64. scoped_refptr<BluetoothAdapter> BluetoothAdapter::CreateAdapter() {
  65. return BluetoothAdapterMac::CreateAdapter();
  66. }
  67. // static
  68. scoped_refptr<BluetoothAdapterMac> BluetoothAdapterMac::CreateAdapter() {
  69. return base::WrapRefCounted(new BluetoothAdapterMac());
  70. }
  71. // static
  72. scoped_refptr<BluetoothAdapterMac> BluetoothAdapterMac::CreateAdapterForTest(
  73. std::string name,
  74. std::string address,
  75. scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
  76. auto adapter = base::WrapRefCounted(new BluetoothAdapterMac());
  77. adapter->InitForTest(ui_task_runner);
  78. adapter->name_ = name;
  79. adapter->should_update_name_ = false;
  80. adapter->address_ = address;
  81. return adapter;
  82. }
  83. // static
  84. BluetoothUUID BluetoothAdapterMac::BluetoothUUIDWithCBUUID(CBUUID* uuid) {
  85. std::string uuid_c_string = base::SysNSStringToUTF8([uuid UUIDString]);
  86. return device::BluetoothUUID(uuid_c_string);
  87. }
  88. // static
  89. std::string BluetoothAdapterMac::String(NSError* error) {
  90. if (!error) {
  91. return "no error";
  92. }
  93. return std::string("error domain: ") + base::SysNSStringToUTF8(error.domain) +
  94. ", code: " + std::to_string(error.code) + ", description: " +
  95. base::SysNSStringToUTF8(error.localizedDescription);
  96. }
  97. BluetoothAdapterMac::BluetoothAdapterMac()
  98. : controller_state_function_(
  99. base::BindRepeating(&BluetoothAdapterMac::GetHostControllerState,
  100. base::Unretained(this))),
  101. power_state_function_(
  102. base::BindRepeating(IOBluetoothPreferenceSetControllerPowerState)),
  103. classic_discovery_manager_(
  104. BluetoothDiscoveryManagerMac::CreateClassic(this)),
  105. low_energy_discovery_manager_(
  106. BluetoothLowEnergyDiscoveryManagerMac::Create(this)),
  107. low_energy_advertisement_manager_(
  108. std::make_unique<BluetoothLowEnergyAdvertisementManagerMac>()),
  109. low_energy_central_manager_delegate_(
  110. [[BluetoothLowEnergyCentralManagerDelegate alloc]
  111. initWithDiscoveryManager:low_energy_discovery_manager_.get()
  112. andAdapter:this]),
  113. low_energy_peripheral_manager_delegate_(
  114. [[BluetoothLowEnergyPeripheralManagerDelegate alloc]
  115. initWithAdvertisementManager:
  116. low_energy_advertisement_manager_.get()
  117. andAdapter:this]),
  118. device_paired_status_callback_(
  119. base::BindRepeating(&IsDeviceSystemPaired)) {
  120. DCHECK(classic_discovery_manager_);
  121. DCHECK(low_energy_discovery_manager_);
  122. }
  123. BluetoothAdapterMac::~BluetoothAdapterMac() {
  124. // When devices will be destroyed, they will need this current instance to
  125. // disconnect the gatt connection. To make sure they don't use the mac
  126. // adapter, they should be explicitly destroyed here.
  127. devices_.clear();
  128. // Explicitly clear out delegates, which might outlive the Adapter.
  129. [low_energy_peripheral_manager_ setDelegate:nil];
  130. [low_energy_central_manager_ setDelegate:nil];
  131. // Set low_energy_central_manager_ to nil so no devices will try to use it
  132. // while being destroyed after this method. |devices_| is owned by
  133. // BluetoothAdapter.
  134. low_energy_central_manager_.reset();
  135. }
  136. std::string BluetoothAdapterMac::GetAddress() const {
  137. const_cast<BluetoothAdapterMac*>(this)->LazyInitialize();
  138. return address_;
  139. }
  140. std::string BluetoothAdapterMac::GetName() const {
  141. if (!should_update_name_) {
  142. return name_;
  143. }
  144. IOBluetoothHostController* controller =
  145. [IOBluetoothHostController defaultController];
  146. name_ = controller != nil ? base::SysNSStringToUTF8([controller nameAsString])
  147. : std::string();
  148. should_update_name_ = name_.empty();
  149. return name_;
  150. }
  151. void BluetoothAdapterMac::SetName(const std::string& name,
  152. base::OnceClosure callback,
  153. ErrorCallback error_callback) {
  154. NOTIMPLEMENTED();
  155. }
  156. bool BluetoothAdapterMac::IsInitialized() const {
  157. // Initialize() does nothing and the lazy initialization state is hidden.
  158. return true;
  159. }
  160. bool BluetoothAdapterMac::IsPresent() const {
  161. // Avoid calling LazyInitialize() so that a Bluetooth permission prompt
  162. // doesn't appear when simply trying to detect whether the system supports
  163. // Bluetooth.
  164. if (is_present_for_testing_.has_value())
  165. return is_present_for_testing_.value();
  166. base::mac::ScopedIOObject<io_iterator_t> iterator;
  167. IOReturn result = IOServiceGetMatchingServices(
  168. kIOMasterPortDefault, IOServiceMatching("IOBluetoothHCIController"),
  169. iterator.InitializeInto());
  170. if (result != kIOReturnSuccess) {
  171. BLUETOOTH_LOG(ERROR) << "Failed to enumerate Bluetooth controller: "
  172. << std::hex << result << ".";
  173. return false;
  174. }
  175. base::mac::ScopedIOObject<io_service_t> service(IOIteratorNext(iterator));
  176. if (!service) {
  177. return false;
  178. }
  179. base::ScopedCFTypeRef<CFBooleanRef> connected(
  180. base::mac::CFCast<CFBooleanRef>(IORegistryEntryCreateCFProperty(
  181. service, CFSTR("BluetoothTransportConnected"), kCFAllocatorDefault,
  182. 0)));
  183. return CFBooleanGetValue(connected);
  184. }
  185. BluetoothAdapter::PermissionStatus BluetoothAdapterMac::GetOsPermissionStatus()
  186. const {
  187. if (@available(macOS 10.15.0, *)) {
  188. switch (CBCentralManager.authorization) {
  189. case CBManagerAuthorizationNotDetermined:
  190. return PermissionStatus::kUndetermined;
  191. case CBManagerAuthorizationRestricted:
  192. case CBManagerAuthorizationDenied:
  193. return PermissionStatus::kDenied;
  194. case CBManagerAuthorizationAllowedAlways:
  195. return PermissionStatus::kAllowed;
  196. }
  197. }
  198. // There are no Core Bluetooth permissions before macOS 10.15 so assume we
  199. // always have permission.
  200. return PermissionStatus::kAllowed;
  201. }
  202. bool BluetoothAdapterMac::IsPowered() const {
  203. const_cast<BluetoothAdapterMac*>(this)->LazyInitialize();
  204. return classic_powered_ || IsLowEnergyPowered();
  205. }
  206. // TODO(krstnmnlsn): If this information is retrievable form IOBluetooth we
  207. // should return the discoverable status.
  208. bool BluetoothAdapterMac::IsDiscoverable() const {
  209. return false;
  210. }
  211. void BluetoothAdapterMac::SetDiscoverable(bool discoverable,
  212. base::OnceClosure callback,
  213. ErrorCallback error_callback) {
  214. NOTIMPLEMENTED();
  215. }
  216. bool BluetoothAdapterMac::IsDiscovering() const {
  217. return classic_discovery_manager_->IsDiscovering() ||
  218. low_energy_discovery_manager_->IsDiscovering();
  219. }
  220. std::unordered_map<BluetoothDevice*, BluetoothDevice::UUIDSet>
  221. BluetoothAdapterMac::RetrieveGattConnectedDevicesWithDiscoveryFilter(
  222. const BluetoothDiscoveryFilter& discovery_filter) {
  223. LazyInitialize();
  224. std::unordered_map<BluetoothDevice*, BluetoothDevice::UUIDSet>
  225. connected_devices;
  226. std::set<device::BluetoothUUID> uuids;
  227. discovery_filter.GetUUIDs(uuids);
  228. if (uuids.empty()) {
  229. for (BluetoothDevice* device :
  230. RetrieveGattConnectedDevicesWithService(nullptr)) {
  231. connected_devices[device] = BluetoothDevice::UUIDSet();
  232. }
  233. return connected_devices;
  234. }
  235. for (const BluetoothUUID& uuid : uuids) {
  236. for (BluetoothDevice* device :
  237. RetrieveGattConnectedDevicesWithService(&uuid)) {
  238. connected_devices[device].insert(uuid);
  239. }
  240. }
  241. return connected_devices;
  242. }
  243. BluetoothAdapter::UUIDList BluetoothAdapterMac::GetUUIDs() const {
  244. NOTIMPLEMENTED();
  245. return UUIDList();
  246. }
  247. void BluetoothAdapterMac::CreateRfcommService(
  248. const BluetoothUUID& uuid,
  249. const ServiceOptions& options,
  250. CreateServiceCallback callback,
  251. CreateServiceErrorCallback error_callback) {
  252. LazyInitialize();
  253. scoped_refptr<BluetoothSocketMac> socket = BluetoothSocketMac::CreateSocket();
  254. socket->ListenUsingRfcomm(this, uuid, options,
  255. base::BindOnce(std::move(callback), socket),
  256. std::move(error_callback));
  257. }
  258. void BluetoothAdapterMac::CreateL2capService(
  259. const BluetoothUUID& uuid,
  260. const ServiceOptions& options,
  261. CreateServiceCallback callback,
  262. CreateServiceErrorCallback error_callback) {
  263. LazyInitialize();
  264. scoped_refptr<BluetoothSocketMac> socket = BluetoothSocketMac::CreateSocket();
  265. socket->ListenUsingL2cap(this, uuid, options,
  266. base::BindOnce(std::move(callback), socket),
  267. std::move(error_callback));
  268. }
  269. void BluetoothAdapterMac::RegisterAdvertisement(
  270. std::unique_ptr<BluetoothAdvertisement::Data> advertisement_data,
  271. CreateAdvertisementCallback callback,
  272. AdvertisementErrorCallback error_callback) {
  273. LazyInitialize();
  274. low_energy_advertisement_manager_->RegisterAdvertisement(
  275. std::move(advertisement_data), std::move(callback),
  276. std::move(error_callback));
  277. }
  278. BluetoothLocalGattService* BluetoothAdapterMac::GetGattService(
  279. const std::string& identifier) const {
  280. return nullptr;
  281. }
  282. BluetoothAdapter::DeviceList BluetoothAdapterMac::GetDevices() {
  283. LazyInitialize();
  284. return BluetoothAdapter::GetDevices();
  285. }
  286. BluetoothAdapter::ConstDeviceList BluetoothAdapterMac::GetDevices() const {
  287. const_cast<BluetoothAdapterMac*>(this)->LazyInitialize();
  288. return BluetoothAdapter::GetDevices();
  289. }
  290. void BluetoothAdapterMac::ClassicDeviceFound(IOBluetoothDevice* device) {
  291. ClassicDeviceAdded(device);
  292. }
  293. void BluetoothAdapterMac::ClassicDiscoveryStopped(bool unexpected) {
  294. if (unexpected) {
  295. DVLOG(1) << "Discovery stopped unexpectedly";
  296. MarkDiscoverySessionsAsInactive();
  297. }
  298. for (auto& observer : observers_)
  299. observer.AdapterDiscoveringChanged(this, false);
  300. }
  301. void BluetoothAdapterMac::DeviceConnected(IOBluetoothDevice* device) {
  302. // TODO(isherman): Investigate whether this method can be replaced with a call
  303. // to +registerForConnectNotifications:selector:.
  304. DVLOG(1) << "Adapter registered a new connection from device with address: "
  305. << BluetoothClassicDeviceMac::GetDeviceAddress(device);
  306. ClassicDeviceAdded(device);
  307. }
  308. base::WeakPtr<BluetoothAdapter> BluetoothAdapterMac::GetWeakPtr() {
  309. return weak_ptr_factory_.GetWeakPtr();
  310. }
  311. bool BluetoothAdapterMac::SetPoweredImpl(bool powered) {
  312. power_state_function_.Run(base::strict_cast<int>(powered));
  313. return true;
  314. }
  315. void BluetoothAdapterMac::RemovePairingDelegateInternal(
  316. BluetoothDevice::PairingDelegate* pairing_delegate) {}
  317. void BluetoothAdapterMac::LazyInitialize() {
  318. if (lazy_initialized_)
  319. return;
  320. low_energy_central_manager_.reset([[CBCentralManager alloc]
  321. initWithDelegate:low_energy_central_manager_delegate_
  322. queue:dispatch_get_main_queue()]);
  323. low_energy_discovery_manager_->SetCentralManager(low_energy_central_manager_);
  324. low_energy_peripheral_manager_.reset([[CBPeripheralManager alloc]
  325. initWithDelegate:low_energy_peripheral_manager_delegate_
  326. queue:dispatch_get_main_queue()]);
  327. lazy_initialized_ = true;
  328. low_energy_advertisement_manager_->Init(ui_task_runner_,
  329. low_energy_peripheral_manager_);
  330. PollAdapter();
  331. // To obtain list of low energy devices known to the system, we need to parse
  332. // and watch system property list file for paired device addresses.
  333. bluetooth_low_energy_device_watcher_ =
  334. BluetoothLowEnergyDeviceWatcherMac::CreateAndStartWatching(
  335. ui_task_runner_,
  336. base::BindRepeating(&BluetoothAdapterMac::UpdateKnownLowEnergyDevices,
  337. weak_ptr_factory_.GetWeakPtr()));
  338. bluetooth_low_energy_device_watcher_->ReadBluetoothPropertyListFile();
  339. }
  340. BluetoothAdapterMac::HostControllerState
  341. BluetoothAdapterMac::GetHostControllerState() {
  342. HostControllerState state;
  343. IOBluetoothHostController* controller =
  344. [IOBluetoothHostController defaultController];
  345. if (controller != nil) {
  346. state.classic_powered =
  347. ([controller powerState] == kBluetoothHCIPowerStateON);
  348. state.address = CanonicalizeBluetoothAddress(
  349. base::SysNSStringToUTF8([controller addressAsString]));
  350. state.is_present = !state.address.empty();
  351. }
  352. return state;
  353. }
  354. void BluetoothAdapterMac::UpdateKnownLowEnergyDevices(
  355. std::map<std::string, std::string> updated_low_energy_devices_info) {
  356. std::map<std::string, std::string> changed_devices;
  357. // Notify DeviceChanged() to devices that have been newly paired as well as to
  358. // devices that have been removed from the pairing list.
  359. std::set_symmetric_difference(
  360. updated_low_energy_devices_info.begin(),
  361. updated_low_energy_devices_info.end(), low_energy_devices_info_.begin(),
  362. low_energy_devices_info_.end(),
  363. std::inserter(changed_devices, changed_devices.end()));
  364. low_energy_devices_info_ = std::move(updated_low_energy_devices_info);
  365. for (const auto& info : changed_devices) {
  366. auto it = devices_.find(
  367. BluetoothLowEnergyDeviceMac::GetPeripheralHashAddress(info.first));
  368. if (it == devices_.end())
  369. continue;
  370. NotifyDeviceChanged(it->second.get());
  371. }
  372. }
  373. void BluetoothAdapterMac::SetPresentForTesting(bool present) {
  374. is_present_for_testing_ = present;
  375. }
  376. void BluetoothAdapterMac::SetCentralManagerForTesting(
  377. CBCentralManager* central_manager) {
  378. central_manager.delegate = low_energy_central_manager_delegate_;
  379. low_energy_central_manager_.reset(central_manager,
  380. base::scoped_policy::RETAIN);
  381. low_energy_discovery_manager_->SetCentralManager(low_energy_central_manager_);
  382. }
  383. CBCentralManager* BluetoothAdapterMac::GetCentralManager() {
  384. return low_energy_central_manager_;
  385. }
  386. CBPeripheralManager* BluetoothAdapterMac::GetPeripheralManager() {
  387. return low_energy_peripheral_manager_;
  388. }
  389. void BluetoothAdapterMac::SetHostControllerStateFunctionForTesting(
  390. HostControllerStateFunction controller_state_function) {
  391. controller_state_function_ = std::move(controller_state_function);
  392. }
  393. void BluetoothAdapterMac::SetPowerStateFunctionForTesting(
  394. SetControllerPowerStateFunction power_state_function) {
  395. power_state_function_ = std::move(power_state_function);
  396. }
  397. void BluetoothAdapterMac::SetLowEnergyDeviceWatcherForTesting(
  398. scoped_refptr<BluetoothLowEnergyDeviceWatcherMac>
  399. bluetooth_low_energy_device_watcher) {
  400. bluetooth_low_energy_device_watcher_ =
  401. std::move(bluetooth_low_energy_device_watcher);
  402. bluetooth_low_energy_device_watcher_->Init();
  403. }
  404. void BluetoothAdapterMac::SetGetDevicePairedStatusCallbackForTesting(
  405. GetDevicePairedStatusCallback device_paired_status_callback) {
  406. device_paired_status_callback_ = std::move(device_paired_status_callback);
  407. }
  408. void BluetoothAdapterMac::UpdateFilter(
  409. std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter,
  410. DiscoverySessionResultCallback callback) {
  411. // In Mac the start scan handles all updates automatically
  412. StartScanWithFilter(std::move(discovery_filter), std::move(callback));
  413. }
  414. void BluetoothAdapterMac::StartScanWithFilter(
  415. std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter,
  416. DiscoverySessionResultCallback callback) {
  417. // Default to dual discovery if |discovery_filter| is NULL. IOBluetooth seems
  418. // to allow starting low energy and classic discovery at once.
  419. BluetoothTransport transport = BLUETOOTH_TRANSPORT_DUAL;
  420. if (discovery_filter) {
  421. transport = discovery_filter->GetTransport();
  422. }
  423. if ((transport & BLUETOOTH_TRANSPORT_CLASSIC) &&
  424. !classic_discovery_manager_->IsDiscovering()) {
  425. // We do not update the filter if already discovering. This will all be
  426. // deprecated soon though.
  427. if (!classic_discovery_manager_->StartDiscovery()) {
  428. DVLOG(1) << "Failed to add a classic discovery session";
  429. ui_task_runner_->PostTask(
  430. FROM_HERE,
  431. base::BindOnce(std::move(callback), /*is_error=*/true,
  432. UMABluetoothDiscoverySessionOutcome::UNKNOWN));
  433. return;
  434. }
  435. }
  436. if (transport & BLUETOOTH_TRANSPORT_LE) {
  437. // Begin a low energy discovery session or update it if one is already
  438. // running.
  439. low_energy_discovery_manager_->StartDiscovery(BluetoothDevice::UUIDList());
  440. }
  441. for (auto& observer : observers_)
  442. observer.AdapterDiscoveringChanged(this, true);
  443. DCHECK(callback);
  444. ui_task_runner_->PostTask(
  445. FROM_HERE, base::BindOnce(std::move(callback), /*is_error=*/false,
  446. UMABluetoothDiscoverySessionOutcome::SUCCESS));
  447. }
  448. void BluetoothAdapterMac::StopScan(DiscoverySessionResultCallback callback) {
  449. low_energy_discovery_manager_->StopDiscovery();
  450. for (const auto& device_id_object_pair : devices_) {
  451. device_id_object_pair.second->ClearAdvertisementData();
  452. }
  453. if (classic_discovery_manager_->IsDiscovering() &&
  454. !classic_discovery_manager_->StopDiscovery()) {
  455. DVLOG(1) << "Failed to stop classic discovery";
  456. // TODO: Provide a more precise error here.
  457. std::move(callback).Run(/*is_error=*/true,
  458. UMABluetoothDiscoverySessionOutcome::UNKNOWN);
  459. return;
  460. }
  461. DVLOG(1) << "Discovery stopped";
  462. std::move(callback).Run(/*is_error=*/false,
  463. UMABluetoothDiscoverySessionOutcome::SUCCESS);
  464. }
  465. bool BluetoothAdapterMac::StartDiscovery(
  466. BluetoothDiscoveryFilter* discovery_filter) {
  467. // Default to dual discovery if |discovery_filter| is NULL. IOBluetooth seems
  468. // allow starting low energy and classic discovery at once.
  469. BluetoothTransport transport = BLUETOOTH_TRANSPORT_DUAL;
  470. if (discovery_filter)
  471. transport = discovery_filter->GetTransport();
  472. if ((transport & BLUETOOTH_TRANSPORT_CLASSIC) &&
  473. !classic_discovery_manager_->IsDiscovering()) {
  474. // TODO(krstnmnlsn): If a classic discovery session is already running then
  475. // we should update its filter. crbug.com/498056
  476. if (!classic_discovery_manager_->StartDiscovery()) {
  477. DVLOG(1) << "Failed to add a classic discovery session";
  478. return false;
  479. }
  480. }
  481. if (transport & BLUETOOTH_TRANSPORT_LE) {
  482. // Begin a low energy discovery session or update it if one is already
  483. // running.
  484. low_energy_discovery_manager_->StartDiscovery(BluetoothDevice::UUIDList());
  485. }
  486. return true;
  487. }
  488. void BluetoothAdapterMac::Initialize(base::OnceClosure callback) {
  489. // Real initialization is deferred to LazyInitialize().
  490. ui_task_runner_ = base::ThreadTaskRunnerHandle::Get();
  491. std::move(callback).Run();
  492. }
  493. void BluetoothAdapterMac::InitForTest(
  494. scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
  495. ui_task_runner_ = ui_task_runner;
  496. lazy_initialized_ = true;
  497. is_present_for_testing_ = false;
  498. }
  499. void BluetoothAdapterMac::PollAdapter() {
  500. const bool was_present = IsPresent();
  501. HostControllerState state = controller_state_function_.Run();
  502. if (address_ != state.address)
  503. should_update_name_ = true;
  504. address_ = std::move(state.address);
  505. if (was_present != state.is_present) {
  506. for (auto& observer : observers_)
  507. observer.AdapterPresentChanged(this, state.is_present);
  508. }
  509. if (classic_powered_ != state.classic_powered) {
  510. classic_powered_ = state.classic_powered;
  511. RunPendingPowerCallbacks();
  512. NotifyAdapterPoweredChanged(classic_powered_);
  513. }
  514. RemoveTimedOutDevices();
  515. AddPairedDevices();
  516. ui_task_runner_->PostDelayedTask(
  517. FROM_HERE,
  518. base::BindOnce(&BluetoothAdapterMac::PollAdapter,
  519. weak_ptr_factory_.GetWeakPtr()),
  520. base::Milliseconds(kPollIntervalMs));
  521. }
  522. void BluetoothAdapterMac::ClassicDeviceAdded(IOBluetoothDevice* device) {
  523. std::string device_address =
  524. BluetoothClassicDeviceMac::GetDeviceAddress(device);
  525. BluetoothDevice* device_classic = GetDevice(device_address);
  526. // Only notify observers once per device.
  527. if (device_classic != nullptr) {
  528. DVLOG(3) << "Updating classic device: " << device_classic->GetAddress();
  529. device_classic->UpdateTimestamp();
  530. return;
  531. }
  532. device_classic = new BluetoothClassicDeviceMac(this, device);
  533. devices_[device_address] = base::WrapUnique(device_classic);
  534. DVLOG(1) << "Adding new classic device: " << device_classic->GetAddress();
  535. for (auto& observer : observers_)
  536. observer.DeviceAdded(this, device_classic);
  537. }
  538. bool BluetoothAdapterMac::IsLowEnergyPowered() const {
  539. return [low_energy_central_manager_ state] == CBManagerStatePoweredOn;
  540. }
  541. void BluetoothAdapterMac::LowEnergyDeviceUpdated(
  542. CBPeripheral* peripheral,
  543. NSDictionary* advertisement_data,
  544. int rssi) {
  545. BluetoothLowEnergyDeviceMac* device_mac =
  546. GetBluetoothLowEnergyDeviceMac(peripheral);
  547. // If has no entry in the map, create new device and insert into |devices_|,
  548. // otherwise update the existing device.
  549. const bool is_new_device = device_mac == nullptr;
  550. if (is_new_device) {
  551. // A new device has been found.
  552. device_mac = new BluetoothLowEnergyDeviceMac(this, peripheral);
  553. DVLOG(1) << *device_mac << ": New Device.";
  554. } else if (DoesCollideWithKnownDevice(peripheral, device_mac)) {
  555. return;
  556. }
  557. DCHECK(device_mac);
  558. DVLOG(3) << *device_mac << ": Device updated with "
  559. << base::SysNSStringToUTF8([advertisement_data description]);
  560. // Get Advertised UUIDs
  561. // Core Specification Supplement (CSS) v7, Part 1.1
  562. // https://developer.apple.com/documentation/corebluetooth/cbadvertisementdataserviceuuidskey
  563. BluetoothDevice::UUIDList advertised_uuids;
  564. NSArray* service_uuids =
  565. advertisement_data[CBAdvertisementDataServiceUUIDsKey];
  566. for (CBUUID* uuid in service_uuids) {
  567. advertised_uuids.push_back(
  568. BluetoothAdapterMac::BluetoothUUIDWithCBUUID(uuid));
  569. }
  570. NSArray* overflow_service_uuids =
  571. advertisement_data[CBAdvertisementDataOverflowServiceUUIDsKey];
  572. for (CBUUID* uuid in overflow_service_uuids) {
  573. advertised_uuids.push_back(
  574. BluetoothAdapterMac::BluetoothUUIDWithCBUUID(uuid));
  575. }
  576. // Get Service Data.
  577. // Core Specification Supplement (CSS) v7, Part 1.11
  578. // https://developer.apple.com/documentation/corebluetooth/cbadvertisementdataservicedatakey
  579. BluetoothDevice::ServiceDataMap service_data_map;
  580. NSDictionary* service_data =
  581. advertisement_data[CBAdvertisementDataServiceDataKey];
  582. for (CBUUID* uuid in service_data) {
  583. NSData* data = service_data[uuid];
  584. const uint8_t* bytes = static_cast<const uint8_t*>([data bytes]);
  585. size_t length = [data length];
  586. service_data_map.emplace(BluetoothAdapterMac::BluetoothUUIDWithCBUUID(uuid),
  587. std::vector<uint8_t>(bytes, bytes + length));
  588. }
  589. // Get Manufacturer Data.
  590. // "Size: 2 or more octets
  591. // The first 2 octets contain the Company Identifier Code followed
  592. // by additional manufacturer specific data"
  593. // Core Specification Supplement (CSS) v7, Part 1.4
  594. // https://developer.apple.com/documentation/corebluetooth/cbadvertisementdatamanufacturerdatakey
  595. //
  596. BluetoothDevice::ManufacturerDataMap manufacturer_data_map;
  597. NSData* manufacturer_data =
  598. advertisement_data[CBAdvertisementDataManufacturerDataKey];
  599. const uint8_t* bytes = static_cast<const uint8_t*>([manufacturer_data bytes]);
  600. size_t length = [manufacturer_data length];
  601. if (length > 1) {
  602. const uint16_t manufacturer_id = bytes[0] | bytes[1] << 8;
  603. manufacturer_data_map.emplace(
  604. manufacturer_id, std::vector<uint8_t>(bytes + 2, bytes + length));
  605. }
  606. // Get Tx Power.
  607. // "Size: 1 octet
  608. // 0xXX: -127 to +127 dBm"
  609. // Core Specification Supplement (CSS) v7, Part 1.5
  610. // https://developer.apple.com/documentation/corebluetooth/cbadvertisementdatatxpowerlevelkey
  611. NSNumber* tx_power = advertisement_data[CBAdvertisementDataTxPowerLevelKey];
  612. int8_t clamped_tx_power = BluetoothDevice::ClampPower([tx_power intValue]);
  613. // Get the Advertising name
  614. NSString* local_name = advertisement_data[CBAdvertisementDataLocalNameKey];
  615. for (auto& observer : observers_) {
  616. absl::optional<std::string> device_name_opt = device_mac->GetName();
  617. absl::optional<std::string> local_name_opt =
  618. base::SysNSStringToUTF8(local_name);
  619. observer.DeviceAdvertisementReceived(
  620. device_mac->GetAddress(), device_name_opt,
  621. local_name == nil ? absl::nullopt : local_name_opt, rssi,
  622. tx_power == nil ? absl::nullopt : absl::make_optional(clamped_tx_power),
  623. absl::nullopt, /* TODO(crbug.com/588083) Implement appearance */
  624. advertised_uuids, service_data_map, manufacturer_data_map);
  625. }
  626. device_mac->UpdateAdvertisementData(
  627. BluetoothDevice::ClampPower(rssi), absl::nullopt /* flags */,
  628. std::move(advertised_uuids),
  629. tx_power == nil ? absl::nullopt : absl::make_optional(clamped_tx_power),
  630. std::move(service_data_map), std::move(manufacturer_data_map));
  631. if (is_new_device) {
  632. std::string device_address =
  633. BluetoothLowEnergyDeviceMac::GetPeripheralHashAddress(peripheral);
  634. devices_[device_address] = base::WrapUnique(device_mac);
  635. for (auto& observer : observers_)
  636. observer.DeviceAdded(this, device_mac);
  637. } else {
  638. for (auto& observer : observers_)
  639. observer.DeviceChanged(this, device_mac);
  640. }
  641. }
  642. void BluetoothAdapterMac::LowEnergyCentralManagerUpdatedState() {
  643. DVLOG(1) << "Central manager state updated: "
  644. << [low_energy_central_manager_ state];
  645. // A state with a value lower than CBManagerStatePoweredOn implies that
  646. // scanning has stopped and that any connected peripherals have been
  647. // disconnected. Call DidDisconnectPeripheral manually to update the devices'
  648. // states since macOS doesn't call it.
  649. // See
  650. // https://developer.apple.com/reference/corebluetooth/cbcentralmanagerdelegate/1518888-centralmanagerdidupdatestate?language=objc
  651. if ([low_energy_central_manager_ state] < CBManagerStatePoweredOn) {
  652. DVLOG(1)
  653. << "Central no longer powered on. Notifying of device disconnection.";
  654. for (BluetoothDevice* device : GetDevices()) {
  655. // GetDevices() returns instances of BluetoothClassicDeviceMac and
  656. // BluetoothLowEnergyDeviceMac. The DidDisconnectPeripheral() method is
  657. // only available on BluetoothLowEnergyDeviceMac.
  658. if (!static_cast<BluetoothDeviceMac*>(device)->IsLowEnergyDevice())
  659. continue;
  660. BluetoothLowEnergyDeviceMac* device_mac =
  661. static_cast<BluetoothLowEnergyDeviceMac*>(device);
  662. if (device_mac->IsGattConnected()) {
  663. device_mac->DidDisconnectPeripheral(nullptr);
  664. }
  665. }
  666. }
  667. }
  668. void BluetoothAdapterMac::AddPairedDevices() {
  669. // Add any new paired devices.
  670. for (IOBluetoothDevice* device in [IOBluetoothDevice pairedDevices]) {
  671. // pairedDevices sometimes includes unknown devices that are not paired.
  672. // Radar issue with id 2282763004 has been filed about it.
  673. if ([device isPaired]) {
  674. ClassicDeviceAdded(device);
  675. }
  676. }
  677. }
  678. std::vector<BluetoothDevice*>
  679. BluetoothAdapterMac::RetrieveGattConnectedDevicesWithService(
  680. const BluetoothUUID* uuid) {
  681. NSArray* cbUUIDs = nil;
  682. if (!uuid) {
  683. DVLOG(1) << "Retrieving all connected devices.";
  684. // It is not possible to ask for all connected peripherals with
  685. // -[CBCentralManager retrieveConnectedPeripheralsWithServices:] by passing
  686. // nil. To try to get most of the peripherals, the search is done with
  687. // Generic Access service.
  688. CBUUID* genericAccessServiceUUID = [CBUUID UUIDWithString:@"1800"];
  689. cbUUIDs = @[ genericAccessServiceUUID ];
  690. } else {
  691. DVLOG(1) << "Retrieving connected devices with UUID: "
  692. << uuid->canonical_value();
  693. NSString* uuidString =
  694. base::SysUTF8ToNSString(uuid->canonical_value().c_str());
  695. cbUUIDs = @[ [CBUUID UUIDWithString:uuidString] ];
  696. }
  697. NSArray* peripherals = [low_energy_central_manager_
  698. retrieveConnectedPeripheralsWithServices:cbUUIDs];
  699. std::vector<BluetoothDevice*> connected_devices;
  700. for (CBPeripheral* peripheral in peripherals) {
  701. BluetoothLowEnergyDeviceMac* device_mac =
  702. GetBluetoothLowEnergyDeviceMac(peripheral);
  703. const bool is_new_device = device_mac == nullptr;
  704. if (!is_new_device && DoesCollideWithKnownDevice(peripheral, device_mac)) {
  705. continue;
  706. }
  707. if (is_new_device) {
  708. device_mac = new BluetoothLowEnergyDeviceMac(this, peripheral);
  709. std::string device_address =
  710. BluetoothLowEnergyDeviceMac::GetPeripheralHashAddress(peripheral);
  711. devices_[device_address] = base::WrapUnique(device_mac);
  712. for (auto& observer : observers_) {
  713. observer.DeviceAdded(this, device_mac);
  714. }
  715. }
  716. connected_devices.push_back(device_mac);
  717. DVLOG(1) << *device_mac << ": New connected device.";
  718. }
  719. return connected_devices;
  720. }
  721. void BluetoothAdapterMac::CreateGattConnection(
  722. BluetoothLowEnergyDeviceMac* device_mac) {
  723. DVLOG(1) << *device_mac << ": Create gatt connection.";
  724. [low_energy_central_manager_ connectPeripheral:device_mac->peripheral_
  725. options:nil];
  726. }
  727. void BluetoothAdapterMac::DisconnectGatt(
  728. BluetoothLowEnergyDeviceMac* device_mac) {
  729. DVLOG(1) << *device_mac << ": Disconnect gatt.";
  730. [low_energy_central_manager_
  731. cancelPeripheralConnection:device_mac->peripheral_];
  732. }
  733. void BluetoothAdapterMac::DidConnectPeripheral(CBPeripheral* peripheral) {
  734. BluetoothLowEnergyDeviceMac* device_mac =
  735. GetBluetoothLowEnergyDeviceMac(peripheral);
  736. if (!device_mac) {
  737. [low_energy_central_manager_ cancelPeripheralConnection:peripheral];
  738. return;
  739. }
  740. device_mac->DidConnectPeripheral();
  741. }
  742. void BluetoothAdapterMac::DidFailToConnectPeripheral(CBPeripheral* peripheral,
  743. NSError* error) {
  744. BluetoothLowEnergyDeviceMac* device_mac =
  745. GetBluetoothLowEnergyDeviceMac(peripheral);
  746. if (!device_mac) {
  747. [low_energy_central_manager_ cancelPeripheralConnection:peripheral];
  748. return;
  749. }
  750. RecordDidFailToConnectPeripheralResult(error);
  751. BluetoothDevice::ConnectErrorCode error_code =
  752. BluetoothDevice::ConnectErrorCode::ERROR_UNKNOWN;
  753. if (error) {
  754. error_code = BluetoothDeviceMac::GetConnectErrorCodeFromNSError(error);
  755. }
  756. DVLOG(1) << *device_mac << ": Failed to connect to peripheral with error "
  757. << BluetoothAdapterMac::String(error)
  758. << ", error code: " << error_code;
  759. device_mac->DidConnectGatt(error_code);
  760. }
  761. void BluetoothAdapterMac::DidDisconnectPeripheral(CBPeripheral* peripheral,
  762. NSError* error) {
  763. BluetoothLowEnergyDeviceMac* device_mac =
  764. GetBluetoothLowEnergyDeviceMac(peripheral);
  765. if (!device_mac) {
  766. [low_energy_central_manager_ cancelPeripheralConnection:peripheral];
  767. return;
  768. }
  769. device_mac->DidDisconnectPeripheral(error);
  770. }
  771. bool BluetoothAdapterMac::IsBluetoothLowEnergyDeviceSystemPaired(
  772. base::StringPiece device_identifier) const {
  773. auto it = std::find_if(
  774. low_energy_devices_info_.begin(), low_energy_devices_info_.end(),
  775. [&](const auto& info) { return info.first == device_identifier; });
  776. if (it == low_energy_devices_info_.end())
  777. return false;
  778. return device_paired_status_callback_.Run(it->second);
  779. }
  780. BluetoothLowEnergyDeviceMac*
  781. BluetoothAdapterMac::GetBluetoothLowEnergyDeviceMac(CBPeripheral* peripheral) {
  782. std::string device_address =
  783. BluetoothLowEnergyDeviceMac::GetPeripheralHashAddress(peripheral);
  784. auto iter = devices_.find(device_address);
  785. if (iter == devices_.end()) {
  786. return nullptr;
  787. }
  788. // device_mac can be BluetoothClassicDeviceMac* or
  789. // BluetoothLowEnergyDeviceMac* To return valid BluetoothLowEnergyDeviceMac*
  790. // we need to first check with IsLowEnergyDevice()
  791. BluetoothDeviceMac* device_mac =
  792. static_cast<BluetoothDeviceMac*>(iter->second.get());
  793. return device_mac->IsLowEnergyDevice()
  794. ? static_cast<BluetoothLowEnergyDeviceMac*>(device_mac)
  795. : nullptr;
  796. }
  797. bool BluetoothAdapterMac::DoesCollideWithKnownDevice(
  798. CBPeripheral* peripheral,
  799. BluetoothLowEnergyDeviceMac* device_mac) {
  800. // Check that there are no collisions.
  801. std::string stored_device_id = device_mac->GetIdentifier();
  802. std::string updated_device_id =
  803. BluetoothLowEnergyDeviceMac::GetPeripheralIdentifier(peripheral);
  804. if (stored_device_id != updated_device_id) {
  805. DVLOG(1) << "LowEnergyDeviceUpdated stored_device_id != updated_device_id: "
  806. << std::endl
  807. << " " << stored_device_id << std::endl
  808. << " " << updated_device_id;
  809. // Collision, two identifiers map to the same hash address. With a 48 bit
  810. // hash the probability of this occuring with 10,000 devices
  811. // simultaneously present is 1e-6 (see
  812. // https://en.wikipedia.org/wiki/Birthday_problem#Probability_table). We
  813. // ignore the second device by returning.
  814. return true;
  815. }
  816. return false;
  817. }
  818. } // namespace device