chromium/mojo/core/channel_binder.h

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

#ifndef MOJO_CORE_CHANNEL_BINDER_H_
#define MOJO_CORE_CHANNEL_BINDER_H_

#include <cstdint>
#include <vector>

#include "base/android/binder.h"
#include "base/containers/circular_deque.h"
#include "base/containers/span.h"
#include "base/memory/scoped_refptr.h"
#include "base/synchronization/lock.h"
#include "base/task/single_thread_task_runner.h"
#include "base/thread_annotations.h"
#include "mojo/core/channel.h"
#include "mojo/core/connection_params.h"
#include "mojo/core/system_impl_export.h"
#include "mojo/public/cpp/platform/platform_handle.h"
#include "third_party/abseil-cpp/absl/types/variant.h"

namespace mojo::core {

// A Binder-based Channel implementation.
class MOJO_SYSTEM_IMPL_EXPORT ChannelBinder : public Channel {
 public:
  ChannelBinder(Delegate* delegate,
                ConnectionParams connection_params,
                HandlePolicy handle_policy,
                scoped_refptr<base::SingleThreadTaskRunner> io_task_runner);

 private:
  DEFINE_BINDER_CLASS(ReceiverInterface);

  friend class Receiver;
  class Receiver : public base::android::SupportsBinder<ReceiverInterface> {
   public:
    using Proxy = ReceiverInterface::BinderRef;

    explicit Receiver(scoped_refptr<ChannelBinder> channel);

    void ShutDown();

    // base::android::SupportsBinder<ReceiverInterface>:
    base::android::BinderStatusOr<void> OnBinderTransaction(
        transaction_code_t code,
        const base::android::ParcelReader& in,
        const base::android::ParcelWriter& out) override;
    void OnBinderDestroyed() override;

   private:
    ~Receiver() override;

    base::Lock lock_;
    scoped_refptr<ChannelBinder> channel_ GUARDED_BY(lock_);
  };

  ~ChannelBinder() override;

  // Channel:
  void Start() override;
  void ShutDownImpl() override;
  void Write(MessagePtr message) override;
  void LeakHandle() override;
  bool GetReadPlatformHandles(const void* payload,
                              size_t payload_size,
                              size_t num_handles,
                              const void* extra_header,
                              size_t extra_header_size,
                              std::vector<PlatformHandle>* handles,
                              bool* deferred) override;
  bool GetReadPlatformHandlesForIpcz(
      size_t num_handles,
      std::vector<PlatformHandle>& handles) override;

  base::android::BinderStatusOr<void> WriteOrEnqueue(MessagePtr message);
  base::android::BinderStatusOr<void> FlushOutgoingMessages()
      EXCLUSIVE_LOCKS_REQUIRED(lock_);

  void SetPeerReceiver(base::android::BinderRef receiver);
  void Receive(base::span<const uint8_t> bytes,
               std::vector<PlatformHandle> handles);
  void OnDisconnect();

  static base::android::BinderStatusOr<void> SendMessageToReceiver(
      Receiver::Proxy& receiver,
      MessagePtr message);

  // Peer state begins as PendingExchange at ChannelBinder construction time.
  //
  // When Start() is called, a binder exchange is initiated and we enter the
  // PendingConnection state while awaiting a SetPeerReceiver() callback from
  // the exchange.
  //
  // Once we have the peer binder we adopt it as a Receiver::Proxy, and this
  // is retained by `peer_` indefinitely or until disconnection.
  //
  // At any point after Start(), if our own Receiver becomes disconnected (i.e.
  // its binder ref count drops to zero), `peer_` enters a permanent
  // Disconnected state.
  struct PendingExchange {
    base::android::BinderRef binder;
  };
  struct PendingConnection {};
  enum class Disconnected {};
  using Peer = absl::variant<PendingExchange,
                             PendingConnection,
                             Receiver::Proxy,
                             Disconnected>;

  const scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;

  base::Lock lock_;

  // Indicates whether LeakHandle() was called by the Channel owner, requiring
  // us to avoid clean destruction of our peer binder once connected.
  bool leak_peer_ GUARDED_BY(lock_) = false;

  // Indicates that writes are no longer accepted on the Channel and that all
  // subsequent outgoing messages will be dropped. This is set permanently once
  // any write fails.
  bool reject_writes_ GUARDED_BY(lock_) = false;

  // The object receiving incoming parcels from our remote peer. Ownership of
  // this object is shared by this ChannelBinder and the peer ChannelBinder (via
  // a binder ref.)
  scoped_refptr<Receiver> receiver_ GUARDED_BY(lock_);

  // The state of our connection to the peer ChannelBinder. In a steady
  // connected state this is the Receiver::Proxy we use to transmit messages to
  // the peer.
  Peer peer_ GUARDED_BY(lock_);

  // A queue of outgoing messages which can accumulate either before connection
  // or while another thread is already actively writing or flushing messages
  // across the channel.
  base::circular_deque<MessagePtr> outgoing_messages_ GUARDED_BY(lock_);

  // Indicates whether a thread is currently writing or flushing messages across
  // the channel. Only one thread may do this at a time.
  bool is_writing_ GUARDED_BY(lock_) = false;
};

}  // namespace mojo::core

#endif  // MOJO_CORE_CHANNEL_BINDER_H_