// Copyright 2020 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_API_BLE_V2_H_ #define PLATFORM_API_BLE_V2_H_ #include #include #include #include #include #include #include #include "absl/container/flat_hash_map.h" #include "absl/functional/any_invocable.h" #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/str_format.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "internal/platform/byte_array.h" #include "internal/platform/cancellation_flag.h" #include "internal/platform/exception.h" #include "internal/platform/input_stream.h" #include "internal/platform/mac_address.h" #include "internal/platform/output_stream.h" #include "internal/platform/uuid.h" namespace nearby { namespace api { namespace ble_v2 { // Coarse representation of power settings throughout all BLE operations. enum class TxPowerLevel { kUnknown = 0, kUltraLow = 1, kLow = 2, kMedium = 3, kHigh = 4, }; // https://developer.android.com/reference/android/bluetooth/le/AdvertisingSetParameters.Builder // // The preferences for Advertising. struct AdvertiseParameters { // The transmission power level for the advertising. TxPowerLevel tx_power_level; // Whether the advertisement type should be connectable or non-connectable. bool is_connectable; }; // https://developer.android.com/reference/android/bluetooth/le/AdvertiseData // // Bundle of data found in a BLE advertisement. // // All service UUIDs will conform to the 16-bit Bluetooth base UUID, // 0000xxxx-0000-1000-8000-00805F9B34FB. This makes it possible to store two // byte service UUIDs in the advertisement. struct BleAdvertisementData { // Broadcasts a BLE extended advertisement if it is true. bool is_extended_advertisement; // Maps service UUIDs to their service data. // // For each platform should follow to set the service UUID(key) and service // data(value): // // iOS : 16 bit service UUID (type=0x03) + LocalName data (type=0x08) // Windows: Service data (type=0x16) // Android: 16 bit service UUID (type=0x03) + Service data (type=0x16) absl::flat_hash_map service_data; }; // Opaque wrapper over a BLE peripheral. Must be able to uniquely identify a // peripheral so that we can connect to its GATT server. class BlePeripheral { public: using UniqueId = std::uint64_t; BlePeripheral(UniqueId unique_id, MacAddress address) : unique_id_(unique_id), address_(std::move(address)) {} explicit BlePeripheral(UniqueId unique_id = 0) : unique_id_(unique_id) {} virtual ~BlePeripheral() = default; // https://developer.android.com/reference/android/bluetooth/BluetoothDevice#getAddress() // // Returns the current address. // // This will always be an empty string on Apple platforms. virtual std::string GetAddress() const { if (address_.IsSet()) { return address_.ToString(); } return ""; } // Returns an immutable unique identifier. The identifier must not change when // the BLE address is rotated. virtual UniqueId GetUniqueId() const { return unique_id_; } // Sets platform specific data that can be retrieved by `GetPlatformData()`. void SetPlatformData(void* platform_data) { platform_data_ = platform_data; } void* GetPlatformData() const { return platform_data_; } bool IsSet() const { return GetUniqueId() != 0 || !GetAddress().empty(); } private: UniqueId unique_id_ = 0; MacAddress address_; void* platform_data_ = nullptr; }; // https://developer.android.com/reference/android/bluetooth/BluetoothGattCharacteristic // // Representation of a GATT characteristic. struct GattCharacteristic { // Represents the GATT characteristic permissions // This enumeration supports a bitwise combination of its member values. // |, &, |= of the values are legal. enum class Permission { kNone = 0, kRead = 1 << 0, kWrite = 1 << 1, kLast, }; // Represents the GATT characteristic properties // This enumeration supports a bitwise combination of its member values. // |, &, |= of the values are legal. enum class Property { kNone = 0, kRead = 1 << 0, kWrite = 1 << 1, kIndicate = 1 << 2, kNotify = 1 << 3, kLast, }; Uuid uuid; Uuid service_uuid; Permission permission; Property property; // overloading operator for enum class Permission and Property friend inline Permission operator|(Permission a, Permission b) { return static_cast(static_cast(a) | static_cast(b)); } friend inline Permission operator&(Permission a, Permission b) { return static_cast(static_cast(a) & static_cast(b)); } friend inline Permission& operator|=(Permission& a, Permission b) { a = a | b; return a; } friend inline Property operator|(Property a, Property b) { return static_cast(static_cast(a) | static_cast(b)); } friend inline Property operator&(Property a, Property b) { return static_cast(static_cast(a) & static_cast(b)); } friend inline Property& operator|=(Property& a, Property b) { a = a | b; return a; } // Hashable template friend H AbslHashValue(H h, const GattCharacteristic& s) { return H::combine(std::move(h), s.uuid, s.service_uuid, s.permission, s.property); } bool operator==(const GattCharacteristic& rhs) const { return this->uuid == rhs.uuid && this->service_uuid == rhs.service_uuid && this->permission == rhs.permission && this->property == rhs.property; } template friend void AbslStringify(Sink& sink, const GattCharacteristic& c) { absl::Format(&sink, "(service:%s, uuid:%s, permission:%v, property%v)", std::string(c.service_uuid), std::string(c.uuid), c.permission, c.property); } }; // https://developer.android.com/reference/android/bluetooth/BluetoothGatt // // Representation of a client GATT connection to a remote GATT server. class GattClient { public: // Specifies what type of GATT write should be performed. enum class WriteType { kWithResponse = 0, kWithoutResponse = 1, }; virtual ~GattClient() = default; // 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. virtual bool DiscoverServiceAndCharacteristics( const Uuid& service_uuid, const std::vector& characteristic_uuids) = 0; // 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) virtual absl::optional GetCharacteristic( const Uuid& service_uuid, const Uuid& characteristic_uuid) = 0; // 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) virtual absl::optional ReadCharacteristic( const GattCharacteristic& characteristic) = 0; // 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. virtual bool WriteCharacteristic(const GattCharacteristic& characteristic, absl::string_view value, WriteType type) = 0; // https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#setCharacteristicNotification(android.bluetooth.BluetoothGattCharacteristic,%20boolean) // // Enable or disable notifications/indications for a given characteristic. virtual bool SetCharacteristicSubscription( const GattCharacteristic& characteristic, bool enable, absl::AnyInvocable on_characteristic_changed_cb) = 0; // https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#disconnect() virtual void Disconnect() = 0; }; // https://developer.android.com/reference/android/bluetooth/BluetoothGattServer // // Representation of a BLE GATT server. // LINT.IfChange class GattServer { public: virtual ~GattServer() = default; // Creates a characteristic and adds it to the GATT server under the given // characteristic and service UUIDs. Returns no value upon error. // // Characteristics of the same service UUID should be put under one // service rather than many services with the same UUID. // // If the INDICATE property is included, the characteristic should include // the official Bluetooth Client Characteristic Configuration descriptor // with UUID 0x2902 and a WRITE permission. This allows remote clients to // write to this descriptor and subscribe for characteristic changes. For // more information about this descriptor, please go to: // https://www.bluetooth.com/specifications/Gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.Gatt.client_characteristic_configuration.xml // NOLINTNEXTLINE(google3-legacy-absl-backports) virtual absl::optional CreateCharacteristic( const Uuid& service_uuid, const Uuid& characteristic_uuid, GattCharacteristic::Permission permission, GattCharacteristic::Property property) = 0; // https://developer.android.com/reference/android/bluetooth/BluetoothGattCharacteristic.html#setValue(byte[]) // // Locally updates the value of a characteristic and returns whether or not // it was successful. virtual bool UpdateCharacteristic(const GattCharacteristic& characteristic, const nearby::ByteArray& value) = 0; // https://developer.android.com/reference/android/bluetooth/BluetoothGattServer#notifyCharacteristicChanged(android.bluetooth.BluetoothDevice,%20android.bluetooth.BluetoothGattCharacteristic,%20boolean,%20byte[]) // // Send a notification or indication that a local characteristic has been // updated and returns an absl::Status indicating success or what went wrong. virtual absl::Status NotifyCharacteristicChanged( const GattCharacteristic& characteristic, bool confirm, const ByteArray& new_value) = 0; // Stops a GATT server. virtual void Stop() = 0; }; // LINT.ThenChange(//depot/google3/third_party/nearby/internal/platform/ble_v2.h) // Callback for asynchronous events on the client side of a GATT connection. struct ClientGattConnectionCallback { public: // Called when the characteristic is changed absl::AnyInvocable on_characteristic_changed_cb = [](absl::string_view) {}; // Called when the client is disconnected from the GATT server. absl::AnyInvocable disconnected_cb = []() {}; }; // Callback for asynchronous events on the server side of a GATT connection. struct ServerGattConnectionCallback { using ReadValueCallback = absl::AnyInvocable data)>; using WriteValueCallback = absl::AnyInvocable; // Called when a remote peripheral connected to us and subscribed to one of // our characteristics. absl::AnyInvocable characteristic_subscription_cb; // Called when a remote peripheral unsubscribed from one of our // characteristics. absl::AnyInvocable characteristic_unsubscription_cb; // Called when a gatt client is reading from the characteristic. // Must call `callback` with the read result. // When a characteristic has a static value set with // `GattServer::UpdateCharacteristic()`, then reading from the characteristic // yields that static value. The read callback is not called. // Otherwise, the gatt server calls the read callback to get the value. absl::AnyInvocable on_characteristic_read_cb; // Called when a gatt client is writing to the characteristic. // Must call `callback` with the write result. absl::AnyInvocable on_characteristic_write_cb; }; // A BLE GATT client socket for requesting GATT socket. class BleSocket { public: virtual ~BleSocket() = default; // Returns the InputStream of the BleSocket. // On error, returned stream will report Exception::kIo on any operation. // // The returned object is not owned by the caller, and can be invalidated once // the BleSocket object is destroyed. virtual InputStream& GetInputStream() = 0; // Returns the OutputStream of the BleSocket. // On error, returned stream will report Exception::kIo on any operation. // // The returned object is not owned by the caller, and can be invalidated once // the BleSocket object is destroyed. virtual OutputStream& GetOutputStream() = 0; // Returns Exception::kIo on error, Exception::kSuccess otherwise. virtual Exception Close() = 0; // Returns BlePeripheral::UniqueId that is connected to this socket. virtual BlePeripheral::UniqueId GetRemotePeripheralId() = 0; }; // A BLE GATT server socket for listening incoming GATT socket. class BleServerSocket { public: virtual ~BleServerSocket() = default; // Blocks until either: // - at least one incoming connection request is available, or // - ServerSocket is closed. // On success, returns connected socket, ready to exchange data. // Returns nullptr on error. // Once error is reported, it is permanent, and ServerSocket has to be closed. virtual std::unique_ptr Accept() = 0; // Returns Exception::kIo on error, Exception::kSuccess otherwise. virtual Exception Close() = 0; }; // A BLE L2CAP client socket for requesting L2CAP socket. class BleL2capSocket { public: virtual ~BleL2capSocket() = default; // Returns the InputStream of the BleL2capSocket. // On error, returned stream will report Exception::kIo on any operation. // // The returned object is not owned by the caller, and can be invalidated once // the BleL2capSocket object is destroyed. virtual InputStream& GetInputStream() = 0; // Returns the OutputStream of the BleL2capSocket. // On error, returned stream will report Exception::kIo on any operation. // // The returned object is not owned by the caller, and can be invalidated once // the BleL2capSocket object is destroyed. virtual OutputStream& GetOutputStream() = 0; // Returns Exception::kIo on error, Exception::kSuccess otherwise. virtual Exception Close() = 0; // Sets the close notifier by client side. virtual void SetCloseNotifier(absl::AnyInvocable notifier) {} // Returns valid BlePeripheral pointer if there is a connection, and // nullptr otherwise. virtual BlePeripheral::UniqueId GetRemotePeripheralId() = 0; }; // A BLE L2CAP server socket for listening incoming L2CAP socket. class BleL2capServerSocket { public: virtual ~BleL2capServerSocket() = default; // Gets PSM value has been published by the server. virtual int GetPSM() const = 0; // Blocks until either: // - at least one incoming connection request is available, or // - ServerSocket is closed. // On success, returns connected socket, ready to exchange data. // Returns nullptr on error. // Once error is reported, it is permanent, and L2CAP ServerSocket has to be // closed. virtual std::unique_ptr Accept() = 0; // Closes the L2CAP server socket. virtual Exception Close() = 0; }; // The main BLE medium used inside of Nearby. This serves as the entry point // for all BLE and GATT related operations. class BleMedium { public: virtual ~BleMedium() = default; // https://developer.android.com/reference/android/bluetooth/le/BluetoothLeAdvertiser.html#startAdvertising(android.bluetooth.le.AdvertiseSettings,%20android.bluetooth.le.AdvertiseData,%20android.bluetooth.le.AdvertiseData,%20android.bluetooth.le.AdvertiseCallback) // // Starts BLE advertising and returns whether or not it was successful. virtual bool StartAdvertising( const BleAdvertisementData& advertising_data, AdvertiseParameters advertise_set_parameters) = 0; struct AdvertisingCallback { absl::AnyInvocable start_advertising_result; }; struct AdvertisingSession { absl::AnyInvocable stop_advertising; }; // Async interface for StartAdvertising. // Result status will be passed to start_advertising_result callback. // To stop advertising, invoke the stop_advertising callback in // AdvertisingSession. virtual std::unique_ptr StartAdvertising( const BleAdvertisementData& advertising_data, AdvertiseParameters advertise_set_parameters, AdvertisingCallback callback) = 0; // https://developer.android.com/reference/android/bluetooth/le/BluetoothLeAdvertiser.html#stopAdvertising(android.bluetooth.le.AdvertiseCallback) // // Stops advertising. virtual bool StopAdvertising() = 0; // https://developer.android.com/reference/android/bluetooth/le/ScanCallback // // Callback for BLE scan results. // // The passed in advertisement_data is the merged combination of both // advertisement data and scan response. // // Every discovery of an advertisement should be reported, even if the // advertisement was discovered before. // // The peripheral is owned by platform implementation and it should outlive // for the whole peripheral(device) connection life cycle. struct ScanCallback { absl::AnyInvocable advertisement_found_cb = [](BlePeripheral::UniqueId, BleAdvertisementData) {}; }; // https://developer.android.com/reference/android/bluetooth/le/BluetoothLeScanner.html#startScan(java.util.List%3Candroid.bluetooth.le.ScanFilter%3E,%20android.bluetooth.le.ScanSettings,%20android.bluetooth.le.ScanCallback) // // Starts scanning and returns whether or not it was successful. // // TX Power level should be interpreted in the following way: // LOW: // - Scan window = ~512ms // - Scan interval = ~5120ms // HIGH: // - Scan window = ~4096ms // - Scan interval = ~4096ms virtual bool StartScanning(const Uuid& service_uuid, TxPowerLevel tx_power_level, ScanCallback callback) = 0; // Starts multiple services scanning and returns whether or not it was // successful. // // service_uuids: The UUIDs of the services to scan for. They are logical OR'd // together. // tx_power_level: The power level to use for the scan. // callback: The callback to invoke for every discovered advertisement. virtual bool StartMultipleServicesScanning( const std::vector& service_uuids, TxPowerLevel tx_power_level, ScanCallback callback) { return false; } // https://developer.android.com/reference/android/bluetooth/le/BluetoothLeScanner.html#stopScan(android.bluetooth.le.ScanCallback) // // Stops scanning. virtual bool StopScanning() = 0; virtual bool PauseMediumScanning() { return true; } virtual bool ResumeMediumScanning() { return true; } struct ScanningSession { absl::AnyInvocable stop_scanning; }; struct ScanningCallback { absl::AnyInvocable start_scanning_result = [](absl::Status) {}; absl::AnyInvocable advertisement_found_cb = [](BlePeripheral::UniqueId, BleAdvertisementData) {}; absl::AnyInvocable advertisement_lost_cb = [](BlePeripheral::UniqueId) {}; }; // Async interface for StartScanning. // Result status will be passed to start_scanning_result callback. // To stop scanning, invoke the stop_scanning callback in // ScanningSession. virtual std::unique_ptr StartScanning( const Uuid& service_uuid, TxPowerLevel tx_power_level, ScanningCallback callback) = 0; // https://developer.android.com/reference/android/bluetooth/BluetoothManager#openGattServer(android.content.Context,%20android.bluetooth.BluetoothGattServerCallback) // // Starts a GATT server. Returns a nullptr upon error. virtual std::unique_ptr StartGattServer( ServerGattConnectionCallback callback) = 0; // https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#connectGatt(android.content.Context,%20boolean,%20android.bluetooth.BluetoothGattCallback) // https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#requestConnectionPriority(int) // https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#requestMtu(int) // // Connects to a GATT server and negotiates the specified connection // parameters. Returns nullptr upon error. // // TX Power level should be interpreted in the following way: // HIGH: // - Connection interval = ~11.25ms - 15ms // LOW: // - Connection interval = ~100ms - 125ms virtual std::unique_ptr ConnectToGattServer( BlePeripheral::UniqueId peripheral_id, TxPowerLevel tx_power_level, ClientGattConnectionCallback callback) = 0; // Opens a BLE server socket based on service ID. // // On success, returns a new BleServerSocket. // On error, returns nullptr. virtual std::unique_ptr OpenServerSocket( const std::string& service_id) = 0; // Opens a BLE l2cap server socket based on service ID. // // On success, returns a new BleL2capServerSocket. // On error, returns nullptr. // Platform implementation should override this method if it supports L2CAP. virtual std::unique_ptr OpenL2capServerSocket( const std::string& service_id) { return nullptr; } // Connects to a BLE peripheral. // // On success, returns a new BleSocket. // On error, returns nullptr. virtual std::unique_ptr Connect( const std::string& service_id, TxPowerLevel tx_power_level, BlePeripheral::UniqueId peripheral_id, CancellationFlag* cancellation_flag) = 0; // Connects to a BLE peripheral over L2CAP. // // On success, returns a new BleL2capSocket. // On error, returns nullptr. // Platform implementation should override this method if it supports L2CAP. virtual std::unique_ptr ConnectOverL2cap( int psm, const std::string& service_id, TxPowerLevel tx_power_level, BlePeripheral::UniqueId peripheral_id, CancellationFlag* cancellation_flag) { return nullptr; } // Requests if support extended advertisement. virtual bool IsExtendedAdvertisementsAvailable() = 0; virtual void AddAlternateUuidForService(uint16_t uuid, const std::string& service_id) {} // Retrieves a BlePeripheral ID from a native BLE peripheral id. // The native ID is platform specific and is used to identify a BLE // peripheral. On iOS, it should be NSUUID in string format. On other // platforms, it should be the Bluetooth address in string format // "XX:XX:XX:XX:XX:XX". // // Returns std::nullopt if cannot retrieve the BlePeripheral from the native // BLE peripheral id. virtual std::optional RetrieveBlePeripheralIdFromNativeId( const std::string& ble_peripheral_native_id) { return std::nullopt; } }; } // namespace ble_v2 } // namespace api } // namespace nearby #endif // PLATFORM_API_BLE_V2_H_