1 // Copyright 2023 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 #include <lib/fit/function.h>
17 
18 #include <unordered_map>
19 
20 #include "pw_bluetooth_sapphire/internal/host/common/random.h"
21 #include "pw_bluetooth_sapphire/internal/host/common/weak_self.h"
22 #include "pw_bluetooth_sapphire/internal/host/l2cap/dynamic_channel.h"
23 #include "pw_bluetooth_sapphire/internal/host/l2cap/l2cap_defs.h"
24 #include "pw_bluetooth_sapphire/internal/host/l2cap/signaling_channel.h"
25 #include "pw_bluetooth_sapphire/internal/host/l2cap/types.h"
26 
27 namespace bt::l2cap::internal {
28 
29 // Base class for registries of dynamic L2CAP channels. It serves both as the
30 // factory and owner of dynamic channels created on a logical link, including
31 // assigning and tracking dynamic channel IDs.
32 //
33 // Registry entries are DynamicChannels that do not implement the l2cap::Channel
34 // interface used for user data transfer.
35 //
36 // This class is not thread-safe and is intended to be created and run on the
37 // L2CAP thread for each logical link connected.
38 class DynamicChannelRegistry : public WeakSelf<DynamicChannelRegistry> {
39  public:
40   // Used to pass an optional channel to clients of the registry. |channel| may
41   // be nullptr upon failure to open. Otherwise, it points to an instance owned
42   // by the registry and should not be retained by the callee.
43   using DynamicChannelCallback =
44       fit::function<void(const DynamicChannel* channel)>;
45   using ServiceInfo = ServiceInfo<DynamicChannelCallback>;
46 
47   // Used to query the upper layers for the presence of a service that is
48   // accepting channels. If the service exists, it should return a callback
49   // that accepts the inbound dynamic channel opened.
50   using ServiceRequestCallback =
51       fit::function<std::optional<ServiceInfo>(Psm psm)>;
52 
53   virtual ~DynamicChannelRegistry() = default;
54 
55   // Create and connect a dynamic channel. The result will be returned by
56   // calling |open_cb| on the L2CAP thread the channel is ready for data
57   // transfer, with a nullptr if unsuccessful. The DynamicChannel passed will
58   // contain the local and remote channel IDs to be used for user data transfer
59   // over the new channel. Preferred channel parameters can be set in |params|.
60   void OpenOutbound(Psm psm,
61                     ChannelParameters params,
62                     DynamicChannelCallback open_cb);
63 
64   // Disconnect and remove the channel identified by |local_cid|. After this
65   // call completes, incoming PDUs with |local_cid| should be discarded as in
66   // error or considered to belong to a subsequent channel with that ID. Any
67   // outbound PDUs passed to the Channel interface for this channel should be
68   // discarded. When the close operation completes, |close_callback| will be
69   // called, the internal channel will be destroyed, and |local_cid| may be
70   // recycled for another dynamic channel. |close_callback| will be called
71   // immediately if the channel doesn't exist.
72   void CloseChannel(ChannelId local_cid, fit::closure close_callback);
73 
74  protected:
75   // |max_num_channels| is the number of dynamic channel IDs that can be
76   // allocated on this link, and must be non-zero and less than 65473.
77   //
78   // |close_cb| will be called upon a remote-initiated closure of an open
79   // channel. The registry's internal channel is passed as a parameter, and it
80   // will be closed for user data transfer before the callback fires. When the
81   // callback returns, the channel is destroyed and its ID may be recycled for
82   // another dynamic channel. Channels that fail to open due to error or are
83   // closed using CloseChannel will not trigger this callback.
84   //
85   // |service_request_cb| will be called upon remote-initiated channel requests.
86   // For services accepting channels, it shall return a callback to accept the
87   // opened channel, which only be called if the channel successfully opens. To
88   // deny the channel creation, |service_request_cb| should return a nullptr.
89   //
90   // If |random_channel_ids| is true then the channel IDs assigned will be
91   // randomized. Otherwise, they will be assigned starting at the lowest
92   // available dynamic channel id (for testing).
93   DynamicChannelRegistry(uint16_t max_num_channels,
94                          DynamicChannelCallback close_cb,
95                          ServiceRequestCallback service_request_cb,
96                          bool random_channel_ids);
97 
98   // Factory method for a DynamicChannel implementation that represents an
99   // outbound channel with an endpoint on this device identified by |local_cid|.
100   virtual DynamicChannelPtr MakeOutbound(Psm psm,
101                                          ChannelId local_cid,
102                                          ChannelParameters params) = 0;
103 
104   // Factory method for a DynamicChannel implementation that represents an
105   // inbound channel from a remote endpoint identified by |remote_cid| to an
106   // endpoint on this device identified by |local_cid|.
107   virtual DynamicChannelPtr MakeInbound(Psm psm,
108                                         ChannelId local_cid,
109                                         ChannelId remote_cid,
110                                         ChannelParameters params) = 0;
111 
112   // Open an inbound channel for a service |psm| from the remote endpoint
113   // identified by |remote_cid| to the local endpoint by |local_cid|.
114   DynamicChannel* RequestService(Psm psm,
115                                  ChannelId local_cid,
116                                  ChannelId remote_cid);
117 
118   // In the range starting at kFirstDynamicChannelId with |max_num_channels_|,
119   // pick a dynamic channel ID that is available on this link. Returns
120   // kInvalidChannelId if all IDs have been exhausted.
121   ChannelId FindAvailableChannelId();
122 
123   // Return the number of alive channels on this link.
124   size_t AliveChannelCount() const;
125 
126   // Returns null if not found. Can be downcast to the derived DynamicChannel
127   // created by MakeOutbound.
128   DynamicChannel* FindChannelByLocalId(ChannelId local_cid) const;
129 
130   // Searches for alive dynamic channel with given remote channel id.
131   // Returns null if not found.
132   DynamicChannel* FindChannelByRemoteId(ChannelId remote_cid) const;
133 
134   // Iterates over all channels, running |f| on each entry synchronously.
135   void ForEach(fit::function<void(DynamicChannel*)> f) const;
136 
137  private:
138   friend class DynamicChannel;
139 
140   // Open a newly-created channel. If |pass_failed| is true, always invoke
141   // |open_callback| with the result of the operation, including with nullptr if
142   // the channel failed to open. Otherwise if |pass_failed| is false, only
143   // invoke |open_callback| for successfully-opened channels.
144   void ActivateChannel(DynamicChannel* channel,
145                        DynamicChannelCallback open_callback,
146                        bool pass_failed);
147 
148   // Signal a remote-initiated closure of a channel owned by this registry, then
149   // delete it. |close_cb_| is invoked if the channel was ever open (see
150   // |DynamicChannel::opened|).
151   void OnChannelDisconnected(DynamicChannel* channel);
152 
153   // Delete a channel owned by this registry. Then, after this returns,
154   // |local_cid| may be recycled for another dynamic channel.
155   void RemoveChannel(DynamicChannel* channel);
156 
157   // Greatest dynamic channel ID that can be assigned on the kind of logical
158   // link associated to this registry.
159   const uint16_t max_num_channels_;
160 
161   // Called only for channels that were already open (see
162   // |DynamicChannel::opened|).
163   DynamicChannelCallback close_cb_;
164   ServiceRequestCallback service_request_cb_;
165 
166   // Maps local CIDs to alive dynamic channels on this logical link.
167   using ChannelMap = std::unordered_map<ChannelId, DynamicChannelPtr>;
168   ChannelMap channels_;
169 
170   bool random_channel_ids_;
171 
172   BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(DynamicChannelRegistry);
173 };
174 
175 }  // namespace bt::l2cap::internal
176