• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 = [&params](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