blob: 91de92a4cf00fd4cafaad50db40a0b89f82039b9 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef DEVICE_BLUETOOTH_FLOSS_FLOSS_SOCKET_MANAGER_H_
#define DEVICE_BLUETOOTH_FLOSS_FLOSS_SOCKET_MANAGER_H_
#include <memory>
#include <string>
#include <unordered_map>
#include "base/files/scoped_file.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "dbus/exported_object.h"
#include "dbus/object_path.h"
#include "device/bluetooth/bluetooth_export.h"
#include "device/bluetooth/floss/floss_dbus_client.h"
#include "device/bluetooth/public/cpp/bluetooth_uuid.h"
namespace floss {
// The socket manager allows creation and connection of RFCOMM/L2CAP services.
// It is managed by FlossClientBundle and will be initialized with an adapter
// when one is powered on.
class DEVICE_BLUETOOTH_EXPORT FlossSocketManager : public FlossDBusClient {
public:
// Id given after register callbacks.
using CallbackId = uint32_t;
// Id given after creating any socket type.
using SocketId = uint64_t;
// Supported socket types for api.
enum class SocketType {
kUnknown = 0,
kRfcomm = 1,
// Not supported via the socket manager api.
kSco_DONOTUSE = 2,
kL2cap = 3,
kL2capLe = 4,
};
// States for server socket.
enum class ServerSocketState {
// FlossListeningSocket is ready. Call |Accept| to accept incoming
// connections.
kReady,
// FlossListeningSocket is closed.
kClosed,
};
// Security level of connection.
enum class Security {
kInsecure,
kSecure,
};
// Flags for changing how Floss constructs a socket.
enum class SocketFlags : int {
kSocketFlagsEncrypt = 1 << 0,
kSocketFlagsAuth = 1 << 1,
kSocketFlagsNoSdp = 1 << 2,
kSocketFlagsAuthMitm = 1 << 3,
kSocketFlagsAuth16Digit = 1 << 4
};
static int GetRawFlossFlagsFromBluetoothFlags(bool encrypt,
bool auth,
bool auth_mitm,
bool auth_16_digit,
bool no_sdp);
// Represents a listening socket.
struct FlossListeningSocket {
SocketId id = FlossSocketManager::kInvalidSocketId;
SocketType type;
int flags;
absl::optional<int> psm;
absl::optional<int> channel;
absl::optional<std::string> name;
absl::optional<device::BluetoothUUID> uuid;
FlossListeningSocket();
FlossListeningSocket(const FlossListeningSocket&);
~FlossListeningSocket();
bool is_valid() const { return id != FlossSocketManager::kInvalidSocketId; }
};
// Represents a connecting socket (either incoming or outgoing).
struct FlossSocket {
SocketId id = FlossSocketManager::kInvalidSocketId;
FlossDeviceId remote_device;
SocketType type;
int flags;
absl::optional<base::ScopedFD> fd;
int port;
absl::optional<device::BluetoothUUID> uuid;
int max_rx_size;
int max_tx_size;
FlossSocket();
~FlossSocket();
// Due to ScopedFD, we don't want to use copy constructor for this.
FlossSocket(const FlossSocket&) = delete;
FlossSocket& operator=(const FlossSocket&) = delete;
// Move constructor and assignment operator is ok.
FlossSocket(FlossSocket&&);
FlossSocket& operator=(FlossSocket&&) = default;
bool is_valid() const { return id != FlossSocketManager::kInvalidSocketId; }
};
// Represents a result from any socket api call.
struct SocketResult {
BtifStatus status;
SocketId id;
};
// Callback sent when a listening socket is ready to accept connections.
using ConnectionStateChanged = base::RepeatingCallback<
void(ServerSocketState, FlossListeningSocket, BtifStatus)>;
// Callback used when a listening socket accepts new connections.
using ConnectionAccepted = base::RepeatingCallback<void(FlossSocket&&)>;
// Callback when a connection socket completes.
using ConnectionCompleted =
base::OnceCallback<void(BtifStatus, absl::optional<FlossSocket>&&)>;
// Error: Callback id is invalid.
static const char kErrorInvalidCallback[];
// Valid callback ids are always greater than 0.
static const CallbackId kInvalidCallbackId = 0;
// Valid socket ids are always greater than 0.
static const SocketId kInvalidSocketId = 0;
static std::unique_ptr<FlossSocketManager> Create();
FlossSocketManager(const FlossSocketManager&) = delete;
FlossSocketManager& operator=(const FlossSocketManager&) = delete;
FlossSocketManager();
~FlossSocketManager() override;
// Listen for connections using a L2Cap channel.
virtual void ListenUsingL2cap(const Security security_level,
ResponseCallback<BtifStatus> callback,
ConnectionStateChanged ready_cb,
ConnectionAccepted new_connection_cb);
// Listen for connections using a connection oriented LE L2Cap channel.
virtual void ListenUsingL2capLe(const Security security_level,
ResponseCallback<BtifStatus> callback,
ConnectionStateChanged ready_cb,
ConnectionAccepted new_connection_cb);
// Listen for connections using an RFCOMM channel. This API exposes all of the
// options supported by Floss and should only be used if there are no safer
// variants capable of supporting a use-case, such as when manually
// constructing SDP records for a listening socket.
virtual void ListenUsingRfcommAlt(
const absl::optional<std::string> name,
const absl::optional<device::BluetoothUUID> application_uuid,
const absl::optional<int> channel,
const absl::optional<int> flags,
ResponseCallback<BtifStatus> callback,
ConnectionStateChanged ready_cb,
ConnectionAccepted new_connection_cb);
// Listen for connections using an RFCOMM channel. Creates SDP record with
// given name and UUID.
virtual void ListenUsingRfcomm(const std::string& name,
const device::BluetoothUUID& uuid,
const Security security_level,
ResponseCallback<BtifStatus> callback,
ConnectionStateChanged ready_cb,
ConnectionAccepted new_connection_cb);
// Connect via a L2Cap channel on given psm.
virtual void ConnectUsingL2cap(const FlossDeviceId& remote_device,
const int psm,
const Security security_level,
ConnectionCompleted callback);
// Connect via a connection oriented LE L2Cap channel on given psm.
virtual void ConnectUsingL2capLe(const FlossDeviceId& remote_device,
const int psm,
const Security security_level,
ConnectionCompleted callback);
// Connect to a remote service using a RFCOMM channel.
virtual void ConnectUsingRfcomm(const FlossDeviceId& remote_device,
const device::BluetoothUUID& uuid,
const Security security_level,
ConnectionCompleted callback);
// Accept new connections on |id|. If the given SocketId is not a listening
// socket or closed, the callback will receive a failing |BtifStatus| value.
virtual void Accept(const SocketId id,
absl::optional<uint32_t> timeout_ms,
ResponseCallback<BtifStatus> callback);
// Closes the socket on |id|. Only works for listening sockets. For connecting
// sockets, simply close the fd to terminate the connection.
virtual void Close(const SocketId id, ResponseCallback<BtifStatus> callback);
// Initializes the socket manager with given adapter.
void Init(dbus::Bus* bus,
const std::string& service_name,
const int adapter_index,
base::OnceClosure on_ready) override;
protected:
friend class FlossSocketManagerTest;
// Complete the method call for |RegisterCallback|.
void CompleteRegisterCallback(dbus::Response* response,
dbus::ErrorResponse* error_response);
// Complete the method call for |UnregisterCallback|.
void CompleteUnregisterCallback(DBusResult<bool> result);
// Complete any of |ListenUsingL2cap| or |ListenUsingRfcomm|.
void CompleteListen(ResponseCallback<BtifStatus> callback,
ConnectionStateChanged ready_cb,
ConnectionAccepted new_connection_cb,
DBusResult<SocketResult> result);
// Complete any of |ConnectUsingL2cap| or |ConnectUsingRfcomm|.
void CompleteConnect(ConnectionCompleted callback,
DBusResult<SocketResult> result);
// Handle callback |IncomingSocketReady| on exported object path.
void OnIncomingSocketReady(
dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender);
// Handle callback |IncomingSocketClosed| on exported object path.
void OnIncomingSocketClosed(
dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender);
// Handle callback |HandleIncomingConnection| on exported object path.
void OnHandleIncomingConnection(
dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender);
// Handle callback |OutgoingConnectionResult| on exported object path.
void OnOutgoingConnectionResult(
dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender);
// Managed by FlossDBusManager - we keep local pointer to access object proxy.
raw_ptr<dbus::Bus> bus_ = nullptr;
// Adapter used for socket connections by this class.
dbus::ObjectPath adapter_path_;
// Service which implements the SocketManager interface.
std::string service_name_;
// Map of listening sockets to callbacks.
std::unordered_map<SocketId,
std::pair<ConnectionStateChanged, ConnectionAccepted>>
listening_sockets_to_callbacks_;
// Map of connection sockets that haven't completed.
std::unordered_map<SocketId, ConnectionCompleted>
connecting_sockets_to_callbacks_;
private:
template <typename R, typename... Args>
void CallSocketMethod(ResponseCallback<R> callback,
const char* member,
Args... args) {
CallMethod(std::move(callback), bus_, service_name_,
kSocketManagerInterface, adapter_path_, member, args...);
}
// Object path for exported callbacks registered against manager interface.
static const char kExportedCallbacksPath[];
// All socket api calls require callback id since callbacks must take
// ownership of the file descriptors. A value of zero is invalid.
CallbackId callback_id_ = kInvalidCallbackId;
// Signal when client is ready to be used.
base::OnceClosure on_ready_;
base::WeakPtrFactory<FlossSocketManager> weak_ptr_factory_{this};
};
} // namespace floss
#endif // DEVICE_BLUETOOTH_FLOSS_FLOSS_SOCKET_MANAGER_H_