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

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "mojo/core/channel.h"

#include <stddef.h>
#include <string.h>

#include <algorithm>
#include <cstdint>
#include <limits>
#include <utility>

#include "base/check_op.h"
#include "base/compiler_specific.h"
#include "base/containers/span.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/histogram_macros.h"
#include "base/numerics/safe_math.h"
#include "base/process/current_process.h"
#include "base/process/process_handle.h"
#include "base/rand_util.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/time.h"
#include "base/trace_event/typed_macros.h"
#include "build/build_config.h"
#include "mojo/core/configuration.h"
#include "mojo/core/embedder/features.h"
#include "mojo/core/ipcz_driver/envelope.h"

#if BUILDFLAG(MOJO_USE_APPLE_CHANNEL)
#include "base/apple/mach_logging.h"
#elif BUILDFLAG(IS_WIN)
#include "base/win/win_util.h"
#endif

namespace mojo::core {

namespace {

std::atomic_bool g_use_trivial_messages{false};

// TODO(crbug.com/40824727): Consider asking the memory allocator for a suitable
// size.
constexpr int kGrowthFactor = 2;

static_assert(
    IsAlignedForChannelMessage(sizeof(Channel::Message::LegacyHeader)),
    "Invalid LegacyHeader size.");

static_assert(IsAlignedForChannelMessage(sizeof(Channel::Message::Header)),
              "Invalid Header size.");

static_assert(sizeof(Channel::Message::LegacyHeader) == 8,
              "LegacyHeader must be 8 bytes on ChromeOS and Android");

static_assert(offsetof(Channel::Message::LegacyHeader, num_bytes) ==
                  offsetof(Channel::Message::Header, num_bytes),
              "num_bytes should be at the same offset in both Header structs.");
static_assert(offsetof(Channel::Message::LegacyHeader, message_type) ==
                  offsetof(Channel::Message::Header, message_type),
              "message_type should be at the same offset in both Header "
              "structs.");

const size_t kReadBufferSize = 4096;
const size_t kMaxUnusedReadBufferCapacity = 4096;

#if BUILDFLAG(IS_FUCHSIA)
// Fuchsia: The zx_channel_write() API supports up to 64 handles.
const size_t kMaxAttachedHandles = 64;
#else
// Linux: The platform imposes a limit of 253 handles per sendmsg().
const size_t kMaxAttachedHandles = 253;
#endif  // BUILDFLAG(IS_FUCHSIA)

static_assert(alignof(std::max_align_t) >= kChannelMessageAlignment, "");
Channel::AlignedBuffer MakeAlignedBuffer(size_t size) {
  // Generic allocators (such as malloc) return a pointer that is suitably
  // aligned for storing any type of object with a fundamental alignment
  // requirement. Buffers have no additional alignment requirement beyond that.
  auto buffer = Channel::AlignedBuffer::Uninit(size);

  // Even though the allocator is configured in such a way that it crashes
  // rather than return nullptr, ASAN and friends don't know about that. This
  // CHECK() prevents Clusterfuzz from complaining. crbug.com/1180576.
  CHECK(buffer.data());
  return buffer;
}

// The type of message always used by a Channel which backs an ipcz transport.
// Most of the inherited Message interface is unused since it's only called by
// the original Mojo Core implementation.
struct IpczMessage final : public Channel::Message {
  IpczMessage(base::span<const uint8_t> data,
              std::vector<PlatformHandle> handles,
              Channel::Message::MessageType message_type,
              uint32_t channel_sequence_number) {
    size_ = sizeof(IpczHeader) + data.size();
    data_ = Channel::AlignedBuffer::Uninit(size_);

    IpczHeader& header = *reinterpret_cast<IpczHeader*>(data_.data());
    header.size = sizeof(IpczHeader);

    DCHECK_LE(handles.size(), std::numeric_limits<uint16_t>::max());
    DCHECK_LE(size_, std::numeric_limits<uint32_t>::max());
    header.num_handles = static_cast<uint16_t>(handles.size());
    header.num_bytes = static_cast<uint32_t>(size_);
    header.v2.creation_timeticks_us =
        (base::TimeTicks::Now() - base::TimeTicks()).InMicroseconds();
    if (Channel::SupportsMultipleNotifiers()) {
      Message::SetType(header, message_type);
      Message::SetChannelSequenceNumber(header, channel_sequence_number);
    }
    data_.subspan(sizeof(IpczHeader)).copy_prefix_from(base::as_chars(data));

    handles_.reserve(handles.size());
    for (PlatformHandle& handle : handles) {
      handles_.emplace_back(std::move(handle));
    }
  }
  ~IpczMessage() override = default;

  // Channel::Message impl:
  base::span<const char> data_span() const override { return data_; }
  base::span<char> mutable_data_span() override { NOTREACHED(); }
  size_t capacity() const override { return size_; }

  // Overridden because ChannelLinux::Write() needs to fallback to ChannelPosix
  // when a handle is present.
  bool has_handles() const override { return !handles_.empty(); }

  bool is_legacy_message() const override { NOTREACHED(); }

  Header* header() override { NOTREACHED(); }
  const Header* header() const override { NOTREACHED(); }

  void SetHandles(std::vector<PlatformHandle>) override { NOTREACHED(); }
  void SetHandles(std::vector<PlatformHandleInTransit>) override {
    NOTREACHED();
  }
  std::vector<PlatformHandleInTransit> TakeHandles() override {
    return std::move(handles_);
  }

  bool ExtendPayload(size_t) override { NOTREACHED(); }

 private:
  Channel::AlignedBuffer data_;
  std::vector<PlatformHandleInTransit> handles_;
};

// A complex message can be large or contain file handles. Not used with ipcz.
struct ComplexMessage final : public Channel::Message {
  ComplexMessage() = default;
  ComplexMessage(size_t capacity,
                 size_t max_handles,
                 size_t payload_size,
                 MessageType message_type);

  ComplexMessage(const ComplexMessage&) = delete;
  ComplexMessage& operator=(const ComplexMessage&) = delete;

  ~ComplexMessage() override = default;

  // Message impl:
  void SetHandles(std::vector<PlatformHandle> new_handles) override;
  void SetHandles(std::vector<PlatformHandleInTransit> new_handles) override;
  std::vector<PlatformHandleInTransit> TakeHandles() override;

