chromium/device/bluetooth/cast/bluetooth_remote_gatt_characteristic_cast.cc

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "device/bluetooth/cast/bluetooth_remote_gatt_characteristic_cast.h"

#include <memory>

#include "base/containers/queue.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/logging.h"
#include "base/memory/weak_ptr.h"
#include "chromecast/device/bluetooth/le/remote_characteristic.h"
#include "chromecast/device/bluetooth/le/remote_descriptor.h"
#include "chromecast/public/bluetooth/gatt.h"
#include "device/bluetooth/cast/bluetooth_remote_gatt_descriptor_cast.h"
#include "device/bluetooth/cast/bluetooth_remote_gatt_service_cast.h"
#include "device/bluetooth/cast/bluetooth_utils.h"
#include "device/bluetooth/public/cpp/bluetooth_uuid.h"

namespace device {
namespace {

BluetoothGattCharacteristic::Permissions ConvertPermissions(
    chromecast::bluetooth_v2_shlib::Gatt::Permissions input) {
  BluetoothGattCharacteristic::Permissions output =
      BluetoothGattCharacteristic::PERMISSION_NONE;
  if (input & chromecast::bluetooth_v2_shlib::Gatt::PERMISSION_READ)
    output |= BluetoothGattCharacteristic::PERMISSION_READ;
  if (input & chromecast::bluetooth_v2_shlib::Gatt::PERMISSION_READ_ENCRYPTED)
    output |= BluetoothGattCharacteristic::PERMISSION_READ_ENCRYPTED;
  if (input & chromecast::bluetooth_v2_shlib::Gatt::PERMISSION_WRITE)
    output |= BluetoothGattCharacteristic::PERMISSION_WRITE;
  if (input & chromecast::bluetooth_v2_shlib::Gatt::PERMISSION_WRITE_ENCRYPTED)
    output |= BluetoothGattCharacteristic::PERMISSION_WRITE_ENCRYPTED;

  // NOTE(slan): Determine the proper mapping for these.
  // if (input & chromecast::bluetooth_v2_shlib::PERMISSION_READ_ENCRYPTED_MITM)
  //   output |= BluetoothGattCharacteristic::PERMISSION_READ_ENCRYPTED_MITM;
  // if (input &
  // chromecast::bluetooth_v2_shlib::PERMISSION_WRITE_ENCRYPTED_MITM)
  //   output |= BluetoothGattCharacteristic::PERMISSION_WRITE_ENCRYPTED_MITM;
  // if (input & chromecast::bluetooth_v2_shlib::PERMISSION_WRITE_SIGNED)
  //   output |= BluetoothGattCharacteristic::PERMISSION_WRITE_SIGNED;
  // if (input & chromecast::bluetooth_v2_shlib::PERMISSION_WRITE_SIGNED_MITM)
  //   output |= BluetoothGattCharacteristic::PERMISSION_WRITE_SIGNED_MITM;

  return output;
}

BluetoothGattCharacteristic::Properties ConvertProperties(
    chromecast::bluetooth_v2_shlib::Gatt::Properties input) {
  BluetoothGattCharacteristic::Properties output =
      BluetoothGattCharacteristic::PROPERTY_NONE;
  if (input & chromecast::bluetooth_v2_shlib::Gatt::PROPERTY_BROADCAST)
    output |= BluetoothGattCharacteristic::PROPERTY_BROADCAST;
  if (input & chromecast::bluetooth_v2_shlib::Gatt::PROPERTY_READ)
    output |= BluetoothGattCharacteristic::PROPERTY_READ;
  if (input & chromecast::bluetooth_v2_shlib::Gatt::PROPERTY_WRITE_NO_RESPONSE)
    output |= BluetoothGattCharacteristic::PROPERTY_WRITE_WITHOUT_RESPONSE;
  if (input & chromecast::bluetooth_v2_shlib::Gatt::PROPERTY_WRITE)
    output |= BluetoothGattCharacteristic::PROPERTY_WRITE;
  if (input & chromecast::bluetooth_v2_shlib::Gatt::PROPERTY_NOTIFY)
    output |= BluetoothGattCharacteristic::PROPERTY_NOTIFY;
  if (input & chromecast::bluetooth_v2_shlib::Gatt::PROPERTY_INDICATE)
    output |= BluetoothGattCharacteristic::PROPERTY_INDICATE;
  if (input & chromecast::bluetooth_v2_shlib::Gatt::PROPERTY_SIGNED_WRITE)
    output |= BluetoothGattCharacteristic::PROPERTY_AUTHENTICATED_SIGNED_WRITES;
  if (input & chromecast::bluetooth_v2_shlib::Gatt::PROPERTY_EXTENDED_PROPS)
    output |= BluetoothGattCharacteristic::PROPERTY_EXTENDED_PROPERTIES;

  return output;
}

// Called back when subscribing or unsubscribing to a remote characteristic.
// If |success| is true, run |callback|. Otherwise run |error_callback|.
void OnSubscribeOrUnsubscribe(
    base::OnceClosure callback,
    BluetoothGattCharacteristic::ErrorCallback error_callback,
    bool success) {
  if (success)
    std::move(callback).Run();
  else
    std::move(error_callback).Run(BluetoothGattService::GattErrorCode::kFailed);
}

}  // namespace

BluetoothRemoteGattCharacteristicCast::BluetoothRemoteGattCharacteristicCast(
    BluetoothRemoteGattServiceCast* service,
    scoped_refptr<chromecast::bluetooth::RemoteCharacteristic> characteristic)
    : service_(service),
      remote_characteristic_(std::move(characteristic)),
      weak_factory_(this) {
  auto descriptors = remote_characteristic_->GetDescriptors();
  descriptors_.reserve(descriptors.size());
  for (const auto& descriptor : descriptors) {
    AddDescriptor(
        std::make_unique<BluetoothRemoteGattDescriptorCast>(this, descriptor));
  }
}

BluetoothRemoteGattCharacteristicCast::
    ~BluetoothRemoteGattCharacteristicCast() {}

std::string BluetoothRemoteGattCharacteristicCast::GetIdentifier() const {
  return GetUUID().canonical_value();
}

BluetoothUUID BluetoothRemoteGattCharacteristicCast::GetUUID() const {
  return UuidToBluetoothUUID(remote_characteristic_->uuid());
}

BluetoothGattCharacteristic::Properties
BluetoothRemoteGattCharacteristicCast::GetProperties() const {
  return ConvertProperties(remote_characteristic_->properties());
}

BluetoothGattCharacteristic::Permissions
BluetoothRemoteGattCharacteristicCast::GetPermissions() const {
  return ConvertPermissions(remote_characteristic_->permissions());
}

const std::vector<uint8_t>& BluetoothRemoteGattCharacteristicCast::GetValue()
    const {
  return value_;
}

BluetoothRemoteGattService* BluetoothRemoteGattCharacteristicCast::GetService()
    const {
  return service_;
}

void BluetoothRemoteGattCharacteristicCast::ReadRemoteCharacteristic(
    ValueCallback callback) {
  remote_characteristic_->Read(base::BindOnce(
      &BluetoothRemoteGattCharacteristicCast::OnReadRemoteCharacteristic,
      weak_factory_.GetWeakPtr(), std::move(callback)));
}

void BluetoothRemoteGattCharacteristicCast::WriteRemoteCharacteristic(
    const std::vector<uint8_t>& value,
    WriteType write_type,
    base::OnceClosure callback,
    ErrorCallback error_callback) {
  using ChromecastWriteType = chromecast::bluetooth_v2_shlib::Gatt::WriteType;

  ChromecastWriteType chromecast_write_type;
  switch (write_type) {
    case WriteType::kWithResponse:
      chromecast_write_type = ChromecastWriteType::WRITE_TYPE_DEFAULT;
      break;
    case WriteType::kWithoutResponse:
      chromecast_write_type = ChromecastWriteType::WRITE_TYPE_NO_RESPONSE;
      break;
  }

  remote_characteristic_->WriteAuth(
      chromecast::bluetooth_v2_shlib::Gatt::Client::AUTH_REQ_NONE,
      chromecast_write_type, value,
      base::BindOnce(
          &BluetoothRemoteGattCharacteristicCast::OnWriteRemoteCharacteristic,
          weak_factory_.GetWeakPtr(), value, std::move(callback),
          std::move(error_callback)));
}

void BluetoothRemoteGattCharacteristicCast::DeprecatedWriteRemoteCharacteristic(
    const std::vector<uint8_t>& value,
    base::OnceClosure callback,
    ErrorCallback error_callback) {
  remote_characteristic_->Write(
      value,
      base::BindOnce(
          &BluetoothRemoteGattCharacteristicCast::OnWriteRemoteCharacteristic,
          weak_factory_.GetWeakPtr(), value, std::move(callback),
          std::move(error_callback)));
}

void BluetoothRemoteGattCharacteristicCast::SubscribeToNotifications(
    [[maybe_unused]] BluetoothRemoteGattDescriptor* ccc_descriptor,
    base::OnceClosure callback,
    ErrorCallback error_callback) {
  DVLOG(2) << __func__ << " " << GetIdentifier();

  // |remote_characteristic_| exposes a method which writes the CCCD after
  // subscribing the GATT client to the notification. This is syntactically
  // nicer and saves us a thread-hop, so we ignore |ccc_descriptor|.

  remote_characteristic_->SetRegisterNotification(
      true, base::BindOnce(&OnSubscribeOrUnsubscribe, std::move(callback),
                           std::move(error_callback)));
}

void BluetoothRemoteGattCharacteristicCast::UnsubscribeFromNotifications(
    [[maybe_unused]] BluetoothRemoteGattDescriptor* ccc_descriptor,
    base::OnceClosure callback,
    ErrorCallback error_callback) {
  DVLOG(2) << __func__ << " " << GetIdentifier();

  // |remote_characteristic_| exposes a method which writes the CCCD after
  // unsubscribing the GATT client from the notification. This is syntactically
  // nicer and saves us a thread-hop, so we ignore |ccc_descriptor|.

  remote_characteristic_->SetRegisterNotification(
      false, base::BindOnce(&OnSubscribeOrUnsubscribe, std::move(callback),
                            std::move(error_callback)));
}

void BluetoothRemoteGattCharacteristicCast::OnReadRemoteCharacteristic(
    ValueCallback callback,
    bool success,
    const std::vector<uint8_t>& result) {
  if (success) {
    value_ = result;
    std::move(callback).Run(/*error_code=*/std::nullopt, result);
    return;
  }
  std::move(callback).Run(BluetoothGattService::GattErrorCode::kFailed,
                          /*value=*/std::vector<uint8_t>());
}

void BluetoothRemoteGattCharacteristicCast::OnWriteRemoteCharacteristic(
    const std::vector<uint8_t>& written_value,
    base::OnceClosure callback,
    ErrorCallback error_callback,
    bool success) {
  if (success) {
    value_ = written_value;
    std::move(callback).Run();
    return;
  }
  std::move(error_callback).Run(BluetoothGattService::GattErrorCode::kFailed);
}

}  // namespace device