chromium/chromeos/ash/services/secure_channel/nearby_connection.h

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

#ifndef CHROMEOS_ASH_SERVICES_SECURE_CHANNEL_NEARBY_CONNECTION_H_
#define CHROMEOS_ASH_SERVICES_SECURE_CHANNEL_NEARBY_CONNECTION_H_

#include <optional>

#include "base/containers/flat_map.h"
#include "base/containers/queue.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "chromeos/ash/services/secure_channel/connection.h"
#include "chromeos/ash/services/secure_channel/file_transfer_update_callback.h"
#include "chromeos/ash/services/secure_channel/public/mojom/nearby_connector.mojom-shared.h"
#include "chromeos/ash/services/secure_channel/public/mojom/nearby_connector.mojom.h"
#include "chromeos/ash/services/secure_channel/public/mojom/secure_channel_types.mojom.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "mojo/public/cpp/bindings/remote.h"

namespace ash::secure_channel {

// Connection implementation which creates a connection to a remote device via
// mojom::NearbyConnector. Implements mojom::NearbyMessageReceiver to receive
// messages from the NearbyConnector and uses a
// mojo::Remote<mojom::NearbyMessageSender> to send messages to the
// NearbyConnector.
//
// When requested to send a message, this class queues messages and only sends
// one message after the previous one has been sent successfully. If sending a
// message fails, this is considered a fatal error, and the connection is
// disconnected.
//
// Also implements mojom::FilePayloadListener to listen to transfer
// updates for file payloads registered via RegisterPayloadFile.
class NearbyConnection : public Connection,
                         public mojom::NearbyMessageReceiver,
                         public mojom::FilePayloadListener,
                         public mojom::NearbyConnectionStateListener {
 public:
  class Factory {
   public:
    static std::unique_ptr<Connection> Create(
        multidevice::RemoteDeviceRef remote_device,
        const std::vector<uint8_t>& eid,
        mojom::NearbyConnector* nearby_connector);
    static void SetFactoryForTesting(Factory* factory);
    virtual ~Factory() = default;

   protected:
    virtual std::unique_ptr<Connection> CreateInstance(
        multidevice::RemoteDeviceRef remote_device,
        const std::vector<uint8_t>& eid,
        mojom::NearbyConnector* nearby_connector) = 0;

   private:
    static Factory* factory_instance_;
  };

  ~NearbyConnection() override;

 private:
  NearbyConnection(multidevice::RemoteDeviceRef remote_device,
                   const std::vector<uint8_t>& eid,
                   mojom::NearbyConnector* nearby_connector);

  // Connection:
  void Connect() override;
  void Disconnect() override;
  std::string GetDeviceAddress() override;
  void SendMessageImpl(std::unique_ptr<WireMessage> message) override;
  void RegisterPayloadFileImpl(
      int64_t payload_id,
      mojom::PayloadFilesPtr payload_files,
      FileTransferUpdateCallback file_transfer_update_callback,
      base::OnceCallback<void(bool)> registration_result_callback) override;

  // mojom::NearbyMessageReceiver:
  void OnMessageReceived(const std::string& message) override;

  // mojom::NearbyConnectionStateListener:
  void OnNearbyConnectionStateChanged(
      mojom::NearbyConnectionStep step,
      mojom::NearbyConnectionStepResult result) override;

  // mojom::FilePayloadListener:
  void OnFileTransferUpdate(mojom::FileTransferUpdatePtr update) override;

  // Returns the the remote device's address as a byte array; note that
  // GetDeviceAddress() returns a colon-separated hex string.
  std::vector<uint8_t> GetRemoteDeviceBluetoothAddressAsVector();

  void OnConnectResult(
      mojo::PendingRemote<mojom::NearbyMessageSender> message_sender,
      mojo::PendingRemote<mojom::NearbyFilePayloadHandler>
          file_payload_handler);
  void OnSendMessageResult(bool success);
  void ProcessQueuedMessagesToSend();
  // Called when Disconnect() is called by the client. Can also be called when
  // the remote endpoint drops the connection and the other remotes, e.g.
  // MessageSender, triggers Disconnect() before the |disconnect_handler|s of
  // the NearbyPayloadListeners are called.
  void CleanUpPendingFileTransfersOnDisconnect();
  // Called when a FilePayloadListener remote endpoint is disconnected.
  void OnFilePayloadListenerRemoteDisconnected();

  raw_ptr<mojom::NearbyConnector> nearby_connector_;
  mojo::Receiver<mojom::NearbyMessageReceiver> message_receiver_{this};
  mojo::Receiver<mojom::NearbyConnectionStateListener>
      nearby_connection_state_listener_{this};
  mojo::Remote<mojom::NearbyMessageSender> message_sender_;
  mojo::Remote<mojom::NearbyFilePayloadHandler> file_payload_handler_;
  // Set of receivers created to listen to file payload transfer updates, one
  // for each payload registered via RegisterPayloadFile(). These receivers will
  // be automatically removed from the set when their corresponding Remote
  // endpoints are destroyed upon transfer completion. current_context() will
  // return the corresponding payload ID when a receiver is called or
  // disconnected.
  mojo::ReceiverSet<mojom::FilePayloadListener, int64_t>
      file_payload_listener_receivers_;

  std::vector<uint8_t> eid_;

  base::queue<std::unique_ptr<WireMessage>> queued_messages_to_send_;

  // Null if no message is being sent.
  std::unique_ptr<WireMessage> message_being_sent_;

  // Callbacks to receive FileTransferUpdates for registered file payloads.
  // Keyed by payload ID. A callback will be emitted from this map when the
  // corresponding mojo::Remote<mojom::FilePayloadListener> is disconnected.
  base::flat_map<int64_t, FileTransferUpdateCallback>
      file_transfer_update_callbacks_;

  base::WeakPtrFactory<NearbyConnection> weak_ptr_factory_{this};
};

}  // namespace ash::secure_channel

#endif  // CHROMEOS_ASH_SERVICES_SECURE_CHANNEL_NEARBY_CONNECTION_H_