blob: cf21bba0f90e79fbc6cdaa1c133093808bff829f [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef EXTENSIONS_BROWSER_API_BLUETOOTH_LOW_ENERGY_BLUETOOTH_LOW_ENERGY_EVENT_ROUTER_H_
#define EXTENSIONS_BROWSER_API_BLUETOOTH_LOW_ENERGY_BLUETOOTH_LOW_ENERGY_EVENT_ROUTER_H_
#include <cstddef>
#include <cstdint>
#include <map>
#include <optional>
#include <set>
#include <string>
#include <vector>
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "base/types/expected.h"
#include "base/values.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/bluetooth_gatt_characteristic.h"
#include "device/bluetooth/bluetooth_gatt_connection.h"
#include "device/bluetooth/bluetooth_gatt_notify_session.h"
#include "device/bluetooth/bluetooth_local_gatt_service.h"
#include "device/bluetooth/bluetooth_remote_gatt_service.h"
#include "device/bluetooth/public/cpp/bluetooth_uuid.h"
#include "extensions/browser/extension_event_histogram_value.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_registry_observer.h"
#include "extensions/common/api/bluetooth_low_energy.h"
#include "extensions/common/extension_id.h"
namespace content {
class BrowserContext;
} // namespace content
namespace device {
class BluetoothLocalGattCharacteristic;
class BluetoothLocalGattDescriptor;
class BluetoothRemoteGattCharacteristic;
class BluetoothRemoteGattDescriptor;
} // namespace device
namespace extensions {
class BluetoothLowEnergyConnection;
class BluetoothLowEnergyNotifySession;
class Extension;
// The BluetoothLowEnergyEventRouter is used by the bluetoothLowEnergy API to
// interface with the internal Bluetooth API in device/bluetooth.
class BluetoothLowEnergyEventRouter
: public device::BluetoothAdapter::Observer,
public device::BluetoothLocalGattService::Delegate,
public extensions::ExtensionRegistryObserver {
public:
struct AttributeValueRequest {
public:
enum RequestType { ATTRIBUTE_READ_REQUEST, ATTRIBUTE_WRITE_REQUEST };
AttributeValueRequest(Delegate::ValueCallback value_callback,
Delegate::ErrorCallback error_callback);
AttributeValueRequest(base::OnceClosure success_callback,
Delegate::ErrorCallback error_callback);
AttributeValueRequest(const AttributeValueRequest&) = delete;
AttributeValueRequest& operator=(const AttributeValueRequest&) = delete;
~AttributeValueRequest();
RequestType type;
Delegate::ValueCallback value_callback;
base::OnceClosure success_callback;
Delegate::ErrorCallback error_callback;
};
explicit BluetoothLowEnergyEventRouter(content::BrowserContext* context);
BluetoothLowEnergyEventRouter(const BluetoothLowEnergyEventRouter&) = delete;
BluetoothLowEnergyEventRouter& operator=(
const BluetoothLowEnergyEventRouter&) = delete;
~BluetoothLowEnergyEventRouter() override;
// Possible ways that an API method can fail or succeed.
enum Status {
kStatusSuccess = 0,
kStatusErrorAlreadyConnected,
kStatusErrorAlreadyNotifying,
kStatusErrorAuthenticationFailed,
kStatusErrorCanceled,
kStatusErrorFailed,
kStatusErrorGattNotSupported,
kStatusErrorHigherSecurity,
kStatusErrorInProgress,
kStatusErrorInsufficientAuthorization,
kStatusErrorInvalidArguments,
kStatusErrorInvalidLength,
kStatusErrorNotConnected,
kStatusErrorNotFound,
kStatusErrorNotNotifying,
kStatusErrorPermissionDenied,
kStatusErrorTimeout,
kStatusErrorUnsupportedDevice,
kStatusErrorInvalidServiceId,
kStatusErrorNotReady,
kStatusErrorAlreadyExists,
kStatusErrorDoesNotExist,
};
// Error callback is used by asynchronous methods to report failures.
using ErrorCallback = base::OnceCallback<void(Status)>;
// Returns true if Bluetooth is supported on the current platform or if the
// internal |adapter_| instance has been initialized for testing.
bool IsBluetoothSupported() const;
// Obtains a handle on the BluetoothAdapter and invokes |callback|. Returns
// false, if Bluetooth is not supported. Otherwise, asynchronously initializes
// it and invokes |callback|. Until the first successful call to this method,
// none of the methods in this class will succeed and no device::Bluetooth*
// API events will be observed.
bool InitializeAdapterAndInvokeCallback(base::OnceClosure callback);
// Returns true, if the BluetoothAdapter was initialized.
bool HasAdapter() const;
// Creates a GATT connection to the device with address |device_address| for
// extension |extension|. The connection is kept alive until the extension is
// unloaded, the device is removed, or is disconnect by the host subsystem.
// |callback| is called with the status of the connect operation. If
// |persistent| is true, then the allocated connection resource is persistent
// across unloads.
void Connect(bool persistent,
const Extension* extension,
const std::string& device_address,
ErrorCallback callback);
// Disconnects the currently open GATT connection of extension |extension| to
// device with address |device_address|. |error_callback| is called with an
// error status in case of failure, e.g. if the device is not found or the
// given
// extension does not have an open connection to the device.
void Disconnect(const Extension* extension,
const std::string& device_address,
base::OnceClosure callback,
ErrorCallback error_callback);
// Returns the list of api::bluetooth_low_energy::Service objects
// associated with the Bluetooth device with address |device_address| if
// successful. Otherwise returns std::nullopt, if no device with the given
// address is known. If the device is found but it has no GATT services, then
// returns an empty list.
typedef std::vector<api::bluetooth_low_energy::Service> ServiceList;
std::optional<ServiceList> GetServices(
const std::string& device_address) const;
// Returns a service based on GATT service with instance ID |instance_id| if
// successful. Otherwise returns an error status.
base::expected<api::bluetooth_low_energy::Service, Status> GetService(
const std::string& instance_id) const;
// Returns a service list of GATT services that are included
// by the GATT service with instance ID |instance_id|. Returns an error status
// if no GATT service with the given ID is known. If the given service has no
// included services, then an empty list is returned.
base::expected<ServiceList, Status> GetIncludedServices(
const std::string& instance_id) const;
// Returns the list of api::bluetooth_low_energy::Characteristic objects
// associated with the GATT service with instance ID |instance_id|. Returns an
// error status, if no service with the given instance ID is known. If the
// service is found but it has no characteristics, then returns empty list.
// |extension| is the extension that made the call.
typedef std::vector<api::bluetooth_low_energy::Characteristic>
CharacteristicList;
base::expected<CharacteristicList, Status> GetCharacteristics(
const Extension* extension,
const std::string& instance_id) const;
// Returns a Characteristic based on GATT characteristic with instance
// ID |instance_id|. |extension| is the
// extension that made the call.
base::expected<api::bluetooth_low_energy::Characteristic, Status>
GetCharacteristic(const Extension* extension,
const std::string& instance_id) const;
// Returns the list of api::bluetooth_low_energy::Descriptor objects
// associated with the GATT characteristic with instance ID |instance_id|. If
// the characteristic is found but it has no descriptors, then returns an
// empty list. In case of failure, returns a error status. |extension| is the
// extension that made the call.
typedef std::vector<api::bluetooth_low_energy::Descriptor> DescriptorList;
base::expected<DescriptorList, Status> GetDescriptors(
const Extension* extension,
const std::string& instance_id) const;
// Returns a descriptor based on GATT characteristic descriptor with
// instance ID |instance_id|. |extension| is the extension that made the call.
base::expected<api::bluetooth_low_energy::Descriptor, Status> GetDescriptor(
const Extension* extension,
const std::string& instance_id) const;
// Sends a request to read the value of the characteristic with intance ID
// |instance_id|. Invokes |callback| on success and |error_callback| on
// failure. |extension| is the extension that made the call.
void ReadCharacteristicValue(const Extension* extension,
const std::string& instance_id,
base::OnceClosure callback,
ErrorCallback error_callback);
// Sends a request to write the value of the characteristic with instance ID
// |instance_id|. Invokes |callback| on success and |error_callback| on
// failure. |extension| is the extension that made the call.
void WriteCharacteristicValue(const Extension* extension,
const std::string& instance_id,
const std::vector<uint8_t>& value,
base::OnceClosure callback,
ErrorCallback error_callback);
// Sends a request to start characteristic notifications from characteristic
// with instance ID |instance_id|, for extension |extension|. Invokes
// |callback| on success and |error_callback| on failure. If |persistent| is
// true, then the allocated connection resource is persistent across unloads.
void StartCharacteristicNotifications(bool persistent,
const Extension* extension,
const std::string& instance_id,
base::OnceClosure callback,
ErrorCallback error_callback);
// Sends a request to stop characteristic notifications from characteristic
// with instance ID |instance_id|, for extension |extension|. Invokes
// |callback| on success and |error_callback| on failure.
void StopCharacteristicNotifications(const Extension* extension,
const std::string& instance_id,
base::OnceClosure callback,
ErrorCallback error_callback);
// Sends a request to read the value of the descriptor with instance ID
// |instance_id|. Invokes |callback| on success and |error_callback| on
// failure. |extension| is the extension that made the call.
void ReadDescriptorValue(const Extension* extension,
const std::string& instance_id,
base::OnceClosure callback,
ErrorCallback error_callback);
// Sends a request to write the value of the descriptor with instance ID
// |instance_id|. Invokes |callback| on success and |error_callback| on
// failure. |extension| is the extension that made the call.
void WriteDescriptorValue(const Extension* extension,
const std::string& instance_id,
const std::vector<uint8_t>& value,
base::OnceClosure callback,
ErrorCallback error_callback);
// Initializes the adapter for testing. Used by unit tests only.
void SetAdapterForTesting(device::BluetoothAdapter* adapter);
// device::BluetoothAdapter::Observer overrides.
void GattServiceAdded(device::BluetoothAdapter* adapter,
device::BluetoothDevice* device,
device::BluetoothRemoteGattService* service) override;
void GattServiceRemoved(device::BluetoothAdapter* adapter,
device::BluetoothDevice* device,
device::BluetoothRemoteGattService* service) override;
void GattDiscoveryCompleteForService(
device::BluetoothAdapter* adapter,
device::BluetoothRemoteGattService* service) override;
void DeviceAddressChanged(device::BluetoothAdapter* adapter,
device::BluetoothDevice* device,
const std::string& old_address) override;
void GattServiceChanged(device::BluetoothAdapter* adapter,
device::BluetoothRemoteGattService* service) override;
void GattCharacteristicAdded(
device::BluetoothAdapter* adapter,
device::BluetoothRemoteGattCharacteristic* characteristic) override;
void GattCharacteristicRemoved(
device::BluetoothAdapter* adapter,
device::BluetoothRemoteGattCharacteristic* characteristic) override;
void GattDescriptorAdded(
device::BluetoothAdapter* adapter,
device::BluetoothRemoteGattDescriptor* descriptor) override;
void GattDescriptorRemoved(
device::BluetoothAdapter* adapter,
device::BluetoothRemoteGattDescriptor* descriptor) override;
void GattCharacteristicValueChanged(
device::BluetoothAdapter* adapter,
device::BluetoothRemoteGattCharacteristic* characteristic,
const std::vector<uint8_t>& value) override;
void GattDescriptorValueChanged(
device::BluetoothAdapter* adapter,
device::BluetoothRemoteGattDescriptor* descriptor,
const std::vector<uint8_t>& value) override;
// device::BluetoothLocalGattService::Delegate overrides.
void OnCharacteristicReadRequest(
const device::BluetoothDevice* device,
const device::BluetoothLocalGattCharacteristic* characteristic,
int offset,
Delegate::ValueCallback value_callback) override;
void OnCharacteristicWriteRequest(
const device::BluetoothDevice* device,
const device::BluetoothLocalGattCharacteristic* characteristic,
const std::vector<uint8_t>& value,
int offset,
base::OnceClosure callback,
Delegate::ErrorCallback error_callback) override;
void OnCharacteristicPrepareWriteRequest(
const device::BluetoothDevice* device,
const device::BluetoothLocalGattCharacteristic* characteristic,
const std::vector<uint8_t>& value,
int offset,
bool has_subsequent_request,
base::OnceClosure callback,
Delegate::ErrorCallback error_callback) override;
void OnDescriptorReadRequest(
const device::BluetoothDevice* device,
const device::BluetoothLocalGattDescriptor* descriptor,
int offset,
Delegate::ValueCallback value_callback) override;
void OnDescriptorWriteRequest(
const device::BluetoothDevice* device,
const device::BluetoothLocalGattDescriptor* descriptor,
const std::vector<uint8_t>& value,
int offset,
base::OnceClosure callback,
Delegate::ErrorCallback error_callback) override;
void OnNotificationsStart(
const device::BluetoothDevice* device,
device::BluetoothGattCharacteristic::NotificationType notification_type,
const device::BluetoothLocalGattCharacteristic* characteristic) override;
void OnNotificationsStop(
const device::BluetoothDevice* device,
const device::BluetoothLocalGattCharacteristic* characteristic) override;
// extensions::ExtensionRegistryObserver overrides:
void OnExtensionUnloaded(content::BrowserContext* browser_context,
const extensions::Extension* extension,
extensions::UnloadedExtensionReason reason) override;
// Adds a mapping for a local characteristic ID to its service ID
void AddLocalCharacteristic(const std::string& id,
const std::string& service_id);
// Returns a BluetoothGattCharacteristic by its ID |id|.
// Returns NULL, if the characteristic cannot be found.
device::BluetoothLocalGattCharacteristic* GetLocalCharacteristic(
const std::string& id) const;
// Adds a mapping for a service_id to the id of the app that owns it.
void AddServiceToApp(const std::string& app_id,
const std::string& service_id);
// Removes a mapping for a service_id to the id of the app that owns it.
void RemoveServiceFromApp(const std::string& app_id,
const std::string& service_id);
// Register a local GATT service.
void RegisterGattService(const Extension* extension,
const std::string& service_id,
base::OnceClosure callback,
ErrorCallback error_callback);
// Unregister a local GATT service.
void UnregisterGattService(const Extension* extension,
const std::string& service_id,
base::OnceClosure callback,
ErrorCallback error_callback);
// Handle a response from the app for the given request id.
void HandleRequestResponse(const Extension* extension,
size_t request_id,
bool is_error,
const std::vector<uint8_t>& value);
device::BluetoothAdapter* adapter() { return adapter_.get(); }
private:
// Called by BluetoothAdapterFactory.
void OnGetAdapter(base::OnceClosure callback,
scoped_refptr<device::BluetoothAdapter> adapter);
// Initializes the identifier for all existing GATT objects and devices.
// Called by OnGetAdapter and SetAdapterForTesting.
void InitializeIdentifierMappings();
// Sends the event named |event_name| to all listeners of that event that
// have the Bluetooth UUID manifest permission for UUID |uuid| and the
// "low_energy" manifest permission, with |args| as the argument to that
// event. If the event involves a characteristic, then |characteristic_id|
// should be the instance ID of the involved characteristic. Otherwise, an
// empty string should be passed.
void DispatchEventToExtensionsWithPermission(
events::HistogramValue histogram_value,
const std::string& event_name,
const device::BluetoothUUID& uuid,
const std::string& characteristic_id,
base::Value::List args);
void DispatchEventToExtension(const ExtensionId& extension_id,
events::HistogramValue histogram_value,
const std::string& event_name,
base::Value::List args);
// Returns a BluetoothRemoteGattService by its instance ID |instance_id|.
// Returns
// NULL, if the service cannot be found.
device::BluetoothRemoteGattService* FindServiceById(
const std::string& instance_id) const;
// Returns a BluetoothRemoteGattCharacteristic by its instance ID
// |instance_id|.
// Returns NULL, if the characteristic cannot be found.
device::BluetoothRemoteGattCharacteristic* FindCharacteristicById(
const std::string& instance_id) const;
// Returns a BluetoothRemoteGattDescriptor by its instance ID |instance_id|.
// Returns NULL, if the descriptor cannot be found.
device::BluetoothRemoteGattDescriptor* FindDescriptorById(
const std::string& instance_id) const;
// Dispatches a BLUETOOTH_LOW_ENERGY_ON_CHARACTERISTIC_VALUE_CHANGED and runs
// |callback|.
void OnReadRemoteCharacteristic(
const std::string& characteristic_instance_id,
base::OnceClosure callback,
ErrorCallback error_callback,
std::optional<device::BluetoothGattService::GattErrorCode> error_code,
const std::vector<uint8_t>& value);
// Runs |callback|.
void OnReadRemoteDescriptor(
base::OnceClosure callback,
ErrorCallback error_callback,
std::optional<device::BluetoothGattService::GattErrorCode> error_code,
const std::vector<uint8_t>& value);
// Called by BluetoothDevice in response to a call to CreateGattConnection.
void OnCreateGattConnection(
bool persistent,
const ExtensionId& extension_id,
const std::string& device_address,
ErrorCallback callback,
std::unique_ptr<device::BluetoothGattConnection> connection,
std::optional<device::BluetoothDevice::ConnectErrorCode> error_code);
// Called by BluetoothGattService in response to Register().
void OnRegisterGattServiceSuccess(const std::string& service_id,
const ExtensionId& extension_id,
base::OnceClosure callback);
// Called by BluetoothGattService in response to Unregister().
void OnUnregisterGattServiceSuccess(const std::string& service_id,
const ExtensionId& extension_id,
base::OnceClosure callback);
// Called by BluetoothRemoteGattCharacteristic and
// BluetoothRemoteGattDescriptor in
// case of an error during the write operations.
void OnError(ErrorCallback error_callback,
device::BluetoothGattService::GattErrorCode error_code);
// Called by BluetoothRemoteGattCharacteristic in response to a call to
// StartNotifySession.
void OnStartNotifySession(
bool persistent,
const ExtensionId& extension_id,
const std::string& characteristic_id,
base::OnceClosure callback,
std::unique_ptr<device::BluetoothGattNotifySession> session);
// Called by BluetoothRemoteGattCharacteristic in response to a call to
// StartNotifySession.
void OnStartNotifySessionError(
const ExtensionId& extension_id,
const std::string& characteristic_id,
ErrorCallback error_callback,
device::BluetoothGattService::GattErrorCode error_code);
// Called by BluetoothGattNotifySession in response to a call to Stop.
void OnStopNotifySession(const ExtensionId& extension_id,
const std::string& characteristic_id,
base::OnceClosure callback);
// Finds and returns a BluetoothLowEnergyConnection to device with address
// |device_address| from the managed API resources for extension with ID
// |extension_id|.
BluetoothLowEnergyConnection* FindConnection(
const ExtensionId& extension_id,
const std::string& device_address);
// Removes the connection to device with address |device_address| from the
// managed API resources for extension with ID |extension_id|. Returns false,
// if the connection could not be found.
bool RemoveConnection(const ExtensionId& extension_id,
const std::string& device_address);
// Finds and returns a BluetoothLowEnergyNotifySession associated with
// characteristic with instance ID |characteristic_id| from the managed API
// API resources for extension with ID |extension_id|.
BluetoothLowEnergyNotifySession* FindNotifySession(
const ExtensionId& extension_id,
const std::string& characteristic_id);
// Removes the notify session associated with characteristic with
// instance ID |characteristic_id| from the managed API resources for
// extension with ID |extension_id|. Returns false, if the session could
// not be found.
bool RemoveNotifySession(const ExtensionId& extension_id,
const std::string& characteristic_id);
// Stores a request associated with an app and returns the ID of the request.
// When an app sends a request to an app for getting/setting an attribute
// value, we store that request so when the response comes in, we know where
// to forward the results of the request.
size_t StoreSentRequest(const ExtensionId& extension_id,
std::unique_ptr<AttributeValueRequest> request);
// Mapping from instance ids to identifiers of owning instances. The keys are
// used to identify individual instances of GATT objects and are used by
// bluetoothLowEnergy API functions to obtain the correct GATT object to
// operate on. Instance IDs are string identifiers that are returned by the
// device/bluetooth API, by calling GetIdentifier() on the corresponding
// device::BluetoothGatt* instance.
//
// This mapping is necessary, as GATT object instances can only be obtained
// from the object that owns it, where raw pointers should not be cached. E.g.
// to obtain a device::BluetoothRemoteGattCharacteristic, it is necessary to
// obtain
// a pointer to the associated device::BluetoothDevice, and then to the
// device::BluetoothRemoteGattService that owns the characteristic.
using InstanceIdMap = std::map<std::string, std::string>;
InstanceIdMap service_id_to_device_address_;
InstanceIdMap chrc_id_to_service_id_;
InstanceIdMap desc_id_to_chrc_id_;
// Map of which services are registered by which app. Used to route attribute
// read/write events.
InstanceIdMap service_id_to_extension_id_;
// Pointer to the current BluetoothAdapter instance. This represents a local
// Bluetooth adapter of the system.
scoped_refptr<device::BluetoothAdapter> adapter_;
// Set of extension ID + device addresses to which a connect/disconnect is
// currently pending.
std::set<std::string> connecting_devices_;
std::set<std::string> disconnecting_devices_;
// Set of extension ID + characteristic ID to which a request to start a
// notify session is currently pending.
std::set<std::string> pending_session_calls_;
using RequestIdToRequestMap =
std::map<size_t, std::unique_ptr<AttributeValueRequest>>;
using ExtensionToRequestsMap =
std::map<std::string, std::unique_ptr<RequestIdToRequestMap>>;
// Map of callback requests sent to the app for attribute value read/write
// requests.
ExtensionToRequestsMap requests_;
// The last request ID we used.
size_t last_callback_request_id_ = 0;
// Map of locally hosted GATT service ids created by app_id. Used for cleanup.
std::map<std::string, std::vector<std::string>> app_id_to_service_ids_;
// BrowserContext passed during initialization.
raw_ptr<content::BrowserContext> browser_context_;
// Listen to extension unloaded notification.
base::ScopedObservation<ExtensionRegistry, ExtensionRegistryObserver>
extension_registry_observation_{this};
// Note: This should remain the last member so it'll be destroyed and
// invalidate its weak pointers before any other members are destroyed.
base::WeakPtrFactory<BluetoothLowEnergyEventRouter> weak_ptr_factory_{this};
};
} // namespace extensions
#endif // EXTENSIONS_BROWSER_API_BLUETOOTH_LOW_ENERGY_BLUETOOTH_LOW_ENERGY_EVENT_ROUTER_H_