diff --git a/internal/platform/implementation/linux/bluez_le_advertisement.cc b/internal/platform/implementation/linux/bluez_le_advertisement.cc index c58b55ca..e2cdaed6 100644 --- a/internal/platform/implementation/linux/bluez_le_advertisement.cc +++ b/internal/platform/implementation/linux/bluez_le_advertisement.cc @@ -43,14 +43,16 @@ LEAdvertisement::LEAdvertisement( std::vector data_bytes(data.size()); const auto* bytes = data.data(); - service_uuids_.push_back(uuid_string); + if (is_extended_advertisement_){ + service_uuids_.push_back(uuid_string); + } // service_uuids_.push_back("0000FE2C-0000-1000-8000-00805F9B34FB"); // service_uuids_.push_back("0000FE2C-0000-1000-8000-00805F9B34FB"); for (size_t i = 0; i < data.size(); i++) { data_bytes[i] = bytes[i]; } - // LOG(INFO)<< __func__ << ": " << uuid_string; - // LOG(INFO)<< __func__ << ": " << BytesToHexString(data_bytes); + LOG(INFO)<< __func__ << ": " << uuid_string; + LOG(INFO)<< __func__ << ": " << BytesToHexString(data_bytes); service_data_.insert({uuid_string, std::move(sdbus::Variant(data_bytes))}); // service_data_.insert({"0000FE2C-0000-1000-8000-00805F9B34FB", std::move(data_bytes)}); } diff --git a/sharing/fast_initiation/BUILD b/sharing/fast_initiation/BUILD index 8a38dcd7..d45d85b4 100644 --- a/sharing/fast_initiation/BUILD +++ b/sharing/fast_initiation/BUILD @@ -14,9 +14,24 @@ load("@rules_cc//cc:cc_library.bzl", "cc_library") load("@rules_cc//cc:cc_test.bzl", "cc_test") +load("@hedron_compile_commands//:refresh_compile_commands.bzl", "refresh_compile_commands") licenses(["notice"]) +refresh_compile_commands( + name = "refresh_compile_commands_fast_initiation", + + # Specify the targets of interest. + # For example, specify a dict of targets and any flags required to build. + targets = { + ":nearby_fast_initiation": "", + }, + # No need to add flags already in .bazelrc. They're automatically picked up. + # If you don't need flags, a list of targets is also okay, as is a single target string. + # Wildcard patterns, like //... for everything, *are* allowed here, just like a build. + # As are additional targets (+) and subtractions (-), like in bazel query https://docs.bazel.build/versions/main/query.html#expressions + # And if you're working on a header-only library, specify a test or binary target that compiles it. +) cc_library( name = "nearby_fast_initiation", srcs = [ diff --git a/sharing/internal/api/BUILD b/sharing/internal/api/BUILD index 4087190d..65251f68 100644 --- a/sharing/internal/api/BUILD +++ b/sharing/internal/api/BUILD @@ -13,9 +13,27 @@ # limitations under the License. load("@rules_cc//cc:cc_library.bzl", "cc_library") +load("@rules_cc//cc:defs.bzl", "cc_binary") licenses(["notice"]) +load("@hedron_compile_commands//:refresh_compile_commands.bzl", "refresh_compile_commands") + +refresh_compile_commands( + name = "refresh_compile_commands_sharing_platform", + + # Specify the targets of interest. + # For example, specify a dict of targets and any flags required to build. + targets = { + ":platform": "", + }, + # No need to add flags already in .bazelrc. They're automatically picked up. + # If you don't need flags, a list of targets is also okay, as is a single target string. + # Wildcard patterns, like //... for everything, *are* allowed here, just like a build. + # As are additional targets (+) and subtractions (-), like in bazel query https://docs.bazel.build/versions/main/query.html#expressions + # And if you're working on a header-only library, specify a test or binary target that compiles it. +) + cc_library( name = "platform", hdrs = [ @@ -43,8 +61,8 @@ cc_library( "//internal/platform:mac_address", "//internal/platform:types", "//internal/platform/implementation:types", - "//location/nearby/sharing/lib/account:account_manager", - "//location/nearby/sharing/lib/sync:sync_binding_prefs_cc_proto", + #"//location/nearby/sharing/lib/account:account_manager", + #"//location/nearby/sharing/lib/sync:sync_binding_prefs_cc_proto", "//sharing/proto:share_cc_proto", "@com_google_absl//absl/functional:any_invocable", "@com_google_absl//absl/strings:string_view", diff --git a/sharing/linux/BUILD b/sharing/linux/BUILD index 29f06fc4..304aaf99 100644 --- a/sharing/linux/BUILD +++ b/sharing/linux/BUILD @@ -24,7 +24,7 @@ refresh_compile_commands( # Specify the targets of interest. # For example, specify a dict of targets and any flags required to build. targets = { - ":nearby_sharing_api_shared": "-s --check_visibility=false --spawn_strategy=standalone --verbose_failures --cxxopt=-std=c++20 --host_cxxopt=-std=c++20 --cxxopt='-fvisibility-inlines-hidden'", + ":linux_fast_init": "", }, # No need to add flags already in .bazelrc. They're automatically picked up. # If you don't need flags, a list of targets is also okay, as is a single target string. @@ -33,6 +33,29 @@ refresh_compile_commands( # And if you're working on a header-only library, specify a test or binary target that compiles it. ) + +cc_library( + name = "linux_fast_init", + hdrs = [ + "nearby_fast_init_ble_beacon.h", + "nearby_fast_init_manager.h", + ], + srcs = [ + "nearby_fast_init_manager.cc", + ], + deps = [ + "//sharing/internal/api:platform", + "//connections:core", + "//internal/platform/implementation/linux" + ] +) + +cc_binary( + name = "fast_init_test", + srcs = ["nearby_fast_init.cc"], + deps = [":linux_fast_init"] +) + cc_library( name = "linux_sharing_platform", srcs = ["platform/linux_sharing_platform.cc"], diff --git a/sharing/linux/nearby_fast_init.cc b/sharing/linux/nearby_fast_init.cc new file mode 100644 index 00000000..b51995fb --- /dev/null +++ b/sharing/linux/nearby_fast_init.cc @@ -0,0 +1,50 @@ +#include "internal/platform/implementation/linux/bluetooth_adapter.h" +#include "internal/platform/implementation/linux/bluez.h" +#include "internal/platform/implementation/linux/dbus.h" +#include "sharing/linux/nearby_fast_init_ble_beacon.h" +#include "sharing/linux/nearby_fast_init_manager.h" +#include +#include + +// For local testing only. Android logs show that our fast init beacon is +// properly detected and they start advertising. + +int main() { + auto system_bus = ::nearby::linux::getSystemBusConnection(); + auto manager = ::nearby::linux::bluez::BluezObjectManager(*system_bus); + std::shared_ptr adapter; + auto interfaces = manager.GetManagedObjects(); + for (auto& [object, properties] : interfaces) { + if (properties.count(sdbus::InterfaceName( + org::bluez::Adapter1_proxy::INTERFACE_NAME)) == 1) { + LOG(INFO) << __func__ << ": found bluetooth adapter " << object; + adapter = std::make_shared<::nearby::linux::BluetoothAdapter>(system_bus, + object); + } + } + auto beacon = ::nearby::sharing::linux::LinuxFastInitBleBeacon(); + + try { + auto interfaces = manager.GetManagedObjects(); + for (auto& [object, properties] : interfaces) { + if (properties.count(sdbus::InterfaceName( + org::bluez::Adapter1_proxy::INTERFACE_NAME)) == 1) { + LOG(INFO) << __func__ << ": found bluetooth adapter " << object; + adapter = std::make_shared<::nearby::linux::BluetoothAdapter>( + system_bus, object); + } + } + } catch (const sdbus::Error& e) { + DBUS_LOG_METHOD_CALL_ERROR(&manager, "GetManagedObjects", e); + } + + auto fast_init_manager = + ::nearby::sharing::linux::LinuxFastInitiationManager(beacon, adapter); + + fast_init_manager.StartAdvertising( + nearby::api::FastInitBleBeacon::FastInitType::kNotify, []() { return; }, + [](nearby::api::FastInitiationManager::Error e) { return; }); + + std::this_thread::sleep_for(std::chrono::seconds(30)); + return 0; +} diff --git a/sharing/linux/nearby_fast_init_ble_beacon.h b/sharing/linux/nearby_fast_init_ble_beacon.h new file mode 100644 index 00000000..14c083cc --- /dev/null +++ b/sharing/linux/nearby_fast_init_ble_beacon.h @@ -0,0 +1,87 @@ +#include "sharing/internal/api/fast_init_ble_beacon.h" + +namespace nearby { +namespace sharing { +namespace linux { + +class LinuxFastInitBleBeacon final : public nearby::api::FastInitBleBeacon { + public: + void SerializeToByteArray() override { + std::array + data{}; + std::size_t offset = 0; + + data[offset++] = nearby::api::FastInitBleBeacon::kFastInitServiceUuid[0]; + data[offset++] = nearby::api::FastInitBleBeacon::kFastInitServiceUuid[1]; + + for (uint8_t byte : nearby::api::FastInitBleBeacon::kFastInitModelId) { + data[offset++] = byte; + } + + const uint8_t metadata = (static_cast(this->GetVersion()) << 5) | + (static_cast(this->GetType()) << 2) | + (this->GetUwbSupported() ? 0x02 : 0x00) | + (this->GetSenderCertSupported() ? 0x01 : 0x00); + data[offset++] = metadata; + data[offset++] = static_cast(this->GetAdjustedTxPower()); + + for (uint8_t byte : this->GetUwbMetadata()) { + data[offset++] = byte; + } + for (uint8_t byte : this->GetUwbAddress()) { + data[offset++] = byte; + } + for (uint8_t byte : this->GetSalt()) { + data[offset++] = byte; + } + for (uint8_t byte : this->GetSecretIdHash()) { + data[offset++] = byte; + } + + data[offset++] = (this->GetRequireBtAdvertising() ? 0x80 : 0x00) | + (this->GetSelfOnlyAdvertising() ? 0x40 : 0x00); + SetAdDataByteArray(data); + } + + void ParseFromByteArray() override { + const auto data = GetAdDataByteArray(); + if (data[0] != nearby::api::FastInitBleBeacon::kFastInitServiceUuid[0] || + data[1] != nearby::api::FastInitBleBeacon::kFastInitServiceUuid[1]) { + return; + } + + const uint8_t metadata = data[5]; + SetVersion(static_cast((metadata >> 5) & 0x07)); + SetType(static_cast((metadata >> 2) & 0x07)); + SetUwbSupported((metadata & 0x02) != 0); + SetSenderCertSupported((metadata & 0x01) != 0); + SetAdjustedTxPower(static_cast(data[6])); + + std::array uwb_metadata{}; + uwb_metadata[0] = data[7]; + SetUwbMetadata(uwb_metadata); + + std::array uwb_address{}; + for (std::size_t i = 0; i < uwb_address.size(); ++i) { + uwb_address[i] = data[8 + i]; + } + SetUwbAddress(uwb_address); + + std::array salt{}; + salt[0] = data[16]; + SetSalt(salt); + + std::array secret_id_hash{}; + for (std::size_t i = 0; i < secret_id_hash.size(); ++i) { + secret_id_hash[i] = data[17 + i]; + } + SetSecretIdHash(secret_id_hash); + + const uint8_t flags = data[25]; + SetRequireBtAdvertising((flags & 0x80) != 0); + SetSelfOnlyAdvertising((flags & 0x40) != 0); + } +}; +} // namespace linux +} // namespace sharing +} // namespace nearby diff --git a/sharing/linux/nearby_fast_init_manager.cc b/sharing/linux/nearby_fast_init_manager.cc new file mode 100644 index 00000000..a0a47c03 --- /dev/null +++ b/sharing/linux/nearby_fast_init_manager.cc @@ -0,0 +1,142 @@ +#include + +#include "sharing/internal/api/fast_init_ble_beacon.h" +#include "sharing/internal/api/fast_initiation_manager.h" + +#include "internal/platform/implementation/linux/bluetooth_adapter.h" +#include "sharing/linux/nearby_fast_init_manager.h" +#include "internal/platform/implementation/linux/bluez.h" +#include "internal/platform/implementation/linux/bluez_le_advertisement.h" + +namespace nearby { +namespace sharing { +namespace linux { + +void LinuxFastInitiationManager::StartAdvertising( + nearby::api::FastInitBleBeacon::FastInitType type, + std::function callback, + std::function + error_callback) { + absl::MutexLock lock(&mutex_); + if (advertisement_ != nullptr) { + if (error_callback) { + error_callback(nearby::api::FastInitiationManager::Error::kResourceInUse); + } + return; + } + + if (adapter_ == nullptr || !adapter_->IsEnabled()) { + if (error_callback) { + error_callback(nearby::api::FastInitiationManager::Error:: + kBluetoothRadioUnavailable); + } + return; + } + + if (adv_manager_ == nullptr) { + if (error_callback) { + error_callback( + nearby::api::FastInitiationManager::Error::kHardwareNotSupported); + } + return; + } + + auto fast_init_uuid = Uuid::FromString(kFastInitServiceUuid); + if (!fast_init_uuid.has_value()) { + if (error_callback) { + error_callback(nearby::api::FastInitiationManager::Error::kUnknown); + } + return; + } + + beacon_.SetVersion(nearby::api::FastInitBleBeacon::FastInitVersion::kV1); + beacon_.SetType(type); + beacon_.SetUwbSupported(false); + beacon_.SetSenderCertSupported(false); + beacon_.SetAdjustedTxPower( + 45); // TODO: make this set the real value from adapter + beacon_.SetUwbMetadata({}); + beacon_.SetUwbAddress({}); + + beacon_.SetSalt({}); + beacon_.SetSecretIdHash({}); + beacon_.SetRequireBtAdvertising(true); + beacon_.SetSelfOnlyAdvertising(false); + beacon_.SerializeToByteArray(); + + const auto ad_data = beacon_.GetAdDataByteArray(); + nearby::api::ble::BleAdvertisementData advertising_data; + advertising_data.is_extended_advertisement = false; + advertising_data.service_data.insert( + {*fast_init_uuid, + nearby::ByteArray(reinterpret_cast(ad_data.data() + 2), + ad_data.size() - 2)}); + + nearby::api::ble::AdvertiseParameters advertising_parameters{ + .tx_power_level = nearby::api::ble::TxPowerLevel::kHigh, + .is_connectable = false, + }; + advertisement_ = + ::nearby::linux::bluez::LEAdvertisement::CreateLEAdvertisement( + *adapter_->GetConnection(), advertising_data, advertising_parameters); + + try { + adv_manager_->RegisterAdvertisementSync( + advertisement_->getObject().getObjectPath(), {}); + } catch (const sdbus::Error& e) { + advertisement_.reset(); + if (error_callback) { + if (e.getName() == "org.bluez.Error.AlreadyExists") { + error_callback( + nearby::api::FastInitiationManager::Error::kResourceInUse); + } else if (e.getName() == "org.bluez.Error.NotPermitted") { + error_callback( + nearby::api::FastInitiationManager::Error::kDisabledByUser); + } else { + error_callback(nearby::api::FastInitiationManager::Error::kUnknown); + } + } + return; + } + + if (callback) { + callback(); + } +} + +void LinuxFastInitiationManager::StopAdvertising( + std::function callback) { + absl::MutexLock lock(&mutex_); + if (advertisement_ != nullptr && adv_manager_ != nullptr) { + try { + adv_manager_->UnregisterAdvertisementSync( + advertisement_->getObject().getObjectPath()); + } catch (const sdbus::Error& e) { + DBUS_LOG_METHOD_CALL_ERROR(adv_manager_.get(), + "UnregisterAdvertisementSync", e); + } + advertisement_.reset(); + } + if (callback) { + callback(); + } +} +void StopScanning(std::function callback) { + if (callback) { + callback(); + } +} +void LinuxFastInitiationManager::StartScanning( + std::function devices_discovered_callback, + std::function devices_not_discovered_callback, + std::function + error_callback) { + if (error_callback) { + error_callback( + nearby::api::FastInitiationManager::Error::kHardwareNotSupported); + } +} +} // namespace linux +} // namespace sharing +} // namespace nearby + diff --git a/sharing/linux/nearby_fast_init_manager.h b/sharing/linux/nearby_fast_init_manager.h new file mode 100644 index 00000000..b2c35573 --- /dev/null +++ b/sharing/linux/nearby_fast_init_manager.h @@ -0,0 +1,87 @@ + +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LINUX_NEARBY_SHARING_INTERNAL_API_FAST_INITIATION_MANAGER_H_ +#define LINUX_NEARBY_SHARING_INTERNAL_API_FAST_INITIATION_MANAGER_H_ + +#include + +#include "sharing/internal/api/fast_init_ble_beacon.h" +#include "sharing/internal/api/fast_initiation_manager.h" + +#include "internal/platform/implementation/linux/bluetooth_adapter.h" +#include "internal/platform/implementation/linux/bluez.h" +#include "internal/platform/implementation/linux/bluez_le_advertisement.h" + +constexpr char kFastInitServiceUuid[] = "0000fe2c-0000-1000-8000-00805f9b34fb"; + +namespace nearby { +namespace sharing { +namespace linux { + +class LinuxFastInitiationManager final + : public nearby::api::FastInitiationManager { + public: + explicit LinuxFastInitiationManager( + nearby::api::FastInitBleBeacon& beacon, + std::shared_ptr<::nearby::linux::BluetoothAdapter> bluetooth_adapter) + : beacon_(beacon), adapter_(std::move(bluetooth_adapter)) { + if (adapter_ != nullptr) { + adv_manager_ = + std::make_unique( + *adapter_->GetConnection(), *adapter_); + } + } + + void StartAdvertising( + nearby::api::FastInitBleBeacon::FastInitType type, + std::function callback, + std::function + error_callback) override ; + + void StopAdvertising(std::function callback) override; + + void StartScanning( + std::function devices_discovered_callback, + std::function devices_not_discovered_callback, + std::function + error_callback) override ; + + void StopScanning(std::function callback) override { + if (callback) { + callback(); + } + } + + bool IsAdvertising() override { + absl::MutexLock lock(&mutex_); + return advertisement_ != nullptr; + } + bool IsScanning() override { return false; } + + private: + nearby::api::FastInitBleBeacon& beacon_; + std::shared_ptr<::nearby::linux::BluetoothAdapter> adapter_; + std::unique_ptr<::nearby::linux::bluez::LEAdvertisementManager> adv_manager_; + absl::Mutex mutex_; + std::unique_ptr<::nearby::linux::bluez::LEAdvertisement> advertisement_ + ABSL_GUARDED_BY(mutex_); +}; + +} // namespace linux +} // namespace sharing +} // namespace nearby + +#endif // LINUX_NEARBY_SHARING_INTERNAL_API_FAST_INITIATION_MANAGER_H_