blob: 32b684db8b661ed771a329338578b65bbd2ff165 [file] [log] [blame]
// Copyright 2017 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 "chromeos/services/secure_channel/error_tolerant_ble_advertisement_impl.h"
#include <memory>
#include "base/bind.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/memory/weak_ptr.h"
#include "base/no_destructor.h"
#include "chromeos/components/multidevice/remote_device_ref.h"
#include "chromeos/components/proximity_auth/logging/logging.h"
#include "chromeos/services/secure_channel/ble_constants.h"
#include "chromeos/services/secure_channel/ble_synchronizer.h"
namespace chromeos {
namespace secure_channel {
namespace {
const uint8_t kInvertedConnectionFlag = 0x01;
} // namespace
// static
ErrorTolerantBleAdvertisementImpl::Factory*
ErrorTolerantBleAdvertisementImpl::Factory::test_factory_ = nullptr;
// static
ErrorTolerantBleAdvertisementImpl::Factory*
ErrorTolerantBleAdvertisementImpl::Factory::Get() {
if (test_factory_)
return test_factory_;
static base::NoDestructor<Factory> factory;
return factory.get();
}
// static
void ErrorTolerantBleAdvertisementImpl::Factory::SetFactoryForTesting(
Factory* test_factory) {
test_factory_ = test_factory;
}
ErrorTolerantBleAdvertisementImpl::Factory::~Factory() = default;
std::unique_ptr<ErrorTolerantBleAdvertisement>
ErrorTolerantBleAdvertisementImpl::Factory::BuildInstance(
const DeviceIdPair& device_id_pair,
std::unique_ptr<DataWithTimestamp> advertisement_data,
BleSynchronizerBase* ble_synchronizer) {
return base::WrapUnique(new ErrorTolerantBleAdvertisementImpl(
device_id_pair, std::move(advertisement_data), ble_synchronizer));
}
ErrorTolerantBleAdvertisementImpl::ErrorTolerantBleAdvertisementImpl(
const DeviceIdPair& device_id_pair,
std::unique_ptr<DataWithTimestamp> advertisement_data,
BleSynchronizerBase* ble_synchronizer)
: ErrorTolerantBleAdvertisement(device_id_pair),
advertisement_data_(std::move(advertisement_data)),
ble_synchronizer_(ble_synchronizer),
weak_ptr_factory_(this) {
UpdateRegistrationStatus();
}
ErrorTolerantBleAdvertisementImpl::~ErrorTolerantBleAdvertisementImpl() {
if (advertisement_)
advertisement_->RemoveObserver(this);
}
void ErrorTolerantBleAdvertisementImpl::Stop(const base::Closure& callback) {
// Stop() should only be called once per instance.
DCHECK(stop_callback_.is_null());
stop_callback_ = callback;
UpdateRegistrationStatus();
}
bool ErrorTolerantBleAdvertisementImpl::HasBeenStopped() {
return !stop_callback_.is_null();
}
void ErrorTolerantBleAdvertisementImpl::AdvertisementReleased(
device::BluetoothAdvertisement* advertisement) {
DCHECK(advertisement_.get() == advertisement);
// If the advertisement was released, delete it and try again. Note that this
// situation is not expected to occur under normal circumstances.
advertisement_->RemoveObserver(this);
advertisement_ = nullptr;
PA_LOG(WARNING) << "Advertisement was released. Trying again. Request: "
<< device_id_pair()
<< ", Service data: " << advertisement_data_->DataInHex();
UpdateRegistrationStatus();
}
void ErrorTolerantBleAdvertisementImpl::UpdateRegistrationStatus() {
if (!advertisement_)
AttemptRegistration();
else if (advertisement_ && HasBeenStopped())
AttemptUnregistration();
}
void ErrorTolerantBleAdvertisementImpl::AttemptRegistration() {
DCHECK(!unregistration_in_progress_);
if (registration_in_progress_)
return;
registration_in_progress_ = true;
std::unique_ptr<device::BluetoothAdvertisement::Data> advertisement_data =
std::make_unique<device::BluetoothAdvertisement::Data>(
device::BluetoothAdvertisement::AdvertisementType::
ADVERTISEMENT_TYPE_BROADCAST);
advertisement_data->set_service_uuids(CreateServiceUuids());
advertisement_data->set_service_data(CreateServiceData());
ble_synchronizer_->RegisterAdvertisement(
std::move(advertisement_data),
base::Bind(&ErrorTolerantBleAdvertisementImpl::OnAdvertisementRegistered,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(
&ErrorTolerantBleAdvertisementImpl::OnErrorRegisteringAdvertisement,
weak_ptr_factory_.GetWeakPtr()));
}
void ErrorTolerantBleAdvertisementImpl::AttemptUnregistration() {
// Should never attempt to unregister before Stop() has been called.
DCHECK(!stop_callback_.is_null());
// If no advertisement has yet been registered, we must wait until it has been
// successfully registered before it is possible to unregister. Likewise, if
// unregistration is still in progress, there is nothing else to do.
if (registration_in_progress_ || unregistration_in_progress_)
return;
unregistration_in_progress_ = true;
ble_synchronizer_->UnregisterAdvertisement(
advertisement_,
base::Bind(
&ErrorTolerantBleAdvertisementImpl::OnAdvertisementUnregistered,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(
&ErrorTolerantBleAdvertisementImpl::OnErrorUnregisteringAdvertisement,
weak_ptr_factory_.GetWeakPtr()));
}
std::unique_ptr<device::BluetoothAdvertisement::UUIDList>
ErrorTolerantBleAdvertisementImpl::CreateServiceUuids() const {
std::unique_ptr<device::BluetoothAdvertisement::UUIDList> list =
std::make_unique<device::BluetoothAdvertisement::UUIDList>();
list->push_back(kAdvertisingServiceUuid);
return list;
}
std::unique_ptr<device::BluetoothAdvertisement::ServiceData>
ErrorTolerantBleAdvertisementImpl::CreateServiceData() const {
DCHECK(!advertisement_data_->data.empty());
std::vector<uint8_t> data_as_vector(advertisement_data_->data.size());
memcpy(data_as_vector.data(), advertisement_data_->data.data(),
advertisement_data_->data.size());
// Add a flag at the end of the service data to signify that the inverted
// connection flow should be used.
data_as_vector.push_back(kInvertedConnectionFlag);
std::unique_ptr<device::BluetoothAdvertisement::ServiceData> service_data =
std::make_unique<device::BluetoothAdvertisement::ServiceData>();
service_data->insert(std::pair<std::string, std::vector<uint8_t>>(
kAdvertisingServiceUuid, data_as_vector));
return service_data;
}
void ErrorTolerantBleAdvertisementImpl::OnAdvertisementRegistered(
scoped_refptr<device::BluetoothAdvertisement> advertisement) {
registration_in_progress_ = false;
advertisement_ = advertisement;
advertisement_->AddObserver(this);
PA_LOG(VERBOSE) << "Advertisement registered. Request: " << device_id_pair()
<< ", Service data: " << advertisement_data_->DataInHex();
UpdateRegistrationStatus();
}
void ErrorTolerantBleAdvertisementImpl::OnErrorRegisteringAdvertisement(
device::BluetoothAdvertisement::ErrorCode error_code) {
registration_in_progress_ = false;
PA_LOG(ERROR) << "Error registering advertisement. Request: "
<< device_id_pair()
<< ", Service data: " << advertisement_data_->DataInHex()
<< ", Error code: " << error_code;
UpdateRegistrationStatus();
}
void ErrorTolerantBleAdvertisementImpl::OnAdvertisementUnregistered() {
unregistration_in_progress_ = false;
advertisement_->RemoveObserver(this);
advertisement_ = nullptr;
DCHECK(!stop_callback_.is_null());
stop_callback_.Run();
}
void ErrorTolerantBleAdvertisementImpl::OnErrorUnregisteringAdvertisement(
device::BluetoothAdvertisement::ErrorCode error_code) {
unregistration_in_progress_ = false;
PA_LOG(ERROR) << "Error unregistering advertisement. Request: "
<< device_id_pair()
<< ", Service data: " << advertisement_data_->DataInHex()
<< ", Error code: " << error_code;
UpdateRegistrationStatus();
}
} // namespace secure_channel
} // namespace chromeos