mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-14 22:56:12 -04:00
219 lines
9.8 KiB
C++
219 lines
9.8 KiB
C++
// Copyright 2023 Google LLC
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#ifndef PLATFORM_IMPL_LINUX_API_BLE_GATT_CLIENT_H_
|
|
#define PLATFORM_IMPL_LINUX_API_BLE_GATT_CLIENT_H_
|
|
|
|
#include <list>
|
|
|
|
#include <sdbus-c++/IConnection.h>
|
|
#include <sdbus-c++/Types.h>
|
|
|
|
#include "absl/container/flat_hash_map.h"
|
|
#include "absl/synchronization/mutex.h"
|
|
#include "internal/platform/cancellation_flag.h"
|
|
#include "internal/platform/implementation/ble.h"
|
|
#include "internal/platform/implementation/linux/bluez.h"
|
|
#include "internal/platform/implementation/linux/bluez_gatt_characteristic_client.h"
|
|
#include "internal/platform/implementation/linux/bluez_gatt_service_client.h"
|
|
#include "internal/platform/logging.h"
|
|
|
|
namespace nearby {
|
|
namespace linux {
|
|
|
|
class BluezGattDiscovery final : public bluez::BluezObjectManager {
|
|
public:
|
|
explicit BluezGattDiscovery(std::shared_ptr<sdbus::IConnection> system_bus)
|
|
: bluez::BluezObjectManager(*system_bus),
|
|
system_bus_(system_bus),
|
|
shutdown_(false),
|
|
pending_discovery_(0) {}
|
|
~BluezGattDiscovery() override { Shutdown(); }
|
|
|
|
bool InitializeKnownServices() ABSL_LOCKS_EXCLUDED(mutex_);
|
|
|
|
using CallbackIter = typename std::list<absl::AnyInvocable<void()>>::iterator;
|
|
CallbackIter AddPeripheralConnection(
|
|
const sdbus::ObjectPath &device_object_path,
|
|
absl::AnyInvocable<void()> disconnected_callback_)
|
|
ABSL_LOCKS_EXCLUDED(peripheral_disconnected_callbacks_mutex_);
|
|
void RemovePeripheralConnection(const sdbus::ObjectPath &device_object_path,
|
|
BluezGattDiscovery::CallbackIter cb)
|
|
ABSL_LOCKS_EXCLUDED(peripheral_disconnected_callbacks_mutex_);
|
|
|
|
bool DiscoverServiceAndCharacteristics(
|
|
const sdbus::ObjectPath &device_object_path, const Uuid &service_uuid,
|
|
const std::vector<Uuid> &characteristic_uuids, CancellationFlag &cancel)
|
|
ABSL_LOCKS_EXCLUDED(mutex_);
|
|
std::unique_ptr<bluez::GattCharacteristicClient> GetCharacteristic(
|
|
const sdbus::ObjectPath &device_object_path, const Uuid &service_uuid,
|
|
const Uuid &characteristic_uuid) ABSL_LOCKS_EXCLUDED(mutex_);
|
|
std::unique_ptr<bluez::GattCharacteristicClient> GetSubscribedCharacteristic(
|
|
const sdbus::ObjectPath &device_object_path, const Uuid &service_uuid,
|
|
const Uuid &characteristic_uuid,
|
|
absl::AnyInvocable<void(absl::string_view value)>
|
|
on_characteristic_changed_cb) ABSL_LOCKS_EXCLUDED(mutex_);
|
|
|
|
protected:
|
|
void onInterfacesAdded(
|
|
const sdbus::ObjectPath &objectPath,
|
|
const std::map<sdbus::InterfaceName,
|
|
std::map<sdbus::PropertyName, sdbus::Variant>>
|
|
&interfacesAndProperties) override ABSL_LOCKS_EXCLUDED(mutex_);
|
|
void onInterfacesRemoved(const sdbus::ObjectPath &objectPath,
|
|
const std::vector<sdbus::InterfaceName> &interfaces)
|
|
override ABSL_LOCKS_EXCLUDED(mutex_);
|
|
|
|
private:
|
|
std::optional<std::tuple<Uuid, Uuid, sdbus::ObjectPath>>
|
|
characteristicProperties(
|
|
const sdbus::ObjectPath &char_path,
|
|
const std::map<sdbus::PropertyName, sdbus::Variant> &properties)
|
|
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
|
|
|
void Shutdown() ABSL_LOCKS_EXCLUDED(mutex_);
|
|
|
|
std::shared_ptr<sdbus::IConnection> system_bus_;
|
|
|
|
absl::Mutex peripheral_disconnected_callbacks_mutex_;
|
|
absl::flat_hash_map<sdbus::ObjectPath, std::list<absl::AnyInvocable<void()>>>
|
|
peripheral_disconnected_callbacks_
|
|
ABSL_GUARDED_BY(peripheral_disconnected_callbacks_mutex_);
|
|
|
|
absl::Mutex mutex_;
|
|
absl::flat_hash_map<sdbus::ObjectPath, std::unique_ptr<GattServiceClient>>
|
|
cached_services_ ABSL_GUARDED_BY(mutex_);
|
|
// Tuple order: service uuid, characteristic uuid, device object path.
|
|
absl::flat_hash_map<std::tuple<Uuid, Uuid, sdbus::ObjectPath>,
|
|
sdbus::ObjectPath>
|
|
discovered_characteristics_ ABSL_GUARDED_BY(mutex_);
|
|
absl::flat_hash_map<sdbus::ObjectPath,
|
|
std::tuple<Uuid, Uuid, sdbus::ObjectPath>>
|
|
characteristics_properties_ ABSL_GUARDED_BY(mutex_);
|
|
bool shutdown_ ABSL_GUARDED_BY(mutex_);
|
|
std::size_t pending_discovery_ ABSL_GUARDED_BY(mutex_);
|
|
};
|
|
|
|
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt
|
|
//
|
|
// Representation of a client GATT connection to a remote GATT server.
|
|
class GattClient : public api::ble::GattClient {
|
|
public:
|
|
GattClient(const GattClient &) = delete;
|
|
GattClient(GattClient &&) = delete;
|
|
GattClient &operator=(const GattClient &) = delete;
|
|
GattClient &operator=(GattClient &&) = delete;
|
|
|
|
explicit GattClient(std::shared_ptr<sdbus::IConnection> system_bus,
|
|
const sdbus::ObjectPath &peripheral_object_path,
|
|
std::shared_ptr<BluezGattDiscovery> gatt_discovery,
|
|
absl::AnyInvocable<void()> disconnected_callback)
|
|
: system_bus_(std::move(system_bus)),
|
|
peripheral_object_path_(peripheral_object_path),
|
|
gatt_discovery_(std::move(gatt_discovery)),
|
|
discovery_cancel_(false) {
|
|
LOG(INFO) << __func__
|
|
<< ": Creating GATT client for peripheral "
|
|
<< peripheral_object_path_;
|
|
disconnected_callback_it_ = gatt_discovery_->AddPeripheralConnection(
|
|
peripheral_object_path_, std::move(disconnected_callback));
|
|
}
|
|
~GattClient() override {
|
|
LOG(INFO) << __func__
|
|
<< ": Destroying GATT client for peripheral "
|
|
<< peripheral_object_path_;
|
|
absl::MutexLock lock(&disconnected_callback_mutex_);
|
|
if (!discovery_cancel_.Cancelled()) {
|
|
discovery_cancel_.Cancel();
|
|
gatt_discovery_->RemovePeripheralConnection(peripheral_object_path_,
|
|
disconnected_callback_it_);
|
|
}
|
|
}
|
|
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#discoverServices()
|
|
//
|
|
// Discovers available service and characteristics on this connection.
|
|
// Returns whether or not discovery finished successfully.
|
|
//
|
|
// This function should block until discovery has finished.
|
|
bool DiscoverServiceAndCharacteristics(
|
|
const Uuid &service_uuid,
|
|
const std::vector<Uuid> &characteristic_uuids) override;
|
|
|
|
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#getService(java.util.UUID)
|
|
// https://developer.android.com/reference/android/bluetooth/BluetoothGattService.html#getCharacteristic(java.util.UUID)
|
|
//
|
|
// Retrieves a GATT characteristic. On error, does not return a value.
|
|
//
|
|
// DiscoverServiceAndCharacteristics() should be called before this method to
|
|
// fetch all available services and characteristics first.
|
|
//
|
|
// It is okay for duplicate services to exist, as long as the specified
|
|
// characteristic UUID is unique among all services of the same UUID.
|
|
// NOLINTNEXTLINE(google3-legacy-absl-backports)
|
|
absl::optional<api::ble::GattCharacteristic> GetCharacteristic(
|
|
const Uuid &service_uuid, const Uuid &characteristic_uuid) override
|
|
ABSL_LOCKS_EXCLUDED(characteristics_mutex_);
|
|
|
|
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#readCharacteristic(android.bluetooth.BluetoothGattCharacteristic)
|
|
// https://developer.android.com/reference/android/bluetooth/BluetoothGattCharacteristic.html#getValue()
|
|
// NOLINTNEXTLINE(google3-legacy-absl-backports)
|
|
absl::optional<std::string> ReadCharacteristic(
|
|
const api::ble::GattCharacteristic &characteristic) override
|
|
ABSL_LOCKS_EXCLUDED(characteristics_mutex_);
|
|
|
|
// https://developer.android.com/reference/android/bluetooth/BluetoothGattCharacteristic.html#setValue(byte[])
|
|
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#writeCharacteristic(android.bluetooth.BluetoothGattCharacteristic)
|
|
//
|
|
// Sends a remote characteristic write request to the server and returns
|
|
// whether or not it was successful.
|
|
bool WriteCharacteristic(
|
|
const api::ble::GattCharacteristic &characteristic,
|
|
absl::string_view value, WriteType type) override
|
|
ABSL_LOCKS_EXCLUDED(characteristics_mutex_);
|
|
|
|
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#setCharacteristicNotification(android.bluetooth.BluetoothGattCharacteristic,%20boolean)
|
|
//
|
|
// Enable or disable notifications/indications for a given characteristic.
|
|
bool SetCharacteristicSubscription(
|
|
const api::ble::GattCharacteristic &characteristic, bool enable,
|
|
absl::AnyInvocable<void(absl::string_view value)>
|
|
on_characteristic_changed_cb) override
|
|
ABSL_LOCKS_EXCLUDED(characteristics_mutex_);
|
|
|
|
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#disconnect()
|
|
void Disconnect() override;
|
|
|
|
private:
|
|
std::shared_ptr<sdbus::IConnection> system_bus_;
|
|
sdbus::ObjectPath peripheral_object_path_;
|
|
std::shared_ptr<BluezGattDiscovery> gatt_discovery_;
|
|
|
|
absl::Mutex disconnected_callback_mutex_;
|
|
BluezGattDiscovery::CallbackIter disconnected_callback_it_
|
|
ABSL_GUARDED_BY(disconnected_callback_mutex_);
|
|
CancellationFlag discovery_cancel_;
|
|
|
|
using CharacteristicProxy =
|
|
std::variant<std::unique_ptr<bluez::GattCharacteristicClient>,
|
|
std::unique_ptr<bluez::SubscribedGattCharacteristicClient>>;
|
|
absl::Mutex characteristics_mutex_;
|
|
absl::flat_hash_map<api::ble::GattCharacteristic, CharacteristicProxy>
|
|
characteristics_ ABSL_GUARDED_BY(characteristics_mutex_);
|
|
};
|
|
|
|
} // namespace linux
|
|
} // namespace nearby
|
|
|
|
#endif
|