910e62b5创建于 1月15日历史提交
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "extensions/browser/api/serial/serial_api.h"

#include <algorithm>
#include <map>
#include <unordered_set>
#include <utility>

#include "base/functional/bind.h"
#include "base/values.h"
#include "build/build_config.h"
#include "extensions/browser/api/api_resource_manager.h"
#include "extensions/browser/api/serial/serial_connection.h"
#include "extensions/browser/api/serial/serial_port_manager.h"
#include "extensions/common/api/serial.h"
#include "extensions/common/extension_id.h"
#include "mojo/public/cpp/bindings/callback_helpers.h"
#include "mojo/public/cpp/bindings/pending_remote.h"

namespace extensions {

namespace api {

namespace {

// It's a fool's errand to come up with a default bitrate, because we don't get
// to control both sides of the communication. Unless the other side has
// implemented auto-bitrate detection (rare), if we pick the wrong rate, then
// you're gonna have a bad time. Close doesn't count.
//
// But we'd like to pick something that has a chance of working, and 9600 is a
// good balance between popularity and speed. So 9600 it is.
const int kDefaultBufferSize = 4096;
const int kDefaultBitrate = 9600;
const serial::DataBits kDefaultDataBits = serial::DataBits::kEight;
const serial::ParityBit kDefaultParityBit = serial::ParityBit::kNo;
const serial::StopBits kDefaultStopBits = serial::StopBits::kOne;
const int kDefaultReceiveTimeout = 0;
const int kDefaultSendTimeout = 0;

const char kErrorConnectFailed[] = "Failed to connect to the port.";
const char kErrorSerialConnectionNotFound[] = "Serial connection not found.";
const char kErrorGetControlSignalsFailed[] = "Failed to get control signals.";

template <typename T>
void SetDefaultOptionalValue(std::optional<T>& field, const T& value) {
  if (!field)
    field = value;
}

}  // namespace

SerialExtensionFunction::SerialExtensionFunction() = default;
SerialExtensionFunction::~SerialExtensionFunction() = default;

SerialConnection* SerialExtensionFunction::GetSerialConnection(
    int api_resource_id) {
  auto* manager = ApiResourceManager<SerialConnection>::Get(browser_context());
  return manager->Get(extension_->id(), api_resource_id);
}

void SerialExtensionFunction::RemoveSerialConnection(int api_resource_id) {
  auto* manager = ApiResourceManager<SerialConnection>::Get(browser_context());
  manager->Remove(extension_->id(), api_resource_id);
}

SerialGetDevicesFunction::SerialGetDevicesFunction() = default;
SerialGetDevicesFunction::~SerialGetDevicesFunction() = default;

ExtensionFunction::ResponseAction SerialGetDevicesFunction::Run() {
  auto* port_manager = SerialPortManager::Get(browser_context());
  DCHECK(port_manager);
  port_manager->GetDevices(mojo::WrapCallbackWithDefaultInvokeIfNotRun(
      base::BindOnce(&SerialGetDevicesFunction::OnGotDevices, this),
      std::vector<device::mojom::SerialPortInfoPtr>()));
  return RespondLater();
}

void SerialGetDevicesFunction::OnGotDevices(
    std::vector<device::mojom::SerialPortInfoPtr> devices) {
  std::vector<extensions::api::serial::DeviceInfo> results;
  for (const auto& device : devices) {
    extensions::api::serial::DeviceInfo info;
    info.path = device->path.AsUTF8Unsafe();
    info.vendor_id = device->vendor_id;
    info.product_id = device->product_id;
    info.display_name = device->display_name;
    results.push_back(std::move(info));

#if BUILDFLAG(IS_MAC)
    if (device->alternate_path) {
      extensions::api::serial::DeviceInfo alternate_info;
      alternate_info.path = device->alternate_path->AsUTF8Unsafe();
      alternate_info.vendor_id = device->vendor_id;
      alternate_info.product_id = device->product_id;
      alternate_info.display_name = device->display_name;

      results.push_back(std::move(alternate_info));
    }
#endif  // BUILDFLAG(IS_MAC)
  }
  Respond(ArgumentList(serial::GetDevices::Results::Create(results)));
}

SerialConnectFunction::SerialConnectFunction() = default;

SerialConnectFunction::~SerialConnectFunction() = default;

ExtensionFunction::ResponseAction SerialConnectFunction::Run() {
  auto params = serial::Connect::Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(params);

  // Fill in any omitted options to ensure a known initial configuration.
  if (!params->options)
    params->options.emplace();
  serial::ConnectionOptions& options = *params->options;

  SetDefaultOptionalValue(options.persistent, false);
  SetDefaultOptionalValue(options.buffer_size, kDefaultBufferSize);
  SetDefaultOptionalValue(options.bitrate, kDefaultBitrate);
  SetDefaultOptionalValue(options.cts_flow_control, false);
  SetDefaultOptionalValue(options.receive_timeout, kDefaultReceiveTimeout);
  SetDefaultOptionalValue(options.send_timeout, kDefaultSendTimeout);

  if (options.data_bits == serial::DataBits::kNone) {
    options.data_bits = kDefaultDataBits;
  }
  if (options.parity_bit == serial::ParityBit::kNone) {
    options.parity_bit = kDefaultParityBit;
  }
  if (options.stop_bits == serial::StopBits::kNone) {
    options.stop_bits = kDefaultStopBits;
  }

  auto* manager = SerialPortManager::Get(browser_context());
  DCHECK(manager);

  connection_ = std::make_unique<SerialConnection>(extension_->id());
  connection_->Open(manager, params->path, *params->options,
                    base::BindOnce(&SerialConnectFunction::OnConnected, this));
  return RespondLater();
}

void SerialConnectFunction::OnConnected(bool success) {
  DCHECK(connection_);

  if (!success) {
    FinishConnect(false, false, nullptr);
    return;
  }

  connection_->GetInfo(
      base::BindOnce(&SerialConnectFunction::FinishConnect, this, true));
}

void SerialConnectFunction::FinishConnect(
    bool connected,
    bool got_complete_info,
    std::unique_ptr<serial::ConnectionInfo> info) {
  DCHECK(connection_);
  if (!connected || !got_complete_info) {
    Respond(Error(kErrorConnectFailed));
    connection_.reset();
  } else {
    DCHECK(info);
    auto* manager =
        ApiResourceManager<SerialConnection>::Get(browser_context());
    int id = manager->Add(connection_.release());
    // If a SerialConnection encountered a mojo connection error, it just
    // becomes useless, we won't try to re-connect it but just remove it
    // completely.
    SerialConnection* connection = GetSerialConnection(id);
    connection->SetConnectionErrorHandler(base::BindOnce(
        [](scoped_refptr<ApiResourceManager<SerialConnection>::ApiResourceData>
               connections,
           const ExtensionId& extension_id, int api_resource_id) {
          connections->Remove(extension_id, api_resource_id);
        },
        manager->data_, extension_->id(), id));
    info->connection_id = id;
    // Start polling.
    auto* port_manager = SerialPortManager::Get(browser_context());
    port_manager->StartConnectionPolling(extension_->id(), id);
    Respond(WithArguments(info->ToValue()));
  }
}

SerialUpdateFunction::SerialUpdateFunction() = default;
SerialUpdateFunction::~SerialUpdateFunction() = default;

ExtensionFunction::ResponseAction SerialUpdateFunction::Run() {
  auto params = serial::Update::Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(params);

  SerialConnection* connection = GetSerialConnection(params->connection_id);
  if (!connection)
    return RespondNow(Error(kErrorSerialConnectionNotFound));

  connection->Configure(params->options,
                        base::BindOnce(&SerialUpdateFunction::OnUpdated, this));
  return RespondLater();
}

void SerialUpdateFunction::OnUpdated(bool success) {
  Respond(WithArguments(success));
}

SerialDisconnectFunction::SerialDisconnectFunction() = default;
SerialDisconnectFunction::~SerialDisconnectFunction() = default;

ExtensionFunction::ResponseAction SerialDisconnectFunction::Run() {
  auto params = serial::Disconnect::Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(params);

  SerialConnection* connection = GetSerialConnection(params->connection_id);
  if (!connection)
    return RespondNow(Error(kErrorSerialConnectionNotFound));

  connection->Close(base::BindOnce(&SerialDisconnectFunction::OnCloseComplete,
                                   this, params->connection_id));
  return RespondLater();
}

void SerialDisconnectFunction::OnCloseComplete(int connection_id) {
  RemoveSerialConnection(connection_id);
  Respond(WithArguments(true));
}

SerialSendFunction::SerialSendFunction() = default;
SerialSendFunction::~SerialSendFunction() = default;

ExtensionFunction::ResponseAction SerialSendFunction::Run() {
  auto params = serial::Send::Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(params);

  SerialConnection* connection = GetSerialConnection(params->connection_id);
  if (!connection)
    return RespondNow(Error(kErrorSerialConnectionNotFound));

  connection->Send(params->data,
                   base::BindOnce(&SerialSendFunction::OnSendComplete, this));
  return RespondLater();
}

void SerialSendFunction::OnSendComplete(uint32_t bytes_sent,
                                        serial::SendError error) {
  serial::SendInfo send_info;
  send_info.bytes_sent = bytes_sent;
  send_info.error = error;
  Respond(WithArguments(send_info.ToValue()));
}

SerialFlushFunction::SerialFlushFunction() = default;
SerialFlushFunction::~SerialFlushFunction() = default;

ExtensionFunction::ResponseAction SerialFlushFunction::Run() {
  auto params = serial::Flush::Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(params);

  SerialConnection* connection = GetSerialConnection(params->connection_id);
  if (!connection)
    return RespondNow(Error(kErrorSerialConnectionNotFound));

  connection->Flush(device::mojom::SerialPortFlushMode::kReceiveAndTransmit,
                    base::BindOnce(&SerialFlushFunction::OnFlushed, this));
  return RespondLater();
}

void SerialFlushFunction::OnFlushed() {
  Respond(WithArguments(true));
}

SerialSetPausedFunction::SerialSetPausedFunction() = default;
SerialSetPausedFunction::~SerialSetPausedFunction() = default;

ExtensionFunction::ResponseAction SerialSetPausedFunction::Run() {
  auto params = serial::SetPaused::Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(params);

  SerialConnection* connection = GetSerialConnection(params->connection_id);
  if (!connection)
    return RespondNow(Error(kErrorSerialConnectionNotFound));

  if (params->paused != connection->paused())
    connection->SetPaused(params->paused);

  return RespondNow(NoArguments());
}

SerialGetInfoFunction::SerialGetInfoFunction() = default;
SerialGetInfoFunction::~SerialGetInfoFunction() = default;

ExtensionFunction::ResponseAction SerialGetInfoFunction::Run() {
  auto params = serial::GetInfo::Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(params);

  SerialConnection* connection = GetSerialConnection(params->connection_id);
  if (!connection)
    return RespondNow(Error(kErrorSerialConnectionNotFound));

  connection->GetInfo(base::BindOnce(&SerialGetInfoFunction::OnGotInfo, this,
                                     params->connection_id));
  return RespondLater();
}

void SerialGetInfoFunction::OnGotInfo(
    int connection_id,
    bool got_complete_info,
    std::unique_ptr<serial::ConnectionInfo> info) {
  DCHECK(info);
  info->connection_id = connection_id;
  Respond(WithArguments(info->ToValue()));
}

SerialGetConnectionsFunction::SerialGetConnectionsFunction() = default;
SerialGetConnectionsFunction::~SerialGetConnectionsFunction() = default;

ExtensionFunction::ResponseAction SerialGetConnectionsFunction::Run() {
  auto* manager = ApiResourceManager<SerialConnection>::Get(browser_context());
  const std::unordered_set<int>* connection_ids =
      manager->GetResourceIds(extension_->id());
  if (connection_ids) {
    for (auto it = connection_ids->cbegin(); it != connection_ids->cend();
         ++it) {
      int connection_id = *it;
      SerialConnection* connection = GetSerialConnection(connection_id);
      if (connection) {
        count_++;
        connection->GetInfo(base::BindOnce(
            &SerialGetConnectionsFunction::OnGotOne, this, connection_id));
      }
    }
  }

  if (count_ > 0)
    return RespondLater();

  return RespondNow(ArgumentList(serial::GetConnections::Results::Create(
      std::vector<serial::ConnectionInfo>())));
}

void SerialGetConnectionsFunction::OnGotOne(
    int connection_id,
    bool got_complete_info,
    std::unique_ptr<serial::ConnectionInfo> info) {
  DCHECK(info);
  info->connection_id = connection_id;
  infos_.push_back(std::move(*info));

  if (infos_.size() == count_)
    Respond(ArgumentList(serial::GetConnections::Results::Create(infos_)));
}

SerialGetControlSignalsFunction::SerialGetControlSignalsFunction() = default;
SerialGetControlSignalsFunction::~SerialGetControlSignalsFunction() = default;

ExtensionFunction::ResponseAction SerialGetControlSignalsFunction::Run() {
  auto params = serial::GetControlSignals::Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(params);

  SerialConnection* connection = GetSerialConnection(params->connection_id);
  if (!connection)
    return RespondNow(Error(kErrorSerialConnectionNotFound));

  connection->GetControlSignals(base::BindOnce(
      &SerialGetControlSignalsFunction::OnGotControlSignals, this));
  return RespondLater();
}

void SerialGetControlSignalsFunction::OnGotControlSignals(
    std::unique_ptr<serial::DeviceControlSignals> signals) {
  if (!signals) {
    Respond(Error(kErrorGetControlSignalsFailed));
  } else {
    Respond(WithArguments(signals->ToValue()));
  }
}

SerialSetControlSignalsFunction::SerialSetControlSignalsFunction() = default;
SerialSetControlSignalsFunction::~SerialSetControlSignalsFunction() = default;

ExtensionFunction::ResponseAction SerialSetControlSignalsFunction::Run() {
  auto params = serial::SetControlSignals::Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(params);

  SerialConnection* connection = GetSerialConnection(params->connection_id);
  if (!connection)
    return RespondNow(Error(kErrorSerialConnectionNotFound));

  connection->SetControlSignals(
      device::mojom::SerialHostControlSignals::From(params->signals),
      base::BindOnce(&SerialSetControlSignalsFunction::OnSetControlSignals,
                     this));
  return RespondLater();
}

void SerialSetControlSignalsFunction::OnSetControlSignals(bool success) {
  Respond(WithArguments(success));
}

SerialSetBreakFunction::SerialSetBreakFunction() = default;
SerialSetBreakFunction::~SerialSetBreakFunction() = default;

ExtensionFunction::ResponseAction SerialSetBreakFunction::Run() {
  auto params = serial::SetBreak::Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(params);

  SerialConnection* connection = GetSerialConnection(params->connection_id);
  if (!connection)
    return RespondNow(Error(kErrorSerialConnectionNotFound));

  auto signals = device::mojom::SerialHostControlSignals::New();
  signals->has_brk = true;
  signals->brk = true;
  connection->SetControlSignals(
      std::move(signals),
      base::BindOnce(&SerialSetBreakFunction::OnSetBreak, this));
  return RespondLater();
}

void SerialSetBreakFunction::OnSetBreak(bool success) {
  Respond(WithArguments(success));
}

SerialClearBreakFunction::SerialClearBreakFunction() = default;
SerialClearBreakFunction::~SerialClearBreakFunction() = default;

ExtensionFunction::ResponseAction SerialClearBreakFunction::Run() {
  auto params = serial::ClearBreak::Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(params);

  SerialConnection* connection = GetSerialConnection(params->connection_id);
  if (!connection)
    return RespondNow(Error(kErrorSerialConnectionNotFound));

  auto signals = device::mojom::SerialHostControlSignals::New();
  signals->has_brk = true;
  signals->brk = false;
  connection->SetControlSignals(
      std::move(signals),
      base::BindOnce(&SerialClearBreakFunction::OnClearBreak, this));
  return RespondLater();
}

void SerialClearBreakFunction::OnClearBreak(bool success) {
  Respond(WithArguments(success));
}

}  // namespace api

}  // namespace extensions