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 <map>
17 #include <memory>
18 #include <vector>
19 
20 #include "pw_bluetooth_sapphire/internal/host/att/error.h"
21 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
22 #include "pw_bluetooth_sapphire/internal/host/gatt/gatt.h"
23 #include "pw_bluetooth_sapphire/internal/host/gatt/gatt_defs.h"
24 #include "pw_bluetooth_sapphire/internal/host/gatt/remote_service.h"
25 
26 namespace bt::gatt {
27 
28 class Client;
29 
30 namespace internal {
31 
32 // Maintains a collection of services that are present on a particular peer
33 // device. The RemoteServiceManager owns the GATT client data bearer, interacts
34 // with the profile-defined GATT service, and allows observers to be notified
35 // as services get discovered.
36 class RemoteServiceManager final {
37  public:
38   RemoteServiceManager(std::unique_ptr<Client> client);
39   ~RemoteServiceManager();
40 
41   // Adds a handler to be notified when services are removed, added, or
42   // modified. NOTE: `removed` services should be handled first because they may
43   // share handles with `added` services.
set_service_watcher(RemoteServiceWatcher watcher)44   void set_service_watcher(RemoteServiceWatcher watcher) {
45     svc_watcher_ = std::move(watcher);
46   }
47 
48   // Initiates the Exchange MTU procedure followed by service discovery.
49   // |callback| is called to notify the result of the procedure.
50   // |mtu_callback| is called when the MTU for the connection is determined,
51   // which may occur before initialization completes. If |services| is empty,
52   // discover all services. If |services| is not empty, only discover services
53   // that match the UUIDs in |services|.
54   // TODO(fxbug.dev/42144310): Support initiating multiple service discoveries
55   // for different service UUIDs.
56   void Initialize(att::ResultFunction<> callback,
57                   fit::callback<void(uint16_t)> mtu_callback,
58                   std::vector<UUID> services = {});
59 
60   // Returns a vector containing discovered services that match any of the given
61   // |uuids| via |callback|. All services will be returned if |uuids| is empty.
62   //
63   // If called while uninitialized, |callback| will be run after initialization.
64   void ListServices(const std::vector<UUID>& uuids,
65                     ServiceListCallback callback);
66 
67   // Returns the RemoteService with the requested range start |handle| or
68   // nullptr if it is not recognized. This method may fail if called before or
69   // during initialization.
70   RemoteService::WeakPtr FindService(att::Handle handle);
71 
72  private:
73   using ServiceMap = std::map<att::Handle, std::unique_ptr<RemoteService>>;
74 
75   // Used to represent a queued ListServices() request.
76   class ServiceListRequest {
77    public:
78     ServiceListRequest(ServiceListCallback callback,
79                        const std::vector<UUID>& uuids);
80 
81     ServiceListRequest() = default;
82     ServiceListRequest(ServiceListRequest&&) = default;
83 
84     // Completes this request by using entries from |services| that match any of
85     // the requested UUIDs.
86     void Complete(att::Result<> status, const ServiceMap& services);
87 
88    private:
89     ServiceListCallback callback_;
90     std::vector<UUID> uuids_;
91 
92     BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(ServiceListRequest);
93   };
94 
95   // State of the current Service Changed notification procedure.
96   struct ServiceChangedState {
97     ServiceChangedCharacteristicValue value;
98     // The cache of services discovered in the changed range.
99     std::map<att::Handle, ServiceData> services;
100   };
101 
102   // If the GATT Profile service has been discovered, returns the service.
103   // Returns nullptr otherwise.
104   RemoteService* GattProfileService();
105 
106   // Attempt to discover the GATT Profile service. This method must complete
107   // before discovery of other services. Notifies |callback| with a status of
108   // HostError::kNotFound if the GATT Profile service is not found.
109   void DiscoverGattProfileService(att::ResultFunction<> callback);
110 
111   // Discovers characteristics of |gatt_profile_service| and enables
112   // notifications of the Service Changed characteristic. Notifies |callback|
113   // with a status of HostError::kNotFound if the GATT Profile service's Service
114   // Changed characteristic is not found.
115   void ConfigureServiceChangedNotifications(RemoteService* gatt_profile_service,
116                                             att::ResultFunction<> callback);
117 
118   // Discover the GATT Profile service and configure the Service Changed
119   // characteristic therein.
120   void InitializeGattProfileService(att::ResultFunction<> callback);
121 
122   // Create a RemoteService and insert it into the services map, discarding
123   // duplicates.
124   void AddService(const ServiceData& service_data);
125 
126   // Discover services of the indicated |kind|.
127   // If |service_uuids| is non-empty, only discovers services with the given
128   // UUIDs. |status_cb| will be called when the operation completes.
129   using ServiceCallback = fit::function<void(const ServiceData&)>;
130   void DiscoverServicesOfKind(ServiceKind kind,
131                               std::vector<UUID> service_uuids,
132                               att::ResultFunction<> status_cb);
133 
134   // Discover primary and secondary services.
135   // If |service_uuids| is non-empty, only discovers services with the given
136   // UUIDs. |status_callback| will be called when the operation completes.
137   void DiscoverServices(std::vector<UUID> service_uuids,
138                         att::ResultFunction<> status_callback);
139   void DiscoverPrimaryAndSecondaryServicesInRange(
140       std::vector<UUID> service_uuids,
141       att::Handle start,
142       att::Handle end,
143       ServiceCallback service_cb,
144       att::ResultFunction<> status_callback);
145 
146   // Called by |client_| when a notification or indication is received.
147   void OnNotification(bool ind,
148                       att::Handle value_handle,
149                       const ByteBuffer& value,
150                       bool maybe_truncated);
151 
152   // Handler of notifications from the Service Changed Characteristic
153   void OnServiceChangedNotification(const ByteBuffer& buffer);
154 
155   // If a service changed notification is queued and none are currently being
156   // handled, start processing the next notification. |on_complete| is called
157   // when all pending service changed notifications have been processed
158   // (immediately if there are none).
159   void MaybeHandleNextServiceChangedNotification(
160       fit::callback<void()> on_complete = nullptr);
161 
162   // Apply service changed discovery results to |services_|. If initialization
163   // is complete, notify |svc_watcher_|.
164   void ProcessServiceChangedDiscoveryResults(
165       const ServiceChangedState& service_changed);
166 
167   // Calculate the difference between the current services and the services
168   // discovered in the Service Changed notification range. Results are appended
169   // to the parameters vectors.
170   void CalculateServiceChanges(
171       const ServiceChangedState& service_changed,
172       std::vector<ServiceMap::iterator>& removed_services,
173       std::vector<ServiceData>& added_services,
174       std::vector<std::pair<ServiceMap::iterator, ServiceData>>&
175           modified_services);
176 
177   std::unique_ptr<Client> client_;
178 
179   bool initialized_;
180   RemoteServiceWatcher svc_watcher_;
181 
182   // Requests queued during calls ListServices() before initialization.
183   std::queue<ServiceListRequest> pending_list_services_requests_;
184 
185   // Services are sorted by handle.
186   ServiceMap services_;
187 
188   // Queue of unprocessed Service Changed notification changes.
189   std::queue<ServiceChangedCharacteristicValue> queued_service_changes_;
190 
191   // Service Changed notification that is currently being processed by
192   // performing service discovery.
193   std::optional<ServiceChangedState> current_service_change_;
194 
195   // Callbacks passed to MaybeHandleNextServiceChangedNotification. Will be
196   // called when all queued service changes have been processed.
197   std::vector<fit::callback<void()>> service_changes_complete_callbacks_;
198 
199   WeakSelf<RemoteServiceManager> weak_self_;
200 
201   BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(RemoteServiceManager);
202 };
203 
204 }  // namespace internal
205 }  // namespace bt::gatt
206