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 <unordered_map>
17 
18 #include "pw_bluetooth_sapphire/internal/host/att/database.h"
19 #include "pw_bluetooth_sapphire/internal/host/common/macros.h"
20 #include "pw_bluetooth_sapphire/internal/host/common/weak_self.h"
21 #include "pw_bluetooth_sapphire/internal/host/gatt/gatt_defs.h"
22 #include "pw_bluetooth_sapphire/internal/host/gatt/types.h"
23 
24 namespace bt::gatt {
25 
26 // Called to read the value of a dynamic characteristic or characteristic
27 // descriptor.
28 //   - |peer_id|: The PeerId of the peer making the request.
29 //   - |service_id|: Identifies the service that the object belongs to.
30 //   - |id|: Identifies the object to be read. This is a user assigned
31 //           identifier provided while registering the service.
32 //   - |offset|: The offset into the value that is being read.
33 //   - |responder|: Should be called to respond to the read request with a
34 //                  characteristic or descriptor value, or an ATT error code.
35 using ReadHandler = fit::function<void(PeerId peer_id,
36                                        IdType service_id,
37                                        IdType id,
38                                        uint16_t offset,
39                                        ReadResponder responder)>;
40 
41 // Called to write the value of a dynamic characteristic or characteristic
42 // descriptor.
43 //   - |peer_id|: The PeerId of the peer making the request.
44 //   - |service_id|: Identifies the service that the object belongs to.
45 //   - |id|: Identifies the object to be written. This is a user assigned
46 //           identifier provided while registering the service.
47 //   - |offset|: The offset into the value that is being written.
48 //   - |responder|: Should be called to respond to the write request with
49 //                  success or an ATT error code. This can be a null callback
50 //                  if the client has initiated a "Write Without Response"
51 //                  procedure, in which case a response is not required.
52 using WriteHandler = fit::function<void(PeerId peer_id,
53                                         IdType service_id,
54                                         IdType id,
55                                         uint16_t offset,
56                                         const ByteBuffer& value,
57                                         WriteResponder responder)>;
58 
59 // Called when the peer device with the given |peer_id| has enabled or disabled
60 // notifications/indications on the characteristic with id |chrc_id|.
61 using ClientConfigCallback = fit::function<void(IdType service_id,
62                                                 IdType chrc_id,
63                                                 PeerId peer_id,
64                                                 bool notify,
65                                                 bool indicate)>;
66 
67 // Called with the ID and range of attributes handles spanned (inclusive) by a
68 // service that was added or removed.
69 using ServiceChangedCallback =
70     fit::function<void(IdType service_id, att::Handle start, att::Handle end)>;
71 
72 // LocalServiceManager allows clients to implement GATT services. This
73 // internally maintains an attribute database and provides hooks for clients to
74 // respond to read and write requests, send notifications/indications,
75 // add/remove services, etc.
76 class LocalServiceManager final : public WeakSelf<LocalServiceManager> {
77  public:
78   LocalServiceManager();
79   // Even though this is a noop, cannot be defaulted due to forward declaration
80   // of ServiceData.
81   ~LocalServiceManager();
82 
83   // Registers the GATT service hierarchy represented by |service| with the
84   // local attribute database. Once successfully registered, the service will be
85   // available for discovery and other ATT protocol requests.
86   //
87   // This method returns an opaque identifier on successful registration, which
88   // can be used by the caller to refer to the service in the future.
89   //
90   // Returns |kInvalidId| on failure. Registration can fail if the attribute
91   // database has run out of handles or if the hierarchy contains
92   // characteristics or descriptors with repeated IDs. Objects under |service|
93   // must have unique identifiers to aid in value request handling.
94   IdType RegisterService(ServicePtr service,
95                          ReadHandler read_handler,
96                          WriteHandler write_handler,
97                          ClientConfigCallback ccc_callback);
98 
99   // Unregisters the GATT service hierarchy identified by |service_id|. Returns
100   // false if |service_id| is unrecognized.
101   bool UnregisterService(IdType service_id);
102 
103   // Returns the client characteristic configuration for the given |peer_id| and
104   // the characteristic identified by |service_id| and |chrc_id|. Returns false
105   // if |service_id| is unknown or no configurations exist for |chrc_id|.
106   struct ClientCharacteristicConfig {
107     att::Handle handle;
108     bool notify;
109     bool indicate;
110   };
111   bool GetCharacteristicConfig(IdType service_id,
112                                IdType chrc_id,
113                                PeerId peer_id,
114                                ClientCharacteristicConfig* out_config);
115 
116   // Erase any client characteristic configuration associated to a specific
117   // client and invoke its ClientConfigCallback to signal that notifications and
118   // indications are now disabled.
119   void DisconnectClient(PeerId peer_id);
120 
set_service_changed_callback(ServiceChangedCallback callback)121   void set_service_changed_callback(ServiceChangedCallback callback) {
122     service_changed_callback_ = std::move(callback);
123   }
124 
database()125   inline att::Database::WeakPtr database() { return db_->GetWeakPtr(); }
126 
127  private:
128   class ServiceData;
129 
130   std::unique_ptr<att::Database> db_;
131   IdType next_service_id_;
132 
133   // Mapping from service instance ids to ServiceData.
134   std::unordered_map<IdType, std::unique_ptr<ServiceData>> services_;
135 
136   ServiceChangedCallback service_changed_callback_;
137 
138   BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(LocalServiceManager);
139 };
140 
141 }  // namespace bt::gatt
142