| // 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. |
| |
| #include "extensions/browser/api/bluetooth_socket/bluetooth_socket_api.h" |
| |
| #include <stdint.h> |
| |
| #include <unordered_set> |
| #include <utility> |
| |
| #include "base/functional/bind.h" |
| #include "base/hash/hash.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/types/optional_util.h" |
| #include "content/public/browser/browser_context.h" |
| #include "device/bluetooth/bluetooth_adapter.h" |
| #include "device/bluetooth/bluetooth_adapter_factory.h" |
| #include "device/bluetooth/bluetooth_device.h" |
| #include "device/bluetooth/bluetooth_socket.h" |
| #include "extensions/browser/api/bluetooth_socket/bluetooth_api_socket.h" |
| #include "extensions/browser/api/bluetooth_socket/bluetooth_socket_event_dispatcher.h" |
| #include "extensions/common/api/bluetooth/bluetooth_manifest_data.h" |
| #include "extensions/common/permissions/permissions_data.h" |
| #include "net/base/io_buffer.h" |
| |
| using content::BrowserThread; |
| using extensions::BluetoothApiSocket; |
| using extensions::api::bluetooth_socket::ListenOptions; |
| using extensions::api::bluetooth_socket::SocketInfo; |
| using extensions::api::bluetooth_socket::SocketProperties; |
| |
| namespace extensions { |
| namespace api { |
| |
| namespace { |
| |
| const char kDeviceNotFoundError[] = "Device not found"; |
| const char kInvalidPsmError[] = "Invalid PSM"; |
| const char kInvalidUuidError[] = "Invalid UUID"; |
| const char kPermissionDeniedError[] = "Permission denied"; |
| const char kSocketNotFoundError[] = "Socket not found"; |
| |
| SocketInfo CreateSocketInfo(int socket_id, BluetoothApiSocket* socket) { |
| DCHECK_CURRENTLY_ON(BluetoothApiSocket::kThreadId); |
| SocketInfo socket_info; |
| // This represents what we know about the socket, and does not call through |
| // to the system. |
| socket_info.socket_id = socket_id; |
| if (socket->name()) { |
| socket_info.name = *socket->name(); |
| } |
| socket_info.persistent = socket->persistent(); |
| if (socket->buffer_size() > 0) { |
| socket_info.buffer_size = socket->buffer_size(); |
| } |
| socket_info.paused = socket->paused(); |
| socket_info.connected = socket->IsConnected(); |
| |
| if (socket->IsConnected()) { |
| socket_info.address = socket->device_address(); |
| } |
| socket_info.uuid = socket->uuid().canonical_value(); |
| |
| return socket_info; |
| } |
| |
| void SetSocketProperties(BluetoothApiSocket* socket, |
| SocketProperties* properties) { |
| if (properties->name) { |
| socket->set_name(*properties->name); |
| } |
| if (properties->persistent) { |
| socket->set_persistent(*properties->persistent); |
| } |
| if (properties->buffer_size) { |
| // buffer size is validated when issuing the actual Recv operation |
| // on the socket. |
| socket->set_buffer_size(*properties->buffer_size); |
| } |
| } |
| |
| BluetoothSocketEventDispatcher* GetSocketEventDispatcher( |
| content::BrowserContext* browser_context) { |
| BluetoothSocketEventDispatcher* socket_event_dispatcher = |
| BluetoothSocketEventDispatcher::Get(browser_context); |
| DCHECK(socket_event_dispatcher) |
| << "There is no socket event dispatcher. " |
| "If this assertion is failing during a test, then it is likely that " |
| "TestExtensionSystem is failing to provide an instance of " |
| "BluetoothSocketEventDispatcher."; |
| return socket_event_dispatcher; |
| } |
| |
| // Returns |true| if |psm| is a valid PSM. |
| // Per the Bluetooth specification, the PSM field must be at least two octets in |
| // length, with least significant bit of the least significant octet equal to |
| // '1' and the least significant bit of the most significant octet equal to '0'. |
| bool IsValidPsm(int psm) { |
| if (psm <= 0) |
| return false; |
| |
| std::vector<int16_t> octets; |
| while (psm > 0) { |
| octets.push_back(psm & 0xFF); |
| psm = psm >> 8; |
| } |
| |
| if (octets.size() < 2U) |
| return false; |
| |
| // The least significant bit of the least significant octet must be '1'. |
| if ((octets.front() & 0x01) != 1) |
| return false; |
| |
| // The least significant bit of the most significant octet must be '0'. |
| if ((octets.back() & 0x01) != 0) |
| return false; |
| |
| return true; |
| } |
| |
| } // namespace |
| |
| BluetoothSocketAsyncApiFunction::BluetoothSocketAsyncApiFunction() = default; |
| |
| BluetoothSocketAsyncApiFunction::~BluetoothSocketAsyncApiFunction() = default; |
| |
| bool BluetoothSocketAsyncApiFunction::PreRunValidation(std::string* error) { |
| if (!ExtensionFunction::PreRunValidation(error)) |
| return false; |
| |
| if (!BluetoothManifestData::CheckSocketPermitted(extension())) { |
| *error = kPermissionDeniedError; |
| return false; |
| } |
| |
| manager_ = ApiResourceManager<BluetoothApiSocket>::Get(browser_context()); |
| DCHECK(manager_) |
| << "There is no socket manager. " |
| "If this assertion is failing during a test, then it is likely that " |
| "TestExtensionSystem is failing to provide an instance of " |
| "ApiResourceManager<BluetoothApiSocket>."; |
| |
| if (!manager_) { |
| *error = "There is no socket manager."; |
| return false; |
| } |
| return true; |
| } |
| |
| int BluetoothSocketAsyncApiFunction::AddSocket(BluetoothApiSocket* socket) { |
| return manager_->Add(socket); |
| } |
| |
| content::BrowserThread::ID |
| BluetoothSocketAsyncApiFunction::work_thread_id() const { |
| return BluetoothApiSocket::kThreadId; |
| } |
| |
| BluetoothApiSocket* BluetoothSocketAsyncApiFunction::GetSocket( |
| int api_resource_id) { |
| return manager_->Get(extension_id(), api_resource_id); |
| } |
| |
| void BluetoothSocketAsyncApiFunction::RemoveSocket(int api_resource_id) { |
| manager_->Remove(extension_id(), api_resource_id); |
| } |
| |
| std::unordered_set<int>* BluetoothSocketAsyncApiFunction::GetSocketIds() { |
| return manager_->GetResourceIds(extension_id()); |
| } |
| |
| BluetoothSocketCreateFunction::BluetoothSocketCreateFunction() = default; |
| |
| BluetoothSocketCreateFunction::~BluetoothSocketCreateFunction() = default; |
| |
| ExtensionFunction::ResponseAction BluetoothSocketCreateFunction::Run() { |
| DCHECK_CURRENTLY_ON(work_thread_id()); |
| |
| auto params = bluetooth_socket::Create::Params::Create(args()); |
| EXTENSION_FUNCTION_VALIDATE(params); |
| |
| BluetoothApiSocket* socket = new BluetoothApiSocket(extension_id()); |
| |
| bluetooth_socket::SocketProperties* properties = |
| base::OptionalToPtr(params->properties); |
| if (properties) |
| SetSocketProperties(socket, properties); |
| |
| bluetooth_socket::CreateInfo create_info; |
| create_info.socket_id = AddSocket(socket); |
| return RespondNow( |
| ArgumentList(bluetooth_socket::Create::Results::Create(create_info))); |
| } |
| |
| BluetoothSocketUpdateFunction::BluetoothSocketUpdateFunction() = default; |
| |
| BluetoothSocketUpdateFunction::~BluetoothSocketUpdateFunction() = default; |
| |
| ExtensionFunction::ResponseAction BluetoothSocketUpdateFunction::Run() { |
| auto params = bluetooth_socket::Update::Params::Create(args()); |
| EXTENSION_FUNCTION_VALIDATE(params); |
| |
| BluetoothApiSocket* socket = GetSocket(params->socket_id); |
| if (!socket) |
| return RespondNow(Error(kSocketNotFoundError)); |
| |
| SetSocketProperties(socket, ¶ms->properties); |
| return RespondNow(ArgumentList(bluetooth_socket::Update::Results::Create())); |
| } |
| |
| BluetoothSocketSetPausedFunction::BluetoothSocketSetPausedFunction() = default; |
| |
| BluetoothSocketSetPausedFunction::~BluetoothSocketSetPausedFunction() = default; |
| |
| ExtensionFunction::ResponseAction BluetoothSocketSetPausedFunction::Run() { |
| auto params = bluetooth_socket::SetPaused::Params::Create(args()); |
| EXTENSION_FUNCTION_VALIDATE(params); |
| |
| BluetoothSocketEventDispatcher* socket_event_dispatcher = |
| GetSocketEventDispatcher(browser_context()); |
| if (!socket_event_dispatcher) |
| return RespondNow(Error("Could not get socket event dispatcher.")); |
| |
| BluetoothApiSocket* socket = GetSocket(params->socket_id); |
| if (!socket) |
| return RespondNow(Error(kSocketNotFoundError)); |
| |
| if (socket->paused() != params->paused) { |
| socket->set_paused(params->paused); |
| if (!params->paused) { |
| socket_event_dispatcher->OnSocketResume(extension_id(), |
| params->socket_id); |
| } |
| } |
| |
| return RespondNow( |
| ArgumentList(bluetooth_socket::SetPaused::Results::Create())); |
| } |
| |
| BluetoothSocketListenFunction::BluetoothSocketListenFunction() = default; |
| |
| BluetoothSocketListenFunction::~BluetoothSocketListenFunction() = default; |
| |
| bool BluetoothSocketListenFunction::PreRunValidation(std::string* error) { |
| if (!BluetoothSocketAsyncApiFunction::PreRunValidation(error)) |
| return false; |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| EXTENSION_FUNCTION_PRERUN_VALIDATE(CreateParams()); |
| socket_event_dispatcher_ = GetSocketEventDispatcher(browser_context()); |
| return socket_event_dispatcher_ != nullptr; |
| } |
| |
| ExtensionFunction::ResponseAction BluetoothSocketListenFunction::Run() { |
| DCHECK_CURRENTLY_ON(work_thread_id()); |
| device::BluetoothAdapterFactory::Get()->GetClassicAdapter( |
| base::BindOnce(&BluetoothSocketListenFunction::OnGetAdapter, this)); |
| return did_respond() ? AlreadyResponded() : RespondLater(); |
| } |
| |
| void BluetoothSocketListenFunction::OnGetAdapter( |
| scoped_refptr<device::BluetoothAdapter> adapter) { |
| DCHECK_CURRENTLY_ON(work_thread_id()); |
| BluetoothApiSocket* socket = GetSocket(socket_id()); |
| if (!socket) { |
| Respond(Error(kSocketNotFoundError)); |
| return; |
| } |
| |
| device::BluetoothUUID bluetooth_uuid(uuid()); |
| if (!bluetooth_uuid.IsValid()) { |
| Respond(Error(kInvalidUuidError)); |
| return; |
| } |
| |
| BluetoothPermissionRequest param(uuid()); |
| if (!BluetoothManifestData::CheckRequest(extension(), param)) { |
| Respond(Error(kPermissionDeniedError)); |
| return; |
| } |
| |
| absl::optional<std::string> name; |
| if (socket->name()) |
| name = *socket->name(); |
| |
| CreateService( |
| adapter, bluetooth_uuid, name, |
| base::BindOnce(&BluetoothSocketListenFunction::OnCreateService, this), |
| base::BindOnce(&BluetoothSocketListenFunction::OnCreateServiceError, |
| this)); |
| } |
| |
| void BluetoothSocketListenFunction::OnCreateService( |
| scoped_refptr<device::BluetoothSocket> socket) { |
| DCHECK_CURRENTLY_ON(work_thread_id()); |
| |
| // Fetch the socket again since this is not a reference-counted object, and |
| // it may have gone away in the meantime (we check earlier to avoid making |
| // a connection in the case of an obvious programming error). |
| BluetoothApiSocket* api_socket = GetSocket(socket_id()); |
| if (!api_socket) { |
| Respond(Error(kSocketNotFoundError)); |
| return; |
| } |
| |
| api_socket->AdoptListeningSocket(socket, |
| device::BluetoothUUID(uuid())); |
| socket_event_dispatcher_->OnSocketListen(extension_id(), socket_id()); |
| Respond(ArgumentList(CreateResults())); |
| } |
| |
| void BluetoothSocketListenFunction::OnCreateServiceError( |
| const std::string& message) { |
| DCHECK_CURRENTLY_ON(work_thread_id()); |
| Respond(Error(message)); |
| } |
| |
| BluetoothSocketListenUsingRfcommFunction:: |
| BluetoothSocketListenUsingRfcommFunction() {} |
| |
| BluetoothSocketListenUsingRfcommFunction:: |
| ~BluetoothSocketListenUsingRfcommFunction() {} |
| |
| int BluetoothSocketListenUsingRfcommFunction::socket_id() const { |
| return params_->socket_id; |
| } |
| |
| const std::string& BluetoothSocketListenUsingRfcommFunction::uuid() const { |
| return params_->uuid; |
| } |
| |
| bool BluetoothSocketListenUsingRfcommFunction::CreateParams() { |
| params_ = bluetooth_socket::ListenUsingRfcomm::Params::Create(args()); |
| return params_.has_value(); |
| } |
| |
| void BluetoothSocketListenUsingRfcommFunction::CreateService( |
| scoped_refptr<device::BluetoothAdapter> adapter, |
| const device::BluetoothUUID& uuid, |
| const absl::optional<std::string>& name, |
| device::BluetoothAdapter::CreateServiceCallback callback, |
| device::BluetoothAdapter::CreateServiceErrorCallback error_callback) { |
| device::BluetoothAdapter::ServiceOptions service_options; |
| service_options.name = std::move(name); |
| |
| const absl::optional<ListenOptions>& options = params_->options; |
| if (options && options->channel) |
| service_options.channel = *options->channel; |
| |
| adapter->CreateRfcommService(uuid, service_options, std::move(callback), |
| std::move(error_callback)); |
| } |
| |
| base::Value::List BluetoothSocketListenUsingRfcommFunction::CreateResults() { |
| return bluetooth_socket::ListenUsingRfcomm::Results::Create(); |
| } |
| |
| BluetoothSocketListenUsingL2capFunction:: |
| BluetoothSocketListenUsingL2capFunction() {} |
| |
| BluetoothSocketListenUsingL2capFunction:: |
| ~BluetoothSocketListenUsingL2capFunction() {} |
| |
| int BluetoothSocketListenUsingL2capFunction::socket_id() const { |
| return params_->socket_id; |
| } |
| |
| const std::string& BluetoothSocketListenUsingL2capFunction::uuid() const { |
| return params_->uuid; |
| } |
| |
| bool BluetoothSocketListenUsingL2capFunction::CreateParams() { |
| params_ = bluetooth_socket::ListenUsingL2cap::Params::Create(args()); |
| return params_.has_value(); |
| } |
| |
| void BluetoothSocketListenUsingL2capFunction::CreateService( |
| scoped_refptr<device::BluetoothAdapter> adapter, |
| const device::BluetoothUUID& uuid, |
| const absl::optional<std::string>& name, |
| device::BluetoothAdapter::CreateServiceCallback callback, |
| device::BluetoothAdapter::CreateServiceErrorCallback error_callback) { |
| device::BluetoothAdapter::ServiceOptions service_options; |
| service_options.name = std::move(name); |
| |
| const absl::optional<ListenOptions>& options = params_->options; |
| if (options && options->psm) { |
| int psm = *options->psm; |
| if (!IsValidPsm(psm)) { |
| std::move(error_callback).Run(kInvalidPsmError); |
| return; |
| } |
| |
| service_options.psm = psm; |
| } |
| |
| adapter->CreateL2capService(uuid, service_options, std::move(callback), |
| std::move(error_callback)); |
| } |
| |
| base::Value::List BluetoothSocketListenUsingL2capFunction::CreateResults() { |
| return bluetooth_socket::ListenUsingL2cap::Results::Create(); |
| } |
| |
| BluetoothSocketAbstractConnectFunction:: |
| BluetoothSocketAbstractConnectFunction() {} |
| |
| BluetoothSocketAbstractConnectFunction:: |
| ~BluetoothSocketAbstractConnectFunction() {} |
| |
| bool BluetoothSocketAbstractConnectFunction::PreRunValidation( |
| std::string* error) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| if (!BluetoothSocketAsyncApiFunction::PreRunValidation(error)) |
| return false; |
| |
| params_ = bluetooth_socket::Connect::Params::Create(args()); |
| EXTENSION_FUNCTION_PRERUN_VALIDATE(params_); |
| |
| socket_event_dispatcher_ = GetSocketEventDispatcher(browser_context()); |
| return socket_event_dispatcher_ != nullptr; |
| } |
| |
| ExtensionFunction::ResponseAction |
| BluetoothSocketAbstractConnectFunction::Run() { |
| DCHECK_CURRENTLY_ON(work_thread_id()); |
| device::BluetoothAdapterFactory::Get()->GetClassicAdapter(base::BindOnce( |
| &BluetoothSocketAbstractConnectFunction::OnGetAdapter, this)); |
| return did_respond() ? AlreadyResponded() : RespondLater(); |
| } |
| |
| void BluetoothSocketAbstractConnectFunction::OnGetAdapter( |
| scoped_refptr<device::BluetoothAdapter> adapter) { |
| DCHECK_CURRENTLY_ON(work_thread_id()); |
| BluetoothApiSocket* socket = GetSocket(params_->socket_id); |
| if (!socket) { |
| Respond(Error(kSocketNotFoundError)); |
| return; |
| } |
| |
| device::BluetoothDevice* device = adapter->GetDevice(params_->address); |
| if (!device) { |
| Respond(Error(kDeviceNotFoundError)); |
| return; |
| } |
| |
| device::BluetoothUUID uuid(params_->uuid); |
| if (!uuid.IsValid()) { |
| Respond(Error(kInvalidUuidError)); |
| return; |
| } |
| |
| BluetoothPermissionRequest param(params_->uuid); |
| if (!BluetoothManifestData::CheckRequest(extension(), param)) { |
| Respond(Error(kPermissionDeniedError)); |
| return; |
| } |
| |
| ConnectToService(device, uuid); |
| } |
| |
| void BluetoothSocketAbstractConnectFunction::OnConnect( |
| scoped_refptr<device::BluetoothSocket> socket) { |
| DCHECK_CURRENTLY_ON(work_thread_id()); |
| |
| // Fetch the socket again since this is not a reference-counted object, and |
| // it may have gone away in the meantime (we check earlier to avoid making |
| // a connection in the case of an obvious programming error). |
| BluetoothApiSocket* api_socket = GetSocket(params_->socket_id); |
| if (!api_socket) { |
| Respond(Error(kSocketNotFoundError)); |
| return; |
| } |
| |
| api_socket->AdoptConnectedSocket(socket, |
| params_->address, |
| device::BluetoothUUID(params_->uuid)); |
| socket_event_dispatcher_->OnSocketConnect(extension_id(), |
| params_->socket_id); |
| |
| Respond(ArgumentList(bluetooth_socket::Connect::Results::Create())); |
| } |
| |
| void BluetoothSocketAbstractConnectFunction::OnConnectError( |
| const std::string& message) { |
| DCHECK_CURRENTLY_ON(work_thread_id()); |
| Respond(Error(message)); |
| } |
| |
| BluetoothSocketConnectFunction::BluetoothSocketConnectFunction() = default; |
| |
| BluetoothSocketConnectFunction::~BluetoothSocketConnectFunction() = default; |
| |
| void BluetoothSocketConnectFunction::ConnectToService( |
| device::BluetoothDevice* device, |
| const device::BluetoothUUID& uuid) { |
| device->ConnectToService( |
| uuid, base::BindOnce(&BluetoothSocketConnectFunction::OnConnect, this), |
| base::BindOnce(&BluetoothSocketConnectFunction::OnConnectError, this)); |
| } |
| |
| BluetoothSocketDisconnectFunction::BluetoothSocketDisconnectFunction() = |
| default; |
| |
| BluetoothSocketDisconnectFunction::~BluetoothSocketDisconnectFunction() = |
| default; |
| |
| ExtensionFunction::ResponseAction BluetoothSocketDisconnectFunction::Run() { |
| DCHECK_CURRENTLY_ON(work_thread_id()); |
| |
| auto params = bluetooth_socket::Disconnect::Params::Create(args()); |
| EXTENSION_FUNCTION_VALIDATE(params); |
| |
| BluetoothApiSocket* socket = GetSocket(params->socket_id); |
| if (!socket) |
| return RespondNow(Error(kSocketNotFoundError)); |
| |
| socket->Disconnect( |
| base::BindOnce(&BluetoothSocketDisconnectFunction::OnSuccess, this)); |
| return did_respond() ? AlreadyResponded() : RespondLater(); |
| } |
| |
| void BluetoothSocketDisconnectFunction::OnSuccess() { |
| DCHECK_CURRENTLY_ON(work_thread_id()); |
| Respond(ArgumentList(bluetooth_socket::Disconnect::Results::Create())); |
| } |
| |
| BluetoothSocketCloseFunction::BluetoothSocketCloseFunction() = default; |
| |
| BluetoothSocketCloseFunction::~BluetoothSocketCloseFunction() = default; |
| |
| ExtensionFunction::ResponseAction BluetoothSocketCloseFunction::Run() { |
| auto params = bluetooth_socket::Close::Params::Create(args()); |
| EXTENSION_FUNCTION_VALIDATE(params); |
| BluetoothApiSocket* socket = GetSocket(params->socket_id); |
| if (!socket) |
| return RespondNow(Error(kSocketNotFoundError)); |
| |
| RemoveSocket(params->socket_id); |
| return RespondNow(ArgumentList(bluetooth_socket::Close::Results::Create())); |
| } |
| |
| BluetoothSocketSendFunction::BluetoothSocketSendFunction() |
| : io_buffer_size_(0) {} |
| |
| BluetoothSocketSendFunction::~BluetoothSocketSendFunction() = default; |
| |
| ExtensionFunction::ResponseAction BluetoothSocketSendFunction::Run() { |
| DCHECK_CURRENTLY_ON(work_thread_id()); |
| |
| params_ = bluetooth_socket::Send::Params::Create(args()); |
| EXTENSION_FUNCTION_VALIDATE(params_); |
| |
| io_buffer_size_ = params_->data.size(); |
| io_buffer_ = base::MakeRefCounted<net::WrappedIOBuffer>( |
| reinterpret_cast<const char*>(params_->data.data()), |
| params_->data.size()); |
| |
| BluetoothApiSocket* socket = GetSocket(params_->socket_id); |
| if (!socket) |
| return RespondNow(Error(kSocketNotFoundError)); |
| |
| socket->Send(io_buffer_, io_buffer_size_, |
| base::BindOnce(&BluetoothSocketSendFunction::OnSuccess, this), |
| base::BindOnce(&BluetoothSocketSendFunction::OnError, this)); |
| return did_respond() ? AlreadyResponded() : RespondLater(); |
| } |
| |
| void BluetoothSocketSendFunction::OnSuccess(int bytes_sent) { |
| DCHECK_CURRENTLY_ON(work_thread_id()); |
| Respond(ArgumentList(bluetooth_socket::Send::Results::Create(bytes_sent))); |
| } |
| |
| void BluetoothSocketSendFunction::OnError( |
| BluetoothApiSocket::ErrorReason reason, |
| const std::string& message) { |
| DCHECK_CURRENTLY_ON(work_thread_id()); |
| Respond(Error(message)); |
| } |
| |
| BluetoothSocketGetInfoFunction::BluetoothSocketGetInfoFunction() = default; |
| |
| BluetoothSocketGetInfoFunction::~BluetoothSocketGetInfoFunction() = default; |
| |
| ExtensionFunction::ResponseAction BluetoothSocketGetInfoFunction::Run() { |
| auto params = bluetooth_socket::GetInfo::Params::Create(args()); |
| EXTENSION_FUNCTION_VALIDATE(params); |
| |
| BluetoothApiSocket* socket = GetSocket(params->socket_id); |
| if (!socket) |
| return RespondNow(Error(kSocketNotFoundError)); |
| |
| return RespondNow(ArgumentList(bluetooth_socket::GetInfo::Results::Create( |
| CreateSocketInfo(params->socket_id, socket)))); |
| } |
| |
| BluetoothSocketGetSocketsFunction::BluetoothSocketGetSocketsFunction() = |
| default; |
| |
| BluetoothSocketGetSocketsFunction::~BluetoothSocketGetSocketsFunction() = |
| default; |
| |
| ExtensionFunction::ResponseAction BluetoothSocketGetSocketsFunction::Run() { |
| std::vector<bluetooth_socket::SocketInfo> socket_infos; |
| std::unordered_set<int>* resource_ids = GetSocketIds(); |
| if (resource_ids) { |
| for (int socket_id : *resource_ids) { |
| BluetoothApiSocket* socket = GetSocket(socket_id); |
| if (socket) { |
| socket_infos.push_back(CreateSocketInfo(socket_id, socket)); |
| } |
| } |
| } |
| return RespondNow(ArgumentList( |
| bluetooth_socket::GetSockets::Results::Create(socket_infos))); |
| } |
| |
| } // namespace api |
| } // namespace extensions |