text: // 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 <iterator>
  #include <memory>
  #include <string>
  
  #include "base/memory/ptr_util.h"
  #include "base/stl_util.h"
  #include "base/strings/string_util.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_service.h"
  #include "device/bluetooth/string_util_icu.h"
  #include "grit/bluetooth_strings.h"
  #include "ui/base/l10n/l10n_util.h"
  
  namespace device {
  
  BluetoothDevice::DeviceUUIDs::DeviceUUIDs() {}
  
  BluetoothDevice::DeviceUUIDs::~DeviceUUIDs() {}
  
  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_.clear();
    std::set_union(advertised_uuids_.begin(), advertised_uuids_.end(),
                   service_uuids_.begin(), service_uuids_.end(),
                   std::inserter(device_uuids_, device_uuids_.begin()));
  }
  
  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() {}
  
  base::string16 BluetoothDevice::GetNameForDisplay() const {
    base::Optional<std::string> 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;
    // 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 &&
         GetName() == std::string("PLAYSTATION(R)3 Controller")))
      return true;
  
    return false;
  }
  
  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<uint8_t>* 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<uint8_t>* BluetoothDevice::GetManufacturerDataForID(
      const ManufacturerId manufacturerID) const {
    auto it = manufacturer_data_.find(manufacturerID);
    if (it != manufacturer_data_.end()) {
      return &it->second;
    }
    return nullptr;
  }
  
  base::Optional<int8_t> BluetoothDevice::GetInquiryRSSI() const {
    return inquiry_rssi_;
  }
  
  base::Optional<int8_t> BluetoothDevice::GetInquiryTxPower() const {
    return inquiry_tx_power_;
  }
  
  base::Optional<uint8_t> BluetoothDevice::GetAdvertisingDataFlags() const {
    return advertising_data_flags_;
  }
  
  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<BluetoothRemoteGattService*> BluetoothDevice::GetGattServices()
      const {
    std::vector<BluetoothRemoteGattService*> services;
    for (const auto& iter : gatt_services_)
      services.push_back(iter.second);
    return services;
  }
  
  BluetoothRemoteGattService* 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(); }
  
  void BluetoothDevice::UpdateAdvertisementData(int8_t rssi,
                                                UUIDList advertised_uuids,
                                                ServiceDataMap service_data,
                                                const int8_t* tx_power) {
    UpdateTimestamp();
  
    inquiry_rssi_ = rssi;
  
    device_uuids_.ReplaceAdvertisedUUIDs(std::move(advertised_uuids));
    service_data_ = std::move(service_data);
  
    if (tx_power != nullptr) {
      inquiry_tx_power_ = *tx_power;
    } else {
      inquiry_tx_power_ = base::nullopt;
    }
  }
  
  void BluetoothDevice::ClearAdvertisementData() {
    inquiry_rssi_ = base::nullopt;
    device_uuids_.ClearAdvertisedUUIDs();
    service_data_.clear();
    inquiry_tx_power_ = base::nullopt;
    GetAdapter()->NotifyDeviceChanged(this);
  }
  
  void BluetoothDevice::DidConnectGatt() {
    for (const auto& callback : create_gatt_connection_success_callbacks_) {
      callback.Run(
          base::MakeUnique<BluetoothGattConnection>(adapter_, GetAddress()));
    }
    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());
  
    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();
    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,
                             const base::Closure& callback,
                             const 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<int8_t>(power);
  }
  
  }  // namespace device
  