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 <map>
19 #include <queue>
20 #include <unordered_map>
21 
22 #include "pw_bluetooth_sapphire/internal/host/att/error.h"
23 #include "pw_bluetooth_sapphire/internal/host/common/macros.h"
24 #include "pw_bluetooth_sapphire/internal/host/gatt/client.h"
25 #include "pw_bluetooth_sapphire/internal/host/gatt/gatt_defs.h"
26 
27 namespace bt::gatt {
28 
29 class Client;
30 
31 // Used by a RemoteService to represent one of its characteristics. This object
32 // maintains information about a characteristic (such as its descriptors, known
33 // permissions, etc) and is responsible for routing notifications to subscribed
34 // clients.
35 //
36 // Instances are created and owned by a RemoteService.
37 //
38 // ID SCHEME:
39 //
40 // The ID that gets assigned to a RemoteCharacteristic is its value_handle
41 // The ID that gets assigned to a Descriptor is its handle. Looking up a
42 // descriptor by id from the service is logarithmic in the number of
43 // descriptors.
44 class RemoteCharacteristic final {
45  public:
46   using ValueCallback =
47       fit::function<void(const ByteBuffer&, bool maybe_truncated)>;
48   using NotifyStatusCallback =
49       fit::function<void(att::Result<>, IdType handler_id)>;
50 
51   // We use an ordered map so that the Descriptors are exposed to the world in
52   // order
53   using DescriptorMap = std::map<DescriptorHandle, DescriptorData>;
54 
55   RemoteCharacteristic(Client::WeakPtr client, const CharacteristicData& info);
56   ~RemoteCharacteristic();
57 
58   // The properties for this characteristic.
properties()59   Properties properties() const { return info_.properties; }
60 
61   // The extended properties for this characteristic.
extended_properties()62   std::optional<ExtendedProperties> extended_properties() const {
63     return info_.extended_properties;
64   }
65 
66   // ATT declaration data for this characteristic.
info()67   const CharacteristicData& info() const { return info_; }
68 
69   // Descriptors of this characteristic.
descriptors()70   const DescriptorMap& descriptors() const { return descriptors_; }
71 
72  private:
73   friend class RemoteService;
74 
75   // The following private methods can only be called by a RemoteService.
76 
77   // `service_changed` indicates whether destruction will occur due to a Service
78   // Changed notification, in which case this characteristic may no longer exist
79   // or may have been changed.
set_service_changed(bool service_changed)80   void set_service_changed(bool service_changed) {
81     service_changed_ = service_changed;
82   }
83 
84   // Updates the CharacteristicData |info_| with the Extended Properties that
85   // are read from the descriptors discovered in |DiscoverDescriptors|.
86   void UpdateDataWithExtendedProperties(ExtendedProperties ext_props);
87 
88   // Discovers the descriptors of this characteristic and reports the status in
89   // |callback|.
90   //
91   // NOTE: The owning RemoteService is responsible for ensuring that this object
92   // outlives the discovery procedure.
93   void DiscoverDescriptors(att::Handle range_end,
94                            att::ResultFunction<> callback);
95 
96   // (See RemoteService::EnableNotifications in remote_service.h).
97   void EnableNotifications(ValueCallback value_callback,
98                            NotifyStatusCallback status_callback);
99   bool DisableNotifications(IdType handler_id);
100 
101   // Sends a request to disable notifications and indications. Called by
102   // DisableNotifications and destructor.
103   void DisableNotificationsInternal();
104 
105   // Resolves all pending notification subscription requests. Called by
106   // EnableNotifications().
107   void ResolvePendingNotifyRequests(att::Result<> status);
108 
109   // Called when a notification is received for this characteristic.
110   void HandleNotification(const ByteBuffer& value, bool maybe_truncated);
111 
112   CharacteristicData info_;
113   DescriptorMap descriptors_;
114   bool discovery_error_;
115 
116   // If true, this characteristic was in a service that has been changed. Values
117   // should not be read/written after a service is changed.
118   bool service_changed_ = false;
119 
120   // Handle of the Client Characteristic Configuration descriptor, or 0 if none.
121   att::Handle ccc_handle_;
122 
123   // Handle of the Characteristic Extended Properties descriptor, or 0 if none.
124   att::Handle ext_prop_handle_;
125 
126   // Represents a pending request to subscribe to notifications or indications.
127   struct PendingNotifyRequest {
128     PendingNotifyRequest(ValueCallback value_callback,
129                          NotifyStatusCallback status_callback);
130 
131     PendingNotifyRequest() = default;
132     PendingNotifyRequest(PendingNotifyRequest&&) = default;
133 
134     ValueCallback value_callback;
135     NotifyStatusCallback status_callback;
136   };
137   std::queue<PendingNotifyRequest> pending_notify_reqs_;
138 
139   // Active notification handlers.
140   std::unordered_map<IdType, ValueCallback> notify_handlers_;
141   // Set to true while handlers in notify_handlers_ are being notified.
142   bool notifying_handlers_ = false;
143   std::vector<IdType> handlers_pending_disable_;
144 
145   // The next available notification handler ID.
146   size_t next_notify_handler_id_;
147 
148   // The GATT client bearer used for ATT requests.
149   Client::WeakPtr client_;
150 
151   WeakSelf<RemoteCharacteristic> weak_self_;
152 
153   BT_DISALLOW_COPY_ASSIGN_AND_MOVE(RemoteCharacteristic);
154 };
155 
156 }  // namespace bt::gatt
157