| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_GATT_REMOTE_SERVICE_MANAGER_H_ |
| #define SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_GATT_REMOTE_SERVICE_MANAGER_H_ |
| |
| #include <lib/async/dispatcher.h> |
| #include <zircon/assert.h> |
| |
| #include <map> |
| #include <memory> |
| #include <vector> |
| |
| #include <fbl/ref_ptr.h> |
| |
| #include "src/connectivity/bluetooth/core/bt-host/att/error.h" |
| #include "src/connectivity/bluetooth/core/bt-host/gatt/gatt.h" |
| #include "src/connectivity/bluetooth/core/bt-host/gatt/gatt_defs.h" |
| #include "src/connectivity/bluetooth/core/bt-host/gatt/remote_service.h" |
| #include "src/lib/fxl/memory/weak_ptr.h" |
| |
| namespace bt::gatt { |
| |
| class Client; |
| |
| namespace internal { |
| |
| // Maintains a collection of services that are present on a particular peer |
| // device. The RemoteServiceManager owns the GATT client data bearer, interacts |
| // with the profile-defined GATT service, and allows observers to be notified |
| // as services get discovered. |
| // |
| // THREAD SAFETY: |
| // |
| // This class is NOT thread safe. It must be created, used, and destroyed on a |
| // specific thread. |
| class RemoteServiceManager final { |
| public: |
| RemoteServiceManager(std::unique_ptr<Client> client, async_dispatcher_t* gatt_dispatcher); |
| ~RemoteServiceManager(); |
| |
| // Adds a handler to be notified when services are removed, added, or modified. |
| // NOTE: `removed` services should be handled first because they may share handles with `added` |
| // services. |
| void set_service_watcher(RemoteServiceWatcher watcher) { svc_watcher_ = std::move(watcher); } |
| |
| // Initiates the Exchange MTU procedure followed by service discovery. |
| // |callback| is called to notify the result of the procedure. |
| // |mtu_cb| is called when the MTU for the connection is determined, which may occur before |
| // initialization completes. |
| // If |services| is empty, discover all services. |
| // If |services| is not empty, only discover services that match the UUIDs in |services|. |
| // TODO(fxbug.dev/65592): Support initiating multiple service discoveries for different service |
| // UUIDs. |
| void Initialize(att::ResultFunction<> callback, fit::callback<void(uint16_t)> mtu_cb, |
| std::vector<UUID> services = {}); |
| |
| // Returns a vector containing discovered services that match any of the given |
| // |uuids| via |callback|. All services will be returned if |uuids| is empty. |
| // |
| // If called while uninitialized, |callback| will be run after initialization. |
| void ListServices(const std::vector<UUID>& uuids, ServiceListCallback callback); |
| |
| // Returns the RemoteService with the requested range start |handle| or |
| // nullptr if it is not recognized. This method may fail if called before or |
| // during initialization. |
| fbl::RefPtr<RemoteService> FindService(att::Handle handle); |
| |
| private: |
| using ServiceMap = std::map<att::Handle, fbl::RefPtr<RemoteService>>; |
| |
| // Used to represent a queued ListServices() request. |
| class ServiceListRequest { |
| public: |
| ServiceListRequest(ServiceListCallback callback, const std::vector<UUID>& uuids); |
| |
| ServiceListRequest() = default; |
| ServiceListRequest(ServiceListRequest&&) = default; |
| |
| // Completes this request by using entries from |services| that match any of |
| // the requested UUIDs. |
| void Complete(att::Result<> status, const ServiceMap& services); |
| |
| private: |
| ServiceListCallback callback_; |
| std::vector<UUID> uuids_; |
| |
| DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(ServiceListRequest); |
| }; |
| |
| // State of the current Service Changed notification procedure. |
| struct ServiceChangedState { |
| ServiceChangedCharacteristicValue value; |
| // The cache of services discovered in the changed range. |
| std::map<att::Handle, ServiceData> services; |
| }; |
| |
| // If the GATT Profile service has been discovered, returns the service. Returns |
| // nullptr otherwise. |
| fbl::RefPtr<RemoteService> GattProfileService(); |
| |
| // Attempt to discover the GATT Profile service. This method must complete before discovery of |
| // other services. Notifies |callback| with a status of HostError::kNotFound if the GATT Profile |
| // service is not found. |
| void DiscoverGattProfileService(att::ResultFunction<> callback); |
| |
| // Discovers characteristics of |gatt_profile_service| and enables notifications of the Service |
| // Changed characteristic. Notifies |callback| with a status of HostError::kNotFound if the GATT |
| // Profile service's Service Changed characteristic is not found. |
| void ConfigureServiceChangedNotifications(fbl::RefPtr<RemoteService> gatt_profile_service, |
| att::ResultFunction<> callback); |
| |
| // Discover the GATT Profile service and configure the Service Changed characteristic therein. |
| void InitializeGattProfileService(att::ResultFunction<> callback); |
| |
| // Create a RemoteService and insert it into the services map, discarding duplicates. |
| void AddService(const ServiceData& service_data); |
| |
| // Discover services of the indicated |kind|. |
| // If |service_uuids| is non-empty, only discovers services with the given UUIDs. |
| // |status_cb| will be called when the operation completes. |
| using ServiceCallback = fit::function<void(const ServiceData&)>; |
| void DiscoverServicesOfKind(ServiceKind kind, std::vector<UUID> service_uuids, |
| att::ResultFunction<> status_cb); |
| |
| // Discover primary and secondary services. |
| // If |service_uuids| is non-empty, only discovers services with the given UUIDs. |
| // |status_cb| will be called when the operation completes. |
| void DiscoverServices(std::vector<UUID> service_uuids, att::ResultFunction<> status_cb); |
| void DiscoverPrimaryAndSecondaryServicesInRange(std::vector<UUID> service_uuids, |
| att::Handle start, att::Handle end, |
| ServiceCallback service_cb, |
| att::ResultFunction<> status_cb); |
| |
| // Shuts down and cleans up all services. |
| void ClearServices(); |
| |
| // Called by |client_| when a notification or indication is received. |
| void OnNotification(bool ind, att::Handle value_handle, const ByteBuffer& value, |
| bool maybe_truncated); |
| |
| // Handler of notifications from the Service Changed Characteristic |
| void OnServiceChangedNotification(const ByteBuffer& buffer); |
| |
| // If a service changed notification is queued and none are currently being handled, start |
| // processing the next notification. |
| // |on_complete| is called when all pending service changed notifications have been processed |
| // (immediately if there are none). |
| void MaybeHandleNextServiceChangedNotification(fit::callback<void()> on_complete = nullptr); |
| |
| // Apply service changed discovery results to |services_|. If initialization is complete, notify |
| // |svc_watcher_|. |
| void ProcessServiceChangedDiscoveryResults(const ServiceChangedState& service_changed); |
| |
| // Calculate the difference between the current services and the services discovered in the |
| // Service Changed notification range. Results are appended to the parameters vectors. |
| void CalculateServiceChanges( |
| const ServiceChangedState& service_changed, |
| std::vector<ServiceMap::iterator>& removed_services, std::vector<ServiceData>& added_services, |
| std::vector<std::pair<ServiceMap::iterator, ServiceData>>& modified_services); |
| |
| async_dispatcher_t* gatt_dispatcher_; |
| std::unique_ptr<Client> client_; |
| |
| bool initialized_; |
| RemoteServiceWatcher svc_watcher_; |
| |
| // Requests queued during calls ListServices() before initialization. |
| std::queue<ServiceListRequest> pending_list_services_requests_; |
| |
| // Services are sorted by handle. |
| ServiceMap services_; |
| |
| // Queue of unprocessed Service Changed notification changes. |
| std::queue<ServiceChangedCharacteristicValue> queued_service_changes_; |
| |
| // Service Changed notification that is currently being processed by performing service |
| // discovery. |
| std::optional<ServiceChangedState> current_service_change_; |
| |
| // Callbacks passed to MaybeHandleNextServiceChangedNotification. Will be called when all queued |
| // service changes have been processed. |
| std::vector<fit::callback<void()>> service_changes_complete_callbacks_; |
| |
| fxl::WeakPtrFactory<RemoteServiceManager> weak_ptr_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(RemoteServiceManager); |
| }; |
| |
| } // namespace internal |
| } // namespace bt::gatt |
| |
| #endif // SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_GATT_REMOTE_SERVICE_MANAGER_H_ |