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