  base::span<const char> data_span() const override { return data_; }
  base::span<char> mutable_data_span() override { return data_.as_span(); }
  size_t capacity() const override;

  bool ExtendPayload(size_t new_payload_size) override;

 private:
  friend struct Channel::Message;

  // The message data buffer.
  Channel::AlignedBuffer data_;

  // The capacity of the buffer at |data_|.
  size_t capacity_ = 0;

  // Maximum number of handles which may be attached to this message.
  size_t max_handles_ = 0;

  std::vector<PlatformHandleInTransit> handle_vector_;

#if BUILDFLAG(IS_WIN)
  // On Windows, handles are serialised into the extra header section.
  raw_ptr<HandleEntry, AllowPtrArithmetic> handles_ = nullptr;
#elif BUILDFLAG(MOJO_USE_APPLE_CHANNEL)
  // On OSX, handles are serialised into the extra header section.
  raw_ptr<MachPortsExtraHeader, AllowPtrArithmetic> mach_ports_header_ =
      nullptr;
#endif
};

// A Message with fixed capacity for payload and no support for carrying
// handles. Allocated instead of IpczMessage for small messages to reduce the
// number of memory allocations.
struct TrivialMessage final : public Channel::Message {
  TrivialMessage(const TrivialMessage&) = delete;
  TrivialMessage& operator=(const TrivialMessage&) = delete;

  ~TrivialMessage() override = default;

  // TryConstruct should be used to build a TrivialMessage. Returns nullptr
  // if |data| is too large to fit.
  static Channel::MessagePtr TryConstruct(
      base::span<const uint8_t> data,
      Channel::Message::MessageType message_type,
      uint32_t message_count);

  // Channel::Message impl:
  base::span<const char> data_span() const override {
    return base::as_chars(base::span(trivial_data_));
  }
  base::span<char> mutable_data_span() override {
    return base::as_writable_chars(base::span(trivial_data_));
  }
  size_t capacity() const override;

  // Overridden because ChannelLinux::Write() needs to fallback to ChannelPosix
  // when a handle is present.
  bool has_handles() const override { return false; }

  bool is_legacy_message() const override { NOTREACHED(); }

  // Not supported by this class. Not used with ipcz. Implemented as NOTREACHED
  // to match IpczMessage.
  void SetHandles(std::vector<PlatformHandle> new_handles) override {
    NOTREACHED();
  }
  void SetHandles(std::vector<PlatformHandleInTransit> new_handles) override {
    NOTREACHED();
  }
  std::vector<PlatformHandleInTransit> TakeHandles() override;

  // The choice of 248 as message size is to allow using the fullness of size
  // class in PartitionAlloc (256) minus the space that can be reserved for
  // MiraclePtr (8).
  static constexpr size_t kIntendedMessageSize = 248;

  // ExtendPayload is not used with ipcz. NOTREACHED allows not worrying about
  // zero-fill for the hypothetical extended part.
  bool ExtendPayload(size_t new_payload_size) override { NOTREACHED(); }

 private:
  TrivialMessage() = default;

  alignas(sizeof(void*)) uint8_t
      trivial_data_[kIntendedMessageSize - sizeof(Channel::Message)];

  static constexpr size_t kInternalCapacity = sizeof(trivial_data_);
};

static_assert(sizeof(TrivialMessage) == TrivialMessage::kIntendedMessageSize,
              "The TrivialMessage is of wrong size");

bool ShouldRecordSubsampledHistograms() {
  return base::ShouldRecordSubsampledMetric(0.001);
}

}  // namespace

#if BUILDFLAG(IS_ANDROID) || \
    (BUILDFLAG(IS_LINUX) && defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION))

namespace {

bool IsMessageTypeForUpgrade(Channel::Message::MessageType message_type) {
  using MessageType = Channel::Message::MessageType;
  switch (message_type) {
    case MessageType::UPGRADE_OFFER:
      [[fallthrough]];
    case MessageType::UPGRADE_ACCEPT:
      [[fallthrough]];
    case MessageType::UPGRADE_REJECT:
      return true;
    default:
      return false;
  }
}

}  // namespace

// static
bool Channel::SupportsMultipleNotifiers() {
  return true;
}

// static
bool Channel::Message::IsExperimentalV3(const IpczHeader& header) {
  if (!Channel::SupportsMultipleNotifiers()) {
    // Platforms without support for message sequencing pretend they do
    // not see the experimental_v3 part of the header.
    return false;
  }
  return header.size >=
         offsetof(IpczHeader, experimental_v3) + sizeof(header.experimental_v3);
}

// static
uint32_t Channel::Message::ExtractChannelSequenceNumber(
    const IpczHeader& header) {
  DCHECK(Channel::SupportsMultipleNotifiers());
  return header.experimental_v3.channel_sequence_number;
}

// static
void Channel::Message::SetChannelSequenceNumber(
    IpczHeader& header,
    uint32_t channel_sequence_number) {
  DCHECK(IsExperimentalV3(header));
  header.experimental_v3.channel_sequence_number = channel_sequence_number;
}

// static
Channel::Message::MessageType Channel::Message::ExtractType(
    const IpczHeader& header) {
  DCHECK(IsExperimentalV3(header));
  if (IsMessageTypeForUpgrade(header.experimental_v3.message_type)) {
    return header.experimental_v3.message_type;
  }
  // Clamp all other values to NORMAL to avoid later confusions from potentially
  // untrusted data.
  return Channel::Message::MessageType::NORMAL;
}

// static
void Channel::Message::SetType(IpczHeader& header, MessageType message_type) {
  header.experimental_v3.message_type = message_type;
}

#else   // BUILDFLAG(IS_ANDROID) || \
        //    (BUILDFLAG(IS_LINUX) && \
        //     defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION))

// static
bool Channel::SupportsMultipleNotifiers() {
  return false;
}

// static
bool Channel::Message::IsExperimentalV3(const IpczHeader& header) {
  return false;
}

// static
uint32_t Channel::Message::ExtractChannelSequenceNumber(
    const IpczHeader& header) {
  NOTREACHED();
}

// static
void Channel::Message::SetChannelSequenceNumber(IpczHeader& header,
                                                uint32_t message_count) {
  // No-op.
}

// static
Channel::Message::MessageType Channel::Message::ExtractType(
    const IpczHeader& header) {
  NOTREACHED();
}

