blob: 80a8d2b9d003b4329c79676a4a86e2eead2f936e [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_bluetooth_util.h"
#include <stdint.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/l2cap.h>
#include <bluetooth/sdp.h>
#include <sys/socket.h>
#include <algorithm>
#include <vector>
#include "base/bind.h"
#include "base/callback.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/task_runner_util.h"
#include "base/threading/sequenced_worker_pool.h"
#include "base/time/time.h"
#include "content/public/browser/browser_thread.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/bluetooth_device_chromeos.h"
#include "net/socket/socket_descriptor.h"
namespace extensions {
namespace api {
namespace easy_unlock {
namespace {
using device::BluetoothDevice;
const char kInvalidDeviceAddress[] =
"Given address is not a valid Bluetooth device.";
const char kUnableToConnectToDevice[] =
"Unable to connect to the remote device.";
// Delay prior to closing an SDP connection opened to register a Bluetooth
// device with the system BlueZ daemon.
const int kCloseSDPConnectionDelaySec = 5;
// Writes |address| into the |result|. Return true on success, false if the
// |address| is not a valid Bluetooth address.
bool BluetoothAddressToBdaddr(const std::string& address,
bdaddr_t* result) {
std::string canonical_address = BluetoothDevice::CanonicalizeAddress(address);
if (canonical_address.empty())
return false;
std::vector<std::string> octets;
base::SplitString(canonical_address, ':', &octets);
DCHECK_EQ(octets.size(), 6U);
// BlueZ expects the octets in the reverse order.
std::reverse(octets.begin(), octets.end());
for (size_t i = 0; i < octets.size(); ++i) {
uint32_t octet;
bool success = base::HexStringToUInt(octets[i], &octet);
DCHECK(success);
result->b[i] = base::checked_cast<uint8_t>(octet);
}
return true;
}
// Closes the socket with the given |socket_descriptor|.
void CloseSocket(net::SocketDescriptor socket_descriptor) {
int result = close(socket_descriptor);
DCHECK_EQ(result, 0);
}
// Connects to the SDP service on the Bluetooth device with the given
// |device_address|, if possible. Returns an indicator of success or an error
// message on failure.
SeekDeviceResult SeekBluetoothDeviceByAddressImpl(
const std::string& device_address) {
base::TaskRunner* blocking_pool = content::BrowserThread::GetBlockingPool();
DCHECK(blocking_pool->RunsTasksOnCurrentThread());
SeekDeviceResult seek_result;
seek_result.success = false;
struct sockaddr_l2 addr;
memset(&addr, 0, sizeof(addr));
addr.l2_family = AF_BLUETOOTH;
addr.l2_psm = htobs(SDP_PSM);
if (!BluetoothAddressToBdaddr(device_address, &addr.l2_bdaddr)) {
seek_result.error_message = kInvalidDeviceAddress;
return seek_result;
}
net::SocketDescriptor socket_descriptor =
socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
int result = connect(socket_descriptor,
reinterpret_cast<struct sockaddr *>(&addr),
sizeof(addr));
if (result == 0) {
seek_result.success = true;
blocking_pool->PostDelayedTask(
FROM_HERE,
base::Bind(&CloseSocket, socket_descriptor),
base::TimeDelta::FromSeconds(kCloseSDPConnectionDelaySec));
} else {
// TODO(isherman): Pass a better message based on the errno?
seek_result.error_message = kUnableToConnectToDevice;
}
return seek_result;
}
} // namespace
void SeekBluetoothDeviceByAddress(const std::string& device_address,
const SeekDeviceCallback& callback) {
base::PostTaskAndReplyWithResult(
content::BrowserThread::GetBlockingPool(),
FROM_HERE,
base::Bind(&SeekBluetoothDeviceByAddressImpl, device_address),
callback);
}
void ConnectToBluetoothServiceInsecurely(
device::BluetoothDevice* device,
const device::BluetoothUUID& uuid,
const BluetoothDevice::ConnectToServiceCallback& callback,
const BluetoothDevice::ConnectToServiceErrorCallback& error_callback) {
static_cast<chromeos::BluetoothDeviceChromeOS*>(device)
->ConnectToServiceInsecurely(uuid, callback, error_callback);
}
} // namespace easy_unlock
} // namespace api
} // namespace extensions