123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933 |
- // Copyright 2013 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "device/bluetooth/bluetooth_adapter_mac.h"
- #import <CoreBluetooth/CBManager.h>
- #include <CoreFoundation/CFNumber.h>
- #import <IOBluetooth/objc/IOBluetoothDevice.h>
- #import <IOBluetooth/objc/IOBluetoothHostController.h>
- #include <IOKit/IOKitLib.h>
- #include <stddef.h>
- #include <memory>
- #include <string>
- #include <utility>
- #include "base/bind.h"
- #include "base/compiler_specific.h"
- #include "base/location.h"
- #include "base/logging.h"
- #include "base/mac/foundation_util.h"
- #include "base/mac/mac_util.h"
- #include "base/mac/scoped_ioobject.h"
- #include "base/memory/ptr_util.h"
- #include "base/numerics/safe_conversions.h"
- #include "base/strings/sys_string_conversions.h"
- #include "base/task/single_thread_task_runner.h"
- #include "base/task/task_traits.h"
- #include "base/threading/thread_task_runner_handle.h"
- #include "base/time/time.h"
- #include "components/device_event_log/device_event_log.h"
- #include "device/bluetooth/bluetooth_adapter_mac_metrics.h"
- #include "device/bluetooth/bluetooth_advertisement_mac.h"
- #include "device/bluetooth/bluetooth_classic_device_mac.h"
- #include "device/bluetooth/bluetooth_common.h"
- #include "device/bluetooth/bluetooth_discovery_session.h"
- #include "device/bluetooth/bluetooth_discovery_session_outcome.h"
- #include "device/bluetooth/bluetooth_low_energy_central_manager_delegate.h"
- #include "device/bluetooth/bluetooth_low_energy_device_watcher_mac.h"
- #include "device/bluetooth/bluetooth_low_energy_peripheral_manager_delegate.h"
- #include "device/bluetooth/bluetooth_socket_mac.h"
- #include "device/bluetooth/public/cpp/bluetooth_address.h"
- extern "C" {
- // Undocumented IOBluetooth Preference API [1]. Used by `blueutil` [2] and
- // `Karabiner` [3] to programmatically control the Bluetooth state. Calling the
- // method with `1` powers the adapter on, calling it with `0` powers it off.
- // Using this API has the same effect as turning Bluetooth on or off using the
- // macOS System Preferences [4], and will effect all adapters.
- //
- // [1] https://goo.gl/Gbjm1x
- // [2] http://www.frederikseiffert.de/blueutil/
- // [3] https://pqrs.org/osx/karabiner/
- // [4] https://support.apple.com/kb/PH25091
- void IOBluetoothPreferenceSetControllerPowerState(int state);
- }
- namespace {
- // The frequency with which to poll the adapter for updates.
- const int kPollIntervalMs = 500;
- bool IsDeviceSystemPaired(const std::string& device_address) {
- IOBluetoothDevice* device = [IOBluetoothDevice
- deviceWithAddressString:base::SysUTF8ToNSString(device_address)];
- return device && [device isPaired];
- }
- } // namespace
- namespace device {
- // static
- scoped_refptr<BluetoothAdapter> BluetoothAdapter::CreateAdapter() {
- return BluetoothAdapterMac::CreateAdapter();
- }
- // static
- scoped_refptr<BluetoothAdapterMac> BluetoothAdapterMac::CreateAdapter() {
- return base::WrapRefCounted(new BluetoothAdapterMac());
- }
- // static
- scoped_refptr<BluetoothAdapterMac> BluetoothAdapterMac::CreateAdapterForTest(
- std::string name,
- std::string address,
- scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
- auto adapter = base::WrapRefCounted(new BluetoothAdapterMac());
- adapter->InitForTest(ui_task_runner);
- adapter->name_ = name;
- adapter->should_update_name_ = false;
- adapter->address_ = address;
- return adapter;
- }
- // static
- BluetoothUUID BluetoothAdapterMac::BluetoothUUIDWithCBUUID(CBUUID* uuid) {
- std::string uuid_c_string = base::SysNSStringToUTF8([uuid UUIDString]);
- return device::BluetoothUUID(uuid_c_string);
- }
- // static
- std::string BluetoothAdapterMac::String(NSError* error) {
- if (!error) {
- return "no error";
- }
- return std::string("error domain: ") + base::SysNSStringToUTF8(error.domain) +
- ", code: " + std::to_string(error.code) + ", description: " +
- base::SysNSStringToUTF8(error.localizedDescription);
- }
- BluetoothAdapterMac::BluetoothAdapterMac()
- : controller_state_function_(
- base::BindRepeating(&BluetoothAdapterMac::GetHostControllerState,
- base::Unretained(this))),
- power_state_function_(
- base::BindRepeating(IOBluetoothPreferenceSetControllerPowerState)),
- classic_discovery_manager_(
- BluetoothDiscoveryManagerMac::CreateClassic(this)),
- low_energy_discovery_manager_(
- BluetoothLowEnergyDiscoveryManagerMac::Create(this)),
- low_energy_advertisement_manager_(
- std::make_unique<BluetoothLowEnergyAdvertisementManagerMac>()),
- low_energy_central_manager_delegate_(
- [[BluetoothLowEnergyCentralManagerDelegate alloc]
- initWithDiscoveryManager:low_energy_discovery_manager_.get()
- andAdapter:this]),
- low_energy_peripheral_manager_delegate_(
- [[BluetoothLowEnergyPeripheralManagerDelegate alloc]
- initWithAdvertisementManager:
- low_energy_advertisement_manager_.get()
- andAdapter:this]),
- device_paired_status_callback_(
- base::BindRepeating(&IsDeviceSystemPaired)) {
- DCHECK(classic_discovery_manager_);
- DCHECK(low_energy_discovery_manager_);
- }
- BluetoothAdapterMac::~BluetoothAdapterMac() {
- // When devices will be destroyed, they will need this current instance to
- // disconnect the gatt connection. To make sure they don't use the mac
- // adapter, they should be explicitly destroyed here.
- devices_.clear();
- // Explicitly clear out delegates, which might outlive the Adapter.
- [low_energy_peripheral_manager_ setDelegate:nil];
- [low_energy_central_manager_ setDelegate:nil];
- // Set low_energy_central_manager_ to nil so no devices will try to use it
- // while being destroyed after this method. |devices_| is owned by
- // BluetoothAdapter.
- low_energy_central_manager_.reset();
- }
- std::string BluetoothAdapterMac::GetAddress() const {
- const_cast<BluetoothAdapterMac*>(this)->LazyInitialize();
- return address_;
- }
- std::string BluetoothAdapterMac::GetName() const {
- if (!should_update_name_) {
- return name_;
- }
- IOBluetoothHostController* controller =
- [IOBluetoothHostController defaultController];
- name_ = controller != nil ? base::SysNSStringToUTF8([controller nameAsString])
- : std::string();
- should_update_name_ = name_.empty();
- return name_;
- }
- void BluetoothAdapterMac::SetName(const std::string& name,
- base::OnceClosure callback,
- ErrorCallback error_callback) {
- NOTIMPLEMENTED();
- }
- bool BluetoothAdapterMac::IsInitialized() const {
- // Initialize() does nothing and the lazy initialization state is hidden.
- return true;
- }
- bool BluetoothAdapterMac::IsPresent() const {
- // Avoid calling LazyInitialize() so that a Bluetooth permission prompt
- // doesn't appear when simply trying to detect whether the system supports
- // Bluetooth.
- if (is_present_for_testing_.has_value())
- return is_present_for_testing_.value();
- base::mac::ScopedIOObject<io_iterator_t> iterator;
- IOReturn result = IOServiceGetMatchingServices(
- kIOMasterPortDefault, IOServiceMatching("IOBluetoothHCIController"),
- iterator.InitializeInto());
- if (result != kIOReturnSuccess) {
- BLUETOOTH_LOG(ERROR) << "Failed to enumerate Bluetooth controller: "
- << std::hex << result << ".";
- return false;
- }
- base::mac::ScopedIOObject<io_service_t> service(IOIteratorNext(iterator));
- if (!service) {
- return false;
- }
- base::ScopedCFTypeRef<CFBooleanRef> connected(
- base::mac::CFCast<CFBooleanRef>(IORegistryEntryCreateCFProperty(
- service, CFSTR("BluetoothTransportConnected"), kCFAllocatorDefault,
- 0)));
- return CFBooleanGetValue(connected);
- }
- BluetoothAdapter::PermissionStatus BluetoothAdapterMac::GetOsPermissionStatus()
- const {
- if (@available(macOS 10.15.0, *)) {
- switch (CBCentralManager.authorization) {
- case CBManagerAuthorizationNotDetermined:
- return PermissionStatus::kUndetermined;
- case CBManagerAuthorizationRestricted:
- case CBManagerAuthorizationDenied:
- return PermissionStatus::kDenied;
- case CBManagerAuthorizationAllowedAlways:
- return PermissionStatus::kAllowed;
- }
- }
- // There are no Core Bluetooth permissions before macOS 10.15 so assume we
- // always have permission.
- return PermissionStatus::kAllowed;
- }
- bool BluetoothAdapterMac::IsPowered() const {
- const_cast<BluetoothAdapterMac*>(this)->LazyInitialize();
- return classic_powered_ || IsLowEnergyPowered();
- }
- // TODO(krstnmnlsn): If this information is retrievable form IOBluetooth we
- // should return the discoverable status.
- bool BluetoothAdapterMac::IsDiscoverable() const {
- return false;
- }
- void BluetoothAdapterMac::SetDiscoverable(bool discoverable,
- base::OnceClosure callback,
- ErrorCallback error_callback) {
- NOTIMPLEMENTED();
- }
- bool BluetoothAdapterMac::IsDiscovering() const {
- return classic_discovery_manager_->IsDiscovering() ||
- low_energy_discovery_manager_->IsDiscovering();
- }
- std::unordered_map<BluetoothDevice*, BluetoothDevice::UUIDSet>
- BluetoothAdapterMac::RetrieveGattConnectedDevicesWithDiscoveryFilter(
- const BluetoothDiscoveryFilter& discovery_filter) {
- LazyInitialize();
- std::unordered_map<BluetoothDevice*, BluetoothDevice::UUIDSet>
- connected_devices;
- std::set<device::BluetoothUUID> uuids;
- discovery_filter.GetUUIDs(uuids);
- if (uuids.empty()) {
- for (BluetoothDevice* device :
- RetrieveGattConnectedDevicesWithService(nullptr)) {
- connected_devices[device] = BluetoothDevice::UUIDSet();
- }
- return connected_devices;
- }
- for (const BluetoothUUID& uuid : uuids) {
- for (BluetoothDevice* device :
- RetrieveGattConnectedDevicesWithService(&uuid)) {
- connected_devices[device].insert(uuid);
- }
- }
- return connected_devices;
- }
- BluetoothAdapter::UUIDList BluetoothAdapterMac::GetUUIDs() const {
- NOTIMPLEMENTED();
- return UUIDList();
- }
- void BluetoothAdapterMac::CreateRfcommService(
- const BluetoothUUID& uuid,
- const ServiceOptions& options,
- CreateServiceCallback callback,
- CreateServiceErrorCallback error_callback) {
- LazyInitialize();
- scoped_refptr<BluetoothSocketMac> socket = BluetoothSocketMac::CreateSocket();
- socket->ListenUsingRfcomm(this, uuid, options,
- base::BindOnce(std::move(callback), socket),
- std::move(error_callback));
- }
- void BluetoothAdapterMac::CreateL2capService(
- const BluetoothUUID& uuid,
- const ServiceOptions& options,
- CreateServiceCallback callback,
- CreateServiceErrorCallback error_callback) {
- LazyInitialize();
- scoped_refptr<BluetoothSocketMac> socket = BluetoothSocketMac::CreateSocket();
- socket->ListenUsingL2cap(this, uuid, options,
- base::BindOnce(std::move(callback), socket),
- std::move(error_callback));
- }
- void BluetoothAdapterMac::RegisterAdvertisement(
- std::unique_ptr<BluetoothAdvertisement::Data> advertisement_data,
- CreateAdvertisementCallback callback,
- AdvertisementErrorCallback error_callback) {
- LazyInitialize();
- low_energy_advertisement_manager_->RegisterAdvertisement(
- std::move(advertisement_data), std::move(callback),
- std::move(error_callback));
- }
- BluetoothLocalGattService* BluetoothAdapterMac::GetGattService(
- const std::string& identifier) const {
- return nullptr;
- }
- BluetoothAdapter::DeviceList BluetoothAdapterMac::GetDevices() {
- LazyInitialize();
- return BluetoothAdapter::GetDevices();
- }
- BluetoothAdapter::ConstDeviceList BluetoothAdapterMac::GetDevices() const {
- const_cast<BluetoothAdapterMac*>(this)->LazyInitialize();
- return BluetoothAdapter::GetDevices();
- }
- void BluetoothAdapterMac::ClassicDeviceFound(IOBluetoothDevice* device) {
- ClassicDeviceAdded(device);
- }
- void BluetoothAdapterMac::ClassicDiscoveryStopped(bool unexpected) {
- if (unexpected) {
- DVLOG(1) << "Discovery stopped unexpectedly";
- MarkDiscoverySessionsAsInactive();
- }
- for (auto& observer : observers_)
- observer.AdapterDiscoveringChanged(this, false);
- }
- void BluetoothAdapterMac::DeviceConnected(IOBluetoothDevice* device) {
- // TODO(isherman): Investigate whether this method can be replaced with a call
- // to +registerForConnectNotifications:selector:.
- DVLOG(1) << "Adapter registered a new connection from device with address: "
- << BluetoothClassicDeviceMac::GetDeviceAddress(device);
- ClassicDeviceAdded(device);
- }
- base::WeakPtr<BluetoothAdapter> BluetoothAdapterMac::GetWeakPtr() {
- return weak_ptr_factory_.GetWeakPtr();
- }
- bool BluetoothAdapterMac::SetPoweredImpl(bool powered) {
- power_state_function_.Run(base::strict_cast<int>(powered));
- return true;
- }
- void BluetoothAdapterMac::RemovePairingDelegateInternal(
- BluetoothDevice::PairingDelegate* pairing_delegate) {}
- void BluetoothAdapterMac::LazyInitialize() {
- if (lazy_initialized_)
- return;
- low_energy_central_manager_.reset([[CBCentralManager alloc]
- initWithDelegate:low_energy_central_manager_delegate_
- queue:dispatch_get_main_queue()]);
- low_energy_discovery_manager_->SetCentralManager(low_energy_central_manager_);
- low_energy_peripheral_manager_.reset([[CBPeripheralManager alloc]
- initWithDelegate:low_energy_peripheral_manager_delegate_
- queue:dispatch_get_main_queue()]);
- lazy_initialized_ = true;
- low_energy_advertisement_manager_->Init(ui_task_runner_,
- low_energy_peripheral_manager_);
- PollAdapter();
- // To obtain list of low energy devices known to the system, we need to parse
- // and watch system property list file for paired device addresses.
- bluetooth_low_energy_device_watcher_ =
- BluetoothLowEnergyDeviceWatcherMac::CreateAndStartWatching(
- ui_task_runner_,
- base::BindRepeating(&BluetoothAdapterMac::UpdateKnownLowEnergyDevices,
- weak_ptr_factory_.GetWeakPtr()));
- bluetooth_low_energy_device_watcher_->ReadBluetoothPropertyListFile();
- }
- BluetoothAdapterMac::HostControllerState
- BluetoothAdapterMac::GetHostControllerState() {
- HostControllerState state;
- IOBluetoothHostController* controller =
- [IOBluetoothHostController defaultController];
- if (controller != nil) {
- state.classic_powered =
- ([controller powerState] == kBluetoothHCIPowerStateON);
- state.address = CanonicalizeBluetoothAddress(
- base::SysNSStringToUTF8([controller addressAsString]));
- state.is_present = !state.address.empty();
- }
- return state;
- }
- void BluetoothAdapterMac::UpdateKnownLowEnergyDevices(
- std::map<std::string, std::string> updated_low_energy_devices_info) {
- std::map<std::string, std::string> changed_devices;
- // Notify DeviceChanged() to devices that have been newly paired as well as to
- // devices that have been removed from the pairing list.
- std::set_symmetric_difference(
- updated_low_energy_devices_info.begin(),
- updated_low_energy_devices_info.end(), low_energy_devices_info_.begin(),
- low_energy_devices_info_.end(),
- std::inserter(changed_devices, changed_devices.end()));
- low_energy_devices_info_ = std::move(updated_low_energy_devices_info);
- for (const auto& info : changed_devices) {
- auto it = devices_.find(
- BluetoothLowEnergyDeviceMac::GetPeripheralHashAddress(info.first));
- if (it == devices_.end())
- continue;
- NotifyDeviceChanged(it->second.get());
- }
- }
- void BluetoothAdapterMac::SetPresentForTesting(bool present) {
- is_present_for_testing_ = present;
- }
- void BluetoothAdapterMac::SetCentralManagerForTesting(
- CBCentralManager* central_manager) {
- central_manager.delegate = low_energy_central_manager_delegate_;
- low_energy_central_manager_.reset(central_manager,
- base::scoped_policy::RETAIN);
- low_energy_discovery_manager_->SetCentralManager(low_energy_central_manager_);
- }
- CBCentralManager* BluetoothAdapterMac::GetCentralManager() {
- return low_energy_central_manager_;
- }
- CBPeripheralManager* BluetoothAdapterMac::GetPeripheralManager() {
- return low_energy_peripheral_manager_;
- }
- void BluetoothAdapterMac::SetHostControllerStateFunctionForTesting(
- HostControllerStateFunction controller_state_function) {
- controller_state_function_ = std::move(controller_state_function);
- }
- void BluetoothAdapterMac::SetPowerStateFunctionForTesting(
- SetControllerPowerStateFunction power_state_function) {
- power_state_function_ = std::move(power_state_function);
- }
- void BluetoothAdapterMac::SetLowEnergyDeviceWatcherForTesting(
- scoped_refptr<BluetoothLowEnergyDeviceWatcherMac>
- bluetooth_low_energy_device_watcher) {
- bluetooth_low_energy_device_watcher_ =
- std::move(bluetooth_low_energy_device_watcher);
- bluetooth_low_energy_device_watcher_->Init();
- }
- void BluetoothAdapterMac::SetGetDevicePairedStatusCallbackForTesting(
- GetDevicePairedStatusCallback device_paired_status_callback) {
- device_paired_status_callback_ = std::move(device_paired_status_callback);
- }
- void BluetoothAdapterMac::UpdateFilter(
- std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter,
- DiscoverySessionResultCallback callback) {
- // In Mac the start scan handles all updates automatically
- StartScanWithFilter(std::move(discovery_filter), std::move(callback));
- }
- void BluetoothAdapterMac::StartScanWithFilter(
- std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter,
- DiscoverySessionResultCallback callback) {
- // Default to dual discovery if |discovery_filter| is NULL. IOBluetooth seems
- // to allow starting low energy and classic discovery at once.
- BluetoothTransport transport = BLUETOOTH_TRANSPORT_DUAL;
- if (discovery_filter) {
- transport = discovery_filter->GetTransport();
- }
- if ((transport & BLUETOOTH_TRANSPORT_CLASSIC) &&
- !classic_discovery_manager_->IsDiscovering()) {
- // We do not update the filter if already discovering. This will all be
- // deprecated soon though.
- if (!classic_discovery_manager_->StartDiscovery()) {
- DVLOG(1) << "Failed to add a classic discovery session";
- ui_task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(std::move(callback), /*is_error=*/true,
- UMABluetoothDiscoverySessionOutcome::UNKNOWN));
- return;
- }
- }
- if (transport & BLUETOOTH_TRANSPORT_LE) {
- // Begin a low energy discovery session or update it if one is already
- // running.
- low_energy_discovery_manager_->StartDiscovery(BluetoothDevice::UUIDList());
- }
- for (auto& observer : observers_)
- observer.AdapterDiscoveringChanged(this, true);
- DCHECK(callback);
- ui_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(std::move(callback), /*is_error=*/false,
- UMABluetoothDiscoverySessionOutcome::SUCCESS));
- }
- void BluetoothAdapterMac::StopScan(DiscoverySessionResultCallback callback) {
- low_energy_discovery_manager_->StopDiscovery();
- for (const auto& device_id_object_pair : devices_) {
- device_id_object_pair.second->ClearAdvertisementData();
- }
- if (classic_discovery_manager_->IsDiscovering() &&
- !classic_discovery_manager_->StopDiscovery()) {
- DVLOG(1) << "Failed to stop classic discovery";
- // TODO: Provide a more precise error here.
- std::move(callback).Run(/*is_error=*/true,
- UMABluetoothDiscoverySessionOutcome::UNKNOWN);
- return;
- }
- DVLOG(1) << "Discovery stopped";
- std::move(callback).Run(/*is_error=*/false,
- UMABluetoothDiscoverySessionOutcome::SUCCESS);
- }
- bool BluetoothAdapterMac::StartDiscovery(
- BluetoothDiscoveryFilter* discovery_filter) {
- // Default to dual discovery if |discovery_filter| is NULL. IOBluetooth seems
- // allow starting low energy and classic discovery at once.
- BluetoothTransport transport = BLUETOOTH_TRANSPORT_DUAL;
- if (discovery_filter)
- transport = discovery_filter->GetTransport();
- if ((transport & BLUETOOTH_TRANSPORT_CLASSIC) &&
- !classic_discovery_manager_->IsDiscovering()) {
- // TODO(krstnmnlsn): If a classic discovery session is already running then
- // we should update its filter. crbug.com/498056
- if (!classic_discovery_manager_->StartDiscovery()) {
- DVLOG(1) << "Failed to add a classic discovery session";
- return false;
- }
- }
- if (transport & BLUETOOTH_TRANSPORT_LE) {
- // Begin a low energy discovery session or update it if one is already
- // running.
- low_energy_discovery_manager_->StartDiscovery(BluetoothDevice::UUIDList());
- }
- return true;
- }
- void BluetoothAdapterMac::Initialize(base::OnceClosure callback) {
- // Real initialization is deferred to LazyInitialize().
- ui_task_runner_ = base::ThreadTaskRunnerHandle::Get();
- std::move(callback).Run();
- }
- void BluetoothAdapterMac::InitForTest(
- scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
- ui_task_runner_ = ui_task_runner;
- lazy_initialized_ = true;
- is_present_for_testing_ = false;
- }
- void BluetoothAdapterMac::PollAdapter() {
- const bool was_present = IsPresent();
- HostControllerState state = controller_state_function_.Run();
- if (address_ != state.address)
- should_update_name_ = true;
- address_ = std::move(state.address);
- if (was_present != state.is_present) {
- for (auto& observer : observers_)
- observer.AdapterPresentChanged(this, state.is_present);
- }
- if (classic_powered_ != state.classic_powered) {
- classic_powered_ = state.classic_powered;
- RunPendingPowerCallbacks();
- NotifyAdapterPoweredChanged(classic_powered_);
- }
- RemoveTimedOutDevices();
- AddPairedDevices();
- ui_task_runner_->PostDelayedTask(
- FROM_HERE,
- base::BindOnce(&BluetoothAdapterMac::PollAdapter,
- weak_ptr_factory_.GetWeakPtr()),
- base::Milliseconds(kPollIntervalMs));
- }
- void BluetoothAdapterMac::ClassicDeviceAdded(IOBluetoothDevice* device) {
- std::string device_address =
- BluetoothClassicDeviceMac::GetDeviceAddress(device);
- BluetoothDevice* device_classic = GetDevice(device_address);
- // Only notify observers once per device.
- if (device_classic != nullptr) {
- DVLOG(3) << "Updating classic device: " << device_classic->GetAddress();
- device_classic->UpdateTimestamp();
- return;
- }
- device_classic = new BluetoothClassicDeviceMac(this, device);
- devices_[device_address] = base::WrapUnique(device_classic);
- DVLOG(1) << "Adding new classic device: " << device_classic->GetAddress();
- for (auto& observer : observers_)
- observer.DeviceAdded(this, device_classic);
- }
- bool BluetoothAdapterMac::IsLowEnergyPowered() const {
- return [low_energy_central_manager_ state] == CBManagerStatePoweredOn;
- }
- void BluetoothAdapterMac::LowEnergyDeviceUpdated(
- CBPeripheral* peripheral,
- NSDictionary* advertisement_data,
- int rssi) {
- BluetoothLowEnergyDeviceMac* device_mac =
- GetBluetoothLowEnergyDeviceMac(peripheral);
- // If has no entry in the map, create new device and insert into |devices_|,
- // otherwise update the existing device.
- const bool is_new_device = device_mac == nullptr;
- if (is_new_device) {
- // A new device has been found.
- device_mac = new BluetoothLowEnergyDeviceMac(this, peripheral);
- DVLOG(1) << *device_mac << ": New Device.";
- } else if (DoesCollideWithKnownDevice(peripheral, device_mac)) {
- return;
- }
- DCHECK(device_mac);
- DVLOG(3) << *device_mac << ": Device updated with "
- << base::SysNSStringToUTF8([advertisement_data description]);
- // Get Advertised UUIDs
- // Core Specification Supplement (CSS) v7, Part 1.1
- // https://developer.apple.com/documentation/corebluetooth/cbadvertisementdataserviceuuidskey
- BluetoothDevice::UUIDList advertised_uuids;
- NSArray* service_uuids =
- advertisement_data[CBAdvertisementDataServiceUUIDsKey];
- for (CBUUID* uuid in service_uuids) {
- advertised_uuids.push_back(
- BluetoothAdapterMac::BluetoothUUIDWithCBUUID(uuid));
- }
- NSArray* overflow_service_uuids =
- advertisement_data[CBAdvertisementDataOverflowServiceUUIDsKey];
- for (CBUUID* uuid in overflow_service_uuids) {
- advertised_uuids.push_back(
- BluetoothAdapterMac::BluetoothUUIDWithCBUUID(uuid));
- }
- // Get Service Data.
- // Core Specification Supplement (CSS) v7, Part 1.11
- // https://developer.apple.com/documentation/corebluetooth/cbadvertisementdataservicedatakey
- BluetoothDevice::ServiceDataMap service_data_map;
- NSDictionary* service_data =
- advertisement_data[CBAdvertisementDataServiceDataKey];
- for (CBUUID* uuid in service_data) {
- NSData* data = service_data[uuid];
- const uint8_t* bytes = static_cast<const uint8_t*>([data bytes]);
- size_t length = [data length];
- service_data_map.emplace(BluetoothAdapterMac::BluetoothUUIDWithCBUUID(uuid),
- std::vector<uint8_t>(bytes, bytes + length));
- }
- // Get Manufacturer Data.
- // "Size: 2 or more octets
- // The first 2 octets contain the Company Identifier Code followed
- // by additional manufacturer specific data"
- // Core Specification Supplement (CSS) v7, Part 1.4
- // https://developer.apple.com/documentation/corebluetooth/cbadvertisementdatamanufacturerdatakey
- //
- BluetoothDevice::ManufacturerDataMap manufacturer_data_map;
- NSData* manufacturer_data =
- advertisement_data[CBAdvertisementDataManufacturerDataKey];
- const uint8_t* bytes = static_cast<const uint8_t*>([manufacturer_data bytes]);
- size_t length = [manufacturer_data length];
- if (length > 1) {
- const uint16_t manufacturer_id = bytes[0] | bytes[1] << 8;
- manufacturer_data_map.emplace(
- manufacturer_id, std::vector<uint8_t>(bytes + 2, bytes + length));
- }
- // Get Tx Power.
- // "Size: 1 octet
- // 0xXX: -127 to +127 dBm"
- // Core Specification Supplement (CSS) v7, Part 1.5
- // https://developer.apple.com/documentation/corebluetooth/cbadvertisementdatatxpowerlevelkey
- NSNumber* tx_power = advertisement_data[CBAdvertisementDataTxPowerLevelKey];
- int8_t clamped_tx_power = BluetoothDevice::ClampPower([tx_power intValue]);
- // Get the Advertising name
- NSString* local_name = advertisement_data[CBAdvertisementDataLocalNameKey];
- for (auto& observer : observers_) {
- absl::optional<std::string> device_name_opt = device_mac->GetName();
- absl::optional<std::string> local_name_opt =
- base::SysNSStringToUTF8(local_name);
- observer.DeviceAdvertisementReceived(
- device_mac->GetAddress(), device_name_opt,
- local_name == nil ? absl::nullopt : local_name_opt, rssi,
- tx_power == nil ? absl::nullopt : absl::make_optional(clamped_tx_power),
- absl::nullopt, /* TODO(crbug.com/588083) Implement appearance */
- advertised_uuids, service_data_map, manufacturer_data_map);
- }
- device_mac->UpdateAdvertisementData(
- BluetoothDevice::ClampPower(rssi), absl::nullopt /* flags */,
- std::move(advertised_uuids),
- tx_power == nil ? absl::nullopt : absl::make_optional(clamped_tx_power),
- std::move(service_data_map), std::move(manufacturer_data_map));
- if (is_new_device) {
- std::string device_address =
- BluetoothLowEnergyDeviceMac::GetPeripheralHashAddress(peripheral);
- devices_[device_address] = base::WrapUnique(device_mac);
- for (auto& observer : observers_)
- observer.DeviceAdded(this, device_mac);
- } else {
- for (auto& observer : observers_)
- observer.DeviceChanged(this, device_mac);
- }
- }
- void BluetoothAdapterMac::LowEnergyCentralManagerUpdatedState() {
- DVLOG(1) << "Central manager state updated: "
- << [low_energy_central_manager_ state];
- // A state with a value lower than CBManagerStatePoweredOn implies that
- // scanning has stopped and that any connected peripherals have been
- // disconnected. Call DidDisconnectPeripheral manually to update the devices'
- // states since macOS doesn't call it.
- // See
- // https://developer.apple.com/reference/corebluetooth/cbcentralmanagerdelegate/1518888-centralmanagerdidupdatestate?language=objc
- if ([low_energy_central_manager_ state] < CBManagerStatePoweredOn) {
- DVLOG(1)
- << "Central no longer powered on. Notifying of device disconnection.";
- for (BluetoothDevice* device : GetDevices()) {
- // GetDevices() returns instances of BluetoothClassicDeviceMac and
- // BluetoothLowEnergyDeviceMac. The DidDisconnectPeripheral() method is
- // only available on BluetoothLowEnergyDeviceMac.
- if (!static_cast<BluetoothDeviceMac*>(device)->IsLowEnergyDevice())
- continue;
- BluetoothLowEnergyDeviceMac* device_mac =
- static_cast<BluetoothLowEnergyDeviceMac*>(device);
- if (device_mac->IsGattConnected()) {
- device_mac->DidDisconnectPeripheral(nullptr);
- }
- }
- }
- }
- void BluetoothAdapterMac::AddPairedDevices() {
- // Add any new paired devices.
- for (IOBluetoothDevice* device in [IOBluetoothDevice pairedDevices]) {
- // pairedDevices sometimes includes unknown devices that are not paired.
- // Radar issue with id 2282763004 has been filed about it.
- if ([device isPaired]) {
- ClassicDeviceAdded(device);
- }
- }
- }
- std::vector<BluetoothDevice*>
- BluetoothAdapterMac::RetrieveGattConnectedDevicesWithService(
- const BluetoothUUID* uuid) {
- NSArray* cbUUIDs = nil;
- if (!uuid) {
- DVLOG(1) << "Retrieving all connected devices.";
- // It is not possible to ask for all connected peripherals with
- // -[CBCentralManager retrieveConnectedPeripheralsWithServices:] by passing
- // nil. To try to get most of the peripherals, the search is done with
- // Generic Access service.
- CBUUID* genericAccessServiceUUID = [CBUUID UUIDWithString:@"1800"];
- cbUUIDs = @[ genericAccessServiceUUID ];
- } else {
- DVLOG(1) << "Retrieving connected devices with UUID: "
- << uuid->canonical_value();
- NSString* uuidString =
- base::SysUTF8ToNSString(uuid->canonical_value().c_str());
- cbUUIDs = @[ [CBUUID UUIDWithString:uuidString] ];
- }
- NSArray* peripherals = [low_energy_central_manager_
- retrieveConnectedPeripheralsWithServices:cbUUIDs];
- std::vector<BluetoothDevice*> connected_devices;
- for (CBPeripheral* peripheral in peripherals) {
- BluetoothLowEnergyDeviceMac* device_mac =
- GetBluetoothLowEnergyDeviceMac(peripheral);
- const bool is_new_device = device_mac == nullptr;
- if (!is_new_device && DoesCollideWithKnownDevice(peripheral, device_mac)) {
- continue;
- }
- if (is_new_device) {
- device_mac = new BluetoothLowEnergyDeviceMac(this, peripheral);
- std::string device_address =
- BluetoothLowEnergyDeviceMac::GetPeripheralHashAddress(peripheral);
- devices_[device_address] = base::WrapUnique(device_mac);
- for (auto& observer : observers_) {
- observer.DeviceAdded(this, device_mac);
- }
- }
- connected_devices.push_back(device_mac);
- DVLOG(1) << *device_mac << ": New connected device.";
- }
- return connected_devices;
- }
- void BluetoothAdapterMac::CreateGattConnection(
- BluetoothLowEnergyDeviceMac* device_mac) {
- DVLOG(1) << *device_mac << ": Create gatt connection.";
- [low_energy_central_manager_ connectPeripheral:device_mac->peripheral_
- options:nil];
- }
- void BluetoothAdapterMac::DisconnectGatt(
- BluetoothLowEnergyDeviceMac* device_mac) {
- DVLOG(1) << *device_mac << ": Disconnect gatt.";
- [low_energy_central_manager_
- cancelPeripheralConnection:device_mac->peripheral_];
- }
- void BluetoothAdapterMac::DidConnectPeripheral(CBPeripheral* peripheral) {
- BluetoothLowEnergyDeviceMac* device_mac =
- GetBluetoothLowEnergyDeviceMac(peripheral);
- if (!device_mac) {
- [low_energy_central_manager_ cancelPeripheralConnection:peripheral];
- return;
- }
- device_mac->DidConnectPeripheral();
- }
- void BluetoothAdapterMac::DidFailToConnectPeripheral(CBPeripheral* peripheral,
- NSError* error) {
- BluetoothLowEnergyDeviceMac* device_mac =
- GetBluetoothLowEnergyDeviceMac(peripheral);
- if (!device_mac) {
- [low_energy_central_manager_ cancelPeripheralConnection:peripheral];
- return;
- }
- RecordDidFailToConnectPeripheralResult(error);
- BluetoothDevice::ConnectErrorCode error_code =
- BluetoothDevice::ConnectErrorCode::ERROR_UNKNOWN;
- if (error) {
- error_code = BluetoothDeviceMac::GetConnectErrorCodeFromNSError(error);
- }
- DVLOG(1) << *device_mac << ": Failed to connect to peripheral with error "
- << BluetoothAdapterMac::String(error)
- << ", error code: " << error_code;
- device_mac->DidConnectGatt(error_code);
- }
- void BluetoothAdapterMac::DidDisconnectPeripheral(CBPeripheral* peripheral,
- NSError* error) {
- BluetoothLowEnergyDeviceMac* device_mac =
- GetBluetoothLowEnergyDeviceMac(peripheral);
- if (!device_mac) {
- [low_energy_central_manager_ cancelPeripheralConnection:peripheral];
- return;
- }
- device_mac->DidDisconnectPeripheral(error);
- }
- bool BluetoothAdapterMac::IsBluetoothLowEnergyDeviceSystemPaired(
- base::StringPiece device_identifier) const {
- auto it = std::find_if(
- low_energy_devices_info_.begin(), low_energy_devices_info_.end(),
- [&](const auto& info) { return info.first == device_identifier; });
- if (it == low_energy_devices_info_.end())
- return false;
- return device_paired_status_callback_.Run(it->second);
- }
- BluetoothLowEnergyDeviceMac*
- BluetoothAdapterMac::GetBluetoothLowEnergyDeviceMac(CBPeripheral* peripheral) {
- std::string device_address =
- BluetoothLowEnergyDeviceMac::GetPeripheralHashAddress(peripheral);
- auto iter = devices_.find(device_address);
- if (iter == devices_.end()) {
- return nullptr;
- }
- // device_mac can be BluetoothClassicDeviceMac* or
- // BluetoothLowEnergyDeviceMac* To return valid BluetoothLowEnergyDeviceMac*
- // we need to first check with IsLowEnergyDevice()
- BluetoothDeviceMac* device_mac =
- static_cast<BluetoothDeviceMac*>(iter->second.get());
- return device_mac->IsLowEnergyDevice()
- ? static_cast<BluetoothLowEnergyDeviceMac*>(device_mac)
- : nullptr;
- }
- bool BluetoothAdapterMac::DoesCollideWithKnownDevice(
- CBPeripheral* peripheral,
- BluetoothLowEnergyDeviceMac* device_mac) {
- // Check that there are no collisions.
- std::string stored_device_id = device_mac->GetIdentifier();
- std::string updated_device_id =
- BluetoothLowEnergyDeviceMac::GetPeripheralIdentifier(peripheral);
- if (stored_device_id != updated_device_id) {
- DVLOG(1) << "LowEnergyDeviceUpdated stored_device_id != updated_device_id: "
- << std::endl
- << " " << stored_device_id << std::endl
- << " " << updated_device_id;
- // Collision, two identifiers map to the same hash address. With a 48 bit
- // hash the probability of this occuring with 10,000 devices
- // simultaneously present is 1e-6 (see
- // https://en.wikipedia.org/wiki/Birthday_problem#Probability_table). We
- // ignore the second device by returning.
- return true;
- }
- return false;
- }
- } // namespace device
|