// static
void Channel::Message::SetType(IpczHeader& header, MessageType message_type) {
  // No-op.
}
#endif  // BUILDFLAG(IS_ANDROID) || \
        //    (BUILDFLAG(IS_LINUX) && \
        //     defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION))

// static
bool Channel::Message::IsExperimentalControlMessage(const IpczHeader& header) {
  if (!IsExperimentalV3(header)) {
    return false;
  }
  return (ExtractType(header) != MessageType::NORMAL);
}

// static
Channel::MessagePtr Channel::Message::CreateMessage(size_t payload_size,
                                                    size_t max_handles) {
  return CreateMessage(payload_size, payload_size, max_handles);
}

// static
Channel::MessagePtr Channel::Message::CreateMessage(size_t payload_size,
                                                    size_t max_handles,
                                                    MessageType message_type) {
  return CreateMessage(payload_size, payload_size, max_handles, message_type);
}

// static
Channel::MessagePtr Channel::Message::CreateMessage(size_t capacity,
                                                    size_t payload_size,
                                                    size_t max_handles) {
#if defined(MOJO_CORE_LEGACY_PROTOCOL)
  return CreateMessage(capacity, payload_size, max_handles,
                       Message::MessageType::NORMAL_LEGACY);
#else
  return CreateMessage(capacity, payload_size, max_handles,
                       Message::MessageType::NORMAL);
#endif
}

// static
Channel::MessagePtr Channel::Message::CreateMessage(size_t capacity,
                                                    size_t payload_size,
                                                    size_t max_handles,
                                                    MessageType message_type) {
  return base::WrapUnique<Channel::Message>(
      new ComplexMessage(capacity, payload_size, max_handles, message_type));
}

// static
Channel::MessagePtr Channel::Message::CreateIpczMessage(
    base::span<const uint8_t> data,
    std::vector<PlatformHandle> handles,
    Channel::Message::MessageType message_type,
    uint32_t channel_sequence_number) {
  if (g_use_trivial_messages && handles.size() == 0) {
    auto msg = TrivialMessage::TryConstruct(data, message_type,
                                            channel_sequence_number);
    if (msg) {
      return msg;
    }
  }
  return std::make_unique<IpczMessage>(data, std::move(handles), message_type,
                                       channel_sequence_number);
}

// static
void Channel::set_use_trivial_messages(bool use_trivial_messages) {
  g_use_trivial_messages = use_trivial_messages;
}

// static
Channel::MessagePtr Channel::Message::CreateRawForFuzzing(
    base::span<const unsigned char> data) {
  auto message = std::make_unique<ComplexMessage>();
  message->size_ = data.size();
  if (data.size()) {
    message->data_ = Channel::AlignedBuffer::CopiedFrom(base::as_chars(data));
  }
  return base::WrapUnique<Channel::Message>(message.release());
}

// static
Channel::MessagePtr Channel::Message::Deserialize(
    const void* data,
    size_t data_num_bytes,
    HandlePolicy handle_policy,
    base::ProcessHandle from_process) {
  if (data_num_bytes < sizeof(LegacyHeader))
    return nullptr;

  const LegacyHeader* legacy_header =
      reinterpret_cast<const LegacyHeader*>(data);
  if (legacy_header->num_bytes != data_num_bytes) {
    DLOG(ERROR) << "Decoding invalid message: " << legacy_header->num_bytes
                << " != " << data_num_bytes;
    return nullptr;
  }

  // If a message isn't explicitly identified as type NORMAL_LEGACY, it is
  // expected to have a full-size header.
  const Header* header = nullptr;
  if (legacy_header->message_type != MessageType::NORMAL_LEGACY)
    header = reinterpret_cast<const Header*>(data);

  uint32_t extra_header_size = 0;
  auto data_span = UNSAFE_TODO(
      base::span<const char>(static_cast<const char*>(data), data_num_bytes));
  base::span<const char> payload_span{};
  if (!header) {
    payload_span = data_span.subspan(sizeof(LegacyHeader),
                                     data_num_bytes - sizeof(LegacyHeader));
  } else {
    if (header->num_bytes < header->num_header_bytes ||
        header->num_header_bytes < sizeof(Header)) {
      DLOG(ERROR) << "Decoding invalid message: " << header->num_bytes << " < "
                  << header->num_header_bytes;
      return nullptr;
    }
    extra_header_size = header->num_header_bytes - sizeof(Header);
    payload_span = data_span.subspan(header->num_header_bytes,
                                     data_num_bytes - header->num_header_bytes);
  }

  if (!IsAlignedForChannelMessage(extra_header_size)) {
    // Well-formed messages always have any extra header bytes aligned to a
    // |kChannelMessageAlignment| boundary.
    DLOG(ERROR) << "Invalid extra header size";
    return nullptr;
  }

#if BUILDFLAG(IS_WIN)
  uint32_t max_handles = extra_header_size / sizeof(HandleEntry);
#elif BUILDFLAG(IS_FUCHSIA)
  uint32_t max_handles = extra_header_size / sizeof(HandleInfoEntry);
#elif BUILDFLAG(MOJO_USE_APPLE_CHANNEL)
  if (extra_header_size > 0 &&
      extra_header_size < sizeof(MachPortsExtraHeader)) {
    DLOG(ERROR) << "Decoding invalid message: " << extra_header_size << " < "
                << sizeof(MachPortsExtraHeader);
    return nullptr;
  }
  uint32_t max_handles =
      extra_header_size == 0
          ? 0
          : (extra_header_size - sizeof(MachPortsExtraHeader)) /
                sizeof(MachPortsEntry);
#else
  const uint32_t max_handles = 0;
  // No extra header expected. Fail if this is detected.
  if (extra_header_size > 0) {
    DLOG(ERROR) << "Decoding invalid message: unexpected extra_header_size > 0";
    return nullptr;
  }
#endif  // BUILDFLAG(IS_WIN)

  const uint16_t num_handles =
      header ? header->num_handles : legacy_header->num_handles;
  if (num_handles > max_handles || max_handles > kMaxAttachedHandles) {
    DLOG(ERROR) << "Decoding invalid message: " << num_handles << " > "
                << max_handles;
    return nullptr;
  }

  if (num_handles > 0 && handle_policy == HandlePolicy::kRejectHandles) {
    DLOG(ERROR) << "Rejecting message with unexpected handle attachments.";
    return nullptr;
  }

  MessagePtr message = CreateMessage(payload_span.size(), max_handles,
                                     legacy_header->message_type);
  DCHECK_EQ(message->data_num_bytes(), data_num_bytes);

  // Copy all payload bytes.
  if (!payload_span.empty()) {
    message->mutable_payload_span().copy_prefix_from(payload_span);
  }

  if (header) {
    DCHECK_EQ(message->extra_header_size(), extra_header_size);
    DCHECK_EQ(message->header()->num_header_bytes, header->num_header_bytes);

    if (message->extra_header_size()) {
      // Copy extra header bytes.
      message->mutable_extra_header_span().copy_prefix_from(
          data_span.subspan(sizeof(Header), message->extra_header_size()));
    }
    message->header()->num_handles = header->num_handles;
  } else {
    message->legacy_header()->num_handles = legacy_header->num_handles;
  }

#if BUILDFLAG(IS_WIN)
  std::vector<PlatformHandleInTransit> handles(num_handles);
  for (size_t i = 0; i < num_handles; i++) {
    HANDLE handle = base::win::Uint32ToHandle(
        static_cast<ComplexMessage*>(message.get())->handles_[i].handle);
    if (PlatformHandleInTransit::IsPseudoHandle(handle))
      return nullptr;
    if (from_process == base::kNullProcessHandle) {
      handles[i] = PlatformHandleInTransit(
          PlatformHandle(base::win::ScopedHandle(handle)));
    } else {
      handles[i] = PlatformHandleInTransit(
          PlatformHandleInTransit::TakeIncomingRemoteHandle(handle,
                                                            from_process));
    }
  }
  message->SetHandles(std::move(handles));
#endif

  return message;
}

