| // 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 <string> |
| |
| #include "base/stl_util.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/values.h" |
| #include "device/bluetooth/bluetooth_adapter.h" |
| #include "device/bluetooth/bluetooth_gatt_connection.h" |
| #include "device/bluetooth/bluetooth_gatt_service.h" |
| #include "grit/bluetooth_strings.h" |
| #include "ui/base/l10n/l10n_util.h" |
| |
| namespace device { |
| |
| BluetoothDevice::BluetoothDevice(BluetoothAdapter* adapter) |
| : adapter_(adapter), |
| gatt_services_discovery_complete_(false), |
| services_data_(new base::DictionaryValue()) {} |
| |
| BluetoothDevice::~BluetoothDevice() { |
| DidDisconnectGatt(); |
| } |
| |
| 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() {} |
| |
| base::string16 BluetoothDevice::GetName() const { |
| std::string name = GetDeviceName(); |
| if (!name.empty()) { |
| return base::UTF8ToUTF16(name); |
| } else { |
| return GetAddressWithLocalizedDeviceTypeName(); |
| } |
| } |
| |
| base::string16 BluetoothDevice::GetAddressWithLocalizedDeviceTypeName() const { |
| base::string16 address_utf16 = base::UTF8ToUTF16(GetAddress()); |
| BluetoothDevice::DeviceType device_type = GetDeviceType(); |
| switch (device_type) { |
| case DEVICE_COMPUTER: |
| return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_COMPUTER, |
| address_utf16); |
| case DEVICE_PHONE: |
| return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_PHONE, |
| address_utf16); |
| case DEVICE_MODEM: |
| return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_MODEM, |
| address_utf16); |
| case DEVICE_AUDIO: |
| return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_AUDIO, |
| address_utf16); |
| case DEVICE_CAR_AUDIO: |
| return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_CAR_AUDIO, |
| address_utf16); |
| case DEVICE_VIDEO: |
| return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_VIDEO, |
| address_utf16); |
| case DEVICE_JOYSTICK: |
| return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_JOYSTICK, |
| address_utf16); |
| case DEVICE_GAMEPAD: |
| return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_GAMEPAD, |
| address_utf16); |
| case DEVICE_KEYBOARD: |
| return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_KEYBOARD, |
| address_utf16); |
| case DEVICE_MOUSE: |
| return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_MOUSE, |
| address_utf16); |
| case DEVICE_TABLET: |
| return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_TABLET, |
| address_utf16); |
| case DEVICE_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); |
| } |
| } |
| |
| BluetoothDevice::DeviceType 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 DEVICE_COMPUTER; |
| case 0x02: |
| // Phone major device class. |
| switch ((bluetooth_class & 0xfc) >> 2) { |
| case 0x01: |
| case 0x02: |
| case 0x03: |
| // Cellular, cordless and smart phones. |
| return DEVICE_PHONE; |
| case 0x04: |
| case 0x05: |
| // Modems: wired or voice gateway and common ISDN access. |
| return DEVICE_MODEM; |
| } |
| break; |
| case 0x04: |
| // Audio major device class. |
| switch ((bluetooth_class & 0xfc) >> 2) { |
| case 0x08: |
| // Car audio. |
| return DEVICE_CAR_AUDIO; |
| case 0x0b: |
| case 0x0c: |
| case 0x0d: |
| case 0x0e: |
| case 0x0f: |
| case 0x010: |
| // Video devices. |
| return DEVICE_VIDEO; |
| default: |
| return DEVICE_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 DEVICE_JOYSTICK; |
| case 0x02: |
| // Gamepad. |
| return DEVICE_GAMEPAD; |
| default: |
| return DEVICE_PERIPHERAL; |
| } |
| break; |
| case 0x01: |
| // Keyboard. |
| return DEVICE_KEYBOARD; |
| case 0x02: |
| // Pointing device. |
| switch ((bluetooth_class & 0x01e) >> 2) { |
| case 0x05: |
| // Digitizer tablet. |
| return DEVICE_TABLET; |
| default: |
| // Mouse. |
| return DEVICE_MOUSE; |
| } |
| break; |
| case 0x03: |
| // Combo device. |
| return DEVICE_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 DEVICE_PHONE; |
| case 0x02: |
| // Generic computer |
| return DEVICE_COMPUTER; |
| case 0x0f: |
| // HID subtype |
| switch (appearance & 0x3f) { |
| case 0x01: |
| // Keyboard. |
| return DEVICE_KEYBOARD; |
| case 0x02: |
| // Mouse |
| return DEVICE_MOUSE; |
| case 0x03: |
| // Joystick |
| return DEVICE_JOYSTICK; |
| case 0x04: |
| // Gamepad |
| return DEVICE_GAMEPAD; |
| case 0x05: |
| // Digitizer tablet |
| return DEVICE_TABLET; |
| } |
| } |
| |
| return DEVICE_UNKNOWN; |
| } |
| |
| bool BluetoothDevice::IsPairable() const { |
| DeviceType 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 == DEVICE_MOUSE && vendor == "00:12:A1") |
| return false; |
| // Microsoft "Microsoft Bluetooth Notebook Mouse 5000", model X807028-001 |
| if (type == DEVICE_MOUSE && vendor == "7C:ED:8D") |
| return false; |
| // Sony PlayStation Dualshock3 |
| if (IsTrustable()) |
| return false; |
| |
| // TODO: Move this database into a config file. |
| |
| return true; |
| } |
| |
| bool BluetoothDevice::IsTrustable() const { |
| // Sony PlayStation Dualshock3 |
| if ((GetVendorID() == 0x054c && GetProductID() == 0x0268 && |
| GetDeviceName() == "PLAYSTATION(R)3 Controller")) |
| return true; |
| |
| return false; |
| } |
| |
| void BluetoothDevice::CreateGattConnection( |
| const GattConnectionCallback& callback, |
| const ConnectErrorCallback& error_callback) { |
| create_gatt_connection_success_callbacks_.push_back(callback); |
| create_gatt_connection_error_callbacks_.push_back(error_callback); |
| |
| if (IsGattConnected()) |
| return DidConnectGatt(); |
| |
| CreateGattConnectionImpl(); |
| } |
| |
| void BluetoothDevice::SetGattServicesDiscoveryComplete(bool complete) { |
| gatt_services_discovery_complete_ = complete; |
| } |
| |
| bool BluetoothDevice::IsGattServicesDiscoveryComplete() const { |
| return gatt_services_discovery_complete_; |
| } |
| |
| std::vector<BluetoothGattService*> |
| BluetoothDevice::GetGattServices() const { |
| std::vector<BluetoothGattService*> services; |
| for (const auto& iter : gatt_services_) |
| services.push_back(iter.second); |
| return services; |
| } |
| |
| BluetoothGattService* BluetoothDevice::GetGattService( |
| const std::string& identifier) const { |
| return gatt_services_.get(identifier); |
| } |
| |
| // static |
| std::string BluetoothDevice::CanonicalizeAddress(const std::string& address) { |
| std::string canonicalized = address; |
| if (address.size() == 12) { |
| // Might be an address in the format "1A2B3C4D5E6F". Add separators. |
| for (size_t i = 2; i < canonicalized.size(); i += 3) { |
| canonicalized.insert(i, ":"); |
| } |
| } |
| |
| // Verify that the length matches the canonical format "1A:2B:3C:4D:5E:6F". |
| const size_t kCanonicalAddressLength = 17; |
| if (canonicalized.size() != kCanonicalAddressLength) |
| return std::string(); |
| |
| const char separator = canonicalized[2]; |
| for (size_t i = 0; i < canonicalized.size(); ++i) { |
| bool is_separator = (i + 1) % 3 == 0; |
| if (is_separator) { |
| // All separators in the input |address| should be consistent. |
| if (canonicalized[i] != separator) |
| return std::string(); |
| |
| canonicalized[i] = ':'; |
| } else { |
| if (!base::IsHexDigit(canonicalized[i])) |
| return std::string(); |
| |
| canonicalized[i] = base::ToUpperASCII(canonicalized[i]); |
| } |
| } |
| |
| return canonicalized; |
| } |
| |
| std::string BluetoothDevice::GetIdentifier() const { return GetAddress(); } |
| |
| base::BinaryValue* BluetoothDevice::GetServiceData( |
| BluetoothUUID serviceUUID) const { |
| base::BinaryValue* value; |
| if (!services_data_->GetBinary(serviceUUID.value(), &value)) |
| return NULL; |
| return value; |
| } |
| |
| BluetoothDevice::UUIDList BluetoothDevice::GetServiceDataUUIDs() const { |
| std::vector<device::BluetoothUUID> uuids; |
| base::DictionaryValue::Iterator iter(*services_data_); |
| while (!iter.IsAtEnd()) { |
| BluetoothUUID uuid(iter.key()); |
| uuids.push_back(uuid); |
| iter.Advance(); |
| } |
| return uuids; |
| } |
| |
| void BluetoothDevice::DidConnectGatt() { |
| for (const auto& callback : create_gatt_connection_success_callbacks_) { |
| callback.Run( |
| make_scoped_ptr(new BluetoothGattConnection(adapter_, GetAddress()))); |
| } |
| create_gatt_connection_success_callbacks_.clear(); |
| create_gatt_connection_error_callbacks_.clear(); |
| } |
| |
| void BluetoothDevice::DidFailToConnectGatt(ConnectErrorCode error) { |
| // Connection request should only be made if there are no active |
| // connections. |
| DCHECK(gatt_connections_.empty()); |
| |
| for (const auto& error_callback : create_gatt_connection_error_callbacks_) |
| 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()); |
| |
| // Invalidate all BluetoothGattConnection objects. |
| for (BluetoothGattConnection* connection : gatt_connections_) { |
| connection->InvalidateConnectionReference(); |
| } |
| gatt_connections_.clear(); |
| } |
| |
| 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::ClearServiceData() { services_data_->Clear(); } |
| |
| void BluetoothDevice::SetServiceData(BluetoothUUID serviceUUID, |
| const char* buffer, size_t size) { |
| services_data_->Set(serviceUUID.value(), |
| base::BinaryValue::CreateWithCopiedBuffer(buffer, size)); |
| } |
| |
| void BluetoothDevice::Pair(PairingDelegate* pairing_delegate, |
| const base::Closure& callback, |
| const ConnectErrorCallback& error_callback) { |
| NOTREACHED(); |
| } |
| |
| } // namespace device |