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.

#ifndef EXTENSIONS_BROWSER_API_SERIAL_SERIAL_CONNECTION_H_
#define EXTENSIONS_BROWSER_API_SERIAL_SERIAL_CONNECTION_H_

#include <memory>
#include <string>
#include <vector>

#include "base/cancelable_callback.h"
#include "base/functional/callback.h"
#include "base/functional/callback_forward.h"
#include "base/memory/weak_ptr.h"
#include "content/public/browser/browser_thread.h"
#include "extensions/browser/api/api_resource.h"
#include "extensions/browser/api/api_resource_manager.h"
#include "extensions/common/api/serial.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/system/data_pipe.h"
#include "mojo/public/cpp/system/simple_watcher.h"
#include "net/base/io_buffer.h"
#include "services/device/public/mojom/serial.mojom.h"

using content::BrowserThread;

namespace extensions {

namespace api {
class SerialPortManager;
}

// Encapsulates an mojo interface ptr of device::mojom::SerialPort, which
// corresponds with an open serial port in remote side(Device Service). NOTE:
// Instances of this object should only be constructed on the IO thread, and all
// methods should only be called on the IO thread unless otherwise noted.
class SerialConnection : public ApiResource,
                         public device::mojom::SerialPortClient {
 public:
  using OpenCompleteCallback = base::OnceCallback<void(bool)>;
  using GetInfoCompleteCallback =
      base::OnceCallback<void(bool,
                              std::unique_ptr<api::serial::ConnectionInfo>)>;

  // This is the callback type expected by Receive. Note that an error result
  // does not necessarily imply an empty `data` string, since a receive may
  // complete partially before being interrupted by an error condition.
  using ReceiveEventCallback =
      base::RepeatingCallback<void(std::vector<uint8_t> data,
                                   api::serial::ReceiveError error)>;
  // This is the callback type expected by Send. Note that an error result
  // does not necessarily imply 0 bytes sent, since a send may complete
  // partially before being interrupted by an error condition.
  using SendCompleteCallback =
      base::OnceCallback<void(uint32_t bytes_sent,
                              api::serial::SendError error)>;

  using ConfigureCompleteCallback =
      device::mojom::SerialPort::ConfigurePortCallback;

  using FlushCompleteCallback = device::mojom::SerialPort::FlushCallback;

  using GetControlSignalsCompleteCallback = base::OnceCallback<void(
      std::unique_ptr<api::serial::DeviceControlSignals>)>;

  using SetControlSignalsCompleteCallback =
      device::mojom::SerialPort::SetControlSignalsCallback;

  explicit SerialConnection(const std::string& owner_extension_id);
  ~SerialConnection() override;

  // ApiResource override.
  bool IsPersistent() const override;

  void set_persistent(bool persistent) { persistent_ = persistent; }
  bool persistent() const { return persistent_; }

  void set_name(const std::string& name) { name_ = name; }
  const std::string& name() const { return name_; }

  void set_buffer_size(int buffer_size);
  int buffer_size() const { return buffer_size_; }

  void set_receive_timeout(int receive_timeout);
  int receive_timeout() const { return receive_timeout_; }

  void set_send_timeout(int send_timeout);
  int send_timeout() const { return send_timeout_; }

  void SetPaused(bool paused);
  bool paused() const { return paused_; }

  void SetConnectionErrorHandler(base::OnceClosure connection_error_handler);

  // Initiates an asynchronous Open of the device. It is the caller's
  // responsibility to ensure that this SerialConnection stays alive
  // until `callback` is run.
  virtual void Open(api::SerialPortManager* port_manager,
                    const std::string& path,
                    const api::serial::ConnectionOptions& options,
                    OpenCompleteCallback callback);

  // Begins an asynchronous send operation. Calling this while a Send
  // is already pending will result in a serial::SEND_ERROR_PENDING error.
  virtual void Send(const std::vector<uint8_t>& data,
                    SendCompleteCallback callback);

  // Start to the polling process from `receive_pipe_`.
  virtual void StartPolling(const ReceiveEventCallback& callback);

  // Flushes input and output buffers.
  void Flush(device::mojom::SerialPortFlushMode mode,
             FlushCompleteCallback callback) const;

  // Configures some subset of port options for this connection.
  // Omitted options are unchanged.
  void Configure(const api::serial::ConnectionOptions& options,
                 ConfigureCompleteCallback callback);

  // Connection configuration query. Returns retrieved ConnectionInfo value via
  // `callback`, and indicates whether it's complete info. Some ConnectionInfo
  // fields are filled with local info from `this`, while some other fields must
  // be retrieved from remote SerialPort interface, which may fail.
  void GetInfo(GetInfoCompleteCallback callback) const;

  // Reads current control signals (DCD, CTS, etc.) and returns via `callback`.
  // Returns nullptr if we failed in getting values.
  void GetControlSignals(GetControlSignalsCompleteCallback callback) const;

  // Sets one or more control signals (DTR, RTS, Break). Returns result success
  // or not via `callback`.
  void SetControlSignals(device::mojom::SerialHostControlSignalsPtr signals,
                         SetControlSignalsCompleteCallback callback);

  // Initiates an asynchronous close of the device.
  void Close(base::OnceClosure callback);

  static const BrowserThread::ID kThreadId = BrowserThread::UI;

 protected:
  // Initializes `serial_port_` with a disconnected Mojo pipe for testing
  // purposes.
  void InitSerialPortForTesting();

 private:
  friend class ApiResourceManager<SerialConnection>;
  static const char* service_name() { return "SerialConnectionManager"; }

  // device::mojom::SerialPortClient override.
  void OnReadError(device::mojom::SerialReceiveError error) override;
  void OnSendError(device::mojom::SerialSendError error) override;

  void OnOpen(
      mojo::PendingReceiver<device::mojom::SerialPortClient> client_receiver,
      OpenCompleteCallback callback,
      mojo::PendingRemote<device::mojom::SerialPort> serial_port);

  // Read data from `receive_pipe_` when the data is ready or dispatch error
  // events in error cases.
  void OnReadPipeReadableOrClosed(MojoResult result,
                                  const mojo::HandleSignalsState& state);
  void OnReadPipeClosed();

  void CreatePipe(mojo::ScopedDataPipeProducerHandle* producer,
                  mojo::ScopedDataPipeConsumerHandle* consumer);
  void SetUpReceiveDataPipe();
  void SetUpSendDataPipe();

  void SetTimeoutCallback();

  // Handles a receive timeout.
  void OnReceiveTimeout();

  // Handles a send timeout.
  void OnSendTimeout();

  void OnSendPipeWritableOrClosed(MojoResult result,
                                  const mojo::HandleSignalsState& state);
  void OnSendPipeClosed();

  // Handles `serial_port_` connection error.
  void OnConnectionError();

  // Handles `client_receiver_` connection error.
  void OnClientReceiverClosed();

  // Flag indicating whether or not the connection should persist when
  // its host app is suspended.
  bool persistent_;

  // User-specified connection name.
  std::string name_;

  // Size of the receive and send buffer.
  int buffer_size_;

  // Amount of time (in ms) to wait for a Read to succeed before triggering a
  // timeout response via onReceiveError.
  int receive_timeout_;

  // Amount of time (in ms) to wait for a Write to succeed before triggering
  // a timeout response.
  int send_timeout_;

  // Flag indicating that the connection is paused. A paused connection will not
  // raise new onReceive events.
  bool paused_;

  // Callback to handle the completion of a pending Receive() request.
  ReceiveEventCallback receive_event_cb_;
  std::optional<device::mojom::SerialReceiveError> read_error_;

  // Callback to handle the completion of a pending Send() request.
  SendCompleteCallback send_complete_;
  size_t bytes_written_;

  // The data needs to be sent.
  std::vector<uint8_t> data_to_send_;

  // Closure which will trigger a receive timeout unless cancelled. Reset on
  // initialization and after every successful Receive().
  base::CancelableOnceClosure receive_timeout_task_;

  // Write timeout closure. Reset on initialization and after every successful
  // Send().
  base::CancelableOnceClosure send_timeout_task_;

  // Mojo interface remote corresponding with remote asynchronous I/O handler.
  mojo::Remote<device::mojom::SerialPort> serial_port_;

  // Pipe for read.
  mojo::ScopedDataPipeConsumerHandle receive_pipe_;
  mojo::SimpleWatcher receive_pipe_watcher_;

  // Pipe for send.
  mojo::ScopedDataPipeProducerHandle send_pipe_;
  mojo::SimpleWatcher send_pipe_watcher_;

  mojo::Receiver<device::mojom::SerialPortClient> client_receiver_{this};

  // Closure which is set by client and will be called when `serial_port_`
  // connection encountered an error.
  base::OnceClosure connection_error_handler_;

  base::WeakPtrFactory<SerialConnection> weak_factory_{this};
};

}  // namespace extensions

namespace mojo {

template <>
struct TypeConverter<device::mojom::SerialHostControlSignalsPtr,
                     extensions::api::serial::HostControlSignals> {
  static device::mojom::SerialHostControlSignalsPtr Convert(
      const extensions::api::serial::HostControlSignals& input);
};

template <>
struct TypeConverter<device::mojom::SerialConnectionOptionsPtr,
                     extensions::api::serial::ConnectionOptions> {
  static device::mojom::SerialConnectionOptionsPtr Convert(
      const extensions::api::serial::ConnectionOptions& input);
};

}  // namespace mojo

#endif  // EXTENSIONS_BROWSER_API_SERIAL_SERIAL_CONNECTION_H_