// static
void Channel::Message::ExtendPayload(MessagePtr& message,
                                     size_t new_payload_size) {
  if (message->ExtendPayload(new_payload_size)) {
    return;
  }

  // ComplexMessage will never fail to extend the payload; therefore, if we do
  // fail it's because the message is a TrivialMessage which has run out of
  // space. In which case we will upgrade the message type to a ComplexMessage.
  size_t capacity_without_header = message->capacity();
  auto m = base::WrapUnique<Channel::Message>(
      new ComplexMessage(new_payload_size, new_payload_size, 0,
                         message->legacy_header()->message_type));
  m->mutable_payload_span().copy_prefix_from(
      message->payload_span().first(capacity_without_header));
  message.swap(m);
}

const void* Channel::Message::extra_header() const {
  DCHECK(!is_legacy_message());
  return data_span().subspan(sizeof(Header)).data();
}

void* Channel::Message::mutable_extra_header() {
  return mutable_extra_header_span().data();
}

base::span<char> Channel::Message::mutable_extra_header_span() {
  DCHECK(!is_legacy_message());
  return mutable_data_span().subspan(sizeof(Header));
}

size_t Channel::Message::extra_header_size() const {
  return header()->num_header_bytes - sizeof(Header);
}

void* Channel::Message::mutable_payload() {
  return mutable_payload_span().data();
}

base::span<char> Channel::Message::mutable_payload_span() {
  if (is_legacy_message()) {
    return mutable_data_span().subspan(sizeof(LegacyHeader));
  }
  return mutable_data_span().subspan(header()->num_header_bytes);
}

const void* Channel::Message::payload() const {
  return payload_span().data();
}

base::span<const char> Channel::Message::payload_span() const {
  if (is_legacy_message()) {
    return data_span().subspan(sizeof(LegacyHeader));
  }
  return data_span().subspan(header()->num_header_bytes);
}

size_t Channel::Message::payload_size() const {
  if (is_legacy_message())
    return legacy_header()->num_bytes - sizeof(LegacyHeader);
  return size_ - header()->num_header_bytes;
}

size_t Channel::Message::num_handles() const {
  return is_legacy_message() ? legacy_header()->num_handles
                             : header()->num_handles;
}

bool Channel::Message::has_handles() const {
  return (is_legacy_message() ? legacy_header()->num_handles
                              : header()->num_handles) > 0;
}

bool Channel::Message::is_legacy_message() const {
  return legacy_header()->message_type == MessageType::NORMAL_LEGACY;
}

Channel::Message::LegacyHeader* Channel::Message::legacy_header() {
  return reinterpret_cast<LegacyHeader*>(mutable_data());
}
const Channel::Message::LegacyHeader* Channel::Message::legacy_header() const {
  return reinterpret_cast<const LegacyHeader*>(data());
}

Channel::Message::Header* Channel::Message::header() {
  DCHECK(!is_legacy_message());
  return reinterpret_cast<Header*>(mutable_data());
}
const Channel::Message::Header* Channel::Message::header() const {
  DCHECK(!is_legacy_message());
  return reinterpret_cast<const Header*>(data());
}

