Commit 840f5cbd authored by tengs's avatar tengs Committed by Commit bot

[EasyUnlock] Delete components/proximity_auth/ble directory.

This code was used in our old BLE protocol, which is now obsolete.

Review-Url: https://codereview.chromium.org/2843443003
Cr-Commit-Position: refs/heads/master@{#467914}
parent 905b7630
...@@ -1959,7 +1959,6 @@ split_static_library("browser") { ...@@ -1959,7 +1959,6 @@ split_static_library("browser") {
"//chrome/common/extensions/api:api_registration", "//chrome/common/extensions/api:api_registration",
"//chrome/common/extensions/api:extensions_features", "//chrome/common/extensions/api:extensions_features",
"//components/drive", "//components/drive",
"//components/proximity_auth/ble",
"//extensions/components/javascript_dialog_extensions_client", "//extensions/components/javascript_dialog_extensions_client",
"//media/cast:net", "//media/cast:net",
] ]
......
...@@ -849,7 +849,6 @@ static_library("extensions") { ...@@ -849,7 +849,6 @@ static_library("extensions") {
"//components/policy/core/browser", "//components/policy/core/browser",
"//components/pref_registry", "//components/pref_registry",
"//components/proximity_auth", "//components/proximity_auth",
"//components/proximity_auth/ble",
"//components/proximity_auth/logging", "//components/proximity_auth/logging",
"//components/proxy_config", "//components/proxy_config",
"//components/rappor", "//components/rappor",
......
# 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.
import("//testing/test.gni")
static_library("ble") {
sources = [
"bluetooth_low_energy_connection.cc",
"bluetooth_low_energy_connection.h",
"bluetooth_low_energy_device_whitelist.cc",
"bluetooth_low_energy_device_whitelist.h",
"pref_names.cc",
"pref_names.h",
]
deps = [
"//base",
"//components/cryptauth",
"//components/cryptauth/ble",
"//components/prefs",
"//components/proximity_auth/logging",
# TODO(https://crbug.com/562683): This component has a circular dependency
# with the root proximity auth target. It is whitelisted in that target for
# includes.
#"//components/proximity_auth",
"//device/bluetooth",
"//net",
]
public_deps = [
"//components/cryptauth/proto",
]
}
source_set("unit_tests") {
testonly = true
sources = [
"bluetooth_low_energy_connection_unittest.cc",
"bluetooth_low_energy_device_whitelist_unittest.cc",
]
configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
deps = [
":ble",
"//base/test:test_support",
"//components/cryptauth",
"//components/cryptauth:test_support",
"//components/cryptauth/ble:ble",
"//components/prefs:test_support",
"//components/proximity_auth:test_support",
"//device/bluetooth:mocks",
"//testing/gmock",
"//testing/gtest",
]
public_deps = [
"//components/cryptauth/proto",
]
}
msarda@chromium.org
sacomoto@chromium.org
tengs@chromium.org
khorimoto@chromium.org
hansberry@chromium.org
# COMPONENT: UI>ProximityAuth
// Copyright 2015 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 "components/proximity_auth/ble/bluetooth_low_energy_connection.h"
#include <utility>
#include "base/bind.h"
#include "base/location.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "components/cryptauth/ble/bluetooth_low_energy_characteristics_finder.h"
#include "components/cryptauth/ble/fake_wire_message.h"
#include "components/cryptauth/bluetooth_throttler.h"
#include "components/cryptauth/connection.h"
#include "components/cryptauth/connection_finder.h"
#include "components/cryptauth/wire_message.h"
#include "components/proximity_auth/logging/logging.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/bluetooth_gatt_connection.h"
#include "device/bluetooth/bluetooth_gatt_notify_session.h"
#include "device/bluetooth/bluetooth_remote_gatt_characteristic.h"
#include "device/bluetooth/bluetooth_uuid.h"
using device::BluetoothAdapter;
using device::BluetoothDevice;
using device::BluetoothGattConnection;
using device::BluetoothRemoteGattService;
using device::BluetoothRemoteGattCharacteristic;
using device::BluetoothGattNotifySession;
using device::BluetoothUUID;
namespace proximity_auth {
namespace {
// The UUID of the characteristic used to send data to the peripheral.
const char kToPeripheralCharUUID[] = "977c6674-1239-4e72-993b-502369b8bb5a";
// The UUID of the characteristic used to receive data from the peripheral.
const char kFromPeripheralCharUUID[] = "f4b904a2-a030-43b3-98a8-221c536c03cb";
// Deprecated signal send as the first byte in send byte operations.
const int kFirstByteZero = 0;
// The maximum number of bytes written in a remote characteristic with a single
// write request. This is not the connection MTU, we are assuming that the
// remote device allows for writes larger than MTU.
const int kMaxChunkSize = 500;
} // namespace
BluetoothLowEnergyConnection::BluetoothLowEnergyConnection(
const cryptauth::RemoteDevice& device,
scoped_refptr<device::BluetoothAdapter> adapter,
const BluetoothUUID remote_service_uuid,
cryptauth::BluetoothThrottler* bluetooth_throttler,
int max_number_of_write_attempts)
: cryptauth::Connection(device),
adapter_(adapter),
remote_service_({remote_service_uuid, ""}),
to_peripheral_char_({BluetoothUUID(kToPeripheralCharUUID), ""}),
from_peripheral_char_({BluetoothUUID(kFromPeripheralCharUUID), ""}),
bluetooth_throttler_(bluetooth_throttler),
task_runner_(base::ThreadTaskRunnerHandle::Get()),
sub_status_(SubStatus::DISCONNECTED),
receiving_bytes_(false),
write_remote_characteristic_pending_(false),
max_number_of_write_attempts_(max_number_of_write_attempts),
max_chunk_size_(kMaxChunkSize),
weak_ptr_factory_(this) {
DCHECK(adapter_);
DCHECK(adapter_->IsInitialized());
adapter_->AddObserver(this);
}
BluetoothLowEnergyConnection::~BluetoothLowEnergyConnection() {
Disconnect();
if (adapter_) {
adapter_->RemoveObserver(this);
adapter_ = NULL;
}
}
void BluetoothLowEnergyConnection::Connect() {
DCHECK(sub_status() == SubStatus::DISCONNECTED);
SetSubStatus(SubStatus::WAITING_GATT_CONNECTION);
base::TimeDelta throttler_delay = bluetooth_throttler_->GetDelay();
PA_LOG(INFO) << "Connecting in " << throttler_delay;
start_time_ = base::TimeTicks::Now();
// If necessary, wait to create a new GATT connection.
//
// Avoid creating a new GATT connection immediately after a given device was
// disconnected. This is a workaround for crbug.com/508919.
if (!throttler_delay.is_zero()) {
task_runner_->PostDelayedTask(
FROM_HERE,
base::Bind(&BluetoothLowEnergyConnection::CreateGattConnection,
weak_ptr_factory_.GetWeakPtr()),
throttler_delay);
return;
}
CreateGattConnection();
}
void BluetoothLowEnergyConnection::CreateGattConnection() {
DCHECK(sub_status() == SubStatus::WAITING_GATT_CONNECTION);
BluetoothDevice* remote_device = GetRemoteDevice();
if (remote_device) {
PA_LOG(INFO) << "Creating GATT connection with "
<< remote_device->GetAddress();
remote_device->CreateGattConnection(
base::Bind(&BluetoothLowEnergyConnection::OnGattConnectionCreated,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&BluetoothLowEnergyConnection::OnCreateGattConnectionError,
weak_ptr_factory_.GetWeakPtr()));
}
}
void BluetoothLowEnergyConnection::Disconnect() {
if (sub_status() != SubStatus::DISCONNECTED) {
weak_ptr_factory_.InvalidateWeakPtrs();
StopNotifySession();
characteristic_finder_.reset();
if (gatt_connection_) {
PA_LOG(INFO) << "Disconnect from device "
<< gatt_connection_->GetDeviceAddress();
// Destroying BluetoothGattConnection also disconnects it.
gatt_connection_.reset();
}
// Only transition to the DISCONNECTED state after perfoming all necessary
// operations. Otherwise, it'll trigger observers that can pontentially
// destroy the current instance (causing a crash).
SetSubStatus(SubStatus::DISCONNECTED);
}
}
void BluetoothLowEnergyConnection::SetSubStatus(SubStatus new_sub_status) {
sub_status_ = new_sub_status;
// Sets the status of parent class cryptauth::Connection accordingly.
if (new_sub_status == SubStatus::CONNECTED) {
SetStatus(CONNECTED);
} else if (new_sub_status == SubStatus::DISCONNECTED) {
SetStatus(DISCONNECTED);
} else {
SetStatus(IN_PROGRESS);
}
}
void BluetoothLowEnergyConnection::SetTaskRunnerForTesting(
scoped_refptr<base::TaskRunner> task_runner) {
task_runner_ = task_runner;
}
void BluetoothLowEnergyConnection::SendMessageImpl(
std::unique_ptr<cryptauth::WireMessage> message) {
PA_LOG(INFO) << "Sending message " << message->Serialize();
std::string serialized_msg = message->Serialize();
// [First write]: Build a header with the [send signal] + [size of the
// message].
WriteRequest write_request = BuildWriteRequest(
ToByteVector(static_cast<uint32_t>(ControlSignal::kSendSignal)),
ToByteVector(static_cast<uint32_t>(serialized_msg.size())), false);
// [First write]: Fill the it with a prefix of |serialized_msg| up to
// |max_chunk_size_|.
size_t first_chunk_size = std::min(
max_chunk_size_ - write_request.value.size(), serialized_msg.size());
std::vector<uint8_t> bytes(serialized_msg.begin(),
serialized_msg.begin() + first_chunk_size);
write_request.value.insert(write_request.value.end(), bytes.begin(),
bytes.end());
bool is_last_write_request = first_chunk_size == serialized_msg.size();
write_request.is_last_write_for_wire_message = is_last_write_request;
WriteRemoteCharacteristic(write_request);
if (is_last_write_request)
return;
// [Other write requests]: Each chunk has to include a deprecated signal:
// |kFirstByteZero| as the first byte.
int chunk_size = max_chunk_size_ - 1;
std::vector<uint8_t> kFirstByteZeroVector;
kFirstByteZeroVector.push_back(static_cast<uint8_t>(kFirstByteZero));
int message_size = static_cast<int>(serialized_msg.size());
int start_index = first_chunk_size;
while (start_index < message_size) {
int end_index = (start_index + chunk_size) <= message_size
? (start_index + chunk_size)
: message_size;
bool is_last_write_request = (end_index == message_size);
write_request = BuildWriteRequest(
kFirstByteZeroVector,
std::vector<uint8_t>(serialized_msg.begin() + start_index,
serialized_msg.begin() + end_index),
is_last_write_request);
WriteRemoteCharacteristic(write_request);
start_index = end_index;
}
}
// Changes in the GATT connection with the remote device should be observed
// here. If the GATT connection is dropped, we should call Disconnect() anyway,
// so the object can notify its observers.
void BluetoothLowEnergyConnection::DeviceChanged(BluetoothAdapter* adapter,
BluetoothDevice* device) {
DCHECK(device);
if (sub_status() == SubStatus::DISCONNECTED ||
device->GetAddress() != GetDeviceAddress())
return;
if (sub_status() != SubStatus::WAITING_GATT_CONNECTION &&
!device->IsConnected()) {
PA_LOG(INFO) << "GATT connection dropped " << GetDeviceAddress()
<< "\ndevice connected: " << device->IsConnected()
<< "\ngatt connection: "
<< (gatt_connection_ ? gatt_connection_->IsConnected()
: false);
Disconnect();
}
}
void BluetoothLowEnergyConnection::DeviceRemoved(BluetoothAdapter* adapter,
BluetoothDevice* device) {
DCHECK(device);
if (sub_status_ == SubStatus::DISCONNECTED ||
device->GetAddress() != GetDeviceAddress())
return;
PA_LOG(INFO) << "Device removed " << GetDeviceAddress();
Disconnect();
}
void BluetoothLowEnergyConnection::GattCharacteristicValueChanged(
BluetoothAdapter* adapter,
BluetoothRemoteGattCharacteristic* characteristic,
const std::vector<uint8_t>& value) {
DCHECK_EQ(adapter, adapter_.get());
if (sub_status() != SubStatus::WAITING_RESPONSE_SIGNAL &&
sub_status() != SubStatus::CONNECTED)
return;
PA_LOG(INFO) << "Characteristic value changed: "
<< characteristic->GetUUID().canonical_value();
if (characteristic->GetIdentifier() == from_peripheral_char_.id) {
if (receiving_bytes_) {
// Ignoring the first byte, as it contains a deprecated signal.
const std::string bytes(value.begin() + 1, value.end());
incoming_bytes_buffer_.append(bytes);
if (incoming_bytes_buffer_.size() >= expected_number_of_incoming_bytes_) {
OnBytesReceived(incoming_bytes_buffer_);
receiving_bytes_ = false;
}
return;
}
if (value.size() < 4) {
PA_LOG(WARNING) << "Incoming data corrupted, no signal found.";
return;
}
const ControlSignal signal = static_cast<ControlSignal>(ToUint32(value));
switch (signal) {
case ControlSignal::kInvitationResponseSignal:
if (sub_status() == SubStatus::WAITING_RESPONSE_SIGNAL)
CompleteConnection();
break;
case ControlSignal::kInviteToConnectSignal:
break;
case ControlSignal::kSendSignal: {
if (value.size() < 8) {
PA_LOG(WARNING)
<< "Incoming data corrupted, expected message size not found.";
return;
}
std::vector<uint8_t> size(value.begin() + 4, value.begin() + 8);
expected_number_of_incoming_bytes_ =
static_cast<size_t>(ToUint32(size));
receiving_bytes_ = true;
incoming_bytes_buffer_.clear();
const std::string bytes(value.begin() + 8, value.end());
incoming_bytes_buffer_.append(bytes);
if (incoming_bytes_buffer_.size() >=
expected_number_of_incoming_bytes_) {
OnBytesReceived(incoming_bytes_buffer_);
receiving_bytes_ = false;
}
break;
}
case ControlSignal::kDisconnectSignal:
PA_LOG(INFO) << "Disconnect signal received.";
Disconnect();
break;
}
}
}
BluetoothLowEnergyConnection::WriteRequest::WriteRequest(
const std::vector<uint8_t>& val,
bool flag)
: value(val),
is_last_write_for_wire_message(flag),
number_of_failed_attempts(0) {}
BluetoothLowEnergyConnection::WriteRequest::WriteRequest(
const WriteRequest& other) = default;
BluetoothLowEnergyConnection::WriteRequest::~WriteRequest() {}
void BluetoothLowEnergyConnection::CompleteConnection() {
PA_LOG(INFO) << "Connection completed. Time elapsed: "
<< base::TimeTicks::Now() - start_time_;
SetSubStatus(SubStatus::CONNECTED);
}
void BluetoothLowEnergyConnection::OnCreateGattConnectionError(
device::BluetoothDevice::ConnectErrorCode error_code) {
DCHECK(sub_status_ == SubStatus::WAITING_GATT_CONNECTION);
PA_LOG(WARNING) << "Error creating GATT connection to "
<< remote_device().bluetooth_address
<< "error code: " << error_code;
Disconnect();
}
void BluetoothLowEnergyConnection::OnGattConnectionCreated(
std::unique_ptr<device::BluetoothGattConnection> gatt_connection) {
DCHECK(sub_status() == SubStatus::WAITING_GATT_CONNECTION);
PA_LOG(INFO) << "GATT connection with " << gatt_connection->GetDeviceAddress()
<< " created.";
PrintTimeElapsed();
// Informing |bluetooth_trottler_| a new connection was established.
bluetooth_throttler_->OnConnection(this);
gatt_connection_ = std::move(gatt_connection);
SetSubStatus(SubStatus::WAITING_CHARACTERISTICS);
characteristic_finder_.reset(CreateCharacteristicsFinder(
base::Bind(&BluetoothLowEnergyConnection::OnCharacteristicsFound,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&BluetoothLowEnergyConnection::OnCharacteristicsFinderError,
weak_ptr_factory_.GetWeakPtr())));
}
cryptauth::BluetoothLowEnergyCharacteristicsFinder*
BluetoothLowEnergyConnection::CreateCharacteristicsFinder(
const cryptauth::BluetoothLowEnergyCharacteristicsFinder::SuccessCallback&
success_callback,
const cryptauth::BluetoothLowEnergyCharacteristicsFinder::ErrorCallback&
error_callback) {
return new cryptauth::BluetoothLowEnergyCharacteristicsFinder(
adapter_, GetRemoteDevice(), remote_service_, to_peripheral_char_,
from_peripheral_char_, success_callback, error_callback);
}
void BluetoothLowEnergyConnection::OnCharacteristicsFound(
const cryptauth::RemoteAttribute& service,
const cryptauth::RemoteAttribute& to_peripheral_char,
const cryptauth::RemoteAttribute& from_peripheral_char) {
PA_LOG(INFO) << "Remote chacteristics found.";
PrintTimeElapsed();
DCHECK(sub_status() == SubStatus::WAITING_CHARACTERISTICS);
remote_service_ = service;
to_peripheral_char_ = to_peripheral_char;
from_peripheral_char_ = from_peripheral_char;
SetSubStatus(SubStatus::CHARACTERISTICS_FOUND);
StartNotifySession();
}
void BluetoothLowEnergyConnection::OnCharacteristicsFinderError(
const cryptauth::RemoteAttribute& to_peripheral_char,
const cryptauth::RemoteAttribute& from_peripheral_char) {
DCHECK(sub_status() == SubStatus::WAITING_CHARACTERISTICS);
PA_LOG(WARNING) << "Connection error, missing characteristics for SmartLock "
"service.\n"
<< (to_peripheral_char.id.empty()
? to_peripheral_char.uuid.canonical_value()
: "")
<< (from_peripheral_char.id.empty()
? ", " + from_peripheral_char.uuid.canonical_value()
: "") << " not found.";
Disconnect();
}
void BluetoothLowEnergyConnection::StartNotifySession() {
if (sub_status() == SubStatus::CHARACTERISTICS_FOUND) {
BluetoothRemoteGattCharacteristic* characteristic =
GetGattCharacteristic(from_peripheral_char_.id);
DCHECK(characteristic);
// This is a workaround for crbug.com/507325. If |characteristic| is already
// notifying |characteristic->StartNotifySession()| will fail with
// GATT_ERROR_FAILED.
if (characteristic->IsNotifying()) {
PA_LOG(INFO) << characteristic->GetUUID().canonical_value()
<< " already notifying.";
SetSubStatus(SubStatus::NOTIFY_SESSION_READY);
SendInviteToConnectSignal();
return;
}
SetSubStatus(SubStatus::WAITING_NOTIFY_SESSION);
characteristic->StartNotifySession(
base::Bind(&BluetoothLowEnergyConnection::OnNotifySessionStarted,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&BluetoothLowEnergyConnection::OnNotifySessionError,
weak_ptr_factory_.GetWeakPtr()));
}
}
void BluetoothLowEnergyConnection::OnNotifySessionError(
BluetoothRemoteGattService::GattErrorCode error) {
DCHECK(sub_status() == SubStatus::WAITING_NOTIFY_SESSION);
PA_LOG(WARNING) << "Error starting notification session: " << error;
Disconnect();
}
void BluetoothLowEnergyConnection::OnNotifySessionStarted(
std::unique_ptr<BluetoothGattNotifySession> notify_session) {
DCHECK(sub_status() == SubStatus::WAITING_NOTIFY_SESSION);
PA_LOG(INFO) << "Notification session started "
<< notify_session->GetCharacteristicIdentifier();
PrintTimeElapsed();
SetSubStatus(SubStatus::NOTIFY_SESSION_READY);
notify_session_ = std::move(notify_session);
SendInviteToConnectSignal();
}
void BluetoothLowEnergyConnection::StopNotifySession() {
if (notify_session_) {
notify_session_->Stop(base::Bind(&base::DoNothing));
notify_session_.reset();
}
}
void BluetoothLowEnergyConnection::SendInviteToConnectSignal() {
if (sub_status() == SubStatus::NOTIFY_SESSION_READY) {
PA_LOG(INFO) << "Sending invite to connect signal";
SetSubStatus(SubStatus::WAITING_RESPONSE_SIGNAL);
WriteRequest write_request = BuildWriteRequest(
ToByteVector(
static_cast<uint32_t>(ControlSignal::kInviteToConnectSignal)),
std::vector<uint8_t>(), false);
WriteRemoteCharacteristic(write_request);
}
}
void BluetoothLowEnergyConnection::WriteRemoteCharacteristic(
WriteRequest request) {
write_requests_queue_.push(request);
ProcessNextWriteRequest();
}
void BluetoothLowEnergyConnection::ProcessNextWriteRequest() {
BluetoothRemoteGattCharacteristic* characteristic =
GetGattCharacteristic(to_peripheral_char_.id);
if (!write_requests_queue_.empty() && !write_remote_characteristic_pending_ &&
characteristic) {
write_remote_characteristic_pending_ = true;
WriteRequest next_request = write_requests_queue_.front();
PA_LOG(INFO) << "Writing characteristic...";
characteristic->WriteRemoteCharacteristic(
next_request.value,
base::Bind(&BluetoothLowEnergyConnection::OnRemoteCharacteristicWritten,
weak_ptr_factory_.GetWeakPtr(),
next_request.is_last_write_for_wire_message),
base::Bind(
&BluetoothLowEnergyConnection::OnWriteRemoteCharacteristicError,
weak_ptr_factory_.GetWeakPtr(),
next_request.is_last_write_for_wire_message));
}
}
void BluetoothLowEnergyConnection::OnRemoteCharacteristicWritten(
bool run_did_send_message_callback) {
PA_LOG(INFO) << "Characteristic written.";
write_remote_characteristic_pending_ = false;
// TODO(sacomoto): Actually pass the current message to the observer.
if (run_did_send_message_callback)
OnDidSendMessage(cryptauth::WireMessage(std::string(), std::string()),
true);
// Removes the top of queue (already processed) and process the next request.
DCHECK(!write_requests_queue_.empty());
write_requests_queue_.pop();
ProcessNextWriteRequest();
}
void BluetoothLowEnergyConnection::OnWriteRemoteCharacteristicError(
bool run_did_send_message_callback,
BluetoothRemoteGattService::GattErrorCode error) {
PA_LOG(WARNING) << "Error " << error << " writing characteristic: "
<< to_peripheral_char_.uuid.canonical_value();
write_remote_characteristic_pending_ = false;
// TODO(sacomoto): Actually pass the current message to the observer.
if (run_did_send_message_callback)
OnDidSendMessage(cryptauth::WireMessage(std::string(), std::string()),
false);
// Increases the number of failed attempts and retry.
DCHECK(!write_requests_queue_.empty());
if (++write_requests_queue_.front().number_of_failed_attempts >=
max_number_of_write_attempts_) {
Disconnect();
return;
}
ProcessNextWriteRequest();
}
BluetoothLowEnergyConnection::WriteRequest
BluetoothLowEnergyConnection::BuildWriteRequest(
const std::vector<uint8_t>& signal,
const std::vector<uint8_t>& bytes,
bool is_last_write_for_wire_message) {
std::vector<uint8_t> value(signal.begin(), signal.end());
value.insert(value.end(), bytes.begin(), bytes.end());
return WriteRequest(value, is_last_write_for_wire_message);
}
void BluetoothLowEnergyConnection::PrintTimeElapsed() {
PA_LOG(INFO) << "Time elapsed: " << base::TimeTicks::Now() - start_time_;
}
std::string BluetoothLowEnergyConnection::GetDeviceAddress() {
// When the remote device is connected we should rely on the address given by
// |gatt_connection_|. As the device address may change if the device is
// paired. The address in |gatt_connection_| is automatically updated in this
// case.
return gatt_connection_ ? gatt_connection_->GetDeviceAddress()
: remote_device().bluetooth_address;
}
BluetoothDevice* BluetoothLowEnergyConnection::GetRemoteDevice() {
// It's not possible to simply use
// |adapter_->GetDevice(GetDeviceAddress())| to find the device with MAC
// address |GetDeviceAddress()|. For paired devices,
// BluetoothAdapter::GetDevice(XXX) searches for the temporary MAC address
// XXX, whereas |GetDeviceAddress()| is the real MAC address. This is a
// bug in the way device::BluetoothAdapter is storing the devices (see
// crbug.com/497841).
std::vector<BluetoothDevice*> devices = adapter_->GetDevices();
for (auto* device : devices) {
if (device->GetAddress() == GetDeviceAddress())
return device;
}
return nullptr;
}
BluetoothRemoteGattService* BluetoothLowEnergyConnection::GetRemoteService() {
BluetoothDevice* remote_device = GetRemoteDevice();
if (!remote_device) {
PA_LOG(WARNING) << "Remote device not found.";
return NULL;
}
if (remote_service_.id.empty()) {
std::vector<BluetoothRemoteGattService*> services =
remote_device->GetGattServices();
for (const auto* service : services)
if (service->GetUUID() == remote_service_.uuid) {
remote_service_.id = service->GetIdentifier();
break;
}
}
return remote_device->GetGattService(remote_service_.id);
}
BluetoothRemoteGattCharacteristic*
BluetoothLowEnergyConnection::GetGattCharacteristic(
const std::string& gatt_characteristic) {
BluetoothRemoteGattService* remote_service = GetRemoteService();
if (!remote_service) {
PA_LOG(WARNING) << "Remote service not found.";
return NULL;
}
return remote_service->GetCharacteristic(gatt_characteristic);
}
// TODO(sacomoto): make this robust to byte ordering in both sides of the
// SmartLock BLE socket.
uint32_t BluetoothLowEnergyConnection::ToUint32(
const std::vector<uint8_t>& bytes) {
return bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24);
}
// TODO(sacomoto): make this robust to byte ordering in both sides of the
// SmartLock BLE socket.
const std::vector<uint8_t> BluetoothLowEnergyConnection::ToByteVector(
const uint32_t value) {
std::vector<uint8_t> bytes(4, 0);
bytes[0] = static_cast<uint8_t>(value);
bytes[1] = static_cast<uint8_t>(value >> 8);
bytes[2] = static_cast<uint8_t>(value >> 16);
bytes[3] = static_cast<uint8_t>(value >> 24);
return bytes;
}
} // namespace proximity_auth
// Copyright 2015 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.
#ifndef COMPONENTS_PROXIMITY_AUTH_BLE_BLUETOOTH_LOW_ENERGY_CONNECTION_H_
#define COMPONENTS_PROXIMITY_AUTH_BLE_BLUETOOTH_LOW_ENERGY_CONNECTION_H_
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <queue>
#include <string>
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "components/cryptauth/ble/bluetooth_low_energy_characteristics_finder.h"
#include "components/cryptauth/ble/fake_wire_message.h"
#include "components/cryptauth/ble/remote_attribute.h"
#include "components/cryptauth/bluetooth_throttler.h"
#include "components/cryptauth/connection.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/bluetooth_gatt_notify_session.h"
#include "device/bluetooth/bluetooth_remote_gatt_characteristic.h"
#include "device/bluetooth/bluetooth_uuid.h"
namespace base {
class TaskRunner;
}
namespace proximity_auth {
// Represents a connection with a remote device over Bluetooth low energy. The
// connection is a persistent bidirectional channel for sending and receiving
// wire messages. The remote device is the peripheral mode and the service
// contains two characteristics: one to send data and another to receive it.
//
// The connection flow is described below.
//
// Discover Reader and Writer Characteristics
// |
// |
// |
// Start Notify Session
// |
// |
// |
// Write kInviteToConnectSignal to Writer Characteristic
// |
// |
// |
// Read kInvitationResponseSignal from Reader Characteristic
// |
// |
// |
// Proximity Auth Connection Established
class BluetoothLowEnergyConnection : public cryptauth::Connection,
public device::BluetoothAdapter::Observer {
public:
// Signals sent to the remote device to indicate connection related events.
enum class ControlSignal : uint32_t {
kInviteToConnectSignal = 0,
kInvitationResponseSignal = 1,
kSendSignal = 2,
kDisconnectSignal = 3,
};
// The sub-state of a proximity_auth::BluetoothLowEnergyConnection class
// extends the IN_PROGRESS state of proximity_auth::Connection::Status.
enum class SubStatus {
DISCONNECTED,
WAITING_GATT_CONNECTION,
WAITING_CHARACTERISTICS,
CHARACTERISTICS_FOUND,
WAITING_NOTIFY_SESSION,
NOTIFY_SESSION_READY,
WAITING_RESPONSE_SIGNAL,
CONNECTED,
};
// Constructs a Bluetooth low energy connection to the service with
// |remote_service_| on the |remote_device|. The |adapter| must be already
// initaalized and ready. The GATT connection may alreaady be established and
// pass through |gatt_connection|. A subsequent call to Connect() must be
// made.
BluetoothLowEnergyConnection(
const cryptauth::RemoteDevice& remote_device,
scoped_refptr<device::BluetoothAdapter> adapter,
const device::BluetoothUUID remote_service_uuid,
cryptauth::BluetoothThrottler* bluetooth_throttler,
int max_number_of_write_attempts);
~BluetoothLowEnergyConnection() override;
// cryptauth::Connection:
void Connect() override;
void Disconnect() override;
std::string GetDeviceAddress() override;
protected:
// Exposed for testing.
void SetSubStatus(SubStatus status);
SubStatus sub_status() { return sub_status_; }
// Sets |task_runner_| for testing.
void SetTaskRunnerForTesting(scoped_refptr<base::TaskRunner> task_runner);
// Virtual for testing.
virtual cryptauth::BluetoothLowEnergyCharacteristicsFinder*
CreateCharacteristicsFinder(
const cryptauth::BluetoothLowEnergyCharacteristicsFinder::SuccessCallback&
success_callback,
const cryptauth::BluetoothLowEnergyCharacteristicsFinder::ErrorCallback&
error_callback);
// cryptauth::Connection:
void SendMessageImpl(
std::unique_ptr<cryptauth::WireMessage> message) override;
// device::BluetoothAdapter::Observer:
void DeviceChanged(device::BluetoothAdapter* adapter,
device::BluetoothDevice* device) override;
void DeviceRemoved(device::BluetoothAdapter* adapter,
device::BluetoothDevice* device) override;
void GattCharacteristicValueChanged(
device::BluetoothAdapter* adapter,
device::BluetoothRemoteGattCharacteristic* characteristic,
const std::vector<uint8_t>& value) override;
private:
// Represents a request to write |value| to a some characteristic.
// |is_last_write_for_wire_messsage| indicates whether this request
// corresponds to the last write request for some wire message.
// A cryptauth::WireMessage corresponds to exactly two WriteRequest: the first
// containing
// a kSendSignal + the size of the cryptauth::WireMessage, and the second
// containing a
// SendStatusSignal + the serialized cryptauth::WireMessage.
struct WriteRequest {
WriteRequest(const std::vector<uint8_t>& val, bool flag);
WriteRequest(const WriteRequest& other);
~WriteRequest();
std::vector<uint8_t> value;
bool is_last_write_for_wire_message;
int number_of_failed_attempts;
};
// Creates the GATT connection with |remote_device|.
void CreateGattConnection();
// Called when a GATT connection is created.
void OnGattConnectionCreated(
std::unique_ptr<device::BluetoothGattConnection> gatt_connection);
// Callback called when there is an error creating the GATT connection.
void OnCreateGattConnectionError(
device::BluetoothDevice::ConnectErrorCode error_code);
// Callback called when |to_peripheral_char_| and |from_peripheral_char_| were
// found.
void OnCharacteristicsFound(
const cryptauth::RemoteAttribute& service,
const cryptauth::RemoteAttribute& to_peripheral_char,
const cryptauth::RemoteAttribute& from_peripheral_char);
// Callback called there was an error finding the characteristics.
void OnCharacteristicsFinderError(
const cryptauth::RemoteAttribute& to_peripheral_char,
const cryptauth::RemoteAttribute& from_peripheral_char);
// Starts a notify session for |from_peripheral_char_| when ready
// (SubStatus::CHARACTERISTICS_FOUND).
void StartNotifySession();
// Called when a notification session is successfully started for
// |from_peripheral_char_| characteristic.
void OnNotifySessionStarted(
std::unique_ptr<device::BluetoothGattNotifySession> notify_session);
// Called when there is an error starting a notification session for
// |from_peripheral_char_| characteristic.
void OnNotifySessionError(device::BluetoothGattService::GattErrorCode);
// Stops |notify_session_|.
void StopNotifySession();
// Sends an invite to connect signal to the peripheral if when ready
// (SubStatus::NOTIFY_SESSION_READY).
void SendInviteToConnectSignal();
// Completes and updates the status accordingly.
void CompleteConnection();
// This is the only entry point for WriteRequests, which are processed
// accordingly the following flow:
// 1) |request| is enqueued;
// 2) |request| will be processed by ProcessNextWriteRequest() when there is
// no pending write request;
// 3) |request| will be dequeued when it's successfully processed
// (OnRemoteCharacteristicWritten());
// 4) |request| is not dequeued if it fails
// (OnWriteRemoteCharacteristicError()), it remains on the queue and will be
// retried. |request| will remain on the queue until it succeeds or it
// triggers a Disconnect() call (after |max_number_of_tries_|).
void WriteRemoteCharacteristic(WriteRequest request);
// Processes the next request in |write_requests_queue_|.
void ProcessNextWriteRequest();
// Called when the
// BluetoothRemoteGattCharacteristic::RemoteCharacteristicWrite() is
// successfully complete.
void OnRemoteCharacteristicWritten(bool run_did_send_message_callback);
// Called when there is an error writing to the remote characteristic
// |to_peripheral_char_|.
void OnWriteRemoteCharacteristicError(
bool run_did_send_message_callback,
device::BluetoothRemoteGattService::GattErrorCode error);
// Builds the value to be written on |to_peripheral_char_|. The value
// corresponds to |signal| concatenated with |payload|.
WriteRequest BuildWriteRequest(const std::vector<uint8_t>& signal,
const std::vector<uint8_t>& bytes,
bool is_last_message_for_wire_message);
// Prints the time elapsed since |Connect()| was called.
void PrintTimeElapsed();
// Returns the device corresponding to |remote_device_address_|.
device::BluetoothDevice* GetRemoteDevice();
// Returns the service corresponding to |remote_service_| in the current
// device.
device::BluetoothRemoteGattService* GetRemoteService();
// Returns the characteristic corresponding to |identifier| in the current
// service.
device::BluetoothRemoteGattCharacteristic* GetGattCharacteristic(
const std::string& identifier);
// Convert the first 4 bytes from a byte vector to a uint32_t.
uint32_t ToUint32(const std::vector<uint8_t>& bytes);
// Convert an uint32_t to a byte vector.
const std::vector<uint8_t> ToByteVector(uint32_t value);
// The Bluetooth adapter over which the Bluetooth connection will be made.
scoped_refptr<device::BluetoothAdapter> adapter_;
// Remote service the |gatt_connection_| was established with.
cryptauth::RemoteAttribute remote_service_;
// Characteristic used to send data to the remote device.
cryptauth::RemoteAttribute to_peripheral_char_;
// Characteristic used to receive data from the remote device.
cryptauth::RemoteAttribute from_peripheral_char_;
// Throttles repeated connection attempts to the same device. This is a
// workaround for crbug.com/508919. Not owned, must outlive this instance.
cryptauth::BluetoothThrottler* bluetooth_throttler_;
scoped_refptr<base::TaskRunner> task_runner_;
// The GATT connection with the remote device.
std::unique_ptr<device::BluetoothGattConnection> gatt_connection_;
// The characteristics finder for remote device.
std::unique_ptr<cryptauth::BluetoothLowEnergyCharacteristicsFinder>
characteristic_finder_;
// The notify session for |from_peripheral_char|.
std::unique_ptr<device::BluetoothGattNotifySession> notify_session_;
// Internal connection status
SubStatus sub_status_;
// Indicates a receiving operation is in progress. This is set after a
// ControlSignal::kSendSignal was received from the remote device.
bool receiving_bytes_;
// Total number of bytes expected for the current receive operation.
std::size_t expected_number_of_incoming_bytes_;
// Bytes already received for the current receive operation.
std::string incoming_bytes_buffer_;
// Indicates there is a
// BluetoothRemoteGattCharacteristic::WriteRemoteCharacteristic
// operation pending.
bool write_remote_characteristic_pending_;
std::queue<WriteRequest> write_requests_queue_;
// Maximum number of tries to send any write request.
int max_number_of_write_attempts_;
// Maximum number of bytes that fit in a single chunk to be written in
// |to_peripheral_char_|. Ideally, this should be the maximum value the
// peripheral supports and it should be agreed when the GATT connection is
// created. Currently, there is no API to find this value. The implementation
// uses a hard-coded constant.
int max_chunk_size_;
// Stores when the instace was created.
base::TimeTicks start_time_;
base::WeakPtrFactory<BluetoothLowEnergyConnection> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(BluetoothLowEnergyConnection);
};
} // namespace proximity_auth
#endif // COMPONENTS_PROXIMITY_AUTH_BLE_BLUETOOTH_LOW_ENERGY_CONNECTION_H_
// Copyright 2015 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 "components/proximity_auth/ble/bluetooth_low_energy_connection.h"
#include <stdint.h>
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/test/test_simple_task_runner.h"
#include "components/cryptauth/ble/bluetooth_low_energy_characteristics_finder.h"
#include "components/cryptauth/bluetooth_throttler.h"
#include "components/cryptauth/connection_finder.h"
#include "components/cryptauth/cryptauth_test_util.h"
#include "components/cryptauth/remote_device.h"
#include "components/cryptauth/wire_message.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/bluetooth_remote_gatt_characteristic.h"
#include "device/bluetooth/bluetooth_uuid.h"
#include "device/bluetooth/test/mock_bluetooth_adapter.h"
#include "device/bluetooth/test/mock_bluetooth_device.h"
#include "device/bluetooth/test/mock_bluetooth_discovery_session.h"
#include "device/bluetooth/test/mock_bluetooth_gatt_characteristic.h"
#include "device/bluetooth/test/mock_bluetooth_gatt_connection.h"
#include "device/bluetooth/test/mock_bluetooth_gatt_notify_session.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
using testing::AtLeast;
using testing::NiceMock;
using testing::Return;
using testing::StrictMock;
using testing::SaveArg;
namespace proximity_auth {
namespace {
const char kServiceUUID[] = "DEADBEEF-CAFE-FEED-FOOD-D15EA5EBEEEF";
const char kToPeripheralCharUUID[] = "977c6674-1239-4e72-993b-502369b8bb5a";
const char kFromPeripheralCharUUID[] = "f4b904a2-a030-43b3-98a8-221c536c03cb";
const char kServiceID[] = "service id";
const char kToPeripheralCharID[] = "to peripheral char id";
const char kFromPeripheralCharID[] = "from peripheral char id";
const char kTestFeature[] = "testFeature";
const device::BluetoothRemoteGattCharacteristic::Properties
kCharacteristicProperties =
device::BluetoothRemoteGattCharacteristic::PROPERTY_BROADCAST |
device::BluetoothRemoteGattCharacteristic::PROPERTY_READ |
device::BluetoothRemoteGattCharacteristic::
PROPERTY_WRITE_WITHOUT_RESPONSE |
device::BluetoothRemoteGattCharacteristic::PROPERTY_INDICATE;
const int kMaxNumberOfTries = 3;
class MockBluetoothThrottler : public cryptauth::BluetoothThrottler {
public:
MockBluetoothThrottler() {}
~MockBluetoothThrottler() override {}
MOCK_CONST_METHOD0(GetDelay, base::TimeDelta());
MOCK_METHOD1(OnConnection, void(cryptauth::Connection* connection));
private:
DISALLOW_COPY_AND_ASSIGN(MockBluetoothThrottler);
};
class MockBluetoothLowEnergyCharacteristicsFinder
: public cryptauth::BluetoothLowEnergyCharacteristicsFinder {
public:
MockBluetoothLowEnergyCharacteristicsFinder() {}
~MockBluetoothLowEnergyCharacteristicsFinder() override {}
private:
DISALLOW_COPY_AND_ASSIGN(MockBluetoothLowEnergyCharacteristicsFinder);
};
class MockBluetoothLowEnergyConnection : public BluetoothLowEnergyConnection {
public:
MockBluetoothLowEnergyConnection(
const cryptauth::RemoteDevice& remote_device,
scoped_refptr<device::BluetoothAdapter> adapter,
const device::BluetoothUUID remote_service_uuid,
cryptauth::BluetoothThrottler* bluetooth_throttler,
int max_number_of_write_attempts)
: BluetoothLowEnergyConnection(remote_device,
adapter,
remote_service_uuid,
bluetooth_throttler,
max_number_of_write_attempts) {}
~MockBluetoothLowEnergyConnection() override {}
MOCK_METHOD2(CreateCharacteristicsFinder,
cryptauth::BluetoothLowEnergyCharacteristicsFinder*(
const cryptauth::BluetoothLowEnergyCharacteristicsFinder::
SuccessCallback& success,
const cryptauth::BluetoothLowEnergyCharacteristicsFinder::
ErrorCallback& error));
MOCK_METHOD2(OnDidSendMessage,
void(const cryptauth::WireMessage& message, bool success));
MOCK_METHOD1(OnBytesReceived, void(const std::string& bytes));
// Exposing inherited protected methods for testing.
using BluetoothLowEnergyConnection::GattCharacteristicValueChanged;
using BluetoothLowEnergyConnection::SetTaskRunnerForTesting;
// Exposing inherited protected fields for testing.
using BluetoothLowEnergyConnection::status;
using BluetoothLowEnergyConnection::sub_status;
private:
DISALLOW_COPY_AND_ASSIGN(MockBluetoothLowEnergyConnection);
};
} // namespace
class ProximityAuthBluetoothLowEnergyConnectionTest : public testing::Test {
public:
ProximityAuthBluetoothLowEnergyConnectionTest()
: adapter_(new NiceMock<device::MockBluetoothAdapter>),
remote_device_(cryptauth::CreateLERemoteDeviceForTest()),
service_uuid_(device::BluetoothUUID(kServiceUUID)),
to_peripheral_char_uuid_(device::BluetoothUUID(kToPeripheralCharUUID)),
from_peripheral_char_uuid_(
device::BluetoothUUID(kFromPeripheralCharUUID)),
notify_session_alias_(NULL),
bluetooth_throttler_(new NiceMock<MockBluetoothThrottler>),
task_runner_(new base::TestSimpleTaskRunner) {}
void SetUp() override {
device_ = base::MakeUnique<NiceMock<device::MockBluetoothDevice>>(
adapter_.get(), 0, cryptauth::kTestRemoteDeviceName,
cryptauth::kTestRemoteDeviceBluetoothAddress, false, false);
service_ = base::MakeUnique<NiceMock<device::MockBluetoothGattService>>(
device_.get(), kServiceID, service_uuid_, true, false);
to_peripheral_char_ =
base::MakeUnique<NiceMock<device::MockBluetoothGattCharacteristic>>(
service_.get(), kToPeripheralCharID, to_peripheral_char_uuid_,
false, kCharacteristicProperties,
device::BluetoothRemoteGattCharacteristic::PERMISSION_NONE);
from_peripheral_char_ =
base::MakeUnique<NiceMock<device::MockBluetoothGattCharacteristic>>(
service_.get(), kFromPeripheralCharID, from_peripheral_char_uuid_,
false, kCharacteristicProperties,
device::BluetoothRemoteGattCharacteristic::PERMISSION_NONE);
device::BluetoothAdapterFactory::SetAdapterForTesting(adapter_);
std::vector<const device::BluetoothDevice*> devices;
devices.push_back(device_.get());
ON_CALL(*adapter_, GetDevices()).WillByDefault(Return(devices));
ON_CALL(*adapter_, GetDevice(cryptauth::kTestRemoteDeviceBluetoothAddress))
.WillByDefault(Return(device_.get()));
ON_CALL(*device_, GetGattService(kServiceID))
.WillByDefault(Return(service_.get()));
ON_CALL(*service_, GetCharacteristic(kFromPeripheralCharID))
.WillByDefault(Return(from_peripheral_char_.get()));
ON_CALL(*service_, GetCharacteristic(kToPeripheralCharID))
.WillByDefault(Return(to_peripheral_char_.get()));
}
// Creates a BluetoothLowEnergyConnection and verifies it's in DISCONNECTED
// state.
std::unique_ptr<MockBluetoothLowEnergyConnection> CreateConnection() {
EXPECT_CALL(*adapter_, AddObserver(_));
EXPECT_CALL(*adapter_, RemoveObserver(_));
std::unique_ptr<MockBluetoothLowEnergyConnection> connection(
new MockBluetoothLowEnergyConnection(
remote_device_, adapter_, service_uuid_, bluetooth_throttler_.get(),
kMaxNumberOfTries));
EXPECT_EQ(connection->sub_status(),
BluetoothLowEnergyConnection::SubStatus::DISCONNECTED);
EXPECT_EQ(connection->status(), cryptauth::Connection::DISCONNECTED);
connection->SetTaskRunnerForTesting(task_runner_);
return connection;
}
// Transitions |connection| from DISCONNECTED to WAITING_CHARACTERISTICS
// state, without an existing GATT connection.
void ConnectGatt(MockBluetoothLowEnergyConnection* connection) {
// Preparing |connection| for a CreateGattConnection call.
EXPECT_CALL(*device_, CreateGattConnection(_, _))
.WillOnce(DoAll(SaveArg<0>(&create_gatt_connection_success_callback_),
SaveArg<1>(&create_gatt_connection_error_callback_)));
// No throttling by default
EXPECT_CALL(*bluetooth_throttler_, GetDelay())
.WillOnce(Return(base::TimeDelta()));
connection->Connect();
EXPECT_EQ(connection->sub_status(),
BluetoothLowEnergyConnection::SubStatus::WAITING_GATT_CONNECTION);
EXPECT_EQ(connection->status(), cryptauth::Connection::IN_PROGRESS);
// Preparing |connection| to run |create_gatt_connection_success_callback_|.
EXPECT_FALSE(create_gatt_connection_error_callback_.is_null());
ASSERT_FALSE(create_gatt_connection_success_callback_.is_null());
EXPECT_CALL(*connection, CreateCharacteristicsFinder(_, _))
.WillOnce(DoAll(
SaveArg<0>(&characteristics_finder_success_callback_),
SaveArg<1>(&characteristics_finder_error_callback_),
Return(new NiceMock<MockBluetoothLowEnergyCharacteristicsFinder>)));
create_gatt_connection_success_callback_.Run(
base::MakeUnique<NiceMock<device::MockBluetoothGattConnection>>(
adapter_, cryptauth::kTestRemoteDeviceBluetoothAddress));
EXPECT_EQ(connection->sub_status(),
BluetoothLowEnergyConnection::SubStatus::WAITING_CHARACTERISTICS);
EXPECT_EQ(connection->status(), cryptauth::Connection::IN_PROGRESS);
}
// Transitions |connection| from WAITING_CHARACTERISTICS to
// WAITING_NOTIFY_SESSION state.
void CharacteristicsFound(MockBluetoothLowEnergyConnection* connection) {
EXPECT_CALL(*from_peripheral_char_, StartNotifySession(_, _))
.WillOnce(DoAll(SaveArg<0>(&notify_session_success_callback_),
SaveArg<1>(&notify_session_error_callback_)));
EXPECT_FALSE(characteristics_finder_error_callback_.is_null());
ASSERT_FALSE(characteristics_finder_success_callback_.is_null());
characteristics_finder_success_callback_.Run(
{service_uuid_, kServiceID},
{to_peripheral_char_uuid_, kToPeripheralCharID},
{from_peripheral_char_uuid_, kFromPeripheralCharID});
EXPECT_EQ(connection->sub_status(),
BluetoothLowEnergyConnection::SubStatus::WAITING_NOTIFY_SESSION);
EXPECT_EQ(connection->status(), cryptauth::Connection::IN_PROGRESS);
}
// Transitions |connection| from WAITING_NOTIFY_SESSION to
// WAITING_RESPONSE_SIGNAL state.
void NotifySessionStarted(MockBluetoothLowEnergyConnection* connection) {
EXPECT_CALL(*to_peripheral_char_, WriteRemoteCharacteristic(_, _, _))
.WillOnce(
DoAll(SaveArg<0>(&last_value_written_on_to_peripheral_char_),
SaveArg<1>(&write_remote_characteristic_success_callback_),
SaveArg<2>(&write_remote_characteristic_error_callback_)));
EXPECT_FALSE(notify_session_error_callback_.is_null());
ASSERT_FALSE(notify_session_success_callback_.is_null());
// Store an alias for the notify session passed |connection|.
std::unique_ptr<device::MockBluetoothGattNotifySession> notify_session(
new NiceMock<device::MockBluetoothGattNotifySession>(
to_peripheral_char_->GetWeakPtr()));
notify_session_alias_ = notify_session.get();
notify_session_success_callback_.Run(std::move(notify_session));
task_runner_->RunUntilIdle();
EXPECT_EQ(connection->sub_status(),
BluetoothLowEnergyConnection::SubStatus::WAITING_RESPONSE_SIGNAL);
EXPECT_EQ(connection->status(), cryptauth::Connection::IN_PROGRESS);
}
// Transitions |connection| from WAITING_RESPONSE_SIGNAL to CONNECTED state.
void ResponseSignalReceived(MockBluetoothLowEnergyConnection* connection) {
// Written value contains only the
// BluetoothLowEneryConnection::ControlSignal::kInviteToConnectSignal.
const std::vector<uint8_t> kInviteToConnectSignal = ToByteVector(
static_cast<uint32_t>(BluetoothLowEnergyConnection::ControlSignal::
kInviteToConnectSignal));
EXPECT_EQ(last_value_written_on_to_peripheral_char_,
kInviteToConnectSignal);
EXPECT_CALL(*connection, OnDidSendMessage(_, _)).Times(0);
RunWriteCharacteristicSuccessCallback();
// Received the
// BluetoothLowEneryConnection::ControlSignal::kInvitationResponseSignal.
const std::vector<uint8_t> kInvitationResponseSignal = ToByteVector(
static_cast<uint32_t>(BluetoothLowEnergyConnection::ControlSignal::
kInvitationResponseSignal));
connection->GattCharacteristicValueChanged(
adapter_.get(), from_peripheral_char_.get(), kInvitationResponseSignal);
EXPECT_EQ(connection->sub_status(),
BluetoothLowEnergyConnection::SubStatus::CONNECTED);
EXPECT_EQ(connection->status(), cryptauth::Connection::CONNECTED);
}
// Transitions |connection| to a DISCONNECTED state regardless of its initial
// state.
void Disconnect(MockBluetoothLowEnergyConnection* connection) {
// A notify session was previously set.
if (notify_session_alias_)
EXPECT_CALL(*notify_session_alias_, Stop(_));
connection->Disconnect();
EXPECT_EQ(connection->sub_status(),
BluetoothLowEnergyConnection::SubStatus::DISCONNECTED);
EXPECT_EQ(connection->status(), cryptauth::Connection::DISCONNECTED);
}
void InitializeConnection(MockBluetoothLowEnergyConnection* connection) {
ConnectGatt(connection);
CharacteristicsFound(connection);
NotifySessionStarted(connection);
ResponseSignalReceived(connection);
}
void RunWriteCharacteristicSuccessCallback() {
EXPECT_FALSE(write_remote_characteristic_error_callback_.is_null());
ASSERT_FALSE(write_remote_characteristic_success_callback_.is_null());
write_remote_characteristic_success_callback_.Run();
}
std::vector<uint8_t> CreateSendSignalWithSize(int message_size) {
std::vector<uint8_t> value = ToByteVector(static_cast<uint32_t>(
BluetoothLowEnergyConnection::ControlSignal::kSendSignal));
std::vector<uint8_t> size =
ToByteVector(static_cast<uint32_t>(message_size));
value.insert(value.end(), size.begin(), size.end());
return value;
}
std::vector<uint8_t> CreateFirstCharacteristicValue(
const std::string& message,
int size) {
std::vector<uint8_t> value(CreateSendSignalWithSize(size));
std::vector<uint8_t> bytes(message.begin(), message.end());
value.insert(value.end(), bytes.begin(), bytes.end());
return value;
}
std::vector<uint8_t> ToByteVector(uint32_t value) {
std::vector<uint8_t> bytes(4, 0);
bytes[0] = static_cast<uint8_t>(value);
bytes[1] = static_cast<uint8_t>(value >> 8);
bytes[2] = static_cast<uint8_t>(value >> 16);
bytes[3] = static_cast<uint8_t>(value >> 24);
return bytes;
}
protected:
scoped_refptr<device::MockBluetoothAdapter> adapter_;
cryptauth::RemoteDevice remote_device_;
device::BluetoothUUID service_uuid_;
device::BluetoothUUID to_peripheral_char_uuid_;
device::BluetoothUUID from_peripheral_char_uuid_;
std::unique_ptr<device::MockBluetoothDevice> device_;
std::unique_ptr<device::MockBluetoothGattService> service_;
std::unique_ptr<device::MockBluetoothGattCharacteristic> to_peripheral_char_;
std::unique_ptr<device::MockBluetoothGattCharacteristic>
from_peripheral_char_;
std::vector<uint8_t> last_value_written_on_to_peripheral_char_;
device::MockBluetoothGattNotifySession* notify_session_alias_;
std::unique_ptr<MockBluetoothThrottler> bluetooth_throttler_;
scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
base::MessageLoop message_loop_;
// Callbacks
device::BluetoothDevice::GattConnectionCallback
create_gatt_connection_success_callback_;
device::BluetoothDevice::ConnectErrorCallback
create_gatt_connection_error_callback_;
cryptauth::BluetoothLowEnergyCharacteristicsFinder::SuccessCallback
characteristics_finder_success_callback_;
cryptauth::BluetoothLowEnergyCharacteristicsFinder::ErrorCallback
characteristics_finder_error_callback_;
device::BluetoothRemoteGattCharacteristic::NotifySessionCallback
notify_session_success_callback_;
device::BluetoothRemoteGattCharacteristic::ErrorCallback
notify_session_error_callback_;
base::Closure write_remote_characteristic_success_callback_;
device::BluetoothRemoteGattCharacteristic::ErrorCallback
write_remote_characteristic_error_callback_;
};
TEST_F(ProximityAuthBluetoothLowEnergyConnectionTest,
CreateAndDestroyWithouthConnectCallDoesntCrash) {
BluetoothLowEnergyConnection connection(
remote_device_, adapter_, service_uuid_, bluetooth_throttler_.get(),
kMaxNumberOfTries);
}
TEST_F(ProximityAuthBluetoothLowEnergyConnectionTest,
Disconect_WithoutConnectDoesntCrash) {
std::unique_ptr<MockBluetoothLowEnergyConnection> connection(
CreateConnection());
Disconnect(connection.get());
}
TEST_F(ProximityAuthBluetoothLowEnergyConnectionTest, Connect_Success) {
std::unique_ptr<MockBluetoothLowEnergyConnection> connection(
CreateConnection());
ConnectGatt(connection.get());
CharacteristicsFound(connection.get());
NotifySessionStarted(connection.get());
ResponseSignalReceived(connection.get());
}
TEST_F(ProximityAuthBluetoothLowEnergyConnectionTest,
Connect_Success_Disconnect) {
std::unique_ptr<MockBluetoothLowEnergyConnection> connection(
CreateConnection());
InitializeConnection(connection.get());
Disconnect(connection.get());
}
TEST_F(ProximityAuthBluetoothLowEnergyConnectionTest,
Connect_Incomplete_Disconnect_FromWaitingCharacteristicsState) {
std::unique_ptr<MockBluetoothLowEnergyConnection> connection(
CreateConnection());
ConnectGatt(connection.get());
Disconnect(connection.get());
}
TEST_F(ProximityAuthBluetoothLowEnergyConnectionTest,
Connect_Incomplete_Disconnect_FromWaitingNotifySessionState) {
std::unique_ptr<MockBluetoothLowEnergyConnection> connection(
CreateConnection());
ConnectGatt(connection.get());
CharacteristicsFound(connection.get());
Disconnect(connection.get());
}
TEST_F(ProximityAuthBluetoothLowEnergyConnectionTest,
Connect_Incomplete_Disconnect_FromWaitingResponseSignalState) {
std::unique_ptr<MockBluetoothLowEnergyConnection> connection(
CreateConnection());
ConnectGatt(connection.get());
CharacteristicsFound(connection.get());
NotifySessionStarted(connection.get());
Disconnect(connection.get());
}
TEST_F(ProximityAuthBluetoothLowEnergyConnectionTest,
Connect_Fails_CharacteristicsNotFound) {
std::unique_ptr<MockBluetoothLowEnergyConnection> connection(
CreateConnection());
ConnectGatt(connection.get());
EXPECT_CALL(*from_peripheral_char_, StartNotifySession(_, _)).Times(0);
EXPECT_FALSE(characteristics_finder_success_callback_.is_null());
ASSERT_FALSE(characteristics_finder_error_callback_.is_null());
characteristics_finder_error_callback_.Run(
{to_peripheral_char_uuid_, kToPeripheralCharID},
{from_peripheral_char_uuid_, kFromPeripheralCharID});
EXPECT_EQ(connection->sub_status(),
BluetoothLowEnergyConnection::SubStatus::DISCONNECTED);
EXPECT_EQ(connection->status(), cryptauth::Connection::DISCONNECTED);
}
TEST_F(ProximityAuthBluetoothLowEnergyConnectionTest,
Connect_Fails_NotifySessionError) {
std::unique_ptr<MockBluetoothLowEnergyConnection> connection(
CreateConnection());
ConnectGatt(connection.get());
CharacteristicsFound(connection.get());
EXPECT_CALL(*to_peripheral_char_, WriteRemoteCharacteristic(_, _, _))
.Times(0);
EXPECT_FALSE(notify_session_success_callback_.is_null());
ASSERT_FALSE(notify_session_error_callback_.is_null());
notify_session_error_callback_.Run(
device::BluetoothRemoteGattService::GATT_ERROR_UNKNOWN);
EXPECT_EQ(connection->sub_status(),
BluetoothLowEnergyConnection::SubStatus::DISCONNECTED);
EXPECT_EQ(connection->status(), cryptauth::Connection::DISCONNECTED);
}
TEST_F(ProximityAuthBluetoothLowEnergyConnectionTest,
Connect_Fails_ErrorSendingInviteToConnectSignal) {
std::unique_ptr<MockBluetoothLowEnergyConnection> connection(
CreateConnection());
ConnectGatt(connection.get());
CharacteristicsFound(connection.get());
NotifySessionStarted(connection.get());
// |connection| will call WriteRemoteCharacteristics(_,_) to try to send the
// message |kMaxNumberOfTries| times. There is alredy one EXPECTA_CALL for
// WriteRemoteCharacteristic(_,_,_) in NotifySessionStated, that's why we use
// |kMaxNumberOfTries-1| in the EXPECT_CALL statement.
EXPECT_CALL(*connection, OnDidSendMessage(_, _)).Times(0);
EXPECT_CALL(*to_peripheral_char_, WriteRemoteCharacteristic(_, _, _))
.Times(kMaxNumberOfTries - 1)
.WillRepeatedly(
DoAll(SaveArg<0>(&last_value_written_on_to_peripheral_char_),
SaveArg<1>(&write_remote_characteristic_success_callback_),
SaveArg<2>(&write_remote_characteristic_error_callback_)));
for (int i = 0; i < kMaxNumberOfTries; i++) {
const std::vector<uint8_t> kInviteToConnectSignal = ToByteVector(
static_cast<uint32_t>(BluetoothLowEnergyConnection::ControlSignal::
kInviteToConnectSignal));
EXPECT_EQ(last_value_written_on_to_peripheral_char_,
kInviteToConnectSignal);
ASSERT_FALSE(write_remote_characteristic_error_callback_.is_null());
EXPECT_FALSE(write_remote_characteristic_success_callback_.is_null());
write_remote_characteristic_error_callback_.Run(
device::BluetoothRemoteGattService::GATT_ERROR_UNKNOWN);
}
EXPECT_EQ(connection->sub_status(),
BluetoothLowEnergyConnection::SubStatus::DISCONNECTED);
EXPECT_EQ(connection->status(), cryptauth::Connection::DISCONNECTED);
}
TEST_F(ProximityAuthBluetoothLowEnergyConnectionTest,
Receive_MessageSmallerThanCharacteristicSize) {
std::unique_ptr<MockBluetoothLowEnergyConnection> connection(
CreateConnection());
InitializeConnection(connection.get());
std::string received_bytes;
EXPECT_CALL(*connection, OnBytesReceived(_))
.WillOnce(SaveArg<0>(&received_bytes));
// Message (bytes) that is going to be received.
std::string message(100, 'A');
// Sending the |kSendSignal| + |message_size| + |message|.
connection->GattCharacteristicValueChanged(
adapter_.get(), from_peripheral_char_.get(),
CreateFirstCharacteristicValue(message, message.size()));
EXPECT_EQ(received_bytes, message);
}
TEST_F(ProximityAuthBluetoothLowEnergyConnectionTest,
Receive_MessageLargerThanCharacteristicSize) {
std::unique_ptr<MockBluetoothLowEnergyConnection> connection(
CreateConnection());
InitializeConnection(connection.get());
std::string received_bytes;
int chunk_size = 500;
EXPECT_CALL(*connection, OnBytesReceived(_))
.WillOnce(SaveArg<0>(&received_bytes));
// Message (bytes) that is going to be received.
int message_size = 600;
std::string message(message_size, 'A');
// Sending the |kSendSignal| + |message_size| + |message| (truncated at
// |chunk_size|).
int first_write_payload_size =
chunk_size - CreateSendSignalWithSize(message_size).size();
connection->GattCharacteristicValueChanged(
adapter_.get(), from_peripheral_char_.get(),
CreateFirstCharacteristicValue(
message.substr(0, first_write_payload_size), message.size()));
// Sending the remaining bytes.
std::vector<uint8_t> value;
value.push_back(0);
value.insert(value.end(), message.begin() + first_write_payload_size,
message.end());
connection->GattCharacteristicValueChanged(
adapter_.get(), from_peripheral_char_.get(), value);
EXPECT_EQ(received_bytes, message);
}
TEST_F(ProximityAuthBluetoothLowEnergyConnectionTest,
SendMessage_SmallerThanCharacteristicSize) {
std::unique_ptr<MockBluetoothLowEnergyConnection> connection(
CreateConnection());
InitializeConnection(connection.get());
// Expecting a first call of WriteRemoteCharacteristic, after SendMessage is
// called.
EXPECT_CALL(*to_peripheral_char_, WriteRemoteCharacteristic(_, _, _))
.WillOnce(
DoAll(SaveArg<0>(&last_value_written_on_to_peripheral_char_),
SaveArg<1>(&write_remote_characteristic_success_callback_),
SaveArg<2>(&write_remote_characteristic_error_callback_)));
// Message (bytes) that is going to be sent.
int message_size = 100;
std::string message(message_size, 'A');
message[0] = 'B';
std::unique_ptr<cryptauth::FakeWireMessage> wire_message =
base::MakeUnique<cryptauth::FakeWireMessage>(message,
std::string(kTestFeature));
std::string serialized_message = wire_message->Serialize();
connection->SendMessage(std::move(wire_message));
// Expecting that |kSendSignal| + |message_size| + |message| was written.
EXPECT_EQ(last_value_written_on_to_peripheral_char_,
CreateFirstCharacteristicValue(serialized_message,
serialized_message.size()));
EXPECT_CALL(*connection, OnDidSendMessage(_, _));
RunWriteCharacteristicSuccessCallback();
}
TEST_F(ProximityAuthBluetoothLowEnergyConnectionTest,
SendMessage_LargerThanCharacteristicSize) {
std::unique_ptr<MockBluetoothLowEnergyConnection> connection(
CreateConnection());
InitializeConnection(connection.get());
// Expecting a first call of WriteRemoteCharacteristic, after SendMessage is
// called.
EXPECT_CALL(*to_peripheral_char_, WriteRemoteCharacteristic(_, _, _))
.WillOnce(
DoAll(SaveArg<0>(&last_value_written_on_to_peripheral_char_),
SaveArg<1>(&write_remote_characteristic_success_callback_),
SaveArg<2>(&write_remote_characteristic_error_callback_)));
// Message (bytes) that is going to be sent.
int message_size = 600;
std::string message(message_size, 'A');
message[0] = 'B';
std::unique_ptr<cryptauth::FakeWireMessage> wire_message =
base::MakeUnique<cryptauth::FakeWireMessage>(message,
std::string(kTestFeature));
std::string serialized_message = wire_message->Serialize();
// Send the message.
connection->SendMessage(std::move(wire_message));
// Expecting that |kSendSignal| + |serialized_message| was written in the
// first 8 bytes.
std::vector<uint8_t> prefix(
last_value_written_on_to_peripheral_char_.begin(),
last_value_written_on_to_peripheral_char_.begin() + 8);
EXPECT_EQ(prefix, CreateSendSignalWithSize(serialized_message.size()));
std::vector<uint8_t> bytes_received(
last_value_written_on_to_peripheral_char_.begin() + 8,
last_value_written_on_to_peripheral_char_.end());
// Expecting a second call of WriteRemoteCharacteristic, after success
// callback is called.
EXPECT_CALL(*to_peripheral_char_, WriteRemoteCharacteristic(_, _, _))
.WillOnce(
DoAll(SaveArg<0>(&last_value_written_on_to_peripheral_char_),
SaveArg<1>(&write_remote_characteristic_success_callback_),
SaveArg<2>(&write_remote_characteristic_error_callback_)));
RunWriteCharacteristicSuccessCallback();
bytes_received.insert(bytes_received.end(),
last_value_written_on_to_peripheral_char_.begin() + 1,
last_value_written_on_to_peripheral_char_.end());
// Expecting that the message was written.
std::vector<uint8_t> expected_value(serialized_message.begin(),
serialized_message.end());
EXPECT_EQ(expected_value.size(), bytes_received.size());
EXPECT_EQ(expected_value, bytes_received);
EXPECT_CALL(*connection, OnDidSendMessage(_, _));
RunWriteCharacteristicSuccessCallback();
}
TEST_F(ProximityAuthBluetoothLowEnergyConnectionTest,
Connect_AfterADelayWhenThrottled) {
std::unique_ptr<MockBluetoothLowEnergyConnection> connection(
CreateConnection());
EXPECT_CALL(*bluetooth_throttler_, GetDelay())
.WillOnce(Return(base::TimeDelta(base::TimeDelta::FromSeconds(1))));
EXPECT_CALL(*device_, CreateGattConnection(_, _))
.WillOnce(DoAll(SaveArg<0>(&create_gatt_connection_success_callback_),
SaveArg<1>(&create_gatt_connection_error_callback_)));
// No GATT connection should be created before the delay.
connection->Connect();
EXPECT_EQ(connection->sub_status(),
BluetoothLowEnergyConnection::SubStatus::WAITING_GATT_CONNECTION);
EXPECT_EQ(connection->status(), cryptauth::Connection::IN_PROGRESS);
EXPECT_TRUE(create_gatt_connection_error_callback_.is_null());
EXPECT_TRUE(create_gatt_connection_success_callback_.is_null());
// A GATT connection should be created after the delay.
task_runner_->RunUntilIdle();
EXPECT_FALSE(create_gatt_connection_error_callback_.is_null());
ASSERT_FALSE(create_gatt_connection_success_callback_.is_null());
// Preparing |connection| to run |create_gatt_connection_success_callback_|.
EXPECT_CALL(*connection, CreateCharacteristicsFinder(_, _))
.WillOnce(DoAll(
SaveArg<0>(&characteristics_finder_success_callback_),
SaveArg<1>(&characteristics_finder_error_callback_),
Return(new NiceMock<MockBluetoothLowEnergyCharacteristicsFinder>)));
create_gatt_connection_success_callback_.Run(
base::MakeUnique<NiceMock<device::MockBluetoothGattConnection>>(
adapter_, cryptauth::kTestRemoteDeviceBluetoothAddress));
CharacteristicsFound(connection.get());
NotifySessionStarted(connection.get());
ResponseSignalReceived(connection.get());
}
} // namespace proximity_auth
// Copyright 2015 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 "components/proximity_auth/ble/bluetooth_low_energy_device_whitelist.h"
#include <vector>
#include "base/macros.h"
#include "base/values.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/proximity_auth/ble/pref_names.h"
#include "components/proximity_auth/logging/logging.h"
namespace proximity_auth {
BluetoothLowEnergyDeviceWhitelist::BluetoothLowEnergyDeviceWhitelist(
PrefService* pref_service)
: pref_service_(pref_service) {
}
BluetoothLowEnergyDeviceWhitelist::~BluetoothLowEnergyDeviceWhitelist() {
}
// static
void BluetoothLowEnergyDeviceWhitelist::RegisterPrefs(
PrefRegistrySimple* registry) {
registry->RegisterDictionaryPref(prefs::kBluetoothLowEnergyDeviceWhitelist);
}
bool BluetoothLowEnergyDeviceWhitelist::HasDeviceWithAddress(
const std::string& bluetooth_address) const {
return pref_service_->GetDictionary(prefs::kBluetoothLowEnergyDeviceWhitelist)
->HasKey(bluetooth_address);
}
bool BluetoothLowEnergyDeviceWhitelist::HasDeviceWithPublicKey(
const std::string& public_key) const {
return !GetDeviceAddress(public_key).empty();
}
std::string BluetoothLowEnergyDeviceWhitelist::GetDevicePublicKey(
const std::string& bluetooth_address) const {
std::string public_key;
GetWhitelistPrefs()->GetStringWithoutPathExpansion(bluetooth_address,
&public_key);
return public_key;
}
std::string BluetoothLowEnergyDeviceWhitelist::GetDeviceAddress(
const std::string& public_key) const {
const base::DictionaryValue* device_whitelist = GetWhitelistPrefs();
for (base::DictionaryValue::Iterator it(*device_whitelist); !it.IsAtEnd();
it.Advance()) {
std::string value_string;
DCHECK(it.value().IsType(base::Value::Type::STRING));
if (it.value().GetAsString(&value_string) && value_string == public_key)
return it.key();
}
return std::string();
}
std::vector<std::string> BluetoothLowEnergyDeviceWhitelist::GetPublicKeys()
const {
std::vector<std::string> public_keys;
const base::DictionaryValue* device_whitelist = GetWhitelistPrefs();
for (base::DictionaryValue::Iterator it(*device_whitelist); !it.IsAtEnd();
it.Advance()) {
std::string value_string;
DCHECK(it.value().IsType(base::Value::Type::STRING));
it.value().GetAsString(&value_string);
public_keys.push_back(value_string);
}
return public_keys;
}
void BluetoothLowEnergyDeviceWhitelist::AddOrUpdateDevice(
const std::string& bluetooth_address,
const std::string& public_key) {
if (HasDeviceWithPublicKey(public_key) &&
GetDeviceAddress(public_key) != bluetooth_address) {
PA_LOG(WARNING) << "Two devices with different bluetooth address, but the "
"same public key were added: " << public_key;
RemoveDeviceWithPublicKey(public_key);
}
DictionaryPrefUpdate device_whitelist_update(
pref_service_, prefs::kBluetoothLowEnergyDeviceWhitelist);
device_whitelist_update->SetStringWithoutPathExpansion(bluetooth_address,
public_key);
}
bool BluetoothLowEnergyDeviceWhitelist::RemoveDeviceWithAddress(
const std::string& bluetooth_address) {
DictionaryPrefUpdate device_whitelist_update(
pref_service_, prefs::kBluetoothLowEnergyDeviceWhitelist);
return device_whitelist_update->RemoveWithoutPathExpansion(bluetooth_address,
nullptr);
}
bool BluetoothLowEnergyDeviceWhitelist::RemoveDeviceWithPublicKey(
const std::string& public_key) {
return RemoveDeviceWithAddress(GetDeviceAddress(public_key));
}
const base::DictionaryValue*
BluetoothLowEnergyDeviceWhitelist::GetWhitelistPrefs() const {
return pref_service_->GetDictionary(
prefs::kBluetoothLowEnergyDeviceWhitelist);
}
} // namespace proximity_auth
// Copyright 2015 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.
#ifndef COMPONENTS_PROXIMITY_AUTH_BLE_BLUETOOTH_LOW_ENERGY_WHITELIST_H
#define COMPONENTS_PROXIMITY_AUTH_BLE_BLUETOOTH_LOW_ENERGY_WHITELIST_H
#include <memory>
#include <string>
#include <vector>
#include "base/macros.h"
class PrefRegistrySimple;
class PrefService;
namespace base {
class DictionaryValue;
} // namespace base
namespace proximity_auth {
// This class stores a persistent set of whitelisted bluetooth devices
// represented by pairs (bluetooth_address, public_key). The class enforces a
// bijective mapping: no two device share the same bluetooth_address or
// the same public_key.
class BluetoothLowEnergyDeviceWhitelist {
public:
// Creates a device whitelist backed by preferences registered in
// |pref_service| (persistent across browser restarts). |pref_service| should
// have been registered using RegisterPrefs(). Not owned, must out live this
// instance.
explicit BluetoothLowEnergyDeviceWhitelist(PrefService* pref_service);
virtual ~BluetoothLowEnergyDeviceWhitelist();
// Registers the prefs used by this class to the given |pref_service|.
static void RegisterPrefs(PrefRegistrySimple* registry);
// Virtual for testing
virtual bool HasDeviceWithAddress(const std::string& bluetooth_address) const;
bool HasDeviceWithPublicKey(const std::string& public_key) const;
std::string GetDevicePublicKey(const std::string& bluetooth_address) const;
std::string GetDeviceAddress(const std::string& public_key) const;
std::vector<std::string> GetPublicKeys() const;
// Adds a device with |bluetooth_address| and |public_key|. The
// bluetooth_address <-> public_key mapping is unique, i.e. if there is
// another device with same bluetooth address or public key that device will
// be replaced.
void AddOrUpdateDevice(const std::string& bluetooth_address,
const std::string& public_key);
// Removes the unique device with |bluetooth_address| (|public_key|). Returns
// false if no device was found.
bool RemoveDeviceWithAddress(const std::string& bluetooth_address);
bool RemoveDeviceWithPublicKey(const std::string& public_key);
private:
const base::DictionaryValue* GetWhitelistPrefs() const;
// Contains perferences that outlive the lifetime of this object and across
// process restarts.
// Not owned and must outlive this instance.
PrefService* pref_service_;
DISALLOW_COPY_AND_ASSIGN(BluetoothLowEnergyDeviceWhitelist);
};
} // namespace proximity_auth
#endif // COMPONENTS_PROXIMITY_AUTH_BLE_BLUETOOTH_WHITELIST_H
// Copyright 2015 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 "components/proximity_auth/ble/bluetooth_low_energy_device_whitelist.h"
#include <algorithm>
#include <string>
#include <vector>
#include "base/macros.h"
#include "components/prefs/testing_pref_service.h"
#include "components/proximity_auth/ble/pref_names.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace proximity_auth {
namespace {
const char kBluetoothAddress1[] = "11:22:33:44:55:66";
const char kPublicKey1[] = "public key 1";
const char kBluetoothAddress2[] = "22:33:44:55:66:77";
const char kPublicKey2[] = "public key 2";
} // namespace
class ProximityAuthBluetoothLowEnergyDeviceWhitelistTest
: public testing::Test {
protected:
ProximityAuthBluetoothLowEnergyDeviceWhitelistTest() {}
void SetUp() override {
BluetoothLowEnergyDeviceWhitelist::RegisterPrefs(pref_service_.registry());
}
void CheckWhitelistedDevice(
const std::string& bluetooth_address,
const std::string& public_key,
BluetoothLowEnergyDeviceWhitelist& device_whitelist) {
EXPECT_TRUE(device_whitelist.HasDeviceWithAddress(bluetooth_address));
EXPECT_EQ(device_whitelist.GetDevicePublicKey(bluetooth_address),
public_key);
EXPECT_TRUE(device_whitelist.HasDeviceWithPublicKey(public_key));
EXPECT_EQ(device_whitelist.GetDeviceAddress(public_key), bluetooth_address);
}
TestingPrefServiceSimple pref_service_;
private:
DISALLOW_COPY_AND_ASSIGN(ProximityAuthBluetoothLowEnergyDeviceWhitelistTest);
};
TEST_F(ProximityAuthBluetoothLowEnergyDeviceWhitelistTest, RegisterPrefs) {
TestingPrefServiceSimple pref_service;
BluetoothLowEnergyDeviceWhitelist::RegisterPrefs(pref_service.registry());
EXPECT_TRUE(
pref_service.FindPreference(prefs::kBluetoothLowEnergyDeviceWhitelist));
}
TEST_F(ProximityAuthBluetoothLowEnergyDeviceWhitelistTest, AddDevice) {
BluetoothLowEnergyDeviceWhitelist device_whitelist(&pref_service_);
device_whitelist.AddOrUpdateDevice(kBluetoothAddress1, kPublicKey1);
CheckWhitelistedDevice(kBluetoothAddress1, kPublicKey1, device_whitelist);
}
TEST_F(ProximityAuthBluetoothLowEnergyDeviceWhitelistTest,
AddDevice_TwoDevicesWithSameAddress) {
BluetoothLowEnergyDeviceWhitelist device_whitelist(&pref_service_);
device_whitelist.AddOrUpdateDevice(kBluetoothAddress1, kPublicKey1);
CheckWhitelistedDevice(kBluetoothAddress1, kPublicKey1, device_whitelist);
device_whitelist.AddOrUpdateDevice(kBluetoothAddress1, kPublicKey2);
CheckWhitelistedDevice(kBluetoothAddress1, kPublicKey2, device_whitelist);
EXPECT_FALSE(device_whitelist.HasDeviceWithPublicKey(kPublicKey1));
}
TEST_F(ProximityAuthBluetoothLowEnergyDeviceWhitelistTest,
AddDevice_TwoDevicesWithSamePublicKey) {
BluetoothLowEnergyDeviceWhitelist device_whitelist(&pref_service_);
device_whitelist.AddOrUpdateDevice(kBluetoothAddress1, kPublicKey1);
CheckWhitelistedDevice(kBluetoothAddress1, kPublicKey1, device_whitelist);
device_whitelist.AddOrUpdateDevice(kBluetoothAddress2, kPublicKey1);
CheckWhitelistedDevice(kBluetoothAddress2, kPublicKey1, device_whitelist);
EXPECT_FALSE(device_whitelist.HasDeviceWithAddress(kBluetoothAddress1));
}
TEST_F(ProximityAuthBluetoothLowEnergyDeviceWhitelistTest,
RemoveDeviceWithAddress) {
BluetoothLowEnergyDeviceWhitelist device_whitelist(&pref_service_);
device_whitelist.AddOrUpdateDevice(kBluetoothAddress1, kPublicKey1);
CheckWhitelistedDevice(kBluetoothAddress1, kPublicKey1, device_whitelist);
ASSERT_TRUE(device_whitelist.RemoveDeviceWithAddress(kBluetoothAddress1));
EXPECT_FALSE(device_whitelist.HasDeviceWithAddress(kBluetoothAddress1));
EXPECT_FALSE(device_whitelist.HasDeviceWithPublicKey(kPublicKey1));
}
TEST_F(ProximityAuthBluetoothLowEnergyDeviceWhitelistTest,
RemoveDeviceWithAddress_DeviceNotPresent) {
BluetoothLowEnergyDeviceWhitelist device_whitelist(&pref_service_);
device_whitelist.AddOrUpdateDevice(kBluetoothAddress1, kPublicKey1);
CheckWhitelistedDevice(kBluetoothAddress1, kPublicKey1, device_whitelist);
ASSERT_FALSE(device_whitelist.RemoveDeviceWithAddress(kBluetoothAddress2));
EXPECT_TRUE(device_whitelist.HasDeviceWithAddress(kBluetoothAddress1));
EXPECT_TRUE(device_whitelist.HasDeviceWithPublicKey(kPublicKey1));
}
TEST_F(ProximityAuthBluetoothLowEnergyDeviceWhitelistTest,
RemoveDeviceWithPublicKey) {
BluetoothLowEnergyDeviceWhitelist device_whitelist(&pref_service_);
device_whitelist.AddOrUpdateDevice(kBluetoothAddress1, kPublicKey1);
CheckWhitelistedDevice(kBluetoothAddress1, kPublicKey1, device_whitelist);
ASSERT_TRUE(device_whitelist.RemoveDeviceWithPublicKey(kPublicKey1));
EXPECT_FALSE(device_whitelist.HasDeviceWithAddress(kBluetoothAddress1));
EXPECT_FALSE(device_whitelist.HasDeviceWithPublicKey(kPublicKey1));
}
TEST_F(ProximityAuthBluetoothLowEnergyDeviceWhitelistTest,
RemoveDeviceWithPublicKey_DeviceNotPresent) {
BluetoothLowEnergyDeviceWhitelist device_whitelist(&pref_service_);
device_whitelist.AddOrUpdateDevice(kBluetoothAddress1, kPublicKey1);
CheckWhitelistedDevice(kBluetoothAddress1, kPublicKey1, device_whitelist);
ASSERT_FALSE(device_whitelist.RemoveDeviceWithPublicKey(kPublicKey2));
EXPECT_TRUE(device_whitelist.HasDeviceWithAddress(kBluetoothAddress1));
EXPECT_TRUE(device_whitelist.HasDeviceWithPublicKey(kPublicKey1));
}
TEST_F(ProximityAuthBluetoothLowEnergyDeviceWhitelistTest, GetPublicKeys) {
BluetoothLowEnergyDeviceWhitelist device_whitelist(&pref_service_);
device_whitelist.AddOrUpdateDevice(kBluetoothAddress1, kPublicKey1);
CheckWhitelistedDevice(kBluetoothAddress1, kPublicKey1, device_whitelist);
device_whitelist.AddOrUpdateDevice(kBluetoothAddress2, kPublicKey2);
CheckWhitelistedDevice(kBluetoothAddress2, kPublicKey2, device_whitelist);
std::vector<std::string> public_keys = device_whitelist.GetPublicKeys();
// Note: it's not guarantee that the order in |public_key| is the same as
// insertion, so directly comparing vectors would not work.
EXPECT_TRUE(public_keys.size() == 2);
EXPECT_TRUE(std::find(public_keys.begin(), public_keys.end(), kPublicKey1) !=
public_keys.end());
EXPECT_TRUE(std::find(public_keys.begin(), public_keys.end(), kPublicKey2) !=
public_keys.end());
}
} // namespace proximity_auth
// Copyright 2015 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 "components/proximity_auth/ble/pref_names.h"
namespace proximity_auth {
namespace prefs {
// The dictionary containing whitelisted BLE devices used by
// proximity_auth::BluetoothLowEnergyDeviceWhitelist.
const char kBluetoothLowEnergyDeviceWhitelist[] =
"proximity_auth_bluetooth_low_energy_device_whitelist";
} // namespace prefs
} // namespace proximity_auth
// Copyright 2015 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.
#ifndef COMPONENTS_PROXIMITY_AUTH_BLE_PREF_NAMES_H
#define COMPONENTS_PROXIMITY_AUTH_BLE_PREF_NAMES_H
namespace proximity_auth {
namespace prefs {
extern const char kBluetoothLowEnergyDeviceWhitelist[];
} // namespace prefs
} // proximity_auth
#endif // COMPONENTS_PROXIMITY_AUTH_BLE_PREF_NAMES_H
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment