Files
nearby/sharing/linux/platform/linux_sharing_platform.cc
T

1382 lines
44 KiB
C++

// Copyright 2026
#include "sharing/linux/platform/linux_sharing_platform.h"
#include <ifaddrs.h>
#include <net/if.h>
#include <sys/sysinfo.h>
#include <sys/utsname.h>
#include <unistd.h>
#include <array>
#include <cstdint>
#include <cstdlib>
#include <filesystem>
#include <functional>
#include <list>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include <sdbus-c++/Error.h>
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "internal/base/file_path.h"
#include "internal/base/files.h"
#include "internal/platform/bluetooth_adapter.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/implementation/account_info.h"
#include "internal/platform/implementation/auth_status.h"
#include "internal/platform/implementation/ble.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"
#include "internal/platform/implementation/linux/dbus.h"
#include "internal/platform/implementation/platform.h"
#include "internal/platform/mac_address.h"
#include "internal/platform/task_runner_impl.h"
#include "internal/platform/uuid.h"
#include "nlohmann/json.hpp"
#include "sharing/internal/api/app_info.h"
#include "sharing/internal/api/bluetooth_adapter.h"
#include "sharing/internal/api/fast_init_ble_beacon.h"
#include "sharing/internal/api/fast_initiation_manager.h"
#include "sharing/internal/api/network_monitor.h"
#include "sharing/internal/api/preference_manager.h"
#include "sharing/internal/api/private_certificate_data.h"
#include "sharing/internal/api/public_certificate_database.h"
#include "sharing/internal/api/system_info.h"
#include "sharing/internal/public/pref_names.h"
#include "sharing/proto/rpc_resources.pb.h"
namespace nearby::sharing {
namespace {
using Json = nlohmann::json;
using ::nearby::sharing::api::PreferenceManager;
using ::nearby::sharing::api::PrivateCertificateData;
using ::nearby::sharing::api::PublicCertificateDatabase;
using ::nearby::sharing::PrefNames;
using ::nearby::sharing::proto::PublicCertificate;
using ::nearby::sharing::sync::SyncBindingPrefs;
using ::nearby::sharing::sync::SyncConfigPrefs;
constexpr absl::string_view kPreferencesFile = "nearby_sharing.json";
constexpr absl::string_view kFileSyncBindingName = "FileSync";
constexpr absl::string_view kAppFirstRunPref = "nearby_sharing.app.first_run";
constexpr absl::string_view kAppActivePref = "nearby_sharing.app.active";
constexpr char kFastInitServiceUuid[] = "0000fe2c-0000-1000-8000-00805f9b34fb";
std::optional<Uuid> GetFastInitUuid() {
return Uuid::FromString(kFastInitServiceUuid);
}
std::string GetFastInitSeed(
nearby::api::FastInitBleBeacon::FastInitType type,
const ::nearby::linux::BluetoothAdapter* adapter) {
std::string seed = absl::StrCat("linux-fast-init:", static_cast<int>(type));
if (adapter != nullptr) {
seed = absl::StrCat(seed, ":", adapter->GetMacAddress().ToString());
}
return seed;
}
std::array<uint8_t, nearby::api::FastInitBleBeacon::kSecretIdHashSize>
BuildFastInitSecretIdHash(absl::string_view seed) {
std::array<uint8_t, nearby::api::FastInitBleBeacon::kSecretIdHashSize> hash{};
const std::uint64_t value = std::hash<std::string>{}(std::string(seed));
for (size_t i = 0; i < hash.size(); ++i) {
hash[i] = static_cast<uint8_t>((value >> (i * 8)) & 0xff);
}
return hash;
}
std::array<uint8_t, nearby::api::FastInitBleBeacon::kSaltSize> BuildFastInitSalt(
absl::string_view seed) {
return {static_cast<uint8_t>(std::hash<std::string>{}(std::string(seed)) &
0xff)};
}
std::array<uint8_t, nearby::api::FastInitBleBeacon::kAdvertiseDataTotalSize>
BuildFastInitAdvertisingData(nearby::api::FastInitBleBeacon& beacon) {
std::array<uint8_t, nearby::api::FastInitBleBeacon::kAdvertiseDataTotalSize>
data{};
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<uint8_t>(beacon.GetVersion()) << 5) |
(static_cast<uint8_t>(beacon.GetType()) << 2) |
(beacon.GetUwbSupported() ? 0x02 : 0x00) |
(beacon.GetSenderCertSupported() ? 0x01 : 0x00);
data[offset++] = metadata;
data[offset++] = static_cast<uint8_t>(beacon.GetAdjustedTxPower());
for (uint8_t byte : beacon.GetUwbMetadata()) {
data[offset++] = byte;
}
for (uint8_t byte : beacon.GetUwbAddress()) {
data[offset++] = byte;
}
for (uint8_t byte : beacon.GetSalt()) {
data[offset++] = byte;
}
for (uint8_t byte : beacon.GetSecretIdHash()) {
data[offset++] = byte;
}
data[offset++] = (beacon.GetRequireBtAdvertising() ? 0x80 : 0x00) |
(beacon.GetSelfOnlyAdvertising() ? 0x40 : 0x00);
return data;
}
std::unique_ptr<::nearby::linux::BluetoothAdapter> CreateFastInitBluetoothAdapter() {
auto system_bus = ::nearby::linux::getSystemBusConnection();
auto manager = ::nearby::linux::bluez::BluezObjectManager(*system_bus);
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;
return std::make_unique<::nearby::linux::BluetoothAdapter>(system_bus,
object);
}
}
} catch (const sdbus::Error& e) {
DBUS_LOG_METHOD_CALL_ERROR(&manager, "GetManagedObjects", e);
}
LOG(ERROR) << __func__ << ": couldn't find a bluetooth adapter on this system";
return nullptr;
}
std::optional<std::string> GetLanguageCode() {
const char* lang = std::getenv("LANG");
if (lang == nullptr || *lang == '\0') {
return std::string("en");
}
std::string value(lang);
size_t dot = value.find('.');
if (dot != std::string::npos) value.resize(dot);
size_t underscore = value.find('_');
if (underscore != std::string::npos) value.resize(underscore);
if (value.empty()) {
return std::string("en");
}
return value;
}
std::string GetEnvOrDefault(const char* key, std::string fallback) {
const char* value = std::getenv(key);
if (value == nullptr || *value == '\0') {
return fallback;
}
return value;
}
FilePath BuildPathFromBase(const std::string& base,
std::initializer_list<std::string> components) {
std::filesystem::path path(base);
for (const std::string& component : components) {
path /= component;
}
return FilePath(path.string());
}
std::string GetHomeDirectory() {
return GetEnvOrDefault("HOME", "/tmp");
}
bool HasNonLoopbackInterface() {
struct ifaddrs* interfaces = nullptr;
if (getifaddrs(&interfaces) != 0) {
return false;
}
bool connected = false;
for (struct ifaddrs* current = interfaces; current != nullptr;
current = current->ifa_next) {
if (current->ifa_name == nullptr || current->ifa_flags == 0) {
continue;
}
if ((current->ifa_flags & IFF_UP) == 0 ||
(current->ifa_flags & IFF_LOOPBACK) != 0) {
continue;
}
connected = true;
break;
}
freeifaddrs(interfaces);
return connected;
}
Json ToJson(const PrivateCertificateData& certificate) {
return Json{
{PrivateCertificateData::kVisibility, certificate.visibility},
{PrivateCertificateData::kNotBefore, certificate.not_before},
{PrivateCertificateData::kNotAfter, certificate.not_after},
{PrivateCertificateData::kKeyPair, certificate.key_pair},
{PrivateCertificateData::kSecretKey, certificate.secret_key},
{PrivateCertificateData::kMetadataEncryptionKey,
certificate.metadata_encryption_key},
{PrivateCertificateData::kId, certificate.id},
{PrivateCertificateData::kUnencryptedMetadata,
certificate.unencrypted_metadata_proto},
{PrivateCertificateData::kConsumedSalts, certificate.consumed_salts},
};
}
std::optional<PrivateCertificateData> FromJson(const Json& value) {
if (!value.is_object()) {
return std::nullopt;
}
PrivateCertificateData certificate;
try {
certificate.visibility = value.at(PrivateCertificateData::kVisibility);
certificate.not_before = value.at(PrivateCertificateData::kNotBefore);
certificate.not_after = value.at(PrivateCertificateData::kNotAfter);
certificate.key_pair = value.at(PrivateCertificateData::kKeyPair);
certificate.secret_key = value.at(PrivateCertificateData::kSecretKey);
certificate.metadata_encryption_key =
value.at(PrivateCertificateData::kMetadataEncryptionKey);
certificate.id = value.at(PrivateCertificateData::kId);
certificate.unencrypted_metadata_proto =
value.at(PrivateCertificateData::kUnencryptedMetadata);
certificate.consumed_salts =
value.at(PrivateCertificateData::kConsumedSalts);
return certificate;
} catch (const Json::exception&) {
return std::nullopt;
}
}
class FailedSigninAttempt final : public SigninAttempt {
public:
std::string Start(
absl::AnyInvocable<void(AuthStatus, absl::string_view, absl::string_view,
const AccountInfo&)>
callback) override {
if (callback) {
std::move(callback)(UNSUPPORTED, "", "", AccountInfo{});
}
return {};
}
void Close() override {}
};
class LinuxAccountManager final : public AccountManager {
public:
std::optional<Account> GetCurrentAccount() override { return std::nullopt; }
std::unique_ptr<SigninAttempt> Login(absl::string_view client_id,
absl::string_view client_secret)
override {
last_client_id_ = std::string(client_id);
last_client_secret_ = std::string(client_secret);
return std::make_unique<FailedSigninAttempt>();
}
void Logout(absl::AnyInvocable<void(absl::Status)> logout_callback) override {
if (logout_callback) {
std::move(logout_callback)(absl::OkStatus());
}
}
bool GetAccessToken(
absl::AnyInvocable<void(absl::StatusOr<std::string>)> callback)
override {
if (!callback) {
return false;
}
std::move(callback)(
absl::UnavailableError("Linux account integration is not available"));
return true;
}
std::pair<std::string, std::string> GetOAuthClientCredential() override {
return {last_client_id_, last_client_secret_};
}
void AddObserver(Observer* observer) override { observers_.insert(observer); }
void RemoveObserver(Observer* observer) override {
observers_.erase(observer);
}
void SaveAccountPrefs(absl::string_view user_id, absl::string_view client_id,
absl::string_view client_secret) override {
last_client_id_ = std::string(client_id);
last_client_secret_ = std::string(client_secret);
}
private:
absl::flat_hash_set<Observer*> observers_;
std::string last_client_id_;
std::string last_client_secret_;
};
class SafeLinuxDeviceInfo final : public nearby::DeviceInfo {
public:
std::string GetOsDeviceName() const override {
char hostname[256] = {};
if (gethostname(hostname, sizeof(hostname)) == 0 && hostname[0] != '\0') {
return hostname;
}
const char* env_hostname = std::getenv("HOSTNAME");
if (env_hostname != nullptr && *env_hostname != '\0') {
return env_hostname;
}
return "Linux";
}
nearby::api::DeviceInfo::DeviceType GetDeviceType() const override {
return nearby::api::DeviceInfo::DeviceType::kLaptop;
}
nearby::api::DeviceInfo::OsType GetOsType() const override {
return nearby::api::DeviceInfo::OsType::kWindows;
}
FilePath GetDownloadPath() const override {
const char* xdg_download_dir = std::getenv("XDG_DOWNLOAD_DIR");
if (xdg_download_dir != nullptr && *xdg_download_dir != '\0') {
return FilePath(std::string(xdg_download_dir));
}
return BuildPathFromBase(GetHomeDirectory(), {"Downloads"});
}
FilePath GetAppDataPath() const override {
const std::string config_home =
GetEnvOrDefault("XDG_CONFIG_HOME",
BuildPathFromBase(GetHomeDirectory(), {".config"})
.ToString());
return BuildPathFromBase(config_home, {"Google Nearby"});
}
FilePath GetTemporaryPath() const override {
const char* runtime_dir = std::getenv("XDG_RUNTIME_DIR");
if (runtime_dir != nullptr && *runtime_dir != '\0') {
return BuildPathFromBase(runtime_dir, {"Google Nearby"});
}
const char* tmpdir = std::getenv("TMPDIR");
if (tmpdir != nullptr && *tmpdir != '\0') {
return BuildPathFromBase(tmpdir, {"Google Nearby"});
}
return BuildPathFromBase("/tmp", {"Google Nearby"});
}
FilePath GetLogPath() const override {
const std::string state_home =
GetEnvOrDefault("XDG_STATE_HOME",
BuildPathFromBase(GetHomeDirectory(),
{".local", "state"})
.ToString());
return BuildPathFromBase(state_home, {"Google Nearby", "logs"});
}
std::optional<size_t> GetAvailableDiskSpaceInBytes(
const FilePath& path) const override {
return Files::GetAvailableDiskSpaceInBytes(path);
}
bool IsScreenLocked() const override { return false; }
void RegisterScreenLockedListener(
absl::string_view listener_name,
std::function<void(nearby::api::DeviceInfo::ScreenStatus)> callback)
override {
absl::MutexLock lock(&mutex_);
screen_lock_listeners_[std::string(listener_name)] = std::move(callback);
}
void UnregisterScreenLockedListener(absl::string_view listener_name) override {
absl::MutexLock lock(&mutex_);
screen_lock_listeners_.erase(std::string(listener_name));
}
bool PreventSleep() override { return true; }
bool AllowSleep() override { return true; }
private:
mutable absl::Mutex mutex_;
absl::flat_hash_map<
std::string,
std::function<void(nearby::api::DeviceInfo::ScreenStatus)>>
screen_lock_listeners_ ABSL_GUARDED_BY(mutex_);
};
class LinuxPreferenceManager final : public PreferenceManager {
public:
LinuxPreferenceManager()
: storage_(nearby::api::ImplementationPlatform::CreatePreferencesManager(
kPreferencesFile)) {}
void SetBoolean(absl::string_view key, bool value) override {
if (storage_ != nullptr && storage_->SetBoolean(key, value)) {
NotifyPreferenceChanged(key);
}
}
void SetInteger(absl::string_view key, int value) override {
if (storage_ != nullptr && storage_->SetInteger(key, value)) {
NotifyPreferenceChanged(key);
}
}
void SetInt64(absl::string_view key, int64_t value) override {
if (storage_ != nullptr && storage_->SetInt64(key, value)) {
NotifyPreferenceChanged(key);
}
}
void SetString(absl::string_view key, absl::string_view value) override {
if (storage_ != nullptr && storage_->SetString(key, value)) {
NotifyPreferenceChanged(key);
}
}
void SetTime(absl::string_view key, absl::Time value) override {
if (storage_ != nullptr && storage_->SetTime(key, value)) {
NotifyPreferenceChanged(key);
}
}
void SetBooleanArray(absl::string_view key,
absl::Span<const bool> value) override {
if (storage_ != nullptr && storage_->SetBooleanArray(key, value)) {
NotifyPreferenceChanged(key);
}
}
void SetIntegerArray(absl::string_view key,
absl::Span<const int> value) override {
if (storage_ != nullptr && storage_->SetIntegerArray(key, value)) {
NotifyPreferenceChanged(key);
}
}
void SetInt64Array(absl::string_view key,
absl::Span<const int64_t> value) override {
if (storage_ != nullptr && storage_->SetInt64Array(key, value)) {
NotifyPreferenceChanged(key);
}
}
void SetStringArray(absl::string_view key,
absl::Span<const std::string> value) override {
if (storage_ != nullptr && storage_->SetStringArray(key, value)) {
NotifyPreferenceChanged(key);
}
}
void SetPrivateCertificateArray(
absl::string_view key, absl::Span<const PrivateCertificateData> value)
override {
Json certificates = Json::array();
for (const PrivateCertificateData& certificate : value) {
certificates.push_back(ToJson(certificate));
}
if (storage_ != nullptr && storage_->Set(key, certificates)) {
NotifyPreferenceChanged(key);
}
}
void SetCertificateExpirationArray(
absl::string_view key,
absl::Span<const std::pair<std::string, int64_t>> value) override {
Json expirations = Json::array();
for (const auto& [id, expiration] : value) {
expirations.push_back(Json{{"id", id}, {"expiration", expiration}});
}
if (storage_ != nullptr && storage_->Set(key, expirations)) {
NotifyPreferenceChanged(key);
}
}
void SetDictionaryBooleanValue(absl::string_view key,
absl::string_view dictionary_item,
bool value) override {
SetDictionaryValue(key, dictionary_item, value);
}
void SetDictionaryIntegerValue(absl::string_view key,
absl::string_view dictionary_item,
int value) override {
SetDictionaryValue(key, dictionary_item, value);
}
void SetDictionaryInt64Value(absl::string_view key,
absl::string_view dictionary_item,
int64_t value) override {
SetDictionaryValue(key, dictionary_item, value);
}
void SetDictionaryStringValue(absl::string_view key,
absl::string_view dictionary_item,
std::string value) override {
SetDictionaryValue(key, dictionary_item, std::move(value));
}
void RemoveDictionaryItem(absl::string_view key,
absl::string_view dictionary_item) override {
if (storage_ == nullptr) {
return;
}
Json dictionary = storage_->Get(key, Json::object());
if (!dictionary.is_object() || !dictionary.contains(std::string(dictionary_item))) {
return;
}
dictionary.erase(std::string(dictionary_item));
if (storage_->Set(key, dictionary)) {
NotifyPreferenceChanged(key);
}
}
void SetSyncConfigValue(absl::string_view binding_id,
const SyncConfigPrefs& value) override {
SetString(absl::StrCat(PrefNames::kSyncConfigPrefix, binding_id),
value.SerializeAsString());
}
void SetSyncBindingValue(const SyncBindingPrefs& value) override {
SetString(absl::StrCat(PrefNames::kBindingConfigPrefix, kFileSyncBindingName),
value.SerializeAsString());
}
bool GetBoolean(absl::string_view key, bool default_value) const override {
return storage_ == nullptr ? default_value
: storage_->GetBoolean(key, default_value);
}
int GetInteger(absl::string_view key, int default_value) const override {
return storage_ == nullptr ? default_value
: storage_->GetInteger(key, default_value);
}
int64_t GetInt64(absl::string_view key,
int64_t default_value) const override {
return storage_ == nullptr ? default_value
: storage_->GetInt64(key, default_value);
}
std::string GetString(absl::string_view key,
const std::string& default_value) const override {
return storage_ == nullptr ? default_value
: storage_->GetString(key, default_value);
}
absl::Time GetTime(absl::string_view key,
absl::Time default_value) const override {
return storage_ == nullptr ? default_value
: storage_->GetTime(key, default_value);
}
std::vector<bool> GetBooleanArray(
absl::string_view key,
absl::Span<const bool> default_value) const override {
return storage_ == nullptr ? std::vector<bool>(default_value.begin(),
default_value.end())
: storage_->GetBooleanArray(key, default_value);
}
std::vector<int> GetIntegerArray(
absl::string_view key, absl::Span<const int> default_value) const
override {
return storage_ == nullptr ? std::vector<int>(default_value.begin(),
default_value.end())
: storage_->GetIntegerArray(key, default_value);
}
std::vector<int64_t> GetInt64Array(
absl::string_view key, absl::Span<const int64_t> default_value) const
override {
return storage_ == nullptr
? std::vector<int64_t>(default_value.begin(), default_value.end())
: storage_->GetInt64Array(key, default_value);
}
std::vector<std::string> GetStringArray(
absl::string_view key,
absl::Span<const std::string> default_value) const override {
return storage_ == nullptr
? std::vector<std::string>(default_value.begin(),
default_value.end())
: storage_->GetStringArray(key, default_value);
}
std::vector<PrivateCertificateData> GetPrivateCertificateArray(
absl::string_view key) const override {
std::vector<PrivateCertificateData> certificates;
if (storage_ == nullptr) {
return certificates;
}
Json values = storage_->Get(key, Json::array());
if (!values.is_array()) {
return certificates;
}
for (const Json& value : values) {
std::optional<PrivateCertificateData> certificate = FromJson(value);
if (certificate.has_value()) {
certificates.push_back(*certificate);
}
}
return certificates;
}
std::vector<std::pair<std::string, int64_t>> GetCertificateExpirationArray(
absl::string_view key) const override {
std::vector<std::pair<std::string, int64_t>> expirations;
if (storage_ == nullptr) {
return expirations;
}
Json values = storage_->Get(key, Json::array());
if (!values.is_array()) {
return expirations;
}
for (const Json& value : values) {
if (!value.is_object()) {
continue;
}
try {
expirations.emplace_back(value.at("id"), value.at("expiration"));
} catch (const Json::exception&) {
}
}
return expirations;
}
std::optional<bool> GetDictionaryBooleanValue(
absl::string_view key, absl::string_view dictionary_item) const
override {
return GetDictionaryValue<bool>(key, dictionary_item);
}
std::optional<int> GetDictionaryIntegerValue(
absl::string_view key, absl::string_view dictionary_item) const
override {
return GetDictionaryValue<int>(key, dictionary_item);
}
std::optional<int64_t> GetDictionaryInt64Value(
absl::string_view key, absl::string_view dictionary_item) const
override {
return GetDictionaryValue<int64_t>(key, dictionary_item);
}
std::optional<std::string> GetDictionaryStringValue(
absl::string_view key, absl::string_view dictionary_item) const
override {
return GetDictionaryValue<std::string>(key, dictionary_item);
}
std::optional<SyncConfigPrefs> GetSyncConfigValue(
absl::string_view binding_id) const override {
std::string serialized =
GetString(absl::StrCat(PrefNames::kSyncConfigPrefix, binding_id), "");
if (serialized.empty()) {
return std::nullopt;
}
SyncConfigPrefs value;
if (!value.ParseFromString(serialized)) {
return std::nullopt;
}
return value;
}
std::optional<SyncBindingPrefs> GetSyncBindingValue() const override {
std::string serialized = GetString(
absl::StrCat(PrefNames::kBindingConfigPrefix, kFileSyncBindingName), "");
if (serialized.empty()) {
return std::nullopt;
}
SyncBindingPrefs value;
if (!value.ParseFromString(serialized)) {
return std::nullopt;
}
return value;
}
void Remove(absl::string_view key) override {
if (storage_ == nullptr) {
return;
}
storage_->Remove(key);
NotifyPreferenceChanged(key);
}
void RemoveAllSyncConfigs() override {
if (storage_ != nullptr) {
storage_->RemoveKeyPrefix(PrefNames::kSyncConfigPrefix);
}
}
void RemoveAllBindingConfigs() override {
if (storage_ != nullptr) {
storage_->RemoveKeyPrefix(PrefNames::kBindingConfigPrefix);
}
}
void AddObserver(
absl::string_view name,
std::function<void(absl::string_view pref_name)> observer) override {
absl::MutexLock lock(&mutex_);
observers_[std::string(name)] = std::move(observer);
}
void RemoveObserver(absl::string_view name) override {
absl::MutexLock lock(&mutex_);
observers_.erase(std::string(name));
}
private:
template <typename T>
void SetDictionaryValue(absl::string_view key,
absl::string_view dictionary_item, T value) {
if (storage_ == nullptr) {
return;
}
Json dictionary = storage_->Get(key, Json::object());
if (!dictionary.is_object()) {
return;
}
const std::string item(dictionary_item);
if (dictionary.contains(item) && dictionary[item] == value) {
return;
}
dictionary[item] = value;
if (storage_->Set(key, dictionary)) {
NotifyPreferenceChanged(key);
}
}
template <typename T>
std::optional<T> GetDictionaryValue(
absl::string_view key, absl::string_view dictionary_item) const {
if (storage_ == nullptr) {
return std::nullopt;
}
Json dictionary = storage_->Get(key, Json::object());
if (!dictionary.is_object()) {
return std::nullopt;
}
const std::string item(dictionary_item);
if (!dictionary.contains(item)) {
return std::nullopt;
}
try {
return dictionary.at(item).get<T>();
} catch (const Json::exception&) {
return std::nullopt;
}
}
void NotifyPreferenceChanged(absl::string_view key) {
absl::flat_hash_map<std::string, std::function<void(absl::string_view)>>
observers_copy;
{
absl::MutexLock lock(&mutex_);
observers_copy = observers_;
}
for (const auto& [name, observer] : observers_copy) {
if (observer) {
observer(key);
}
}
}
std::unique_ptr<nearby::api::PreferencesManager> storage_;
mutable absl::Mutex mutex_;
absl::flat_hash_map<std::string, std::function<void(absl::string_view)>>
observers_ ABSL_GUARDED_BY(mutex_);
};
class LinuxNetworkMonitor final : public nearby::api::NetworkMonitor {
public:
LinuxNetworkMonitor(std::function<void(bool)> lan_connected_callback,
std::function<void(bool)> internet_connected_callback)
: nearby::api::NetworkMonitor(std::move(lan_connected_callback),
std::move(internet_connected_callback)) {
const bool lan_connected = IsLanConnected();
const bool internet_connected = IsInternetConnected();
if (lan_connected_callback_) {
lan_connected_callback_(lan_connected);
}
if (internet_connected_callback_) {
internet_connected_callback_(internet_connected);
}
}
bool IsLanConnected() override { return HasNonLoopbackInterface(); }
bool IsInternetConnected() override { return HasNonLoopbackInterface(); }
};
class LinuxSystemInfo final : public nearby::api::SystemInfo {
public:
std::string GetComputerManufacturer() override { return "Unknown"; }
std::string GetComputerModel() override { return "Unknown"; }
int64_t GetComputerPhysicalMemory() override {
struct sysinfo info;
if (sysinfo(&info) != 0) {
return 0;
}
return static_cast<int64_t>(info.totalram) * info.mem_unit;
}
int GetComputerProcessorCount() override {
long processors = sysconf(_SC_NPROCESSORS_CONF);
return processors > 0 ? static_cast<int>(processors) : 1;
}
int GetComputerLogicProcessorCount() override {
long processors = sysconf(_SC_NPROCESSORS_ONLN);
return processors > 0 ? static_cast<int>(processors) : 1;
}
int GetProcessorMemoryInfo() override { return 0; }
BatteryChargeStatus QueryBatteryInfo(int& seconds, int& percent,
bool& battery_saver) override {
seconds = 0;
percent = 0;
battery_saver = false;
return BatteryChargeStatus::UNKNOWN;
}
std::string GetOsManufacturer() override { return "Linux"; }
std::string GetOsName() override { return "Linux"; }
std::string GetOsVersion() override {
struct utsname info;
if (uname(&info) != 0) {
return {};
}
return info.release;
}
std::string GetOsArchitecture() override {
struct utsname info;
if (uname(&info) != 0) {
return {};
}
return info.machine;
}
std::string GetOsLanguage() override {
return GetLanguageCode().value_or("en");
}
std::string GetProcessorManufacturer() override { return "Unknown"; }
std::string GetProcessorName() override { return "Unknown"; }
std::list<DriverInfo> GetBluetoothDriverInfos() override { return {}; }
std::list<DriverInfo> GetNetworkDriverInfos() override { return {}; }
void GetBatteryUsageReport(const FilePath& save_path) override {}
};
class LinuxAppInfo final : public nearby::api::AppInfo {
public:
explicit LinuxAppInfo(PreferenceManager& preference_manager)
: preference_manager_(preference_manager) {}
std::optional<std::string> GetAppVersion() override {
return std::string("linux");
}
std::optional<std::string> GetAppLanguage() override {
return GetLanguageCode();
}
std::optional<std::string> GetUpdateTrack() override { return std::nullopt; }
std::optional<std::string> GetAppInstallSource() override {
return std::string("manual");
}
bool GetFirstRunDone() override {
return preference_manager_.GetBoolean(kAppFirstRunPref, false);
}
bool SetFirstRunDone(bool value) override {
preference_manager_.SetBoolean(kAppFirstRunPref, value);
return true;
}
bool SetActiveFlag() override {
preference_manager_.SetInt64(kAppActivePref, absl::ToUnixNanos(absl::Now()));
return true;
}
private:
PreferenceManager& preference_manager_;
};
class LinuxFastInitBleBeacon final : public nearby::api::FastInitBleBeacon {
public:
void SerializeToByteArray() override {
SetAdDataByteArray(BuildFastInitAdvertisingData(*this));
}
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<FastInitVersion>((metadata >> 5) & 0x07));
SetType(static_cast<FastInitType>((metadata >> 2) & 0x07));
SetUwbSupported((metadata & 0x02) != 0);
SetSenderCertSupported((metadata & 0x01) != 0);
SetAdjustedTxPower(static_cast<int8_t>(data[6]));
std::array<uint8_t, kUwbMetadataSize> uwb_metadata{};
uwb_metadata[0] = data[7];
SetUwbMetadata(uwb_metadata);
std::array<uint8_t, kUwbAddressSize> uwb_address{};
for (size_t i = 0; i < uwb_address.size(); ++i) {
uwb_address[i] = data[8 + i];
}
SetUwbAddress(uwb_address);
std::array<uint8_t, kSaltSize> salt{};
salt[0] = data[16];
SetSalt(salt);
std::array<uint8_t, kSecretIdHashSize> secret_id_hash{};
for (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);
}
};
class LinuxFastInitiationManager final
: public nearby::api::FastInitiationManager {
public:
explicit LinuxFastInitiationManager(nearby::api::FastInitBleBeacon& beacon)
: beacon_(beacon), adapter_(CreateFastInitBluetoothAdapter()) {
if (adapter_ != nullptr) {
adv_manager_ =
std::make_unique<::nearby::linux::bluez::LEAdvertisementManager>(
*adapter_->GetConnection(), *adapter_);
}
}
void StartAdvertising(
nearby::api::FastInitBleBeacon::FastInitType type,
std::function<void()> callback,
std::function<void(nearby::api::FastInitiationManager::Error)>
error_callback) override {
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 = GetFastInitUuid();
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(0);
beacon_.SetUwbMetadata({});
beacon_.SetUwbAddress({});
const std::string seed = GetFastInitSeed(type, adapter_.get());
beacon_.SetSalt(BuildFastInitSalt(seed));
beacon_.SetSecretIdHash(BuildFastInitSecretIdHash(seed));
beacon_.SetRequireBtAdvertising(false);
beacon_.SetSelfOnlyAdvertising(
type == nearby::api::FastInitBleBeacon::FastInitType::kSilent);
beacon_.SerializeToByteArray();
const auto ad_data = beacon_.GetAdDataByteArray();
nearby::api::ble::BleAdvertisementData advertising_data;
advertising_data.is_extended_advertisement = true;
advertising_data.service_data.insert(
{*fast_init_uuid,
nearby::ByteArray(reinterpret_cast<const char*>(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 StopAdvertising(std::function<void()> callback) override {
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 StartScanning(
std::function<void()> devices_discovered_callback,
std::function<void()> devices_not_discovered_callback,
std::function<void(nearby::api::FastInitiationManager::Error)>
error_callback) override {
if (error_callback) {
error_callback(
nearby::api::FastInitiationManager::Error::kHardwareNotSupported);
}
}
void StopScanning(std::function<void()> 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::unique_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_);
};
class LinuxBluetoothAdapter final
: public nearby::sharing::api::BluetoothAdapter {
public:
bool IsPresent() const override { return GetAddress().IsSet(); }
bool IsPowered() const override {
return adapter_.IsValid() && adapter_.IsEnabled();
}
bool IsLowEnergySupported() const override { return true; }
bool IsScanOffloadSupported() const override { return false; }
bool IsAdvertisementOffloadSupported() const override { return false; }
bool IsExtendedAdvertisingSupported() const override { return false; }
bool IsPeripheralRoleSupported() const override { return true; }
PermissionStatus GetOsPermissionStatus() const override {
return PermissionStatus::kAllowed;
}
void SetPowered(bool powered, std::function<void()> success_callback,
std::function<void()> error_callback) override {
if (!adapter_.IsValid()) {
if (error_callback) {
error_callback();
}
return;
}
const bool success = adapter_.SetStatus(
powered ? nearby::api::BluetoothAdapter::Status::kEnabled
: nearby::api::BluetoothAdapter::Status::kDisabled);
if (success) {
if (success_callback) {
success_callback();
}
return;
}
if (error_callback) {
error_callback();
}
}
std::optional<std::string> GetAdapterId() const override {
if (!adapter_.IsValid()) {
return std::nullopt;
}
std::string name = adapter_.GetName();
if (name.empty()) {
return std::nullopt;
}
return name;
}
MacAddress GetAddress() const override {
if (!adapter_.IsValid()) {
return {};
}
return adapter_.GetAddress();
}
void AddObserver(Observer* observer) override { observers_.insert(observer); }
void RemoveObserver(Observer* observer) override {
observers_.erase(observer);
}
bool HasObserver(Observer* observer) override {
return observers_.contains(observer);
}
private:
nearby::BluetoothAdapter adapter_;
absl::flat_hash_set<Observer*> observers_;
};
class LinuxPublicCertificateDatabase final : public PublicCertificateDatabase {
public:
void Initialize(absl::AnyInvocable<void(InitStatus) &&> callback) override {
if (callback) {
std::move(callback)(InitStatus::kOk);
}
}
void LoadEntries(
absl::AnyInvocable<void(
bool, std::unique_ptr<std::vector<PublicCertificate>>) &&>
callback) override {
auto entries = std::make_unique<std::vector<PublicCertificate>>();
{
absl::MutexLock lock(&mutex_);
for (const auto& [id, certificate] : entries_) {
entries->push_back(certificate);
}
}
if (callback) {
std::move(callback)(true, std::move(entries));
}
}
void LoadCertificate(
absl::string_view id,
absl::AnyInvocable<void(bool, std::unique_ptr<PublicCertificate>) &&>
callback) override {
std::unique_ptr<PublicCertificate> certificate;
{
absl::MutexLock lock(&mutex_);
auto it = entries_.find(std::string(id));
if (it != entries_.end()) {
certificate = std::make_unique<PublicCertificate>(it->second);
}
}
if (callback) {
std::move(callback)(certificate != nullptr, std::move(certificate));
}
}
void AddCertificates(absl::Span<const PublicCertificate> certificates,
absl::AnyInvocable<void(bool) &&> callback) override {
{
absl::MutexLock lock(&mutex_);
for (const PublicCertificate& certificate : certificates) {
entries_[certificate.secret_id()] = certificate;
}
}
if (callback) {
std::move(callback)(true);
}
}
void RemoveCertificatesById(
std::vector<std::string> ids_to_remove,
absl::AnyInvocable<void(bool) &&> callback) override {
{
absl::MutexLock lock(&mutex_);
for (const std::string& id : ids_to_remove) {
entries_.erase(id);
}
}
if (callback) {
std::move(callback)(true);
}
}
void Destroy(absl::AnyInvocable<void(bool) &&> callback) override {
{
absl::MutexLock lock(&mutex_);
entries_.clear();
}
if (callback) {
std::move(callback)(true);
}
}
private:
absl::Mutex mutex_;
std::map<std::string, PublicCertificate> entries_ ABSL_GUARDED_BY(mutex_);
};
} // namespace
LinuxSharingPlatform::LinuxSharingPlatform() { Initialize({}); }
LinuxSharingPlatform::LinuxSharingPlatform(std::string device_name_override) {
Initialize(std::move(device_name_override));
}
LinuxSharingPlatform::~LinuxSharingPlatform() = default;
void LinuxSharingPlatform::Initialize(std::string device_name_override) {
preference_manager_ = std::make_unique<LinuxPreferenceManager>();
account_manager_ = std::make_unique<LinuxAccountManager>();
bluetooth_adapter_ = std::make_unique<LinuxBluetoothAdapter>();
fast_init_ble_beacon_ = std::make_unique<LinuxFastInitBleBeacon>();
fast_initiation_manager_ =
std::make_unique<LinuxFastInitiationManager>(*fast_init_ble_beacon_);
default_task_runner_ = std::make_unique<TaskRunnerImpl>(1);
device_info_ = std::make_unique<SafeLinuxDeviceInfo>();
if (!device_name_override.empty()) {
preference_manager_->SetString(PrefNames::kDeviceName, device_name_override);
}
}
void LinuxSharingPlatform::InitProductIdGetter(
absl::string_view (*product_id_getter)()) {
product_id_getter_ = product_id_getter;
}
std::unique_ptr<nearby::api::NetworkMonitor>
LinuxSharingPlatform::CreateNetworkMonitor(
std::function<void(bool)> lan_connected_callback,
std::function<void(bool)> internet_connected_callback) {
return std::make_unique<LinuxNetworkMonitor>(
std::move(lan_connected_callback), std::move(internet_connected_callback));
}
nearby::sharing::api::BluetoothAdapter&
LinuxSharingPlatform::GetBluetoothAdapter() {
return *bluetooth_adapter_;
}
nearby::api::FastInitBleBeacon& LinuxSharingPlatform::GetFastInitBleBeacon() {
return *fast_init_ble_beacon_;
}
nearby::api::FastInitiationManager&
LinuxSharingPlatform::GetFastInitiationManager() {
return *fast_initiation_manager_;
}
std::unique_ptr<nearby::api::SystemInfo>
LinuxSharingPlatform::CreateSystemInfo() {
return std::make_unique<LinuxSystemInfo>();
}
std::unique_ptr<nearby::api::AppInfo> LinuxSharingPlatform::CreateAppInfo() {
return std::make_unique<LinuxAppInfo>(*preference_manager_);
}
nearby::sharing::api::PreferenceManager&
LinuxSharingPlatform::GetPreferenceManager() {
return *preference_manager_;
}
AccountManager& LinuxSharingPlatform::GetAccountManager() {
return *account_manager_;
}
TaskRunner& LinuxSharingPlatform::GetDefaultTaskRunner() {
return *default_task_runner_;
}
nearby::DeviceInfo& LinuxSharingPlatform::GetDeviceInfo() {
return *device_info_;
}
std::unique_ptr<nearby::sharing::api::PublicCertificateDatabase>
LinuxSharingPlatform::CreatePublicCertificateDatabase(
const FilePath& database_path) {
return std::make_unique<LinuxPublicCertificateDatabase>();
}
bool LinuxSharingPlatform::UpdateFileOriginMetadata(
std::vector<FilePath>& file_paths) {
return true;
}
} // namespace nearby::sharing::linux