ComplexMessage::ComplexMessage(size_t capacity,
                               size_t payload_size,
                               size_t max_handles,
                               MessageType message_type)
    : max_handles_(max_handles) {
  DCHECK_GE(capacity, payload_size);
  DCHECK_LE(max_handles_, kMaxAttachedHandles);

  const bool is_legacy_message = (message_type == MessageType::NORMAL_LEGACY);
  size_t extra_header_size = 0;
#if BUILDFLAG(IS_WIN)
  // On Windows we serialize HANDLEs into the extra header space.
  extra_header_size = max_handles_ * sizeof(HandleEntry);
#elif BUILDFLAG(IS_FUCHSIA)
  // On Fuchsia we serialize handle types into the extra header space.
  extra_header_size = max_handles_ * sizeof(HandleInfoEntry);
#elif BUILDFLAG(MOJO_USE_APPLE_CHANNEL)
  // On OSX, some of the platform handles may be mach ports, which are
  // serialised into the message buffer. Since there could be a mix of fds and
  // mach ports, we store the mach ports as an <index, port> pair (of uint32_t),
  // so that the original ordering of handles can be re-created.
  if (max_handles) {
    extra_header_size =
        sizeof(MachPortsExtraHeader) + (max_handles * sizeof(MachPortsEntry));
  }
#endif
  // Pad extra header data to be aliged to |kChannelMessageAlignment| bytes.
  if (!IsAlignedForChannelMessage(extra_header_size)) {
    extra_header_size += kChannelMessageAlignment -
                         (extra_header_size % kChannelMessageAlignment);
  }
  DCHECK(IsAlignedForChannelMessage(extra_header_size));
  const size_t header_size =
      is_legacy_message ? sizeof(LegacyHeader) : sizeof(Header);
  DCHECK(extra_header_size == 0 || !is_legacy_message);

  capacity_ = header_size + extra_header_size + capacity;
  size_ = header_size + extra_header_size + payload_size;
  data_ = MakeAlignedBuffer(capacity_);
  // Only zero out the header and not the payload. Since the payload is going to
  // be memcpy'd, zeroing the payload is unnecessary work and a significant
  // performance issue when dealing with large messages. Any sanitizer errors
  // complaining about an uninitialized read in the payload area should be
  // treated as an error and fixed.
  std::ranges::fill(mutable_data_span().first(header_size + extra_header_size),
                    0);

  DCHECK(base::IsValueInRangeForNumericType<uint32_t>(size_));
  legacy_header()->num_bytes = static_cast<uint32_t>(size_);

  DCHECK(base::IsValueInRangeForNumericType<uint16_t>(header_size +
                                                      extra_header_size));
  legacy_header()->message_type = message_type;

  if (is_legacy_message) {
    legacy_header()->num_handles = static_cast<uint16_t>(max_handles);
  } else {
    header()->num_header_bytes =
        static_cast<uint16_t>(header_size + extra_header_size);
  }

  if (max_handles_ > 0) {
#if BUILDFLAG(IS_WIN)
    handles_ = reinterpret_cast<HandleEntry*>(mutable_extra_header());
    // Initialize all handles to invalid values.
    for (size_t i = 0; i < max_handles_; ++i)
      handles_[i].handle = base::win::HandleToUint32(INVALID_HANDLE_VALUE);
#elif BUILDFLAG(MOJO_USE_APPLE_CHANNEL)
    mach_ports_header_ =
        reinterpret_cast<MachPortsExtraHeader*>(mutable_extra_header());
    mach_ports_header_->num_ports = 0;
    // Initialize all handles to invalid values.
    for (size_t i = 0; i < max_handles_; ++i) {
      mach_ports_header_->entries[i] = {0};
    }
#endif
  }
}

size_t ComplexMessage::capacity() const {
  if (is_legacy_message())
    return capacity_ - sizeof(LegacyHeader);
  return capacity_ - header()->num_header_bytes;
}

bool ComplexMessage::ExtendPayload(size_t new_payload_size) {
  size_t capacity_without_header = capacity();
  size_t header_size = capacity_ - capacity_without_header;
  if (new_payload_size > capacity_without_header) {
    size_t new_capacity =
        std::max(static_cast<size_t>(capacity_without_header * kGrowthFactor),
                 new_payload_size) +
        header_size;
    Channel::AlignedBuffer new_data = MakeAlignedBuffer(new_capacity);
    new_data.copy_prefix_from(data_);
    data_ = std::move(new_data);
    capacity_ = new_capacity;

    if (max_handles_ > 0) {
// We also need to update the cached extra header addresses in case the
// payload buffer has been relocated.
#if BUILDFLAG(IS_WIN)
      handles_ = reinterpret_cast<HandleEntry*>(mutable_extra_header());
#elif BUILDFLAG(MOJO_USE_APPLE_CHANNEL)
      mach_ports_header_ =
          reinterpret_cast<MachPortsExtraHeader*>(mutable_extra_header());
#endif
    }
  }
  size_ = header_size + new_payload_size;
  DCHECK(base::IsValueInRangeForNumericType<uint32_t>(size_));
  legacy_header()->num_bytes = static_cast<uint32_t>(size_);

  return true;
}

void ComplexMessage::SetHandles(std::vector<PlatformHandle> new_handles) {
  std::vector<PlatformHandleInTransit> handles;
  handles.reserve(new_handles.size());
  for (auto& h : new_handles) {
    handles.emplace_back(PlatformHandleInTransit(std::move(h)));
  }
  SetHandles(std::move(handles));
}

void ComplexMessage::SetHandles(
    std::vector<PlatformHandleInTransit> new_handles) {
  if (is_legacy_message()) {
    // Old semantics for ChromeOS and Android
    if (legacy_header()->num_handles == 0) {
      CHECK(new_handles.empty());
      return;
    }
    CHECK_EQ(new_handles.size(), legacy_header()->num_handles);
    std::swap(handle_vector_, new_handles);
    return;
  }

  if (max_handles_ == 0) {
    CHECK(new_handles.empty());
    return;
  }

  CHECK_LE(new_handles.size(), max_handles_);
  header()->num_handles = static_cast<uint16_t>(new_handles.size());
  std::swap(handle_vector_, new_handles);
#if BUILDFLAG(IS_WIN)
  memset(handles_, 0, extra_header_size());
  for (size_t i = 0; i < handle_vector_.size(); i++) {
    HANDLE handle = handle_vector_[i].remote_handle();
    if (handle == INVALID_HANDLE_VALUE)
      handle = handle_vector_[i].handle().GetHandle().Get();
    handles_[i].handle = base::win::HandleToUint32(handle);
  }
#endif  // BUILDFLAG(IS_WIN)

#if BUILDFLAG(MOJO_USE_APPLE_CHANNEL)
  if (mach_ports_header_) {
    for (size_t i = 0; i < max_handles_; ++i) {
      mach_ports_header_->entries[i] = {0};
    }
    for (size_t i = 0; i < handle_vector_.size(); i++) {
      mach_ports_header_->entries[i].type =
          static_cast<uint8_t>(handle_vector_[i].handle().type());
    }
    mach_ports_header_->num_ports = handle_vector_.size();
  }
#endif
}

std::vector<PlatformHandleInTransit> ComplexMessage::TakeHandles() {
  return std::move(handle_vector_);
}

// static
Channel::MessagePtr TrivialMessage::TryConstruct(
    base::span<const uint8_t> data,
    Channel::Message::MessageType message_type,
    uint32_t channel_sequence_number) {
  const size_t header_size = sizeof(IpczHeader);

  size_t size = header_size + data.size();
  DCHECK(base::IsValueInRangeForNumericType<uint32_t>(size));
  if (size > kInternalCapacity) {
    return nullptr;
  }

  auto message = base::WrapUnique(new TrivialMessage);
  std::ranges::fill(message->mutable_data_span().first(sizeof(IpczHeader)), 0);

  IpczHeader& header = *reinterpret_cast<IpczHeader*>(message->trivial_data_);
  header.size = sizeof(IpczHeader);
  header.num_handles = 0;
  header.num_bytes = size;
  header.v2.creation_timeticks_us =
      (base::TimeTicks::Now() - base::TimeTicks()).InMicroseconds();
  Message::SetType(header, message_type);
  Message::SetChannelSequenceNumber(header, channel_sequence_number);
  message->size_ = size;
  base::span(message->trivial_data_)
      .subspan(sizeof(IpczHeader))
      .copy_prefix_from(data);
  return message;
}

