// Copyright (c) 2012 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_device.h" #include #include #include #include #include #include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" #include "base/values.h" #include "device/bluetooth/bluetooth_adapter.h" #include "device/bluetooth/bluetooth_gatt_connection.h" #include "device/bluetooth/bluetooth_remote_gatt_characteristic.h" #include "device/bluetooth/bluetooth_remote_gatt_descriptor.h" #include "device/bluetooth/bluetooth_remote_gatt_service.h" #include "device/bluetooth/public/cpp/bluetooth_uuid.h" #include "device/bluetooth/string_util_icu.h" #include "device/bluetooth/strings/grit/bluetooth_strings.h" #include "ui/base/l10n/l10n_util.h" namespace device { BluetoothDevice::DeviceUUIDs::DeviceUUIDs() = default; BluetoothDevice::DeviceUUIDs::~DeviceUUIDs() = default; BluetoothDevice::DeviceUUIDs::DeviceUUIDs(const DeviceUUIDs& other) = default; BluetoothDevice::DeviceUUIDs& BluetoothDevice::DeviceUUIDs::operator=( const DeviceUUIDs& other) = default; void BluetoothDevice::DeviceUUIDs::ReplaceAdvertisedUUIDs( UUIDList new_advertised_uuids) { advertised_uuids_.clear(); for (auto& it : new_advertised_uuids) { advertised_uuids_.insert(std::move(it)); } UpdateDeviceUUIDs(); } void BluetoothDevice::DeviceUUIDs::ClearAdvertisedUUIDs() { advertised_uuids_.clear(); UpdateDeviceUUIDs(); } void BluetoothDevice::DeviceUUIDs::ReplaceServiceUUIDs( const GattServiceMap& gatt_services) { service_uuids_.clear(); for (const auto& gatt_service_pair : gatt_services) service_uuids_.insert(gatt_service_pair.second->GetUUID()); UpdateDeviceUUIDs(); } void BluetoothDevice::DeviceUUIDs::ClearServiceUUIDs() { service_uuids_.clear(); UpdateDeviceUUIDs(); } const BluetoothDevice::UUIDSet& BluetoothDevice::DeviceUUIDs::GetUUIDs() const { return device_uuids_; } void BluetoothDevice::DeviceUUIDs::UpdateDeviceUUIDs() { device_uuids_ = base::STLSetUnion(advertised_uuids_, service_uuids_); } BluetoothDevice::BluetoothDevice(BluetoothAdapter* adapter) : adapter_(adapter), gatt_services_discovery_complete_(false), last_update_time_(base::Time()) {} BluetoothDevice::~BluetoothDevice() { for (BluetoothGattConnection* connection : gatt_connections_) { connection->InvalidateConnectionReference(); } } BluetoothDevice::ConnectionInfo::ConnectionInfo() : rssi(kUnknownPower), transmit_power(kUnknownPower), max_transmit_power(kUnknownPower) {} BluetoothDevice::ConnectionInfo::ConnectionInfo(int rssi, int transmit_power, int max_transmit_power) : rssi(rssi), transmit_power(transmit_power), max_transmit_power(max_transmit_power) {} BluetoothDevice::ConnectionInfo::~ConnectionInfo() = default; base::string16 BluetoothDevice::GetNameForDisplay() const { base::Optional name = GetName(); if (name && HasGraphicCharacter(name.value())) { return base::UTF8ToUTF16(name.value()); } else { return GetAddressWithLocalizedDeviceTypeName(); } } base::string16 BluetoothDevice::GetAddressWithLocalizedDeviceTypeName() const { base::string16 address_utf16 = base::UTF8ToUTF16(GetAddress()); BluetoothDeviceType device_type = GetDeviceType(); switch (device_type) { case BluetoothDeviceType::COMPUTER: return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_COMPUTER, address_utf16); case BluetoothDeviceType::PHONE: return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_PHONE, address_utf16); case BluetoothDeviceType::MODEM: return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_MODEM, address_utf16); case BluetoothDeviceType::AUDIO: return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_AUDIO, address_utf16); case BluetoothDeviceType::CAR_AUDIO: return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_CAR_AUDIO, address_utf16); case BluetoothDeviceType::VIDEO: return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_VIDEO, address_utf16); case BluetoothDeviceType::JOYSTICK: return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_JOYSTICK, address_utf16); case BluetoothDeviceType::GAMEPAD: return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_GAMEPAD, address_utf16); case BluetoothDeviceType::KEYBOARD: return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_KEYBOARD, address_utf16); case BluetoothDeviceType::MOUSE: return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_MOUSE, address_utf16); case BluetoothDeviceType::TABLET: return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_TABLET, address_utf16); case BluetoothDeviceType::KEYBOARD_MOUSE_COMBO: return l10n_util::GetStringFUTF16( IDS_BLUETOOTH_DEVICE_KEYBOARD_MOUSE_COMBO, address_utf16); default: return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_UNKNOWN, address_utf16); } } BluetoothDeviceType BluetoothDevice::GetDeviceType() const { // https://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm uint32_t bluetooth_class = GetBluetoothClass(); switch ((bluetooth_class & 0x1f00) >> 8) { case 0x01: // Computer major device class. return BluetoothDeviceType::COMPUTER; case 0x02: // Phone major device class. switch ((bluetooth_class & 0xfc) >> 2) { case 0x01: case 0x02: case 0x03: // Cellular, cordless and smart phones. return BluetoothDeviceType::PHONE; case 0x04: case 0x05: // Modems: wired or voice gateway and common ISDN access. return BluetoothDeviceType::MODEM; } break; case 0x04: // Audio major device class. switch ((bluetooth_class & 0xfc) >> 2) { case 0x08: // Car audio. return BluetoothDeviceType::CAR_AUDIO; case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f: case 0x010: // Video devices. return BluetoothDeviceType::VIDEO; default: return BluetoothDeviceType::AUDIO; } break; case 0x05: // Peripheral major device class. switch ((bluetooth_class & 0xc0) >> 6) { case 0x00: // "Not a keyboard or pointing device." switch ((bluetooth_class & 0x01e) >> 2) { case 0x01: // Joystick. return BluetoothDeviceType::JOYSTICK; case 0x02: // Gamepad. return BluetoothDeviceType::GAMEPAD; default: return BluetoothDeviceType::PERIPHERAL; } break; case 0x01: // Keyboard. return BluetoothDeviceType::KEYBOARD; case 0x02: // Pointing device. switch ((bluetooth_class & 0x01e) >> 2) { case 0x05: // Digitizer tablet. return BluetoothDeviceType::TABLET; default: // Mouse. return BluetoothDeviceType::MOUSE; } break; case 0x03: // Combo device. return BluetoothDeviceType::KEYBOARD_MOUSE_COMBO; } break; } // Some bluetooth devices, e.g., Microsoft Universal Foldable Keyboard, // do not expose its bluetooth class. Use its appearance as a work-around. // https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.gap.appearance.xml uint16_t appearance = GetAppearance(); // appearance: 10-bit category and 6-bit sub-category switch ((appearance & 0xffc0) >> 6) { case 0x01: // Generic phone return BluetoothDeviceType::PHONE; case 0x02: // Generic computer return BluetoothDeviceType::COMPUTER; case 0x0f: // HID subtype switch (appearance & 0x3f) { case 0x01: // Keyboard. return BluetoothDeviceType::KEYBOARD; case 0x02: // Mouse return BluetoothDeviceType::MOUSE; case 0x03: // Joystick return BluetoothDeviceType::JOYSTICK; case 0x04: // Gamepad return BluetoothDeviceType::GAMEPAD; case 0x05: // Digitizer tablet return BluetoothDeviceType::TABLET; } } return BluetoothDeviceType::UNKNOWN; } bool BluetoothDevice::IsPairable() const { BluetoothDeviceType type = GetDeviceType(); // Get the vendor part of the address: "00:11:22" for "00:11:22:33:44:55" std::string vendor = GetAddress().substr(0, 8); // Verbatim "Bluetooth Mouse", model 96674 if (type == BluetoothDeviceType::MOUSE && vendor == "00:12:A1") return false; // Microsoft "Microsoft Bluetooth Notebook Mouse 5000", model X807028-001 if (type == BluetoothDeviceType::MOUSE && vendor == "7C:ED:8D") return false; // TODO: Move this database into a config file. return true; } BluetoothDevice::UUIDSet BluetoothDevice::GetUUIDs() const { return device_uuids_.GetUUIDs(); } const BluetoothDevice::ServiceDataMap& BluetoothDevice::GetServiceData() const { return service_data_; } BluetoothDevice::UUIDSet BluetoothDevice::GetServiceDataUUIDs() const { UUIDSet service_data_uuids; for (const auto& uuid_service_data_pair : service_data_) { service_data_uuids.insert(uuid_service_data_pair.first); } return service_data_uuids; } const std::vector* BluetoothDevice::GetServiceDataForUUID( const BluetoothUUID& uuid) const { auto it = service_data_.find(uuid); if (it != service_data_.end()) { return &it->second; } return nullptr; } const BluetoothDevice::ManufacturerDataMap& BluetoothDevice::GetManufacturerData() const { return manufacturer_data_; } BluetoothDevice::ManufacturerIDSet BluetoothDevice::GetManufacturerDataIDs() const { ManufacturerIDSet manufacturer_data_ids; for (const auto& manufacturer_data_pair : manufacturer_data_) { manufacturer_data_ids.insert(manufacturer_data_pair.first); } return manufacturer_data_ids; } const std::vector* BluetoothDevice::GetManufacturerDataForID( const ManufacturerId manufacturerID) const { auto it = manufacturer_data_.find(manufacturerID); if (it != manufacturer_data_.end()) { return &it->second; } return nullptr; } base::Optional BluetoothDevice::GetInquiryRSSI() const { return inquiry_rssi_; } base::Optional BluetoothDevice::GetAdvertisingDataFlags() const { return advertising_data_flags_; } base::Optional BluetoothDevice::GetInquiryTxPower() const { return inquiry_tx_power_; } void BluetoothDevice::CreateGattConnection( GattConnectionCallback callback, ConnectErrorCallback error_callback, base::Optional service_uuid) { if (!supports_service_specific_discovery_) service_uuid.reset(); const bool connection_already_pending = !create_gatt_connection_success_callbacks_.empty(); create_gatt_connection_success_callbacks_.push_back(std::move(callback)); create_gatt_connection_error_callbacks_.push_back(std::move(error_callback)); // If a service-specific discovery was originally requested, but this request // is for a different or non-specific discovery, then the previous discovery // needs to be redone. if (target_service_.has_value() && target_service_ != service_uuid) { DCHECK(IsGattConnected() || connection_already_pending); target_service_ = service_uuid; UpgradeToFullDiscovery(); } if (IsGattConnected()) { DCHECK(!connection_already_pending); return DidConnectGatt(); } if (connection_already_pending) { // The correct callback will be run when the existing connection attempt // completes. return; } target_service_ = service_uuid; CreateGattConnectionImpl(std::move(service_uuid)); } void BluetoothDevice::SetGattServicesDiscoveryComplete(bool complete) { gatt_services_discovery_complete_ = complete; } bool BluetoothDevice::IsGattServicesDiscoveryComplete() const { return !target_service_ && gatt_services_discovery_complete_; } std::vector BluetoothDevice::GetGattServices() const { std::vector services; for (const auto& iter : gatt_services_) services.push_back(iter.second.get()); return services; } BluetoothRemoteGattService* BluetoothDevice::GetGattService( const std::string& identifier) const { auto it = gatt_services_.find(identifier); if (it == gatt_services_.end()) return nullptr; return it->second.get(); } // static std::string BluetoothDevice::CanonicalizeAddress(base::StringPiece address) { std::array bytes; if (!ParseAddress(address, bytes)) return std::string(); std::string canonicalized; canonicalized.reserve(17); for (size_t i = 0; i < bytes.size(); ++i) { if (i != 0) canonicalized.push_back(':'); base::StringAppendF(&canonicalized, "%02X", bytes[i]); } return canonicalized; } bool BluetoothDevice::ParseAddress(base::StringPiece input, base::span output) { if (output.size() != 6) return false; // Try parsing addresses that lack separators, like "1A2B3C4D5E6F". if (input.size() == 12) return base::HexStringToSpan(input, output); // Try parsing MAC address with separators like: "00:11:22:33:44:55" or // "00-11-22-33-44-55". Separator can be either '-' or ':', but must use the // same style throughout. if (input.size() == 17) { const char separator = input[2]; if (separator != '-' && separator != ':') return false; return (input[2] == separator) && (input[5] == separator) && (input[8] == separator) && (input[11] == separator) && (input[14] == separator) && base::HexStringToSpan(input.substr(0, 2), output.subspan<0, 1>()) && base::HexStringToSpan(input.substr(3, 2), output.subspan<1, 1>()) && base::HexStringToSpan(input.substr(6, 2), output.subspan<2, 1>()) && base::HexStringToSpan(input.substr(9, 2), output.subspan<3, 1>()) && base::HexStringToSpan(input.substr(12, 2), output.subspan<4, 1>()) && base::HexStringToSpan(input.substr(15, 2), output.subspan<5, 1>()); } return false; } std::string BluetoothDevice::GetIdentifier() const { return GetAddress(); } void BluetoothDevice::UpdateAdvertisementData( int8_t rssi, base::Optional flags, UUIDList advertised_uuids, base::Optional tx_power, ServiceDataMap service_data, ManufacturerDataMap manufacturer_data) { UpdateTimestamp(); inquiry_rssi_ = rssi; advertising_data_flags_ = std::move(flags); device_uuids_.ReplaceAdvertisedUUIDs(std::move(advertised_uuids)); inquiry_tx_power_ = std::move(tx_power); service_data_ = std::move(service_data); manufacturer_data_ = std::move(manufacturer_data); } void BluetoothDevice::ClearAdvertisementData() { inquiry_rssi_.reset(); advertising_data_flags_.reset(); inquiry_tx_power_.reset(); device_uuids_.ClearAdvertisedUUIDs(); service_data_.clear(); manufacturer_data_.clear(); GetAdapter()->NotifyDeviceChanged(this); } std::vector BluetoothDevice::GetPrimaryServices() { std::vector services; DVLOG(2) << "Looking for services."; for (BluetoothRemoteGattService* service : GetGattServices()) { DVLOG(2) << "Service in cache: " << service->GetUUID().canonical_value(); if (service->IsPrimary()) { services.push_back(service); } } return services; } std::vector BluetoothDevice::GetPrimaryServicesByUUID(const BluetoothUUID& service_uuid) { std::vector services; DVLOG(2) << "Looking for service: " << service_uuid.canonical_value(); for (BluetoothRemoteGattService* service : GetGattServices()) { DVLOG(2) << "Service in cache: " << service->GetUUID().canonical_value(); if (service->GetUUID() == service_uuid && service->IsPrimary()) { services.push_back(service); } } return services; } #if defined(OS_CHROMEOS) void BluetoothDevice::SetBatteryPercentage( base::Optional battery_percentage) { if (battery_percentage) DCHECK_LE(battery_percentage.value(), 100); if (battery_percentage_ == battery_percentage) return; battery_percentage_ = battery_percentage; GetAdapter()->NotifyDeviceBatteryChanged(this); } #endif bool BluetoothDevice::supports_service_specific_discovery() const { return supports_service_specific_discovery_; } void BluetoothDevice::UpgradeToFullDiscovery() { // Must be overridden by any subclass that sets // |supports_service_specific_discovery_|. NOTREACHED(); } std::unique_ptr BluetoothDevice::CreateBluetoothGattConnectionObject() { return std::make_unique(adapter_, GetAddress()); } void BluetoothDevice::DidConnectGatt() { for (auto& callback : create_gatt_connection_success_callbacks_) std::move(callback).Run(CreateBluetoothGattConnectionObject()); create_gatt_connection_success_callbacks_.clear(); create_gatt_connection_error_callbacks_.clear(); GetAdapter()->NotifyDeviceChanged(this); } void BluetoothDevice::DidFailToConnectGatt(ConnectErrorCode error) { // Connection request should only be made if there are no active // connections. DCHECK(gatt_connections_.empty()); target_service_.reset(); for (auto& error_callback : create_gatt_connection_error_callbacks_) std::move(error_callback).Run(error); create_gatt_connection_success_callbacks_.clear(); create_gatt_connection_error_callbacks_.clear(); } void BluetoothDevice::DidDisconnectGatt() { // Pending calls to connect GATT are not expected, if they were then // DidFailToConnectGatt should have been called. DCHECK(create_gatt_connection_error_callbacks_.empty()); target_service_.reset(); // Invalidate all BluetoothGattConnection objects. for (BluetoothGattConnection* connection : gatt_connections_) { connection->InvalidateConnectionReference(); } gatt_connections_.clear(); GetAdapter()->NotifyDeviceChanged(this); } void BluetoothDevice::AddGattConnection(BluetoothGattConnection* connection) { auto result = gatt_connections_.insert(connection); DCHECK(result.second); // Check insert happened; there was no duplicate. } void BluetoothDevice::RemoveGattConnection( BluetoothGattConnection* connection) { size_t erased_count = gatt_connections_.erase(connection); DCHECK(erased_count); if (gatt_connections_.size() == 0) DisconnectGatt(); } void BluetoothDevice::SetAsExpiredForTesting() { last_update_time_ = base::Time::NowFromSystemTime() - (BluetoothAdapter::timeoutSec + base::TimeDelta::FromSeconds(1)); } void BluetoothDevice::Pair(PairingDelegate* pairing_delegate, base::OnceClosure callback, ConnectErrorCallback error_callback) { NOTREACHED(); } void BluetoothDevice::UpdateTimestamp() { last_update_time_ = base::Time::NowFromSystemTime(); } base::Time BluetoothDevice::GetLastUpdateTime() const { return last_update_time_; } // static int8_t BluetoothDevice::ClampPower(int power) { if (power < INT8_MIN) { return INT8_MIN; } if (power > INT8_MAX) { return INT8_MAX; } return static_cast(power); } } // namespace device