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 #include "pw_bluetooth_proxy/internal/l2cap_status_tracker.h"
16
17 #include <mutex>
18
19 #include "pw_containers/algorithm.h"
20 #include "pw_log/log.h"
21
22 namespace pw::bluetooth::proxy {
23
RegisterDelegate(L2capStatusDelegate & delegate)24 void L2capStatusTracker::RegisterDelegate(L2capStatusDelegate& delegate) {
25 std::lock_guard lock(mutex_);
26 delegates_.push_front(delegate);
27 }
28
UnregisterDelegate(L2capStatusDelegate & delegate)29 void L2capStatusTracker::UnregisterDelegate(L2capStatusDelegate& delegate) {
30 std::lock_guard lock(mutex_);
31 delegates_.remove(delegate);
32 }
33
HandleConnectionComplete(const L2capChannelConnectionInfo & info)34 void L2capStatusTracker::HandleConnectionComplete(
35 const L2capChannelConnectionInfo& info) {
36 std::lock_guard lock(mutex_);
37 if (pending_connection_complete_.has_value()) {
38 PW_LOG_ERROR("Connection complete already pending");
39 return;
40 }
41 pending_connection_complete_ = info;
42 }
43
HandleAclDisconnectionComplete(uint16_t connection_handle)44 void L2capStatusTracker::HandleAclDisconnectionComplete(
45 uint16_t connection_handle) {
46 std::lock_guard lock(mutex_);
47 if (pending_acl_disconnection_complete_.has_value()) {
48 PW_LOG_ERROR("ACL disconnection complete already pending");
49 return;
50 }
51 pending_acl_disconnection_complete_ = connection_handle;
52 }
53
HandleDisconnectionComplete(const DisconnectParams & params)54 void L2capStatusTracker::HandleDisconnectionComplete(
55 const DisconnectParams& params) {
56 std::lock_guard lock(mutex_);
57 if (pending_disconnection_complete_.has_value()) {
58 PW_LOG_ERROR("Disconnection complete already pending");
59 return;
60 }
61 pending_disconnection_complete_ = params;
62 }
63
DeliverPendingConnectionComplete(const L2capChannelConnectionInfo & info)64 void L2capStatusTracker::DeliverPendingConnectionComplete(
65 const L2capChannelConnectionInfo& info) {
66 bool track = false;
67 for (L2capStatusDelegate& delegate : delegates_) {
68 if (!delegate.ShouldTrackPsm(info.psm)) {
69 continue;
70 }
71
72 track = true;
73 delegate.HandleConnectionComplete(info);
74 }
75
76 if (track) {
77 if (connected_channel_infos_.full()) {
78 // TODO: https://pwbug.dev/379558046 - Let client know we won't be able to
79 // notify on disconnect.
80 PW_LOG_ERROR(
81 "Couldn't track l2cap channel connection as requested, so will not "
82 "be able to send disconnect event to client.");
83 return;
84 }
85 connected_channel_infos_.push_back(info);
86 }
87 }
88
DeliverPendingAclDisconnectionComplete(uint16_t connection_handle)89 void L2capStatusTracker::DeliverPendingAclDisconnectionComplete(
90 uint16_t connection_handle) {
91 for (size_t i = 0; i < connected_channel_infos_.size();) {
92 L2capChannelConnectionInfo& info = connected_channel_infos_[i];
93
94 if (info.connection_handle == connection_handle) {
95 containers::ForEach(delegates_, [info](L2capStatusDelegate& delegate) {
96 if (delegate.ShouldTrackPsm(info.psm)) {
97 delegate.HandleDisconnectionComplete(info);
98 }
99 });
100 // Deleting this entry in Vector, so do not increment index.
101 connected_channel_infos_.erase(&info);
102 } else {
103 // Not deleting this entry in Vector, so increment index.
104 ++i;
105 }
106 }
107 }
108
DeliverPendingDisconnectionComplete(const DisconnectParams & params)109 void L2capStatusTracker::DeliverPendingDisconnectionComplete(
110 const DisconnectParams& params) {
111 for (L2capStatusDelegate& delegate : delegates_) {
112 auto match = [¶ms](const L2capChannelConnectionInfo& i) {
113 return params.connection_handle == i.connection_handle &&
114 params.remote_cid == i.remote_cid &&
115 params.local_cid == i.local_cid;
116 };
117 auto connection_it = std::find_if(connected_channel_infos_.begin(),
118 connected_channel_infos_.end(),
119 match);
120 if (connection_it == connected_channel_infos_.end()) {
121 return;
122 }
123
124 delegate.HandleDisconnectionComplete(*connection_it);
125 connected_channel_infos_.erase(connection_it);
126 }
127 }
128
DeliverPendingEvents()129 void L2capStatusTracker::DeliverPendingEvents() {
130 std::lock_guard lock(mutex_);
131 if (pending_connection_complete_.has_value()) {
132 DeliverPendingConnectionComplete(*pending_connection_complete_);
133 pending_connection_complete_.reset();
134 }
135
136 if (pending_acl_disconnection_complete_.has_value()) {
137 DeliverPendingAclDisconnectionComplete(
138 *pending_acl_disconnection_complete_);
139 pending_acl_disconnection_complete_.reset();
140 }
141
142 if (pending_disconnection_complete_.has_value()) {
143 DeliverPendingDisconnectionComplete(*pending_disconnection_complete_);
144 pending_disconnection_complete_.reset();
145 }
146 }
147
148 } // namespace pw::bluetooth::proxy
149