blob: b7bc257790856a3b2cfc2afdcde85321df44a36c [file] [log] [blame]
// 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.
#include "bluetooth/newblued/newblue.h"
#include <newblue/sm.h>
#include <algorithm>
#include <memory>
#include <utility>
#include <vector>
#include <base/bind.h>
#include <base/message_loop/message_loop.h>
#include <base/strings/stringprintf.h>
#include <chromeos/dbus/service_constants.h>
#include "bluetooth/common/util.h"
namespace bluetooth {
namespace {
// Converts the uint8_t[6] MAC address into std::string form, e.g.
// {0x05, 0x04, 0x03, 0x02, 0x01, 0x00} will be 00:01:02:03:04:05.
std::string ConvertBtAddrToString(const struct bt_addr& addr) {
return base::StringPrintf("%02X:%02X:%02X:%02X:%02X:%02X", addr.addr[5],
addr.addr[4], addr.addr[3], addr.addr[2],
addr.addr[1], addr.addr[0]);
}
} // namespace
Newblue::Newblue(std::unique_ptr<LibNewblue> libnewblue)
: libnewblue_(std::move(libnewblue)), weak_ptr_factory_(this) {}
base::WeakPtr<Newblue> Newblue::GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
bool Newblue::Init() {
if (base::MessageLoop::current())
origin_task_runner_ = base::MessageLoop::current()->task_runner();
return true;
}
void Newblue::RegisterPairingAgent(PairingAgent* pairing_agent) {
pairing_agent_ = pairing_agent;
}
void Newblue::UnregisterPairingAgent() {
pairing_agent_ = nullptr;
}
bool Newblue::ListenReadyForUp(base::Closure callback) {
// Dummy MAC address. NewBlue doesn't actually use the MAC address as it's
// exclusively controlled by BlueZ.
static const uint8_t kZeroMac[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
if (!libnewblue_->HciUp(kZeroMac, &Newblue::OnStackReadyForUpThunk, this))
return false;
ready_for_up_callback_ = callback;
return true;
}
bool Newblue::BringUp() {
if (!libnewblue_->HciIsUp()) {
LOG(ERROR) << "HCI is not ready for up";
return false;
}
if (libnewblue_->L2cInit()) {
LOG(ERROR) << "Failed to initialize L2CAP";
return false;
}
if (!libnewblue_->AttInit()) {
LOG(ERROR) << "Failed to initialize ATT";
return false;
}
if (!libnewblue_->GattProfileInit()) {
LOG(ERROR) << "Failed to initialize GATT";
return false;
}
if (!libnewblue_->GattBuiltinInit()) {
LOG(ERROR) << "Failed to initialize GATT services";
return false;
}
if (!libnewblue_->SmInit()) {
LOG(ERROR) << "Failed to init SM";
return false;
}
// Always register passkey display observer, assuming that our UI always
// supports this.
// TODO(sonnysasaka): We may optimize this by registering passkey display
// observer only if there is currently a default agent registered.
passkey_display_observer_id_ = libnewblue_->SmRegisterPasskeyDisplayObserver(
this, &Newblue::PasskeyDisplayObserverCallbackThunk);
pair_state_handle_ = libnewblue_->SmRegisterPairStateObserver(
this, &Newblue::PairStateCallbackThunk);
if (!pair_state_handle_) {
LOG(ERROR) << "Failed to register as an observer of pairing state";
return false;
}
return true;
}
bool Newblue::StartDiscovery(DeviceDiscoveredCallback callback) {
if (discovery_handle_ != 0) {
LOG(WARNING) << "Discovery is already started, ignoring request";
return false;
}
discovery_handle_ = libnewblue_->HciDiscoverLeStart(
&Newblue::DiscoveryCallbackThunk, this, true /* active */,
false /* use_random_addr */);
if (discovery_handle_ == 0) {
LOG(ERROR) << "Failed to start LE discovery";
return false;
}
device_discovered_callback_ = callback;
return true;
}
bool Newblue::StopDiscovery() {
if (discovery_handle_ == 0) {
LOG(WARNING) << "Discovery is not started, ignoring request";
return false;
}
bool ret = libnewblue_->HciDiscoverLeStop(discovery_handle_);
if (!ret) {
LOG(ERROR) << "Failed to stop LE discovery";
return false;
}
device_discovered_callback_.Reset();
discovery_handle_ = 0;
return true;
}
UniqueId Newblue::RegisterAsPairObserver(PairStateChangedCallback callback) {
UniqueId observer_id = GetNextId();
if (observer_id != kInvalidUniqueId)
pair_observers_.emplace(observer_id, callback);
return observer_id;
}
void Newblue::UnregisterAsPairObserver(UniqueId observer_id) {
pair_observers_.erase(observer_id);
}
bool Newblue::PostTask(const tracked_objects::Location& from_here,
const base::Closure& task) {
CHECK(origin_task_runner_.get());
return origin_task_runner_->PostTask(from_here, task);
}
void Newblue::OnStackReadyForUpThunk(void* data) {
Newblue* newblue = static_cast<Newblue*>(data);
newblue->PostTask(FROM_HERE, base::Bind(&Newblue::OnStackReadyForUp,
newblue->GetWeakPtr()));
}
void Newblue::OnStackReadyForUp() {
if (ready_for_up_callback_.is_null()) {
// libnewblue says it's ready for up but I don't have any callback. Most
// probably another stack (e.g. BlueZ) just re-initialized the adapter.
LOG(WARNING) << "No callback when stack is ready for up";
return;
}
ready_for_up_callback_.Run();
// It only makes sense to bring up the stack once and for all. Reset the
// callback here so we won't bring up the stack twice.
ready_for_up_callback_.Reset();
}
void Newblue::DiscoveryCallbackThunk(void* data,
const struct bt_addr* addr,
int8_t rssi,
uint8_t reply_type,
const void* eir,
uint8_t eir_len) {
Newblue* newblue = static_cast<Newblue*>(data);
std::vector<uint8_t> eir_vector(static_cast<const uint8_t*>(eir),
static_cast<const uint8_t*>(eir) + eir_len);
std::string address = ConvertBtAddrToString(*addr);
newblue->PostTask(
FROM_HERE, base::Bind(&Newblue::DiscoveryCallback, newblue->GetWeakPtr(),
address, addr->type, rssi, reply_type, eir_vector));
}
void Newblue::DiscoveryCallback(const std::string& address,
uint8_t address_type,
int8_t rssi,
uint8_t reply_type,
const std::vector<uint8_t>& eir) {
VLOG(1) << __func__;
if (device_discovered_callback_.is_null()) {
LOG(WARNING) << "DiscoveryCallback called when not discovering";
return;
}
device_discovered_callback_.Run(address, address_type, rssi, reply_type, eir);
}
bool Newblue::Pair(const std::string& device_address,
bool is_random_address,
smPairSecurityRequirements security_requirement) {
struct bt_addr address;
if (!ConvertToBtAddr(is_random_address, device_address, &address))
return false;
libnewblue_->SmPair(&address, &security_requirement);
return true;
}
bool Newblue::CancelPair(const std::string& device_address,
bool is_random_address) {
struct bt_addr address;
if (!ConvertToBtAddr(is_random_address, device_address, &address))
return false;
libnewblue_->SmUnpair(&address);
return true;
}
void Newblue::PairStateCallbackThunk(void* data,
const void* pair_state_change,
uniq_t observer_id) {
CHECK(data && pair_state_change);
Newblue* newblue = static_cast<Newblue*>(data);
const smPairStateChange* change =
static_cast<const smPairStateChange*>(pair_state_change);
newblue->PostTask(
FROM_HERE, base::Bind(&Newblue::PairStateCallback, newblue->GetWeakPtr(),
*change, observer_id));
}
void Newblue::PasskeyDisplayObserverCallbackThunk(
void* data,
const struct smPasskeyDisplay* passkey_display,
uniq_t observer_id) {
if (!passkey_display) {
LOG(WARNING) << "passkey display is not given";
return;
}
Newblue* newblue = static_cast<Newblue*>(data);
newblue->PostTask(
FROM_HERE,
base::Bind(&Newblue::PasskeyDisplayObserverCallback,
newblue->GetWeakPtr(), *passkey_display, observer_id));
}
void Newblue::PasskeyDisplayObserverCallback(
struct smPasskeyDisplay passkey_display, uniq_t observer_id) {
if (observer_id != passkey_display_observer_id_) {
LOG(WARNING) << "passkey display observer id mismatch";
return;
}
if (passkey_display.valid) {
CHECK(pairing_agent_);
pairing_agent_->DisplayPasskey(
ConvertBtAddrToString(passkey_display.peerAddr),
passkey_display.passkey);
} else {
VLOG(1) << "The passkey session expired with the device";
}
}
void Newblue::PairStateCallback(const smPairStateChange& change,
uniq_t observer_id) {
VLOG(1) << __func__;
CHECK_EQ(observer_id, pair_state_handle_);
std::string address = ConvertBtAddrToString(change.peerAddr);
PairState state = static_cast<PairState>(change.pairState);
PairError error = static_cast<PairError>(change.pairErr);
// Notify |pair_observers|.
for (const auto& observer : pair_observers_)
observer.second.Run(address, state, error);
}
} // namespace bluetooth