| // Copyright 2023 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ash/arc/bluetooth/arc_bluez_bridge.h" |
| |
| #include <bluetooth/bluetooth.h> |
| #include <bluetooth/l2cap.h> |
| #include <bluetooth/rfcomm.h> |
| #include <fcntl.h> |
| #include <stdint.h> |
| #include <sys/socket.h> |
| |
| #include "ash/components/arc/bluetooth/bluetooth_type_converters.h" |
| #include "ash/components/arc/session/arc_bridge_service.h" |
| #include "base/check.h" |
| #include "base/posix/eintr_wrapper.h" |
| #include "device/bluetooth/bluetooth_common.h" |
| #include "device/bluetooth/bluetooth_device.h" |
| #include "device/bluetooth/bluez/bluetooth_device_bluez.h" |
| |
| using device::BluetoothAdapter; |
| using device::BluetoothAdapterFactory; |
| using device::BluetoothAdvertisement; |
| using device::BluetoothDevice; |
| using device::BluetoothDiscoveryFilter; |
| using device::BluetoothDiscoverySession; |
| using device::BluetoothGattCharacteristic; |
| using device::BluetoothGattConnection; |
| using device::BluetoothGattDescriptor; |
| using device::BluetoothGattNotifySession; |
| using device::BluetoothGattService; |
| using device::BluetoothLocalGattCharacteristic; |
| using device::BluetoothLocalGattDescriptor; |
| using device::BluetoothLocalGattService; |
| using device::BluetoothRemoteGattCharacteristic; |
| using device::BluetoothRemoteGattDescriptor; |
| using device::BluetoothRemoteGattService; |
| using device::BluetoothTransport; |
| using device::BluetoothUUID; |
| |
| namespace { |
| |
| // Bluetooth SDP Service Class ID List Attribute identifier |
| constexpr uint16_t kServiceClassIDListAttributeID = 0x0001; |
| |
| void OnCreateServiceRecordDone( |
| arc::ArcBluetoothBridge::CreateSdpRecordCallback callback, |
| uint32_t service_handle) { |
| arc::mojom::BluetoothCreateSdpRecordResultPtr result = |
| arc::mojom::BluetoothCreateSdpRecordResult::New(); |
| result->status = arc::mojom::BluetoothStatus::SUCCESS; |
| result->service_handle = service_handle; |
| |
| std::move(callback).Run(std::move(result)); |
| } |
| |
| void OnCreateServiceRecordError( |
| arc::ArcBluetoothBridge::CreateSdpRecordCallback callback, |
| bluez::BluetoothServiceRecordBlueZ::ErrorCode error_code) { |
| arc::mojom::BluetoothCreateSdpRecordResultPtr result = |
| arc::mojom::BluetoothCreateSdpRecordResult::New(); |
| if (error_code == |
| bluez::BluetoothServiceRecordBlueZ::ErrorCode::ERROR_ADAPTER_NOT_READY) { |
| result->status = arc::mojom::BluetoothStatus::NOT_READY; |
| } else { |
| result->status = arc::mojom::BluetoothStatus::FAIL; |
| } |
| |
| std::move(callback).Run(std::move(result)); |
| } |
| |
| void OnRemoveServiceRecordDone( |
| arc::ArcBluetoothBridge::RemoveSdpRecordCallback callback) { |
| std::move(callback).Run(arc::mojom::BluetoothStatus::SUCCESS); |
| } |
| |
| void OnRemoveServiceRecordError( |
| arc::ArcBluetoothBridge::RemoveSdpRecordCallback callback, |
| bluez::BluetoothServiceRecordBlueZ::ErrorCode error_code) { |
| arc::mojom::BluetoothStatus status; |
| if (error_code == |
| bluez::BluetoothServiceRecordBlueZ::ErrorCode::ERROR_ADAPTER_NOT_READY) { |
| status = arc::mojom::BluetoothStatus::NOT_READY; |
| } else { |
| status = arc::mojom::BluetoothStatus::FAIL; |
| } |
| |
| std::move(callback).Run(status); |
| } |
| |
| } // namespace |
| |
| namespace arc { |
| |
| ArcBluezBridge::ArcBluezBridge(content::BrowserContext* context, |
| ArcBridgeService* bridge_service) |
| : ArcBluetoothBridge(context, bridge_service) {} |
| |
| ArcBluezBridge::~ArcBluezBridge() = default; |
| |
| bluez::BluetoothAdapterBlueZ* ArcBluezBridge::GetAdapter() const { |
| return static_cast<bluez::BluetoothAdapterBlueZ*>(bluetooth_adapter_.get()); |
| } |
| |
| void ArcBluezBridge::HandlePoweredOn() {} |
| |
| void ArcBluezBridge::GetSdpRecords(mojom::BluetoothAddressPtr remote_addr, |
| const BluetoothUUID& target_uuid) { |
| BluetoothDevice* device = |
| GetAdapter()->GetDevice(remote_addr->To<std::string>()); |
| if (!device) { |
| OnGetServiceRecordsError(std::move(remote_addr), target_uuid, |
| bluez::BluetoothServiceRecordBlueZ::ErrorCode:: |
| ERROR_DEVICE_DISCONNECTED); |
| return; |
| } |
| |
| bluez::BluetoothDeviceBlueZ* device_bluez = |
| static_cast<bluez::BluetoothDeviceBlueZ*>(device); |
| |
| mojom::BluetoothAddressPtr remote_addr_clone = remote_addr.Clone(); |
| |
| device_bluez->GetServiceRecords( |
| base::BindOnce(&ArcBluezBridge::OnGetServiceRecordsFinished, |
| weak_factory_.GetWeakPtr(), std::move(remote_addr), |
| target_uuid), |
| base::BindOnce(&ArcBluezBridge::OnGetServiceRecordsError, |
| weak_factory_.GetWeakPtr(), std::move(remote_addr_clone), |
| target_uuid)); |
| } |
| |
| void ArcBluezBridge::CreateSdpRecord( |
| mojom::BluetoothSdpRecordPtr record_mojo, |
| arc::ArcBluetoothBridge::CreateSdpRecordCallback callback) { |
| auto record = record_mojo.To<bluez::BluetoothServiceRecordBlueZ>(); |
| |
| // Check if ServiceClassIDList attribute (attribute ID 0x0001) is included |
| // after type conversion, since it is mandatory for creating a service record. |
| if (!record.IsAttributePresented(kServiceClassIDListAttributeID)) { |
| mojom::BluetoothCreateSdpRecordResultPtr result = |
| mojom::BluetoothCreateSdpRecordResult::New(); |
| result->status = mojom::BluetoothStatus::FAIL; |
| std::move(callback).Run(std::move(result)); |
| return; |
| } |
| |
| auto split_callback = base::SplitOnceCallback(std::move(callback)); |
| GetAdapter()->CreateServiceRecord( |
| record, |
| base::BindOnce(&OnCreateServiceRecordDone, |
| std::move(split_callback.first)), |
| base::BindOnce(&OnCreateServiceRecordError, |
| std::move(split_callback.second))); |
| } |
| |
| void ArcBluezBridge::RemoveSdpRecord(uint32_t service_handle, |
| RemoveSdpRecordCallback callback) { |
| auto split_callback = base::SplitOnceCallback(std::move(callback)); |
| GetAdapter()->RemoveServiceRecord( |
| service_handle, |
| base::BindOnce(&OnRemoveServiceRecordDone, |
| std::move(split_callback.first)), |
| base::BindOnce(&OnRemoveServiceRecordError, |
| std::move(split_callback.second))); |
| } |
| |
| void ArcBluezBridge::CloseBluetoothListeningSocket( |
| BluetoothListeningSocket* ptr) { |
| auto itr = listening_sockets_.find(ptr); |
| CHECK(itr != listening_sockets_.end()); |
| listening_sockets_.erase(itr); |
| } |
| |
| void ArcBluezBridge::CloseBluetoothConnectingSocket( |
| BluetoothConnectingSocket* ptr) { |
| auto itr = connecting_sockets_.find(ptr); |
| CHECK(itr != connecting_sockets_.end()); |
| connecting_sockets_.erase(itr); |
| } |
| |
| void ArcBluezBridge::OnGetServiceRecordsDone( |
| mojom::BluetoothAddressPtr remote_addr, |
| const BluetoothUUID& target_uuid, |
| const std::vector<bluez::BluetoothServiceRecordBlueZ>& records_bluez) { |
| auto* sdp_bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD( |
| arc_bridge_service_->bluetooth(), OnGetSdpRecords); |
| if (!sdp_bluetooth_instance) { |
| return; |
| } |
| |
| std::vector<mojom::BluetoothSdpRecordPtr> records; |
| for (const auto& r : records_bluez) { |
| records.push_back(mojom::BluetoothSdpRecord::From(r)); |
| } |
| |
| sdp_bluetooth_instance->OnGetSdpRecords(mojom::BluetoothStatus::SUCCESS, |
| std::move(remote_addr), target_uuid, |
| std::move(records)); |
| } |
| |
| void ArcBluezBridge::OnGetServiceRecordsError( |
| mojom::BluetoothAddressPtr remote_addr, |
| const BluetoothUUID& target_uuid, |
| bluez::BluetoothServiceRecordBlueZ::ErrorCode error_code) { |
| auto* sdp_bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD( |
| arc_bridge_service_->bluetooth(), OnGetSdpRecords); |
| if (!sdp_bluetooth_instance) { |
| return; |
| } |
| |
| mojom::BluetoothStatus status; |
| |
| switch (error_code) { |
| case bluez::BluetoothServiceRecordBlueZ::ErrorCode::ERROR_ADAPTER_NOT_READY: |
| status = mojom::BluetoothStatus::NOT_READY; |
| break; |
| case bluez::BluetoothServiceRecordBlueZ::ErrorCode:: |
| ERROR_DEVICE_DISCONNECTED: |
| status = mojom::BluetoothStatus::RMT_DEV_DOWN; |
| break; |
| default: |
| status = mojom::BluetoothStatus::FAIL; |
| break; |
| } |
| |
| sdp_bluetooth_instance->OnGetSdpRecords( |
| status, std::move(remote_addr), target_uuid, |
| std::vector<mojom::BluetoothSdpRecordPtr>()); |
| } |
| |
| namespace { |
| |
| union BluetoothSocketAddress { |
| sockaddr sock; |
| sockaddr_rc rfcomm; |
| sockaddr_l2 l2cap; |
| }; |
| |
| int32_t GetSockOptvalFromFlags(mojom::BluetoothSocketType sock_type, |
| mojom::BluetoothSocketFlagsPtr sock_flags) { |
| int optval = 0; |
| switch (sock_type) { |
| case mojom::BluetoothSocketType::TYPE_RFCOMM: |
| optval |= sock_flags->encrypt ? RFCOMM_LM_ENCRYPT : 0; |
| optval |= sock_flags->auth ? RFCOMM_LM_AUTH : 0; |
| optval |= sock_flags->auth_mitm ? RFCOMM_LM_SECURE : 0; |
| optval |= sock_flags->auth_16_digit ? RFCOMM_LM_SECURE : 0; |
| return optval; |
| case mojom::BluetoothSocketType::TYPE_L2CAP_LE: |
| optval |= sock_flags->encrypt ? L2CAP_LM_ENCRYPT : 0; |
| optval |= sock_flags->auth ? L2CAP_LM_AUTH : 0; |
| optval |= sock_flags->auth_mitm ? L2CAP_LM_SECURE : 0; |
| optval |= sock_flags->auth_16_digit ? L2CAP_LM_SECURE : 0; |
| return optval; |
| } |
| } |
| |
| // Opens an AF_BLUETOOTH socket with |sock_type|, sets L2CAP_LM or RFCOMM_LM |
| // with |optval|, and binds the socket to address with |port|. |
| base::ScopedFD OpenBluetoothSocketImpl(mojom::BluetoothSocketType sock_type, |
| int32_t optval, |
| uint16_t port) { |
| int protocol; |
| int level; |
| int optname; |
| switch (sock_type) { |
| case mojom::BluetoothSocketType::TYPE_RFCOMM: |
| protocol = BTPROTO_RFCOMM; |
| level = SOL_RFCOMM; |
| optname = RFCOMM_LM; |
| break; |
| case mojom::BluetoothSocketType::TYPE_L2CAP_LE: |
| protocol = BTPROTO_L2CAP; |
| level = SOL_L2CAP; |
| optname = L2CAP_LM; |
| break; |
| default: |
| LOG(ERROR) << "Unknown socket type " << sock_type; |
| return {}; |
| } |
| |
| base::ScopedFD sock(socket(AF_BLUETOOTH, SOCK_STREAM, protocol)); |
| if (!sock.is_valid()) { |
| PLOG(ERROR) << "Failed to open bluetooth socket."; |
| return {}; |
| } |
| if (setsockopt(sock.get(), level, optname, &optval, sizeof(optval)) == -1) { |
| PLOG(ERROR) << "Failed to setopt() on socket."; |
| return {}; |
| } |
| if (fcntl(sock.get(), F_SETFL, O_NONBLOCK | fcntl(sock.get(), F_GETFL)) == |
| -1) { |
| PLOG(ERROR) << "Failed to fcntl() on socket."; |
| return {}; |
| } |
| |
| BluetoothSocketAddress sa = {}; |
| switch (sock_type) { |
| case mojom::BluetoothSocketType::TYPE_RFCOMM: |
| sa.rfcomm.rc_family = AF_BLUETOOTH; |
| sa.rfcomm.rc_channel = port; |
| break; |
| case mojom::BluetoothSocketType::TYPE_L2CAP_LE: |
| sa.l2cap.l2_family = AF_BLUETOOTH; |
| sa.l2cap.l2_psm = htobs(port); |
| sa.l2cap.l2_bdaddr_type = BDADDR_LE_PUBLIC; |
| break; |
| default: |
| LOG(ERROR) << "Unknown socket type " << sock_type; |
| return {}; |
| } |
| |
| if (bind(sock.get(), &sa.sock, sizeof(sa)) == -1) { |
| PLOG(ERROR) << "Failed to bind()"; |
| return {}; |
| } |
| |
| return sock; |
| } |
| |
| } // namespace |
| |
| void ArcBluezBridge::OnBluezListeningSocketReady( |
| ArcBluezBridge::BluetoothListeningSocket* sock_wrapper) { |
| BluetoothSocketAddress sa; |
| socklen_t addr_len = sizeof(sa); |
| base::ScopedFD accept_fd( |
| accept(sock_wrapper->file.get(), &sa.sock, &addr_len)); |
| if (!accept_fd.is_valid()) { |
| PLOG(ERROR) << "Failed to accept()"; |
| return; |
| } |
| if (fcntl(accept_fd.get(), F_SETFL, |
| O_NONBLOCK | fcntl(accept_fd.get(), F_GETFL)) == -1) { |
| PLOG(ERROR) << "Failed to fnctl()"; |
| return; |
| } |
| |
| mojo::ScopedHandle handle = |
| mojo::WrapPlatformHandle(mojo::PlatformHandle(std::move(accept_fd))); |
| |
| // Tells Android we successfully accept() a new connection. |
| auto connection = mojom::BluetoothSocketConnection::New(); |
| connection->sock = std::move(handle); |
| switch (sock_wrapper->sock_type) { |
| case mojom::BluetoothSocketType::TYPE_RFCOMM: |
| connection->addr = |
| mojom::BluetoothAddress::From<bdaddr_t>(sa.rfcomm.rc_bdaddr); |
| connection->port = sa.rfcomm.rc_channel; |
| break; |
| case mojom::BluetoothSocketType::TYPE_L2CAP_LE: |
| connection->addr = |
| mojom::BluetoothAddress::From<bdaddr_t>(sa.l2cap.l2_bdaddr); |
| connection->port = btohs(sa.l2cap.l2_psm); |
| break; |
| default: |
| LOG(ERROR) << "Unknown socket type " << sock_wrapper->sock_type; |
| return; |
| } |
| |
| sock_wrapper->remote->OnAccepted(std::move(connection)); |
| } |
| |
| void ArcBluezBridge::OnBluezConnectingSocketReady( |
| ArcBluezBridge::BluetoothConnectingSocket* sock_wrapper) { |
| // When connect() is ready, we will transfer this fd to Android, and Android |
| // is responsible for closing it. The file watcher |controller| needs to be |
| // disabled first, and then the fd ownership is transferred. |
| sock_wrapper->controller.reset(); |
| base::ScopedFD fd = std::move(sock_wrapper->file); |
| |
| // Checks whether connect() succeeded. |
| int err = 0; |
| socklen_t len = sizeof(err); |
| int ret = getsockopt(fd.get(), SOL_SOCKET, SO_ERROR, &err, &len); |
| if (ret != 0 || err != 0) { |
| LOG(ERROR) << "Failed to connect. err=" << err; |
| sock_wrapper->remote->OnConnectFailed(); |
| } |
| |
| // Gets peer address. |
| BluetoothSocketAddress peer_sa; |
| socklen_t peer_sa_len = sizeof(peer_sa); |
| if (getpeername(fd.get(), &peer_sa.sock, &peer_sa_len) == -1) { |
| PLOG(ERROR) << "Failed to getpeername()."; |
| sock_wrapper->remote->OnConnectFailed(); |
| } |
| |
| // Gets our port. |
| BluetoothSocketAddress local_sa; |
| socklen_t local_sa_len = sizeof(local_sa); |
| if (getsockname(fd.get(), &local_sa.sock, &local_sa_len) == -1) { |
| PLOG(ERROR) << "Failed to getsockname()"; |
| sock_wrapper->remote->OnConnectFailed(); |
| } |
| |
| mojo::ScopedHandle handle = |
| mojo::WrapPlatformHandle(mojo::PlatformHandle(std::move(fd))); |
| |
| // Notifies Android. |
| auto connection = mojom::BluetoothSocketConnection::New(); |
| connection->sock = std::move(handle); |
| switch (sock_wrapper->sock_type) { |
| case mojom::BluetoothSocketType::TYPE_RFCOMM: |
| connection->addr = |
| mojom::BluetoothAddress::From<bdaddr_t>(peer_sa.rfcomm.rc_bdaddr); |
| connection->port = local_sa.rfcomm.rc_channel; |
| break; |
| case mojom::BluetoothSocketType::TYPE_L2CAP_LE: |
| connection->addr = |
| mojom::BluetoothAddress::From<bdaddr_t>(peer_sa.l2cap.l2_bdaddr); |
| connection->port = btohs(local_sa.l2cap.l2_psm); |
| break; |
| default: |
| LOG(ERROR) << "Unknown socket type " << sock_wrapper->sock_type; |
| return; |
| } |
| sock_wrapper->remote->OnConnected(std::move(connection)); |
| } |
| |
| void ArcBluezBridge::CreateBluetoothListenSocket( |
| mojom::BluetoothSocketType type, |
| mojom::BluetoothSocketFlagsPtr flags, |
| int port, |
| BluetoothSocketListenCallback callback) { |
| std::unique_ptr<ArcBluezBridge::BluetoothListeningSocket> sock_wrapper = |
| nullptr; |
| int32_t optval = GetSockOptvalFromFlags(type, std::move(flags)); |
| uint16_t listen_port = static_cast<uint16_t>(port); |
| |
| do { |
| base::ScopedFD sock = OpenBluetoothSocketImpl(type, optval, listen_port); |
| if (!sock.is_valid()) { |
| LOG(ERROR) << "Failed to open listen socket."; |
| break; |
| } |
| |
| if (listen(sock.get(), /*backlog=*/1) == -1) { |
| PLOG(ERROR) << "Failed to listen()"; |
| break; |
| } |
| |
| BluetoothSocketAddress local_addr; |
| socklen_t addr_len = sizeof(local_addr); |
| if (getsockname(sock.get(), &local_addr.sock, &addr_len) == -1) { |
| PLOG(ERROR) << "Failed to getsockname()"; |
| break; |
| } |
| |
| sock_wrapper = std::make_unique<BluetoothListeningSocket>(); |
| sock_wrapper->sock_type = type; |
| sock_wrapper->controller = base::FileDescriptorWatcher::WatchReadable( |
| sock.get(), |
| base::BindRepeating(&ArcBluezBridge::OnBluezListeningSocketReady, |
| weak_factory_.GetWeakPtr(), sock_wrapper.get())); |
| sock_wrapper->file = std::move(sock); |
| |
| if (type == mojom::BluetoothSocketType::TYPE_RFCOMM) { |
| listen_port = local_addr.rfcomm.rc_channel; |
| } else if (type == mojom::BluetoothSocketType::TYPE_L2CAP_LE) { |
| listen_port = btohs(local_addr.l2cap.l2_psm); |
| } else { |
| LOG(ERROR) << "Unknown socket type " << type; |
| break; |
| } |
| } while (!sock_wrapper.get()); |
| |
| if (sock_wrapper) { |
| std::move(callback).Run(mojom::BluetoothStatus::SUCCESS, listen_port, |
| sock_wrapper->remote.BindNewPipeAndPassReceiver()); |
| BluetoothListeningSocket* socket = sock_wrapper.get(); |
| listening_sockets_.insert(std::move(sock_wrapper)); |
| socket->remote.set_disconnect_handler( |
| base::BindOnce(&ArcBluezBridge::CloseBluetoothListeningSocket, |
| weak_factory_.GetWeakPtr(), socket)); |
| } else { |
| std::move(callback).Run( |
| mojom::BluetoothStatus::FAIL, /*port=*/0, |
| mojo::PendingReceiver<mojom::BluetoothListenSocketClient>()); |
| } |
| } |
| |
| void ArcBluezBridge::CreateBluetoothConnectSocket( |
| mojom::BluetoothSocketType type, |
| mojom::BluetoothSocketFlagsPtr flags, |
| mojom::BluetoothAddressPtr addr, |
| int port, |
| BluetoothSocketConnectCallback callback) { |
| std::unique_ptr<ArcBluetoothBridge::BluetoothConnectingSocket> sock_wrapper = |
| nullptr; |
| int32_t optval = GetSockOptvalFromFlags(type, std::move(flags)); |
| base::ScopedFD sock = OpenBluetoothSocketImpl(type, optval, kAutoSockPort); |
| int ret = 0; |
| |
| do { |
| if (!sock.is_valid()) { |
| LOG(ERROR) << "Failed to open connect socket."; |
| break; |
| } |
| |
| std::string addr_str = addr->To<std::string>(); |
| BluetoothDevice* device = bluetooth_adapter_->GetDevice(addr_str); |
| if (!device) { |
| break; |
| } |
| |
| const auto addr_type = device->GetAddressType(); |
| if (addr_type == BluetoothDevice::ADDR_TYPE_UNKNOWN) { |
| LOG(ERROR) << "Unknown address type."; |
| break; |
| } |
| |
| BluetoothSocketAddress sa = {}; |
| if (type == mojom::BluetoothSocketType::TYPE_RFCOMM) { |
| sa.rfcomm.rc_family = AF_BLUETOOTH; |
| sa.rfcomm.rc_bdaddr = addr->To<bdaddr_t>(); |
| sa.rfcomm.rc_channel = static_cast<uint8_t>(port); |
| } else if (type == mojom::BluetoothSocketType::TYPE_L2CAP_LE) { |
| sa.l2cap.l2_family = AF_BLUETOOTH; |
| sa.l2cap.l2_bdaddr = addr->To<bdaddr_t>(); |
| sa.l2cap.l2_psm = htobs(port); |
| sa.l2cap.l2_bdaddr_type = addr_type == BluetoothDevice::ADDR_TYPE_PUBLIC |
| ? BDADDR_LE_PUBLIC |
| : BDADDR_LE_RANDOM; |
| } else { |
| LOG(ERROR) << "Unknown socket type " << type; |
| } |
| |
| ret = HANDLE_EINTR(connect( |
| sock.get(), reinterpret_cast<const struct sockaddr*>(&sa), sizeof(sa))); |
| |
| sock_wrapper = |
| std::make_unique<ArcBluetoothBridge::BluetoothConnectingSocket>(); |
| sock_wrapper->sock_type = type; |
| } while (sock_wrapper == nullptr); |
| |
| if (sock_wrapper) { |
| if (ret == 0) { |
| // connect() returns success immediately. |
| sock_wrapper->file = std::move(sock); |
| // BluetoothSocketConnect() is a blocking mojo call on the ARC side, so |
| // the callback needs to be triggered asynchronously and thus we use a |
| // PostTask here. |
| base::SequencedTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, |
| base::BindOnce(&ArcBluezBridge::OnBluezConnectingSocketReady, |
| weak_factory_.GetWeakPtr(), sock_wrapper.get())); |
| } else if (errno == EINPROGRESS) { |
| sock_wrapper->controller = base::FileDescriptorWatcher::WatchWritable( |
| sock.get(), |
| base::BindRepeating(&ArcBluezBridge::OnBluezConnectingSocketReady, |
| weak_factory_.GetWeakPtr(), sock_wrapper.get())); |
| sock_wrapper->file = std::move(sock); |
| } else { |
| PLOG(ERROR) << "Failed to connect."; |
| std::move(callback).Run( |
| mojom::BluetoothStatus::FAIL, |
| mojo::PendingReceiver<arc::mojom::BluetoothConnectSocketClient>()); |
| return; |
| } |
| } else { |
| std::move(callback).Run( |
| mojom::BluetoothStatus::FAIL, |
| mojo::PendingReceiver<arc::mojom::BluetoothConnectSocketClient>()); |
| return; |
| } |
| |
| std::move(callback).Run(mojom::BluetoothStatus::SUCCESS, |
| sock_wrapper->remote.BindNewPipeAndPassReceiver()); |
| BluetoothConnectingSocket* socket = sock_wrapper.get(); |
| connecting_sockets_.insert(std::move(sock_wrapper)); |
| socket->remote.set_disconnect_handler( |
| base::BindOnce(&ArcBluezBridge::CloseBluetoothConnectingSocket, |
| weak_factory_.GetWeakPtr(), socket)); |
| } |
| |
| } // namespace arc |