mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-14 14:46:12 -04:00
added nearby presence advertiser and scanner demo
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
load("@rules_cc//cc:cc_library.bzl", "cc_library")
|
||||
load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
|
||||
load("@rules_cc//cc:cc_test.bzl", "cc_test")
|
||||
|
||||
package(default_visibility = ["//:__subpackages__"])
|
||||
@@ -156,6 +157,46 @@ cc_test(
|
||||
}),
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "presence_advertiser_demo",
|
||||
srcs = ["presence_advertiser_demo.cc"],
|
||||
deps = [
|
||||
":types",
|
||||
":presence_advertiser_demo_platform_impl",
|
||||
"//internal/platform:types",
|
||||
"//presence/implementation:internal",
|
||||
"@com_google_absl//absl/status",
|
||||
"@com_google_absl//absl/synchronization",
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "presence_scanner_demo",
|
||||
srcs = ["presence_scanner_demo.cc"],
|
||||
deps = [
|
||||
":presence",
|
||||
":presence_advertiser_demo_platform_impl",
|
||||
"//internal/platform:types",
|
||||
"@com_google_absl//absl/synchronization",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "presence_advertiser_demo_platform_impl",
|
||||
alwayslink = 1,
|
||||
deps = select({
|
||||
"@platforms//os:windows": [
|
||||
"//internal/platform/implementation/windows",
|
||||
],
|
||||
"@platforms//os:linux": [
|
||||
"//internal/platform/implementation/linux:linux",
|
||||
],
|
||||
"//conditions:default": [
|
||||
"//internal/platform/implementation/g3",
|
||||
],
|
||||
}),
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "credential_test",
|
||||
size = "small",
|
||||
|
||||
@@ -0,0 +1,236 @@
|
||||
// Copyright 2026 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.
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/synchronization/notification.h"
|
||||
#include "internal/platform/system_clock.h"
|
||||
#include "internal/platform/single_thread_executor.h"
|
||||
#include "presence/broadcast_request.h"
|
||||
#include "presence/data_element.h"
|
||||
#include "presence/implementation/broadcast_manager.h"
|
||||
#include "presence/implementation/credential_manager.h"
|
||||
#include "presence/implementation/mediums/mediums.h"
|
||||
#include "presence/power_mode.h"
|
||||
|
||||
namespace {
|
||||
|
||||
absl::Notification g_shutdown;
|
||||
|
||||
void QuitHandler(int, siginfo_t*, void*) {
|
||||
if (!g_shutdown.HasBeenNotified()) g_shutdown.Notify();
|
||||
}
|
||||
|
||||
void PrintUsage(const char* program) {
|
||||
std::cerr
|
||||
<< "Usage: " << program << " [--name <device_name>] [--tx_power <int>]\n"
|
||||
<< "Example:\n"
|
||||
<< " " << program << " --name \"Linux Presence Demo\" --tx_power 20\n";
|
||||
}
|
||||
|
||||
// For a public-identity broadcast demo, credentials are never requested.
|
||||
class DemoCredentialManager : public nearby::presence::CredentialManager {
|
||||
public:
|
||||
void GenerateCredentials(
|
||||
const nearby::internal::DeviceIdentityMetaData&,
|
||||
absl::string_view,
|
||||
const std::vector<nearby::internal::IdentityType>&,
|
||||
int,
|
||||
int,
|
||||
nearby::presence::GenerateCredentialsResultCallback callback) override {
|
||||
callback.credentials_generated_cb(
|
||||
absl::UnimplementedError("Not used in public broadcast demo"));
|
||||
}
|
||||
|
||||
void UpdateRemotePublicCredentials(
|
||||
absl::string_view,
|
||||
absl::string_view,
|
||||
const std::vector<nearby::internal::SharedCredential>&,
|
||||
nearby::presence::UpdateRemotePublicCredentialsCallback callback)
|
||||
override {
|
||||
callback.credentials_updated_cb(
|
||||
absl::UnimplementedError("Not used in public broadcast demo"));
|
||||
}
|
||||
|
||||
void UpdateLocalCredential(
|
||||
const nearby::presence::CredentialSelector&,
|
||||
nearby::internal::LocalCredential,
|
||||
nearby::presence::SaveCredentialsResultCallback callback) override {
|
||||
callback.credentials_saved_cb(
|
||||
absl::UnimplementedError("Not used in public broadcast demo"));
|
||||
}
|
||||
|
||||
void GetLocalCredentials(
|
||||
const nearby::presence::CredentialSelector&,
|
||||
nearby::presence::GetLocalCredentialsResultCallback callback) override {
|
||||
callback.credentials_fetched_cb(
|
||||
absl::NotFoundError("No local credentials in demo manager"));
|
||||
}
|
||||
|
||||
void GetPublicCredentials(
|
||||
const nearby::presence::CredentialSelector&,
|
||||
nearby::presence::PublicCredentialType,
|
||||
nearby::presence::GetPublicCredentialsResultCallback callback) override {
|
||||
callback.credentials_fetched_cb(
|
||||
absl::NotFoundError("No public credentials in demo manager"));
|
||||
}
|
||||
|
||||
nearby::presence::SubscriberId SubscribeForPublicCredentials(
|
||||
const nearby::presence::CredentialSelector&,
|
||||
nearby::presence::PublicCredentialType,
|
||||
nearby::presence::GetPublicCredentialsResultCallback callback) override {
|
||||
callback.credentials_fetched_cb(
|
||||
absl::NotFoundError("No subscriptions in demo manager"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void UnsubscribeFromPublicCredentials(nearby::presence::SubscriberId) override {
|
||||
}
|
||||
|
||||
std::string DecryptDeviceIdentityMetaData(
|
||||
absl::string_view,
|
||||
absl::string_view,
|
||||
absl::string_view) override {
|
||||
return "";
|
||||
}
|
||||
|
||||
void SetDeviceIdentityMetaData(
|
||||
const nearby::internal::DeviceIdentityMetaData& device_identity_metadata,
|
||||
bool,
|
||||
absl::string_view,
|
||||
const std::vector<nearby::internal::IdentityType>&,
|
||||
int,
|
||||
int,
|
||||
nearby::presence::GenerateCredentialsResultCallback) override {
|
||||
metadata_ = device_identity_metadata;
|
||||
}
|
||||
|
||||
nearby::internal::DeviceIdentityMetaData GetDeviceIdentityMetaData()
|
||||
override {
|
||||
return metadata_;
|
||||
}
|
||||
|
||||
private:
|
||||
nearby::internal::DeviceIdentityMetaData metadata_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
// Force-link the platform SystemClock implementation from static archives.
|
||||
(void)nearby::SystemClock::ElapsedRealtime();
|
||||
|
||||
std::string device_name = "Nearby Presence Demo";
|
||||
int tx_power = 20;
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
std::string arg = argv[i];
|
||||
if ((arg == "-h") || (arg == "--help")) {
|
||||
PrintUsage(argv[0]);
|
||||
return 0;
|
||||
}
|
||||
if (arg == "--name") {
|
||||
if (i + 1 >= argc) {
|
||||
std::cerr << "Missing value for --name\n";
|
||||
return 2;
|
||||
}
|
||||
device_name = argv[++i];
|
||||
continue;
|
||||
}
|
||||
if (arg == "--tx_power") {
|
||||
if (i + 1 >= argc) {
|
||||
std::cerr << "Missing value for --tx_power\n";
|
||||
return 2;
|
||||
}
|
||||
tx_power = std::atoi(argv[++i]);
|
||||
continue;
|
||||
}
|
||||
std::cerr << "Unknown argument: " << arg << "\n";
|
||||
PrintUsage(argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
struct sigaction action {};
|
||||
action.sa_sigaction = QuitHandler;
|
||||
action.sa_flags = SA_SIGINFO;
|
||||
sigaction(SIGINT, &action, nullptr);
|
||||
sigaction(SIGTERM, &action, nullptr);
|
||||
|
||||
nearby::SingleThreadExecutor executor;
|
||||
nearby::presence::Mediums mediums;
|
||||
DemoCredentialManager credential_manager;
|
||||
nearby::presence::BroadcastManager manager(mediums, credential_manager,
|
||||
executor);
|
||||
|
||||
nearby::presence::PresenceBroadcast::BroadcastSection section = {
|
||||
.identity = nearby::internal::IdentityType::IDENTITY_TYPE_PUBLIC,
|
||||
.extended_properties = {
|
||||
nearby::presence::DataElement(
|
||||
nearby::presence::ActionBit::kNearbyShareAction),
|
||||
nearby::presence::DataElement(
|
||||
nearby::presence::ActionBit::kPresenceManagerAction),
|
||||
},
|
||||
.account_name = "",
|
||||
.manager_app_id = "",
|
||||
};
|
||||
nearby::presence::PresenceBroadcast presence_broadcast = {
|
||||
.sections = {section},
|
||||
};
|
||||
nearby::presence::BroadcastRequest request = {
|
||||
.tx_power = tx_power,
|
||||
.power_mode = nearby::presence::PowerMode::kLowLatency,
|
||||
.variant = presence_broadcast,
|
||||
};
|
||||
|
||||
absl::Status broadcast_status;
|
||||
absl::Notification started;
|
||||
auto session = manager.StartBroadcast(
|
||||
request,
|
||||
nearby::presence::BroadcastCallback{
|
||||
.start_broadcast_cb =
|
||||
[&broadcast_status, &started](absl::Status status) {
|
||||
broadcast_status = status;
|
||||
started.Notify();
|
||||
},
|
||||
});
|
||||
if (!session.ok()) {
|
||||
std::cerr << "Failed to initiate broadcast: " << session.status() << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
started.WaitForNotification();
|
||||
if (!broadcast_status.ok()) {
|
||||
std::cerr << "Broadcast start failed: " << broadcast_status << "\n";
|
||||
manager.StopBroadcast(*session);
|
||||
executor.Shutdown();
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "Nearby Presence advertising started.\n"
|
||||
<< " device_name: " << device_name << "\n"
|
||||
<< " session_id: " << *session << "\n"
|
||||
<< "Press Ctrl+C to stop.\n";
|
||||
|
||||
g_shutdown.WaitForNotification();
|
||||
manager.StopBroadcast(*session);
|
||||
executor.Shutdown();
|
||||
std::cout << "Stopped Presence advertising.\n";
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,444 @@
|
||||
// Copyright 2026 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.
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/synchronization/notification.h"
|
||||
#include "internal/platform/bluetooth_adapter.h"
|
||||
#include "internal/platform/system_clock.h"
|
||||
#include "internal/proto/credential.pb.h"
|
||||
#include "presence/power_mode.h"
|
||||
#include "presence/presence_device.h"
|
||||
#include "presence/presence_service_impl.h"
|
||||
#include "presence/scan_request.h"
|
||||
|
||||
#if defined(__linux__)
|
||||
#include "internal/platform/implementation/linux/bluetooth_adapter.h"
|
||||
#include "internal/platform/implementation/linux/bluetooth_devices.h"
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
absl::Notification g_shutdown;
|
||||
|
||||
void QuitHandler(int, siginfo_t*, void*) {
|
||||
if (!g_shutdown.HasBeenNotified()) g_shutdown.Notify();
|
||||
}
|
||||
|
||||
std::string RangeTypeToString(
|
||||
const std::string& range) {
|
||||
return range;
|
||||
}
|
||||
|
||||
std::string EstimateZone(double distance_meters) {
|
||||
constexpr double kTapDistanceThresholdMeters = 0.08;
|
||||
constexpr double kReachDistanceThresholdMeters = 0.56;
|
||||
constexpr double kShortRangeDistanceThresholdMeters = 1.26;
|
||||
constexpr double kLongRangeDistanceThresholdMeters = 3.06;
|
||||
|
||||
if (distance_meters <= kTapDistanceThresholdMeters) return "tap";
|
||||
if (distance_meters <= kReachDistanceThresholdMeters) return "reach";
|
||||
if (distance_meters <= kShortRangeDistanceThresholdMeters) return "short";
|
||||
if (distance_meters <= kLongRangeDistanceThresholdMeters) return "long";
|
||||
return "far";
|
||||
}
|
||||
|
||||
double EstimateDistanceMeters(int adjusted_rssi) {
|
||||
// Mirrors presence/fpp/fpp/src/fspl_converter.rs.
|
||||
constexpr int kTxPowerAtZeroMetersDb = -20;
|
||||
constexpr int kFsplAtOneMeterDb = 40;
|
||||
const int fspl = kTxPowerAtZeroMetersDb - adjusted_rssi;
|
||||
return std::pow(10.0, static_cast<double>(fspl - kFsplAtOneMeterDb) / 20.0);
|
||||
}
|
||||
|
||||
std::string FormatProximity(int rssi, std::optional<int8_t> tx_power) {
|
||||
const int adjusted_rssi = rssi + tx_power.value_or(0);
|
||||
const double distance_meters = EstimateDistanceMeters(adjusted_rssi);
|
||||
const std::string zone = EstimateZone(distance_meters);
|
||||
std::ostringstream stream;
|
||||
stream << std::fixed << std::setprecision(2) << "distance_m="
|
||||
<< distance_meters << " zone=" << RangeTypeToString(zone)
|
||||
<< " rssi=" << rssi;
|
||||
if (tx_power.has_value()) {
|
||||
stream << " tx_power=" << static_cast<int>(*tx_power);
|
||||
}
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::string FormatProximityUnavailable() { return "proximity=unavailable"; }
|
||||
|
||||
std::string IdentityTypeToString(nearby::internal::IdentityType identity_type) {
|
||||
switch (identity_type) {
|
||||
case nearby::internal::IDENTITY_TYPE_PUBLIC:
|
||||
return "public";
|
||||
case nearby::internal::IDENTITY_TYPE_PRIVATE_GROUP:
|
||||
return "private_group";
|
||||
case nearby::internal::IDENTITY_TYPE_CONTACTS_GROUP:
|
||||
return "contacts_group";
|
||||
default:
|
||||
return "unspecified";
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<uint64_t> ParseHexId(const std::string& hex_id) {
|
||||
if (hex_id.empty()) return std::nullopt;
|
||||
try {
|
||||
size_t parsed_size = 0;
|
||||
uint64_t id = std::stoull(hex_id, &parsed_size, 16);
|
||||
if (parsed_size != hex_id.size()) return std::nullopt;
|
||||
return id;
|
||||
} catch (...) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
class SignalProvider {
|
||||
public:
|
||||
SignalProvider() {
|
||||
#if defined(__linux__)
|
||||
nearby::BluetoothAdapter adapter;
|
||||
auto* linux_adapter =
|
||||
dynamic_cast<nearby::linux::BluetoothAdapter*>(&adapter.GetImpl());
|
||||
if (linux_adapter == nullptr) {
|
||||
return;
|
||||
}
|
||||
shared_devices_ = nearby::linux::GetSharedBluetoothDevices(
|
||||
linux_adapter->GetConnection(), linux_adapter->GetObjectPath());
|
||||
#endif
|
||||
}
|
||||
|
||||
std::optional<int> GetRssi(uint64_t device_id) const {
|
||||
#if defined(__linux__)
|
||||
if (!shared_devices_) return std::nullopt;
|
||||
auto device = shared_devices_->devices->get_device_by_unique_id(device_id);
|
||||
if (!device) return std::nullopt;
|
||||
auto rssi = device->GetRssi();
|
||||
if (!rssi.has_value()) return std::nullopt;
|
||||
return static_cast<int>(*rssi);
|
||||
#else
|
||||
(void)device_id;
|
||||
return std::nullopt;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::optional<int8_t> GetTxPower(uint64_t device_id) const {
|
||||
#if defined(__linux__)
|
||||
if (!shared_devices_) return std::nullopt;
|
||||
auto device = shared_devices_->devices->get_device_by_unique_id(device_id);
|
||||
if (!device) return std::nullopt;
|
||||
auto tx_power = device->GetTxPower();
|
||||
if (!tx_power.has_value()) return std::nullopt;
|
||||
if (*tx_power < std::numeric_limits<int8_t>::min() ||
|
||||
*tx_power > std::numeric_limits<int8_t>::max()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return static_cast<int8_t>(*tx_power);
|
||||
#else
|
||||
(void)device_id;
|
||||
return std::nullopt;
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
#if defined(__linux__)
|
||||
std::shared_ptr<nearby::linux::SharedBluetoothDevices> shared_devices_;
|
||||
#endif
|
||||
};
|
||||
|
||||
void PrintUsage(const char* program) {
|
||||
std::cerr << "Usage: " << program
|
||||
<< " [--power low|balanced|low-latency] [--screen-on-only]"
|
||||
<< " [--identities all|public|private|contacts|csv]"
|
||||
<< " [--manager-app-id <id>] [--account-name <name>]\n";
|
||||
}
|
||||
|
||||
nearby::presence::PowerMode ParsePowerMode(const std::string& arg) {
|
||||
if (arg == "low") return nearby::presence::PowerMode::kLowPower;
|
||||
if (arg == "low-latency") return nearby::presence::PowerMode::kLowLatency;
|
||||
return nearby::presence::PowerMode::kBalanced;
|
||||
}
|
||||
|
||||
bool AddIdentityTypeFromToken(
|
||||
const std::string& token,
|
||||
std::vector<nearby::internal::IdentityType>& identity_types) {
|
||||
using IdentityType = nearby::internal::IdentityType;
|
||||
IdentityType identity = IdentityType::IDENTITY_TYPE_UNSPECIFIED;
|
||||
if (token == "public") {
|
||||
identity = IdentityType::IDENTITY_TYPE_PUBLIC;
|
||||
} else if (token == "private") {
|
||||
identity = IdentityType::IDENTITY_TYPE_PRIVATE_GROUP;
|
||||
} else if (token == "contacts") {
|
||||
identity = IdentityType::IDENTITY_TYPE_CONTACTS_GROUP;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto existing : identity_types) {
|
||||
if (existing == identity) return true;
|
||||
}
|
||||
identity_types.push_back(identity);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseIdentityTypes(
|
||||
const std::string& arg,
|
||||
std::vector<nearby::internal::IdentityType>& identity_types) {
|
||||
identity_types.clear();
|
||||
if (arg == "all") {
|
||||
return true;
|
||||
}
|
||||
size_t start = 0;
|
||||
while (start < arg.size()) {
|
||||
size_t comma = arg.find(',', start);
|
||||
std::string token = arg.substr(
|
||||
start, comma == std::string::npos ? std::string::npos : comma - start);
|
||||
if (!AddIdentityTypeFromToken(token, identity_types)) return false;
|
||||
if (comma == std::string::npos) break;
|
||||
start = comma + 1;
|
||||
}
|
||||
return !identity_types.empty();
|
||||
}
|
||||
|
||||
bool ContainsIdentityType(const std::vector<nearby::internal::IdentityType>& ids,
|
||||
nearby::internal::IdentityType identity_type) {
|
||||
for (auto id : ids) {
|
||||
if (id == identity_type) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string IdentitySelectionToString(
|
||||
const std::vector<nearby::internal::IdentityType>& identity_types) {
|
||||
if (identity_types.empty()) {
|
||||
return "all";
|
||||
}
|
||||
std::string result;
|
||||
for (size_t i = 0; i < identity_types.size(); ++i) {
|
||||
if (i > 0) result += ",";
|
||||
result += IdentityTypeToString(identity_types[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
(void)nearby::SystemClock::ElapsedRealtime();
|
||||
|
||||
nearby::presence::PowerMode power_mode = nearby::presence::PowerMode::kBalanced;
|
||||
bool screen_on_only = false;
|
||||
std::string identities_arg = "all";
|
||||
std::string manager_app_id;
|
||||
std::string account_name;
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
std::string arg = argv[i];
|
||||
if ((arg == "-h") || (arg == "--help")) {
|
||||
PrintUsage(argv[0]);
|
||||
return 0;
|
||||
}
|
||||
if (arg == "--power") {
|
||||
if (i + 1 >= argc) {
|
||||
std::cerr << "Missing value for --power\n";
|
||||
return 2;
|
||||
}
|
||||
power_mode = ParsePowerMode(argv[++i]);
|
||||
continue;
|
||||
}
|
||||
if (arg == "--screen-on-only") {
|
||||
screen_on_only = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "--identities") {
|
||||
if (i + 1 >= argc) {
|
||||
std::cerr << "Missing value for --identities\n";
|
||||
return 2;
|
||||
}
|
||||
identities_arg = argv[++i];
|
||||
continue;
|
||||
}
|
||||
if (arg == "--manager-app-id") {
|
||||
if (i + 1 >= argc) {
|
||||
std::cerr << "Missing value for --manager-app-id\n";
|
||||
return 2;
|
||||
}
|
||||
manager_app_id = argv[++i];
|
||||
continue;
|
||||
}
|
||||
if (arg == "--account-name") {
|
||||
if (i + 1 >= argc) {
|
||||
std::cerr << "Missing value for --account-name\n";
|
||||
return 2;
|
||||
}
|
||||
account_name = argv[++i];
|
||||
continue;
|
||||
}
|
||||
std::cerr << "Unknown argument: " << arg << "\n";
|
||||
PrintUsage(argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
std::vector<nearby::internal::IdentityType> identity_types;
|
||||
if (!ParseIdentityTypes(identities_arg, identity_types)) {
|
||||
std::cerr << "Invalid --identities value: " << identities_arg << "\n";
|
||||
PrintUsage(argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
struct sigaction action {};
|
||||
action.sa_sigaction = QuitHandler;
|
||||
action.sa_flags = SA_SIGINFO;
|
||||
sigaction(SIGINT, &action, nullptr);
|
||||
sigaction(SIGTERM, &action, nullptr);
|
||||
|
||||
SignalProvider signal_provider;
|
||||
nearby::presence::PresenceServiceImpl service;
|
||||
auto client = service.CreatePresenceClient();
|
||||
|
||||
nearby::presence::ScanRequest request;
|
||||
request.use_ble = true;
|
||||
request.scan_type = nearby::presence::ScanType::kPresenceScan;
|
||||
request.power_mode = power_mode;
|
||||
request.scan_only_when_screen_on = screen_on_only;
|
||||
request.manager_app_id = manager_app_id;
|
||||
request.account_name = account_name;
|
||||
request.identity_types = identity_types;
|
||||
|
||||
const bool scans_non_public =
|
||||
request.identity_types.empty() ||
|
||||
ContainsIdentityType(request.identity_types,
|
||||
nearby::internal::IdentityType::
|
||||
IDENTITY_TYPE_PRIVATE_GROUP) ||
|
||||
ContainsIdentityType(request.identity_types,
|
||||
nearby::internal::IdentityType::
|
||||
IDENTITY_TYPE_CONTACTS_GROUP);
|
||||
if (scans_non_public && request.manager_app_id.empty()) {
|
||||
std::cerr << "Warning: scanning private/contact identities without "
|
||||
"--manager-app-id usually results in empty remote credentials.\n";
|
||||
}
|
||||
|
||||
absl::Status start_status;
|
||||
absl::Notification started;
|
||||
auto scan_session = client->StartScan(
|
||||
request,
|
||||
nearby::presence::ScanCallback{
|
||||
.start_scan_cb =
|
||||
[&start_status, &started](absl::Status status) {
|
||||
start_status = status;
|
||||
started.Notify();
|
||||
},
|
||||
.on_discovered_cb =
|
||||
[&signal_provider](nearby::presence::PresenceDevice device) {
|
||||
std::string remote_hex =
|
||||
device.GetDeviceIdentityMetadata().bluetooth_mac_address();
|
||||
std::optional<uint64_t> device_id = ParseHexId(remote_hex);
|
||||
if (!device_id.has_value()) {
|
||||
std::cout << "[DISCOVERED] endpoint=" << device.GetEndpointId()
|
||||
<< " identity="
|
||||
<< IdentityTypeToString(device.GetIdentityType())
|
||||
<< " remote_hex=" << remote_hex
|
||||
<< " " << FormatProximityUnavailable() << "\n";
|
||||
return;
|
||||
}
|
||||
std::optional<int> rssi = signal_provider.GetRssi(*device_id);
|
||||
std::optional<int8_t> tx_power =
|
||||
signal_provider.GetTxPower(*device_id);
|
||||
std::string proximity_desc = FormatProximityUnavailable();
|
||||
if (rssi.has_value()) {
|
||||
proximity_desc = FormatProximity(*rssi, tx_power);
|
||||
}
|
||||
std::cout << "[DISCOVERED] endpoint=" << device.GetEndpointId()
|
||||
<< " identity="
|
||||
<< IdentityTypeToString(device.GetIdentityType())
|
||||
<< " remote_hex=" << remote_hex << " "
|
||||
<< proximity_desc << "\n";
|
||||
},
|
||||
.on_updated_cb =
|
||||
[&signal_provider](nearby::presence::PresenceDevice device) {
|
||||
std::string remote_hex =
|
||||
device.GetDeviceIdentityMetadata().bluetooth_mac_address();
|
||||
std::optional<uint64_t> device_id = ParseHexId(remote_hex);
|
||||
if (!device_id.has_value()) {
|
||||
std::cout << "[UPDATED] endpoint=" << device.GetEndpointId()
|
||||
<< " identity="
|
||||
<< IdentityTypeToString(device.GetIdentityType())
|
||||
<< " remote_hex=" << remote_hex
|
||||
<< " " << FormatProximityUnavailable() << "\n";
|
||||
return;
|
||||
}
|
||||
std::optional<int> rssi = signal_provider.GetRssi(*device_id);
|
||||
std::optional<int8_t> tx_power =
|
||||
signal_provider.GetTxPower(*device_id);
|
||||
std::string proximity_desc = FormatProximityUnavailable();
|
||||
if (rssi.has_value()) {
|
||||
proximity_desc = FormatProximity(*rssi, tx_power);
|
||||
}
|
||||
std::cout << "[UPDATED] endpoint=" << device.GetEndpointId()
|
||||
<< " identity="
|
||||
<< IdentityTypeToString(device.GetIdentityType())
|
||||
<< " remote_hex=" << remote_hex << " "
|
||||
<< proximity_desc << "\n";
|
||||
},
|
||||
.on_lost_cb =
|
||||
[](nearby::presence::PresenceDevice device) {
|
||||
std::cout << "[LOST] endpoint=" << device.GetEndpointId()
|
||||
<< " remote_hex="
|
||||
<< device.GetDeviceIdentityMetadata()
|
||||
.bluetooth_mac_address()
|
||||
<< "\n";
|
||||
}});
|
||||
|
||||
if (!scan_session.ok()) {
|
||||
std::cerr << "Failed to initiate scan: " << scan_session.status() << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
started.WaitForNotification();
|
||||
if (!start_status.ok()) {
|
||||
std::cerr << "Scan start failed: " << start_status << "\n";
|
||||
client->StopScan(*scan_session);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "Presence scan started.\n"
|
||||
<< " session_id: " << *scan_session << "\n"
|
||||
<< " identities: "
|
||||
<< IdentitySelectionToString(request.identity_types) << "\n"
|
||||
<< " manager_app_id: "
|
||||
<< (request.manager_app_id.empty() ? "<empty>"
|
||||
: request.manager_app_id)
|
||||
<< "\n"
|
||||
<< " account_name: "
|
||||
<< (request.account_name.empty() ? "<empty>" : request.account_name)
|
||||
<< "\n"
|
||||
<< " mode: printing discovered/updated/lost events with proximity "
|
||||
"estimates when RSSI is available\n"
|
||||
<< "Press Ctrl+C to stop.\n";
|
||||
|
||||
g_shutdown.WaitForNotification();
|
||||
client->StopScan(*scan_session);
|
||||
std::cout << "Stopped Presence scan.\n";
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user