blob: fae563d70197d5c5622b7bdc8e2c365ad60cae26 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_SOCKET_SOCKET_FACTORY_H_
#define SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_SOCKET_SOCKET_FACTORY_H_
#include <lib/async/dispatcher.h>
#include <lib/zx/socket.h>
#include <zircon/status.h>
#include <memory>
#include <unordered_map>
#include "socket_channel_relay.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/assert.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/log.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/macros.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/weak_self.h"
namespace bt::socket {
// A SocketFactory vends zx::socket objects that an IPC peer can use to
// communicate with l2cap::Channels.
//
// Over time, the factory may grow more responsibility and intelligence. For
// example, the factory might manage QoS by configuring the number of packets a
// SocketChannelRelay can process before yielding control back to the
// dispatcher.
//
// THREAD-SAFETY: This class is thread-hostile. An instance must be
// created and destroyed on a single thread. Said thread must have a
// single-threaded dispatcher. Failure to follow those rules may cause the
// program to abort.
template <typename ChannelT>
class SocketFactory final {
public:
SocketFactory();
~SocketFactory();
// Creates a zx::socket which can be used to read from, and write to,
// |channel|.
//
// |channel| will automatically be Deactivated() when the zx::socket is
// closed, or the creation thread's dispatcher shuts down.
//
// |closed_callback| will be called when the channel or socket is closed. This
// callback may be nullptr to ignore closures.
//
// Similarly, the local end corresponding to the returned zx::socket will
// automatically be closed when |channel| is closed, or the creation thread's
// dispatcher shuts down.
//
// It is an error to call MakeSocketForChannel() multiple times for
// the same Channel.
//
// Returns the new socket on success, and an invalid socket otherwise
// (including if |channel| is nullptr).
zx::socket MakeSocketForChannel(typename ChannelT::WeakPtr channel,
fit::callback<void()> closed_callback = nullptr);
private:
using RelayT = SocketChannelRelay<ChannelT>;
using ChannelIdT = typename ChannelT::UniqueId;
// TODO(https://fxbug.dev/42145980): Figure out what we need to do handle the possibility that a
// channel id is recycled. (See comment in LogicalLink::HandleRxPacket.)
std::unordered_map<ChannelIdT, std::unique_ptr<RelayT>> channel_to_relay_;
WeakSelf<SocketFactory> weak_self_; // Keep last.
BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(SocketFactory);
};
template <typename ChannelT>
SocketFactory<ChannelT>::SocketFactory() : weak_self_(this) {}
template <typename ChannelT>
SocketFactory<ChannelT>::~SocketFactory() {}
template <typename ChannelT>
zx::socket SocketFactory<ChannelT>::MakeSocketForChannel(typename ChannelT::WeakPtr channel,
fit::callback<void()> closed_callback) {
if (!channel.is_alive()) {
return zx::socket();
}
const auto unique_id = channel->unique_id();
if (channel_to_relay_.find(unique_id) != channel_to_relay_.end()) {
bt_log(ERROR, "l2cap", "channel %u is already bound to a socket", channel->id());
return zx::socket();
}
zx::socket local_socket, remote_socket;
const auto status = zx::socket::create(ZX_SOCKET_DATAGRAM, &local_socket, &remote_socket);
if (status != ZX_OK) {
bt_log(ERROR, "data", "Failed to create socket for channel %u: %s", channel->unique_id(),
zx_status_get_string(status));
return zx::socket();
}
auto relay = std::make_unique<RelayT>(
std::move(local_socket), channel,
typename RelayT::DeactivationCallback([self = weak_self_.GetWeakPtr(), id = unique_id,
closed_cb = std::move(closed_callback)]() mutable {
BT_DEBUG_ASSERT_MSG(self.is_alive(), "(unique_id=%u)", id);
size_t n_erased = self->channel_to_relay_.erase(id);
BT_DEBUG_ASSERT_MSG(n_erased == 1, "(n_erased=%zu, unique_id=%u)", n_erased, id);
if (closed_cb) {
closed_cb();
}
}));
// Note: Activate() may abort, if |channel| has been Activated() without
// going through this SocketFactory.
if (!relay->Activate()) {
bt_log(ERROR, "l2cap", "Failed to Activate() relay for channel %u", channel->id());
return zx::socket();
}
channel_to_relay_.emplace(unique_id, std::move(relay));
return remote_socket;
}
} // namespace bt::socket
#endif // SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_SOCKET_SOCKET_FACTORY_H_