size_t TrivialMessage::capacity() const {
  return kInternalCapacity - sizeof(IpczHeader);
}

std::vector<PlatformHandleInTransit> TrivialMessage::TakeHandles() {
  return std::vector<PlatformHandleInTransit>();
}

// Helper class for managing a Channel's read buffer allocations. This maintains
// a single contiguous buffer with the layout:
//
//   [discarded bytes][occupied bytes][unoccupied bytes]
//
// The Reserve() method ensures that a certain capacity of unoccupied bytes are
// available. It does not claim that capacity and only allocates new capacity
// when strictly necessary.
//
// Claim() marks unoccupied bytes as occupied.
//
// Discard() marks occupied bytes as discarded, signifying that their contents
// can be forgotten or overwritten.
//
// Realign() moves occupied bytes to the front of the buffer so that those
// occupied bytes are properly aligned.
//
// The most common Channel behavior in practice should result in very few
// allocations and copies, as memory is claimed and discarded shortly after
// being reserved, and future reservations will immediately reuse discarded
// memory.
class Channel::ReadBuffer {
 public:
  ReadBuffer() {
    size_ = kReadBufferSize;
    data_ = MakeAlignedBuffer(size_);
  }

  ReadBuffer(const ReadBuffer&) = delete;
  ReadBuffer& operator=(const ReadBuffer&) = delete;

  ~ReadBuffer() { DCHECK(data_.data()); }

  const char* occupied_bytes() const {
    return data_.subspan(num_discarded_bytes_).data();
  }

  size_t num_occupied_bytes() const {
    return num_occupied_bytes_ - num_discarded_bytes_;
  }

  // Ensures the ReadBuffer has enough contiguous space allocated to hold
  // |num_bytes| more bytes; returns the address of the first available byte.
  char* Reserve(size_t num_bytes) {
    if (num_occupied_bytes_ + num_bytes > size_) {
      size_ = std::max(static_cast<size_t>(size_ * kGrowthFactor),
                       num_occupied_bytes_ + num_bytes);
      AlignedBuffer new_data = MakeAlignedBuffer(size_);
      new_data.copy_prefix_from(data_.first(num_occupied_bytes_));
      data_ = std::move(new_data);
    }

    return data_.subspan(num_occupied_bytes_).data();
  }

  // Marks the first |num_bytes| unoccupied bytes as occupied.
  void Claim(size_t num_bytes) {
    DCHECK_LE(num_occupied_bytes_ + num_bytes, size_);
    num_occupied_bytes_ += num_bytes;
  }

  // Marks the first |num_bytes| occupied bytes as discarded. This may result in
  // shrinkage of the internal buffer, and it is not safe to assume the result
  // of a previous Reserve() call is still valid after this.
  void Discard(size_t num_bytes) {
    DCHECK_LE(num_discarded_bytes_ + num_bytes, num_occupied_bytes_);
    num_discarded_bytes_ += num_bytes;

    if (num_discarded_bytes_ == num_occupied_bytes_) {
      // We can just reuse the buffer from the beginning in this common case.
      num_discarded_bytes_ = 0;
      num_occupied_bytes_ = 0;
    }

    if (num_discarded_bytes_ > kMaxUnusedReadBufferCapacity) {
      // In the uncommon case that we have a lot of discarded data at the
      // front of the buffer, simply move remaining data to a smaller buffer.
      size_t num_preserved_bytes = num_occupied_bytes_ - num_discarded_bytes_;
      size_ = std::max(num_preserved_bytes, kReadBufferSize);
      AlignedBuffer new_data = MakeAlignedBuffer(size_);
      new_data.copy_prefix_from(
          data_.subspan(num_discarded_bytes_, num_preserved_bytes));
      data_ = std::move(new_data);
      num_discarded_bytes_ = 0;
      num_occupied_bytes_ = num_preserved_bytes;
    }

    if (num_occupied_bytes_ == 0 && size_ > kMaxUnusedReadBufferCapacity) {
      // Opportunistically shrink the read buffer back down to a small size if
      // it's grown very large. We only do this if there are no remaining
      // unconsumed bytes in the buffer to avoid copies in most the common
      // cases.
      size_ = kMaxUnusedReadBufferCapacity;
      data_ = MakeAlignedBuffer(size_);
    }
  }

  void Realign() {
    size_t num_bytes = num_occupied_bytes();
    auto new_data = MakeAlignedBuffer(data_.size());
    new_data.copy_prefix_from(data_.subspan(num_discarded_bytes_, num_bytes));
    data_ = std::move(new_data);
    num_discarded_bytes_ = 0;
    num_occupied_bytes_ = num_bytes;
  }

 private:
  AlignedBuffer data_;

  // The total size of the allocated buffer.
  size_t size_ = 0;

  // The number of discarded bytes at the beginning of the allocated buffer.
  size_t num_discarded_bytes_ = 0;

  // The total number of occupied bytes, including discarded bytes.
  size_t num_occupied_bytes_ = 0;
};

bool Channel::Delegate::IsIpczTransport() const {
  return false;
}

void Channel::Delegate::OnChannelDestroyed() {}

Channel::Channel(Delegate* delegate,
                 HandlePolicy handle_policy,
                 DispatchBufferPolicy buffer_policy)
    : is_for_ipcz_(delegate && delegate->IsIpczTransport()),
      delegate_(delegate),
      handle_policy_(handle_policy),
      read_buffer_(buffer_policy == DispatchBufferPolicy::kManaged
                       ? new ReadBuffer
                       : nullptr) {}

Channel::~Channel() {
  if (is_for_ipcz()) {
    DCHECK(delegate_);
    delegate_->OnChannelDestroyed();
  }
}

// static
scoped_refptr<Channel> Channel::CreateForIpczDriver(
    Delegate* delegate,
    PlatformChannelEndpoint endpoint,
    scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) {
  return Create(delegate, ConnectionParams{std::move(endpoint)},
                HandlePolicy::kAcceptHandles, std::move(io_task_runner));
}

