| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/nearby_sharing/fast_initiation/fast_initiation_advertiser.h" |
| |
| #include <string> |
| |
| #include "base/functional/callback_helpers.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_util.h" |
| #include "chrome/browser/nearby_sharing/fast_initiation/constants.h" |
| #include "components/cross_device/logging/logging.h" |
| #include "device/bluetooth/bluetooth_advertisement.h" |
| |
| namespace { |
| enum class FastInitVersion : uint8_t { |
| kV1 = 0, |
| }; |
| |
| // This is the canonical service UUID used in outgoing Fast Initiation |
| // advertisements. The service UUID of 0xfe2c is shared with |
| // FastInitiationScanner in constants.h. |
| constexpr const char kFastInitiationServiceUuid[] = |
| "0000fe2c-0000-1000-8000-00805f9b34fb"; |
| const FastInitVersion kVersion = FastInitVersion::kV1; |
| const uint8_t kVersionBitmask = 0b111; |
| const uint8_t kTypeBitmask = 0b111; |
| |
| // TODO(crbug.com/1099846): This value comes from Android, but we may need to |
| // find a more appropriate power setting for Chrome OS devices. |
| const int8_t kAdjustedTxPower = -66; |
| |
| } // namespace |
| |
| // static |
| FastInitiationAdvertiser::Factory* |
| FastInitiationAdvertiser::Factory::factory_instance_ = nullptr; |
| |
| // static |
| std::unique_ptr<FastInitiationAdvertiser> |
| FastInitiationAdvertiser::Factory::Create( |
| scoped_refptr<device::BluetoothAdapter> adapter) { |
| if (factory_instance_) |
| return factory_instance_->CreateInstance(adapter); |
| |
| return std::make_unique<FastInitiationAdvertiser>(adapter); |
| } |
| |
| // static |
| void FastInitiationAdvertiser::Factory::SetFactoryForTesting( |
| FastInitiationAdvertiser::Factory* factory) { |
| factory_instance_ = factory; |
| } |
| |
| FastInitiationAdvertiser::FastInitiationAdvertiser( |
| scoped_refptr<device::BluetoothAdapter> adapter) { |
| DCHECK(adapter && adapter->IsPresent() && adapter->IsPowered()); |
| adapter_ = adapter; |
| } |
| |
| FastInitiationAdvertiser::~FastInitiationAdvertiser() { |
| StopAdvertising(base::DoNothing()); |
| } |
| |
| void FastInitiationAdvertiser::AdvertisementReleased( |
| device::BluetoothAdvertisement* advertisement) { |
| StopAdvertising(base::DoNothing()); |
| } |
| |
| void FastInitiationAdvertiser::StartAdvertising( |
| FastInitType type, |
| base::OnceCallback<void()> callback, |
| base::OnceCallback<void()> error_callback) { |
| DCHECK(adapter_->IsPresent() && adapter_->IsPowered()); |
| DCHECK(!advertisement_); |
| RegisterAdvertisement(type, std::move(callback), std::move(error_callback)); |
| } |
| |
| void FastInitiationAdvertiser::StopAdvertising( |
| base::OnceCallback<void()> callback) { |
| if (!advertisement_) { |
| std::move(callback).Run(); |
| // |this| might be destroyed here, do not access local fields. |
| return; |
| } |
| |
| UnregisterAdvertisement(std::move(callback)); |
| } |
| |
| void FastInitiationAdvertiser::RegisterAdvertisement( |
| FastInitiationAdvertiser::FastInitType type, |
| base::OnceClosure callback, |
| base::OnceClosure error_callback) { |
| auto advertisement_data = |
| std::make_unique<device::BluetoothAdvertisement::Data>( |
| device::BluetoothAdvertisement::ADVERTISEMENT_TYPE_BROADCAST); |
| |
| device::BluetoothAdvertisement::UUIDList list; |
| list.push_back(kFastInitiationServiceUuid); |
| advertisement_data->set_service_uuids(std::move(list)); |
| |
| device::BluetoothAdvertisement::ServiceData service_data; |
| auto payload = std::vector<uint8_t>(std::begin(kFastInitiationModelId), |
| std::end(kFastInitiationModelId)); |
| auto metadata = GenerateFastInitV1Metadata(type); |
| payload.insert(std::end(payload), std::begin(metadata), std::end(metadata)); |
| service_data.insert(std::pair<std::string, std::vector<uint8_t>>( |
| kFastInitiationServiceUuid, payload)); |
| advertisement_data->set_service_data(std::move(service_data)); |
| |
| adapter_->RegisterAdvertisement( |
| std::move(advertisement_data), |
| base::BindOnce(&FastInitiationAdvertiser::OnRegisterAdvertisement, |
| weak_ptr_factory_.GetWeakPtr(), std::move(callback)), |
| base::BindOnce(&FastInitiationAdvertiser::OnRegisterAdvertisementError, |
| weak_ptr_factory_.GetWeakPtr(), |
| std::move(error_callback))); |
| } |
| |
| void FastInitiationAdvertiser::OnRegisterAdvertisement( |
| base::OnceClosure callback, |
| scoped_refptr<device::BluetoothAdvertisement> advertisement) { |
| advertisement_ = advertisement; |
| advertisement_->AddObserver(this); |
| std::move(callback).Run(); |
| } |
| |
| void FastInitiationAdvertiser::OnRegisterAdvertisementError( |
| base::OnceClosure error_callback, |
| device::BluetoothAdvertisement::ErrorCode error_code) { |
| CD_LOG(ERROR, Feature::NS) |
| << "FastInitiationAdvertiser::StartAdvertising() failed with " |
| "error code = " |
| << error_code; |
| std::move(error_callback).Run(); |
| // |this| might be destroyed here, do not access local fields. |
| } |
| |
| void FastInitiationAdvertiser::UnregisterAdvertisement( |
| base::OnceClosure callback) { |
| stop_callback_ = std::move(callback); |
| advertisement_->RemoveObserver(this); |
| advertisement_->Unregister( |
| base::BindOnce(&FastInitiationAdvertiser::OnUnregisterAdvertisement, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::BindOnce(&FastInitiationAdvertiser::OnUnregisterAdvertisementError, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void FastInitiationAdvertiser::OnUnregisterAdvertisement() { |
| advertisement_.reset(); |
| std::move(stop_callback_).Run(); |
| // |this| might be destroyed here, do not access local fields. |
| } |
| |
| void FastInitiationAdvertiser::OnUnregisterAdvertisementError( |
| device::BluetoothAdvertisement::ErrorCode error_code) { |
| CD_LOG(WARNING, Feature::NS) |
| << "FastInitiationAdvertiser::StopAdvertising() failed with error code = " |
| << error_code; |
| advertisement_.reset(); |
| std::move(stop_callback_).Run(); |
| // |this| might be destroyed here, do not access local fields. |
| } |
| |
| std::vector<uint8_t> FastInitiationAdvertiser::GenerateFastInitV1Metadata( |
| FastInitiationAdvertiser::FastInitType type) { |
| std::vector<uint8_t> metadata; |
| uint8_t versionConverted = (static_cast<uint8_t>(kVersion) & kVersionBitmask) |
| << 5; |
| uint8_t typeConverted = (static_cast<uint8_t>(type) & kTypeBitmask) << 2; |
| |
| // Note: We convert this to a positive value before transport to align with |
| // Android's behavior. |
| int8_t powerConverted = -kAdjustedTxPower; |
| |
| // Note: the last two bits of this first byte correspond to 'uwb_enable' and |
| // 'reserved'. The Chrome implementation does not support UWB (Ultra wideband) |
| // and the 'reserved' bit is currently unused, so both are left empty. |
| metadata.push_back(versionConverted | typeConverted); |
| metadata.push_back(powerConverted); |
| return metadata; |
| } |