| // Copyright 2018 The Chromium OS 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 BLUETOOTH_NEWBLUED_DEVICE_INTERFACE_HANDLER_H_ |
| #define BLUETOOTH_NEWBLUED_DEVICE_INTERFACE_HANDLER_H_ |
| |
| #include <map> |
| #include <memory> |
| #include <set> |
| #include <string> |
| #include <vector> |
| |
| #include <base/memory/ref_counted.h> |
| #include <base/memory/weak_ptr.h> |
| #include <base/observer_list.h> |
| #include <dbus/bus.h> |
| |
| #include "bluetooth/common/exported_object_manager_wrapper.h" |
| #include "bluetooth/newblued/newblue.h" |
| #include "bluetooth/newblued/property.h" |
| #include "bluetooth/newblued/uuid.h" |
| |
| namespace bluetooth { |
| |
| // Appended at the end of LE device names. This is a UX mark to indicate to user |
| // that this device originates from NewBlue stack. This will be removed once |
| // NewBlue is stable and enabled by default. |
| // TODO(sonnysasaka): Remove this when NewBlue is enabled by default. |
| constexpr char kNewblueNameSuffix[] = " \xF0\x9F\x90\xBE"; |
| |
| // Structure representing a discovered device. |
| struct Device { |
| Device(); |
| explicit Device(const std::string& address); |
| |
| // TODO(yudiliu): We need a proper "address" struct to represent the address. |
| // MAC address (in format XX:XX:XX:XX:XX:XX). |
| std::string address; |
| // Whether the MAC address is a random address. |
| bool is_random_address; |
| |
| // [mandatory] Whether the device is paired. |
| Property<bool> paired; |
| // [mandatory] Whether the device is connected. |
| Property<bool> connected; |
| // [mandatory] Whether the device is in the white list. |
| Property<bool> trusted; |
| // [mandatory] Whether the device is in the black list. |
| Property<bool> blocked; |
| // [mandatory] Whether the services provided by the device has been resolved. |
| Property<bool> services_resolved; |
| |
| // [mandatory] A readable and writable alias given to the device. |
| Property<std::string> alias; |
| // Actual alias provided by the user. |
| std::string internal_alias; |
| // [optional] A readable name of the device. |
| Property<std::string> name; |
| |
| // [optional] Transmission power level of the advertisement packet |
| Property<int16_t> tx_power; |
| // [optional] RSSI of last received inquiry response. |
| Property<int16_t> rssi; |
| |
| // [optional] Class of the device. |
| Property<uint32_t> eir_class; |
| // [optional] External appearance of the device. |
| Property<uint16_t> appearance; |
| // [optional] Icon type of the device based on the value of |appearance|. |
| Property<std::string> icon; |
| |
| // [optional] Advertising flags. |
| Property<std::vector<uint8_t>> flags; |
| // [optional] Service UUIDs of 16-bit 32-bit and 128-bit. |
| Property<std::set<Uuid>> service_uuids; |
| // [optional] Service data associated with UUIDs. |
| Property<std::map<Uuid, std::vector<uint8_t>>> service_data; |
| |
| // [optional] Manufacturer identifier with the extra manufacturer data |
| Property<std::map<uint16_t, std::vector<uint8_t>>> manufacturer; |
| |
| // Identity address (in format XX:XX:XX:XX:XX:XX). |
| Property<std::string> identity_address; |
| |
| // Latest advertising address (in format XX:XX:XX:XX:XX:XX). |
| Property<std::string> advertised_address; |
| |
| DISALLOW_COPY_AND_ASSIGN(Device); |
| }; |
| |
| // Structure used by scan_manager to provide discovered device information to |
| // device interface |
| struct DeviceInfo { |
| DeviceInfo(bool has_active_discovery_client, |
| const std::string& adv_address, |
| uint8_t address_type, |
| const std::string& resolved_address, |
| int8_t rssi, |
| uint8_t reply_type); |
| |
| // Whether has an active discovery client when receive the device info. |
| bool has_active_discovery_client; |
| // Advertised address (in format XX:XX:XX:XX:XX:XX). |
| std::string advertised_address; |
| // libnewblue's BT_ADDR_TYPE_* |
| uint8_t address_type; |
| // Resolved address (in format XX:XX:XX:XX:XX:XX). |
| std::string resolved_address; |
| // RSSI of last received inquiry response. |
| int8_t rssi; |
| uint8_t reply_type; |
| // Advertising flags. |
| std::vector<uint8_t> flags; |
| // Service UUIDs of 16-bit 32-bit and 128-bit. |
| std::set<Uuid> service_uuids; |
| // A readable name of the device. |
| std::string name; |
| // Transmission power level of the advertisement packet |
| int16_t tx_power; |
| // Class of the device. |
| uint32_t eir_class; |
| // Service data associated with UUIDs. |
| std::map<Uuid, std::vector<uint8_t>> service_data; |
| // External appearance of the device. |
| uint16_t appearance; |
| // Icon type of the device based on the value of |appearance|. |
| std::string icon; |
| // Manufacturer identifier with the extra manufacturer data |
| std::map<uint16_t, std::vector<uint8_t>> manufacturer; |
| |
| DISALLOW_COPY_AND_ASSIGN(DeviceInfo); |
| }; |
| |
| // These are based on the connection state defined in newblue/gatt.h. |
| enum class ConnectState : uint8_t { |
| CONNECTED, |
| DISCONNECTED, |
| ERROR, |
| DISCONNECTED_BY_US, |
| }; |
| |
| // Handles org.bluez.Device1 interface. |
| class DeviceInterfaceHandler { |
| // Represents a pairing session. |
| struct PairSession { |
| std::string address; |
| std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<>> pair_response; |
| std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<>> |
| cancel_pair_response; |
| }; |
| |
| // Represents a connection session. |
| struct ConnectSession { |
| ConnectSession() : connect_by_us(false), disconnect_by_us(false) {} |
| std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<>> connect_response; |
| std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<>> |
| disconnect_response; |
| |
| // *_by_us are used to distinguish whether the connection/disconnection is |
| // initiated by either newblued (us) or our clients. With only the above |
| // |*_response|, we cannot tell whether the connection/disconnection comes |
| // from nowhere or from newblued. |
| bool connect_by_us; |
| bool disconnect_by_us; |
| }; |
| |
| // Structure representing a connection. |
| struct Connection { |
| gatt_client_conn_t conn_id; |
| ble_hid_conn_t hid_id; // This can be invalid if not a HID device. |
| }; |
| |
| public: |
| // Interface for observing changes to devices |
| class DeviceObserver { |
| public: |
| virtual ~DeviceObserver() {} |
| virtual void OnGattConnected(const std::string& device_address, |
| gatt_client_conn_t conn_id) {} |
| // |is_disconnected_by_newblue| indicate if the device is disconnected by |
| // the newblue stack |
| // TODO(b:140626102): Add disconnection intention information to the persist |
| virtual void OnGattDisconnected(const std::string& device_address, |
| gatt_client_conn_t conn_id, |
| bool is_disconnected_by_newblue) {} |
| virtual void OnDevicePaired(const std::string& device_address) {} |
| virtual void OnDeviceUnpaired(const std::string& device_address) {} |
| }; |
| |
| using ConnectCallback = base::Callback<void(const std::string& device_address, |
| bool success, |
| const std::string& dbus_error)>; |
| |
| // |newblue| and |exported_object_manager_wrapper| not owned, caller must make |
| // sure it outlives this object. |
| DeviceInterfaceHandler( |
| scoped_refptr<dbus::Bus> bus, |
| Newblue* newblue, |
| ExportedObjectManagerWrapper* exported_object_manager_wrapper); |
| virtual ~DeviceInterfaceHandler() = default; |
| |
| // Starts listening pair state events from Newblue. |
| bool Init(); |
| |
| // Returns weak pointer of this object. |
| base::WeakPtr<DeviceInterfaceHandler> GetWeakPtr(); |
| |
| // Called when an update of a device info is received. |
| // |scanned_by_client| being true means that the scan result is due to scan |
| // requested by clients rather than a background scan. |
| void OnDeviceDiscovered(const DeviceInfo& device_info); |
| |
| // Removes a device D-Bus object and forgets its pairing information. |
| bool RemoveDevice(const std::string& address, std::string* dbus_error); |
| |
| // Add/remove observer for device events. |
| void AddDeviceObserver(DeviceObserver* observer); |
| void RemoveDeviceObserver(DeviceObserver* observer); |
| |
| // Returns device address if the given connection ID exists, empty otherwise. |
| std::string GetAddressByConnectionId(gatt_client_conn_t conn_id); |
| |
| // Returns device connection ID if connection exists, |
| // |kInvalidGattConnectionId| otherwise. |
| gatt_client_conn_t GetConnectionIdByAddress(const std::string& address); |
| |
| // Once GATT primary services traversal is done, GATT should call this with |
| // |resolved| set to true. If GATT invalidates services, GATT should also call |
| // this with |resolved| set to false. |
| void SetGattServicesResolved(const std::string& device_address, |
| bool resolved); |
| |
| private: |
| // Returns the in-memory discovered device based on its key address, adding |
| // it if does not already exist. |
| Device* AddOrGetDiscoveredDevice(const std::string& key_address, |
| const std::string& adv_address, |
| uint8_t address_type); |
| |
| // Exports the device to D-Bus (if not already exported) and updates its |
| // current properties. |
| void ExportOrUpdateDevice(Device* device); |
| |
| // Installs org.bluez.Device1 method handlers. |
| void AddDeviceMethodHandlers(ExportedInterface* device_interface); |
| |
| // D-Bus method handlers for device objects. |
| void HandlePair( |
| std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<>> response, |
| dbus::Message* message); |
| void HandleCancelPairing( |
| std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<>> response, |
| dbus::Message* message); |
| void HandleConnect( |
| std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<>> response, |
| dbus::Message* message); |
| void HandleDisconnect( |
| std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<>> response, |
| dbus::Message* message); |
| void HandleExecuteWrite( |
| std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<>> response, |
| dbus::Message* message); |
| // TODO(mcchou): Handle the rest of the D-Bus methods of the device interface. |
| // ConnectProfile() - No op, but we may need dummy implementation later. |
| // DisconnectPorfile() - No op, but we may need dummy implementation later. |
| // GetServiceRecords() - No op, but we may need dummy implementation later. |
| |
| // Initiates LE connection/disconnection to a peer device. These are internal |
| // functions called by the user facing D-Bus Connect() method and newblued for |
| // internal stack logic. |*_by_us| indicates whether the |
| // connection/disconnection is initiated by the internal logic but not D-Bus |
| // clients. |
| void ConnectInternal(const std::string& device_address, |
| std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<>> |
| connect_response, |
| bool connect_by_us); |
| void DisconnectInternal( |
| const std::string& device_address, |
| std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<>> |
| disconnect_response, |
| bool disconnect_by_us); |
| |
| // Called when a connection/disconnection request is fulfilled and we are |
| // ready to send a reply of the Connect()/Disconnect() method. |
| void ConnectReply(const std::string& device_address, |
| bool success, |
| const std::string& dbus_error); |
| // Called on update of GATT client connection. |
| void OnGattClientConnectCallback(gatt_client_conn_t conn_id, uint8_t status); |
| |
| void SetDeviceConnected(Device* device, bool is_connected); |
| void SetDevicePaired(Device* device, bool is_connected); |
| |
| // Finds a device from |discovered_devices_| with the given |device_address|. |
| // Returns nullptr if no such device is found. |
| Device* FindDevice(const std::string& device_address); |
| |
| // Exposes all mandatory device object's properties and update the properties |
| // for the existing devices by either exposing them if not exposed before or |
| // emitting the value changes if any. |
| void UpdateDeviceProperties(ExportedInterface* interface, |
| const Device& device, |
| bool is_new_device); |
| |
| // Updates EIR data of |device|. |
| static void UpdateDevice(Device* device, const DeviceInfo& device_info); |
| |
| // Resets the update status of device properties. |
| void ClearPropertiesUpdated(Device* device); |
| |
| // Determines the security requirements based on the appearance of a device. |
| // Returns true if determined. The default security requirements |
| // (bond:true MITM:false) are used. |
| struct smPairSecurityRequirements DetermineSecurityRequirements( |
| const Device& device); |
| |
| // Called when a pairing state changed event is received. |
| void OnPairStateChanged(const std::string& address, |
| PairState pair_state, |
| PairError pair_error, |
| const std::string& identity_address); |
| |
| scoped_refptr<dbus::Bus> bus_; |
| Newblue* newblue_; |
| ExportedObjectManagerWrapper* exported_object_manager_wrapper_; |
| |
| // Keeps the discovered devices. |
| // TODO(sonnysasaka): Clear old devices according to BlueZ mechanism. |
| std::map<std::string, std::unique_ptr<Device>> discovered_devices_; |
| |
| UniqueId pair_observer_id_; |
| |
| // Device object path and its response to the ongoing pairing/cancelpairing |
| // request. <device address, D-Bus method response to pairing, D-Bus |
| // method response to cancel pairing> |
| struct PairSession ongoing_pairing_; |
| |
| // Contains pairs of <device address, connection session> to store the |
| // D-Bus method response(s) to the ongoing connection/disconnection requests. |
| std::map<std::string, struct ConnectSession> connection_sessions_; |
| // Contains pairs of <device address, connection info>. |
| std::map<std::string, struct Connection> connections_; |
| // Contains pairs of <device address, connection attempt>. |
| std::map<std::string, gatt_client_conn_t> connection_attempts_; |
| |
| base::ObserverList<DeviceObserver> observers_; |
| |
| // Must come last so that weak pointers will be invalidated before other |
| // members are destroyed. |
| base::WeakPtrFactory<DeviceInterfaceHandler> weak_ptr_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(DeviceInterfaceHandler); |
| }; |
| |
| } // namespace bluetooth |
| |
| #endif // BLUETOOTH_NEWBLUED_DEVICE_INTERFACE_HANDLER_H_ |