blob: e6c05f5c769f380c0477327a8189382fe1ee30da [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/fit/thread_checker.h>
#include <lib/zx/socket.h>
#include <zircon/assert.h>
#include <zircon/status.h>
#include <memory>
#include <unordered_map>
#include <fbl/macros.h>
#include <fbl/ref_ptr.h>
#include "socket_channel_relay.h"
#include "src/connectivity/bluetooth/core/bt-host/common/log.h"
#include "src/lib/fxl/memory/weak_ptr.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.
//
// 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(fbl::RefPtr<ChannelT> channel);
private:
using RelayT = SocketChannelRelay<ChannelT>;
using ChannelIdT = typename ChannelT::UniqueId;
const fit::thread_checker thread_checker_;
// TODO(fxbug.dev/671): 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_;
fxl::WeakPtrFactory<SocketFactory> weak_ptr_factory_; // Keep last.
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(SocketFactory);
};
template <typename ChannelT>
SocketFactory<ChannelT>::SocketFactory() : weak_ptr_factory_(this) {}
template <typename ChannelT>
SocketFactory<ChannelT>::~SocketFactory() {
ZX_DEBUG_ASSERT(thread_checker_.is_thread_valid());
}
template <typename ChannelT>
zx::socket SocketFactory<ChannelT>::MakeSocketForChannel(fbl::RefPtr<ChannelT> channel) {
ZX_DEBUG_ASSERT(thread_checker_.is_thread_valid());
if (!channel) {
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_ptr_factory_.GetWeakPtr(), id = unique_id]() mutable {
ZX_DEBUG_ASSERT_MSG(self, "(unique_id=%u)", id);
size_t n_erased = self->channel_to_relay_.erase(id);
ZX_DEBUG_ASSERT_MSG(n_erased == 1, "(n_erased=%zu, unique_id=%u)", n_erased, id);
}));
// 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_