void Channel::ShutDown() {
  ShutDownImpl();
  if (!is_for_ipcz()) {
    // When Channel is used for an ipcz transport, we leave `delegate_` intact
    // so the Channel can notify once it's finally being destroyed.
    delegate_ = nullptr;
  }
}

void Channel::WriteNextIpczMessage(
    base::span<const uint8_t> data,
    std::vector<PlatformHandle> platform_handles) {
  Write(Message::CreateIpczMessage(data, std::move(platform_handles),
                                   Message::MessageType::NORMAL,
                                   IncrementLastSentChannelSequenceNumber()));
}

char* Channel::GetReadBuffer(size_t* buffer_capacity) {
  DCHECK(read_buffer_);
  size_t required_capacity = *buffer_capacity;
  if (!required_capacity)
    required_capacity = kReadBufferSize;

  *buffer_capacity = required_capacity;
  return read_buffer_->Reserve(required_capacity);
}

bool Channel::OnReadComplete(size_t bytes_read, size_t* next_read_size_hint) {
  DCHECK(read_buffer_);
  *next_read_size_hint = kReadBufferSize;
  read_buffer_->Claim(bytes_read);

  const size_t header_size = is_for_ipcz_ ? sizeof(Message::IpczHeader)
                                          : sizeof(Message::LegacyHeader);
  while (read_buffer_->num_occupied_bytes() >= header_size) {
    // Ensure the occupied data is properly aligned. If it isn't, a SIGBUS could
    // happen on architectures that don't allow misaligned words access (i.e.
    // anything other than x86). Only re-align when necessary to avoid copies.
    if (!IsAlignedForChannelMessage(
            reinterpret_cast<uintptr_t>(read_buffer_->occupied_bytes()))) {
      read_buffer_->Realign();
    }

    DispatchResult result =
        TryDispatchMessage(base::span(read_buffer_->occupied_bytes(),
                                      read_buffer_->num_occupied_bytes()),
                           next_read_size_hint);
    if (result == DispatchResult::kOK) {
      if (ShouldRecordSubsampledHistograms()) {
        RecordReceivedMessageProcessType();
      }
      read_buffer_->Discard(*next_read_size_hint);
      *next_read_size_hint = 0;

      if (!DispatchDelayedMessages()) {
        return false;
      }
    } else if (result == DispatchResult::kNotEnoughData) {
      return true;
    } else if (result == DispatchResult::kMissingHandles) {
      break;
    } else if (result == DispatchResult::kError) {
      return false;
    }
  }
  return true;
}

Channel::DispatchResult Channel::TryDispatchMessage(
    base::span<const char> buffer,
    size_t* size_hint) {
  return TryDispatchMessage(buffer, std::nullopt, nullptr, size_hint);
}

Channel::DispatchResult Channel::TryDispatchMessage(
    base::span<const char> buffer,
    std::optional<std::vector<PlatformHandle>> received_handles,
    scoped_refptr<ipcz_driver::Envelope> envelope,
    size_t* size_hint) {
  TRACE_EVENT(TRACE_DISABLED_BY_DEFAULT("toplevel.ipc"),
              "Mojo dispatch message");
  if (is_for_ipcz_) {
    // This has already been validated.
    DCHECK_GE(buffer.size(), Message::kMinIpczHeaderSize);

    const auto& header =
        *reinterpret_cast<const Message::IpczHeader*>(buffer.data());
    const size_t header_size = header.size;
    const size_t num_bytes = header.num_bytes;
    const size_t num_handles = header.num_handles;
    if (header_size < Message::kMinIpczHeaderSize || num_bytes < header_size) {
      return DispatchResult::kError;
    }

    if (buffer.size() < num_bytes) {
      *size_hint = num_bytes - buffer.size();
      return DispatchResult::kNotEnoughData;
    }

    std::vector<PlatformHandle> handles;
    if (num_handles > 0) {
      if (handle_policy_ == HandlePolicy::kRejectHandles) {
        return DispatchResult::kError;
      }

      if (received_handles) {
        handles = std::move(*received_handles);
      } else if (!GetReadPlatformHandlesForIpcz(num_handles, handles)) {
        return DispatchResult::kError;
      }

      if (handles.size() < num_handles) {
        return DispatchResult::kMissingHandles;
      }
    }

    if (ShouldRecordSubsampledHistograms() && Message::IsAtLeastV2(header)) {
      base::TimeTicks creation_time =
          base::TimeTicks() +
          base::Microseconds(header.v2.creation_timeticks_us);
      UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
          "Mojo.Channel.WriteToReadLatencyUs",
          base::TimeTicks::Now() - creation_time, base::Microseconds(1),
          base::Seconds(1), 100);
    }

    auto data = buffer.first(num_bytes).subspan(header_size);

    // If a v3 message arrives earlier than it should, according to
    // |dispatched_message_count_|, delay dispatching it.
    // Note: On Android all messages are IsExperimentalV3(), independent of the
    // Finch state. On other platforms messages are not v3 (except for fuzzing
    // on Linux).
    if (Message::IsExperimentalV3(header)) {
      uint32_t sequence_number = Message::ExtractChannelSequenceNumber(header);
      if (sequence_number != dispatched_message_count_ + 1) {
        DelayMessage(sequence_number, buffer.first(num_bytes),
                     std::move(handles), std::move(envelope));
        *size_hint = num_bytes;
        return DispatchResult::kOK;
      }
      dispatched_message_count_ = sequence_number;
    }

    // Handle the message.
    if (Message::IsExperimentalControlMessage(header)) {
      // Note: IsExperimentalControlMessage() implies IsExperimentalV3().
#if !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
      // Let fuzzers go further without crashing.
      CHECK(SupportsChannelUpgrade()) << "Cannot handle control messages";
#endif
      if (!OnControlMessage(Message::ExtractType(header), data.data(),
                            data.size(), std::move(handles))) {
        return DispatchResult::kError;
      }
    } else {
      delegate_->OnChannelMessage(data.data(), data.size(), std::move(handles),
                                  std::move(envelope));
    }
    *size_hint = num_bytes;
    return DispatchResult::kOK;
  }

  // We have at least enough data available for a LegacyHeader.
  const Message::LegacyHeader* legacy_header =
      reinterpret_cast<const Message::LegacyHeader*>(buffer.data());

  if (legacy_header->num_bytes < sizeof(Message::LegacyHeader)) {
    LOG(ERROR) << "Invalid message size: " << legacy_header->num_bytes;
    return DispatchResult::kError;
  }

  if (buffer.size() < legacy_header->num_bytes) {
    // Not enough data available to read the full message. Hint to the
    // implementation that it should try reading the full size of the message.
    *size_hint = legacy_header->num_bytes - buffer.size();
    return DispatchResult::kNotEnoughData;
  }

  const Message::Header* header = nullptr;
  if (legacy_header->message_type != Message::MessageType::NORMAL_LEGACY) {
    header = reinterpret_cast<const Message::Header*>(legacy_header);
  }

  size_t extra_header_size = 0;
  const void* extra_header = nullptr;
  size_t payload_size = 0;
  void* payload = nullptr;
  if (header) {
    if (header->num_header_bytes < sizeof(Message::Header) ||
        header->num_header_bytes > header->num_bytes) {
      LOG(ERROR) << "Invalid message header size: " << header->num_header_bytes;
      return DispatchResult::kError;
    }
    extra_header_size = header->num_header_bytes - sizeof(Message::Header);
    extra_header = extra_header_size ? header + 1 : nullptr;
    payload_size = header->num_bytes - header->num_header_bytes;
    payload = payload_size
                  ? reinterpret_cast<Message::Header*>(const_cast<char*>(
                        buffer.subspan(header->num_header_bytes).data()))
                  : nullptr;
  } else {
    payload_size = legacy_header->num_bytes - sizeof(Message::LegacyHeader);
    payload = payload_size
                  ? const_cast<Message::LegacyHeader*>(&legacy_header[1])
                  : nullptr;
  }

  const uint16_t num_handles =
      header ? header->num_handles : legacy_header->num_handles;
  std::vector<PlatformHandle> handles;
  if (num_handles > 0) {
    if (handle_policy_ == HandlePolicy::kRejectHandles) {
      return DispatchResult::kError;
    }

    if (received_handles) {
      handles = std::move(*received_handles);
    } else if (!GetReadPlatformHandles(payload, payload_size, num_handles,
                                       extra_header, extra_header_size,
                                       &handles)) {
      return DispatchResult::kError;
    }

    if (handles.size() < num_handles) {
      // Not enough handles available for this message.
      return DispatchResult::kMissingHandles;
    }
  }

  // We've got a complete message! Dispatch it and try another.
  if (legacy_header->message_type != Message::MessageType::NORMAL_LEGACY &&
      legacy_header->message_type != Message::MessageType::NORMAL) {
    if (!OnControlMessage(legacy_header->message_type, payload, payload_size,
                          std::move(handles))) {
      return DispatchResult::kError;
    }
  } else if (delegate_) {
    delegate_->OnChannelMessage(payload, payload_size, std::move(handles),
                                std::move(envelope));
  }

  *size_hint = legacy_header->num_bytes;
  return DispatchResult::kOK;
}

