1 // Copyright 2024 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #pragma once
16
17 #include <lib/async/dispatcher.h>
18 #include <lib/zx/socket.h>
19 #include <zircon/status.h>
20
21 #include <memory>
22 #include <unordered_map>
23
24 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
25 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
26 #include "pw_bluetooth_sapphire/internal/host/common/macros.h"
27 #include "pw_bluetooth_sapphire/internal/host/common/weak_self.h"
28 #include "socket_channel_relay.h"
29
30 namespace bt::socket {
31
32 // A SocketFactory vends zx::socket objects that an IPC peer can use to
33 // communicate with l2cap::Channels.
34 //
35 // Over time, the factory may grow more responsibility and intelligence. For
36 // example, the factory might manage QoS by configuring the number of packets a
37 // SocketChannelRelay can process before yielding control back to the
38 // dispatcher.
39 //
40 // THREAD-SAFETY: This class is thread-hostile. An instance must be
41 // created and destroyed on a single thread. Said thread must have a
42 // single-threaded dispatcher. Failure to follow those rules may cause the
43 // program to abort.
44 template <typename ChannelT>
45 class SocketFactory final {
46 public:
47 SocketFactory();
48 ~SocketFactory();
49
50 // Creates a zx::socket which can be used to read from, and write to,
51 // |channel|.
52 //
53 // |channel| will automatically be Deactivated() when the zx::socket is
54 // closed, or the creation thread's dispatcher shuts down.
55 //
56 // |closed_callback| will be called when the channel or socket is closed. This
57 // callback may be nullptr to ignore closures.
58 //
59 // Similarly, the local end corresponding to the returned zx::socket will
60 // automatically be closed when |channel| is closed, or the creation thread's
61 // dispatcher shuts down.
62 //
63 // It is an error to call MakeSocketForChannel() multiple times for
64 // the same Channel.
65 //
66 // Returns the new socket on success, and an invalid socket otherwise
67 // (including if |channel| is nullptr).
68 zx::socket MakeSocketForChannel(
69 typename ChannelT::WeakPtr channel,
70 fit::callback<void()> closed_callback = nullptr);
71
72 private:
73 using RelayT = SocketChannelRelay<ChannelT>;
74 using ChannelIdT = typename ChannelT::UniqueId;
75
76 // TODO(fxbug.dev/42145980): Figure out what we need to do handle the
77 // possibility that a channel id is recycled. (See comment in
78 // LogicalLink::HandleRxPacket.)
79 std::unordered_map<ChannelIdT, std::unique_ptr<RelayT>> channel_to_relay_;
80
81 WeakSelf<SocketFactory> weak_self_; // Keep last.
82
83 BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(SocketFactory);
84 };
85
86 template <typename ChannelT>
SocketFactory()87 SocketFactory<ChannelT>::SocketFactory() : weak_self_(this) {}
88
89 template <typename ChannelT>
~SocketFactory()90 SocketFactory<ChannelT>::~SocketFactory() {}
91
92 template <typename ChannelT>
MakeSocketForChannel(typename ChannelT::WeakPtr channel,fit::callback<void ()> closed_callback)93 zx::socket SocketFactory<ChannelT>::MakeSocketForChannel(
94 typename ChannelT::WeakPtr channel, fit::callback<void()> closed_callback) {
95 if (!channel.is_alive()) {
96 return zx::socket();
97 }
98
99 const auto unique_id = channel->unique_id();
100 if (channel_to_relay_.find(unique_id) != channel_to_relay_.end()) {
101 bt_log(ERROR,
102 "l2cap",
103 "channel %u is already bound to a socket",
104 channel->id());
105 return zx::socket();
106 }
107
108 zx::socket local_socket, remote_socket;
109 const auto status =
110 zx::socket::create(ZX_SOCKET_DATAGRAM, &local_socket, &remote_socket);
111 if (status != ZX_OK) {
112 bt_log(ERROR,
113 "data",
114 "Failed to create socket for channel %u: %s",
115 channel->unique_id(),
116 zx_status_get_string(status));
117 return zx::socket();
118 }
119
120 auto relay = std::make_unique<RelayT>(
121 std::move(local_socket),
122 channel,
123 typename RelayT::DeactivationCallback([self = weak_self_.GetWeakPtr(),
124 id = unique_id,
125 closed_cb = std::move(
126 closed_callback)]() mutable {
127 PW_DCHECK(self.is_alive(), "(unique_id=%u)", id);
128 size_t n_erased = self->channel_to_relay_.erase(id);
129 PW_DCHECK(n_erased == 1, "(n_erased=%zu, unique_id=%u)", n_erased, id);
130
131 if (closed_cb) {
132 closed_cb();
133 }
134 }));
135
136 // Note: Activate() may abort, if |channel| has been Activated() without
137 // going through this SocketFactory.
138 if (!relay->Activate()) {
139 bt_log(ERROR,
140 "l2cap",
141 "Failed to Activate() relay for channel %u",
142 channel->id());
143 return zx::socket();
144 }
145
146 channel_to_relay_.emplace(unique_id, std::move(relay));
147 return remote_socket;
148 }
149
150 } // namespace bt::socket
151