text: // Copyright 2018 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/cast/bluetooth_remote_gatt_characteristic_cast.h"
  
  #include <memory>
  
  #include "base/bind.h"
  #include "base/callback.h"
  #include "base/containers/queue.h"
  #include "base/logging.h"
  #include "base/macros.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::GATT_ERROR_FAILED);
  }
  
  }  // 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(
      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 can ignore |ccc_descriptor|.
    (void)ccc_descriptor;
  
    remote_characteristic_->SetRegisterNotification(
        true, base::BindOnce(&OnSubscribeOrUnsubscribe, std::move(callback),
                             std::move(error_callback)));
  }
  
  void BluetoothRemoteGattCharacteristicCast::UnsubscribeFromNotifications(
      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 can ignore |ccc_descriptor|.
    (void)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=*/absl::nullopt, result);
      return;
    }
    std::move(callback).Run(BluetoothGattService::GATT_ERROR_FAILED,
                            /*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::GATT_ERROR_FAILED);
  }
  
  }  // namespace device
  