void Channel::OnError(Error error) {
  if (delegate_)
    delegate_->OnChannelError(error);
}

bool Channel::OnControlMessage(Message::MessageType message_type,
                               const void* payload,
                               size_t payload_size,
                               std::vector<PlatformHandle> handles) {
  DCHECK(!is_for_ipcz());
  return false;
}

// Currently only CrOs, Linux, and Android support upgrades.
#if !(BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID))
// static
MOJO_SYSTEM_IMPL_EXPORT bool Channel::SupportsChannelUpgrade() {
  return false;
}

MOJO_SYSTEM_IMPL_EXPORT void Channel::OfferChannelUpgrade() {
  NOTREACHED();
}
#endif

void Channel::RecordSentMessageMetrics(size_t payload_size) {
  if (ShouldRecordSubsampledHistograms()) {
    UMA_HISTOGRAM_COUNTS_100000("Mojo.Channel.WriteMessageSize", payload_size);
    RecordSentMessageProcessType();
  }
}

bool Channel::DispatchDelayedMessages() {
  if (!SupportsMultipleNotifiers()) {
    return true;
  }
  while (true) {
    auto it = delayed_messages_.find(dispatched_message_count_ + 1);
    if (it == delayed_messages_.end()) {
      return true;
    }

    DelayedMessage delayed_message = std::move(it->second);
    delayed_messages_.erase(it);
    dispatched_message_count_++;
    const auto& header = *reinterpret_cast<const Message::IpczHeader*>(
        delayed_message.data.data());
    auto data =
        base::span<const char>(delayed_message.data).subspan(header.size);

    if (Message::IsExperimentalControlMessage(header)) {
      DCHECK(Message::IsExperimentalV3(header));
      if (!OnControlMessage(Message::ExtractType(header), data.data(),
                            data.size(), std::move(delayed_message.handles))) {
        return false;
      }
    } else {
      delegate_->OnChannelMessage(data.data(), data.size(),
                                  std::move(delayed_message.handles),
                                  std::move(delayed_message.envelope));
    }
  }
}

Channel::DelayedMessage::DelayedMessage() = default;
Channel::DelayedMessage::~DelayedMessage() = default;
Channel::DelayedMessage::DelayedMessage(DelayedMessage&&) = default;
Channel::DelayedMessage& Channel::DelayedMessage::operator=(DelayedMessage&&) =
    default;

void Channel::DelayMessage(uint32_t channel_sequence_number,
                           base::span<const char> data,
                           std::vector<PlatformHandle> handles,
                           scoped_refptr<ipcz_driver::Envelope> envelope) {
  DelayedMessage delayed_message;
  delayed_message.data.assign(data.begin(), data.end());
  delayed_message.handles = std::move(handles);
  delayed_message.envelope = std::move(envelope);
  delayed_messages_.emplace(channel_sequence_number,
                            std::move(delayed_message));
}

// static
void Channel::RecordReceivedMessageProcessType() {
  UMA_HISTOGRAM_ENUMERATION(
      "Mojo.Channel.WriteReceiveMessageProcessType",
      base::CurrentProcess::GetInstance().GetShortType({}));
}

// static
void Channel::RecordSentMessageProcessType() {
  UMA_HISTOGRAM_ENUMERATION(
      "Mojo.Channel.WriteSendMessageProcessType",
      base::CurrentProcess::GetInstance().GetShortType({}));
}

}  // namespace mojo::core