mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-14 22:56:12 -04:00
Internal change
PiperOrigin-RevId: 360321561
This commit is contained in:
@@ -84,10 +84,19 @@ Status BasePcpHandler::StartAdvertising(ClientProxy* client,
|
||||
ConnectionOptions advertising_options = options.CompatibleOptions();
|
||||
RunOnPcpHandlerThread([this, client, &service_id, &info, &advertising_options,
|
||||
&response]() {
|
||||
// The endpoint id inside of the advertisement is different to high
|
||||
// visibility and low visibility mode. In order to decide if client should
|
||||
// grab the high visibility or low visibility id, it needs to tell client
|
||||
// which one right now, before client#StartedAdvertising.
|
||||
if (ShouldEnterHighVisibilityMode(advertising_options)) {
|
||||
client->EnterHighVisibilityMode();
|
||||
}
|
||||
|
||||
auto result =
|
||||
StartAdvertisingImpl(client, service_id, client->GetLocalEndpointId(),
|
||||
info.endpoint_info, advertising_options);
|
||||
if (!result.status.Ok()) {
|
||||
client->ExitHighVisibilityMode();
|
||||
response.Set(result.status);
|
||||
return;
|
||||
}
|
||||
@@ -103,9 +112,8 @@ Status BasePcpHandler::StartAdvertising(ClientProxy* client,
|
||||
advertising_options);
|
||||
response.Set({Status::kSuccess});
|
||||
});
|
||||
return WaitForResult(
|
||||
absl::StrCat("StartAdvertising(", service_id, ")"),
|
||||
client->GetClientId(), &response);
|
||||
return WaitForResult(absl::StrCat("StartAdvertising(", service_id, ")"),
|
||||
client->GetClientId(), &response);
|
||||
}
|
||||
|
||||
void BasePcpHandler::StopAdvertising(ClientProxy* client) {
|
||||
@@ -130,6 +138,11 @@ std::string BasePcpHandler::GetStringValueOfSupportedMediums(
|
||||
return result.str();
|
||||
}
|
||||
|
||||
bool BasePcpHandler::ShouldEnterHighVisibilityMode(
|
||||
const ConnectionOptions& options) {
|
||||
return !options.low_power && options.allowed.bluetooth;
|
||||
}
|
||||
|
||||
Status BasePcpHandler::StartDiscovery(ClientProxy* client,
|
||||
const std::string& service_id,
|
||||
const ConnectionOptions& options,
|
||||
|
||||
@@ -465,6 +465,12 @@ class BasePcpHandler : public PcpHandler,
|
||||
std::string GetStringValueOfSupportedMediums(
|
||||
const ConnectionOptions& options) const;
|
||||
|
||||
// The endpoint id in high visibility mode is stable for 30 seconds, while in
|
||||
// low visibility mode it always rotates. We assume a client is trying to
|
||||
// rotate endpoint id when the options is "low power" (3P) or "disable
|
||||
// Bluetooth classic" (1P).
|
||||
bool ShouldEnterHighVisibilityMode(const ConnectionOptions& options);
|
||||
|
||||
ScheduledExecutor alarm_executor_;
|
||||
SingleThreadExecutor serial_executor_;
|
||||
|
||||
|
||||
@@ -42,28 +42,42 @@ std::int64_t ClientProxy::GetClientId() const { return client_id_; }
|
||||
|
||||
std::string ClientProxy::GetLocalEndpointId() {
|
||||
if (local_endpoint_id_.empty()) {
|
||||
// 1) Concatenate the Random 64-bit value with "client" string.
|
||||
// 2) Compute a hash of that concatenation.
|
||||
// 3) Base64-encode that hash, to make it human-readable.
|
||||
// 4) Use only the first kEndpointIdLength bytes to make ID.
|
||||
ByteArray id_hash =
|
||||
Crypto::Sha256(absl::StrCat("client", prng_.NextInt64()));
|
||||
std::string id = Base64Utils::Encode(id_hash).substr(0, kEndpointIdLength);
|
||||
NEARBY_LOG(
|
||||
INFO,
|
||||
"ClientProxy [Local Endpoint Generated]: client=%p; endpoint_id=%s",
|
||||
this, id.c_str());
|
||||
local_endpoint_id_ = id;
|
||||
local_endpoint_id_ = GenerateLocalEndpointId();
|
||||
}
|
||||
return local_endpoint_id_;
|
||||
}
|
||||
|
||||
std::string ClientProxy::GenerateLocalEndpointId() {
|
||||
if (high_vis_mode_) {
|
||||
if (!local_high_vis_mode_cache_endpoint_id_.empty()) {
|
||||
NEARBY_LOG(INFO,
|
||||
"ClientProxy [Local Endpoint not Generated but return Cache]: "
|
||||
"client=%p; "
|
||||
"local_high_vis_mode_cache_endpoint_id_=%s",
|
||||
this, local_high_vis_mode_cache_endpoint_id_.c_str());
|
||||
return local_high_vis_mode_cache_endpoint_id_;
|
||||
}
|
||||
}
|
||||
// 1) Concatenate the Random 64-bit value with "client" string.
|
||||
// 2) Compute a hash of that concatenation.
|
||||
// 3) Base64-encode that hash, to make it human-readable.
|
||||
// 4) Use only the first kEndpointIdLength bytes to make ID.
|
||||
ByteArray id_hash = Crypto::Sha256(absl::StrCat("client", prng_.NextInt64()));
|
||||
std::string id = Base64Utils::Encode(id_hash).substr(0, kEndpointIdLength);
|
||||
NEARBY_LOG(
|
||||
INFO, "ClientProxy [Local Endpoint Generated]: client=%p; endpoint_id=%s",
|
||||
this, id.c_str());
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void ClientProxy::Reset() {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
StoppedAdvertising();
|
||||
StoppedDiscovery();
|
||||
RemoveAllEndpoints();
|
||||
ExitHighVisibilityMode();
|
||||
}
|
||||
|
||||
void ClientProxy::StartedAdvertising(
|
||||
@@ -72,18 +86,33 @@ void ClientProxy::StartedAdvertising(
|
||||
absl::Span<proto::connections::Medium> mediums,
|
||||
const ConnectionOptions& advertising_options) {
|
||||
MutexLock lock(&mutex_);
|
||||
NEARBY_LOG(INFO, "ClientProxy [StartedAdvertising]: client=%p; ", this);
|
||||
|
||||
if (high_vis_mode_) {
|
||||
local_high_vis_mode_cache_endpoint_id_ = local_endpoint_id_;
|
||||
NEARBY_LOG(
|
||||
INFO,
|
||||
"ClientProxy [High Visibility Mode Adv, Cache EndpointId]: client=%p; "
|
||||
"local_high_vis_mode_cache_endpoint_id_=%s",
|
||||
this, local_high_vis_mode_cache_endpoint_id_.c_str());
|
||||
CancelClearLocalHighVisModeCacheEndpointIdAlarm();
|
||||
}
|
||||
|
||||
advertising_info_ = {service_id, listener};
|
||||
advertising_options_ = advertising_options;
|
||||
}
|
||||
|
||||
void ClientProxy::StoppedAdvertising() {
|
||||
MutexLock lock(&mutex_);
|
||||
NEARBY_LOG(INFO, "ClientProxy [StoppedAdvertising]: client=%p; ", this);
|
||||
|
||||
if (IsAdvertising()) {
|
||||
advertising_info_.Clear();
|
||||
}
|
||||
// advertising_options_ is purposefully not cleared here.
|
||||
ResetLocalEndpointIdIfNeeded();
|
||||
|
||||
ExitHighVisibilityMode();
|
||||
}
|
||||
|
||||
bool ClientProxy::IsAdvertising() const {
|
||||
@@ -621,6 +650,46 @@ ConnectionOptions ClientProxy::GetDiscoveryOptions() const {
|
||||
return discovery_options_;
|
||||
}
|
||||
|
||||
void ClientProxy::EnterHighVisibilityMode() {
|
||||
MutexLock lock(&mutex_);
|
||||
NEARBY_LOG(INFO, "ClientProxy [EnterHighVisibilityMode]: client=%p; ", this);
|
||||
|
||||
high_vis_mode_ = true;
|
||||
}
|
||||
|
||||
void ClientProxy::ExitHighVisibilityMode() {
|
||||
MutexLock lock(&mutex_);
|
||||
NEARBY_LOG(INFO, "ClientProxy [ExitHighVisibilityMode]: client=%p; ", this);
|
||||
|
||||
high_vis_mode_ = false;
|
||||
ScheduleClearLocalHighVisModeCacheEndpointIdAlarm();
|
||||
}
|
||||
|
||||
void ClientProxy::ScheduleClearLocalHighVisModeCacheEndpointIdAlarm() {
|
||||
CancelClearLocalHighVisModeCacheEndpointIdAlarm();
|
||||
|
||||
if (local_high_vis_mode_cache_endpoint_id_.empty()) return;
|
||||
|
||||
// Schedule to clear cache high visibility mode advertisement endpoint id in
|
||||
// 30s.
|
||||
NEARBY_LOG(INFO,
|
||||
"ClientProxy [High Visibility Mode Adv, Schedule to Clear Cache "
|
||||
"EndpointId]: client=%p; "
|
||||
"local_high_vis_mode_cache_endpoint_id_=%s",
|
||||
this, local_high_vis_mode_cache_endpoint_id_.c_str());
|
||||
clear_local_high_vis_mode_cache_endpoint_id_alarm_ = CancelableAlarm(
|
||||
"clear_high_power_endpoint_id_cache",
|
||||
[this]() { local_high_vis_mode_cache_endpoint_id_.clear(); },
|
||||
kHighPowerAdvertisementEndpointIdCacheTimeout, &single_thread_executor_);
|
||||
}
|
||||
|
||||
void ClientProxy::CancelClearLocalHighVisModeCacheEndpointIdAlarm() {
|
||||
if (clear_local_high_vis_mode_cache_endpoint_id_alarm_.IsValid()) {
|
||||
clear_local_high_vis_mode_cache_endpoint_id_alarm_.Cancel();
|
||||
clear_local_high_vis_mode_cache_endpoint_id_alarm_ = CancelableAlarm();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "platform/base/byte_array.h"
|
||||
#include "platform/base/cancellation_flag.h"
|
||||
#include "platform/base/prng.h"
|
||||
#include "platform/public/cancelable_alarm.h"
|
||||
#include "platform/public/mutex.h"
|
||||
#include "proto/connections_enums.pb.h"
|
||||
// Prefer using absl:: versions of a set and a map; they tend to be more
|
||||
@@ -43,6 +44,8 @@ namespace connections {
|
||||
class ClientProxy final {
|
||||
public:
|
||||
static constexpr int kEndpointIdLength = 4;
|
||||
static constexpr absl::Duration
|
||||
kHighPowerAdvertisementEndpointIdCacheTimeout = absl::Seconds(30);
|
||||
|
||||
ClientProxy();
|
||||
~ClientProxy();
|
||||
@@ -168,6 +171,15 @@ class ClientProxy final {
|
||||
ConnectionOptions GetAdvertisingOptions() const;
|
||||
ConnectionOptions GetDiscoveryOptions() const;
|
||||
|
||||
// The endpoint id will be stable for 30 seconds after high visibility mode
|
||||
// (high power and Bluetooth Classic) advertisement stops.
|
||||
// If client re-enters high visibility mode within 30 seconds, he is going to
|
||||
// have the same endpoint id.
|
||||
void EnterHighVisibilityMode();
|
||||
// Cleans up any modifications in high visibility mode. The endpoint id always
|
||||
// rotates.
|
||||
void ExitHighVisibilityMode();
|
||||
|
||||
private:
|
||||
struct Connection {
|
||||
// Status: may be either:
|
||||
@@ -223,11 +235,31 @@ class ClientProxy final {
|
||||
Connection::Status status) const;
|
||||
std::vector<std::string> GetMatchingEndpoints(
|
||||
std::function<bool(const Connection&)> pred) const;
|
||||
std::string GenerateLocalEndpointId();
|
||||
|
||||
void ScheduleClearLocalHighVisModeCacheEndpointIdAlarm();
|
||||
void CancelClearLocalHighVisModeCacheEndpointIdAlarm();
|
||||
|
||||
mutable RecursiveMutex mutex_;
|
||||
Prng prng_;
|
||||
std::int64_t client_id_;
|
||||
std::string local_endpoint_id_;
|
||||
Prng prng_;
|
||||
// If currently is advertising in high visibility mode is true: high power and
|
||||
// Bluetooth Classic enabled. When high_visibility_mode_ is true, the endpoint
|
||||
// id is stable for 30s. When high_visibility_mode_ is false, the endpoint id
|
||||
// always rotates.
|
||||
bool high_vis_mode_{false};
|
||||
// Caches the endpoint id when it is in high visibility mode advertisement for
|
||||
// 30s. Currently, Nearby Connections keeps rotating endpoint id. The client
|
||||
// (Nearby Share) treats different endpoints as different receivers, duplicate
|
||||
// share targets for same devices occur on share sheet in this case.
|
||||
// Therefore, we remember the high visibility mode advertisement endpoint id
|
||||
// here. empty if 1) There is no high power advertisement before 2) The
|
||||
// endpoint id cached here in previous high visibility mode advertisement
|
||||
// expires.
|
||||
std::string local_high_vis_mode_cache_endpoint_id_;
|
||||
ScheduledExecutor single_thread_executor_;
|
||||
CancelableAlarm clear_local_high_vis_mode_cache_endpoint_id_alarm_;
|
||||
|
||||
// If not empty, we are currently advertising and accepting connection
|
||||
// requests for the given service_id.
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "absl/container/flat_hash_set.h"
|
||||
#include "absl/time/clock.h"
|
||||
#include "absl/time/time.h"
|
||||
#include "absl/types/span.h"
|
||||
|
||||
namespace location {
|
||||
@@ -83,16 +85,27 @@ class ClientProxyTest : public ::testing::TestWithParam<FeatureFlags> {
|
||||
std::string id;
|
||||
};
|
||||
|
||||
Endpoint StartAdvertising(ClientProxy* client, ConnectionListener listener) {
|
||||
bool ShouldEnterHighVisibilityMode(const ConnectionOptions& options) {
|
||||
return !options.low_power && options.allowed.bluetooth;
|
||||
}
|
||||
|
||||
Endpoint StartAdvertising(
|
||||
ClientProxy* client, ConnectionListener listener,
|
||||
ConnectionOptions advertising_options = ConnectionOptions{}) {
|
||||
if (ShouldEnterHighVisibilityMode(advertising_options)) {
|
||||
client->EnterHighVisibilityMode();
|
||||
}
|
||||
Endpoint endpoint{
|
||||
.info = ByteArray{"advertising endpoint name"},
|
||||
.id = client->GetLocalEndpointId(),
|
||||
};
|
||||
client->StartedAdvertising(service_id_, strategy_, listener,
|
||||
absl::MakeSpan(mediums_));
|
||||
absl::MakeSpan(mediums_), advertising_options);
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
void StopAdvertising(ClientProxy* client) { client->StoppedAdvertising(); }
|
||||
|
||||
Endpoint StartDiscovery(ClientProxy* client, DiscoveryListener listener) {
|
||||
Endpoint endpoint{
|
||||
.info = ByteArray{"discovery endpoint name"},
|
||||
@@ -479,6 +492,129 @@ TEST_F(ClientProxyTest, OnPayloadProgressChangesState) {
|
||||
OnPayloadProgress(&client2_, advertising_endpoint);
|
||||
}
|
||||
|
||||
TEST_F(ClientProxyTest,
|
||||
EndpointIdCacheWhenHighVizAdvertisementAgainImmediately) {
|
||||
ConnectionOptions advertising_options{.strategy = strategy_,
|
||||
.allowed =
|
||||
{
|
||||
.bluetooth = true,
|
||||
},
|
||||
.low_power = false};
|
||||
|
||||
Endpoint advertising_endpoint_1 = StartAdvertising(
|
||||
&client1_, advertising_connection_listener_, advertising_options);
|
||||
|
||||
StopAdvertising(&client1_);
|
||||
|
||||
// Advertise immediately.
|
||||
Endpoint advertising_endpoint_2 = StartAdvertising(
|
||||
&client1_, advertising_connection_listener_, advertising_options);
|
||||
|
||||
EXPECT_EQ(advertising_endpoint_1.id, advertising_endpoint_2.id);
|
||||
}
|
||||
|
||||
TEST_F(ClientProxyTest,
|
||||
EndpointIdRotateWhenHighVizAdvertisementAgainForAWhile) {
|
||||
ConnectionOptions advertising_options{.strategy = strategy_,
|
||||
.allowed =
|
||||
{
|
||||
.bluetooth = true,
|
||||
},
|
||||
.low_power = false};
|
||||
|
||||
Endpoint advertising_endpoint_1 = StartAdvertising(
|
||||
&client1_, advertising_connection_listener_, advertising_options);
|
||||
|
||||
StopAdvertising(&client1_);
|
||||
|
||||
// Wait to expire and then advertise.
|
||||
absl::SleepFor(ClientProxy::kHighPowerAdvertisementEndpointIdCacheTimeout +
|
||||
absl::Milliseconds(10));
|
||||
Endpoint advertising_endpoint_2 = StartAdvertising(
|
||||
&client1_, advertising_connection_listener_, advertising_options);
|
||||
|
||||
EXPECT_NE(advertising_endpoint_1.id, advertising_endpoint_2.id);
|
||||
}
|
||||
|
||||
TEST_F(ClientProxyTest,
|
||||
EndpointIdRotateWhenLowVizAdvertisementAfterHighVizAdvertisement) {
|
||||
ConnectionOptions high_viz_advertising_options{.strategy = strategy_,
|
||||
.allowed =
|
||||
{
|
||||
.bluetooth = true,
|
||||
},
|
||||
.low_power = false};
|
||||
Endpoint advertising_endpoint_1 =
|
||||
StartAdvertising(&client1_, advertising_connection_listener_,
|
||||
high_viz_advertising_options);
|
||||
|
||||
StopAdvertising(&client1_);
|
||||
|
||||
ConnectionOptions low_viz_advertising_options{.strategy = strategy_,
|
||||
.low_power = true};
|
||||
|
||||
Endpoint advertising_endpoint_2 = StartAdvertising(
|
||||
&client1_, advertising_connection_listener_, low_viz_advertising_options);
|
||||
|
||||
EXPECT_NE(advertising_endpoint_1.id, advertising_endpoint_2.id);
|
||||
}
|
||||
|
||||
// Tests endpoint_id rotates when discover.
|
||||
TEST_F(ClientProxyTest, EndpointIdRotateWhenStartDiscovery) {
|
||||
ConnectionOptions advertising_options{.strategy = strategy_,
|
||||
.allowed =
|
||||
{
|
||||
.bluetooth = true,
|
||||
},
|
||||
.low_power = false};
|
||||
|
||||
Endpoint advertising_endpoint_1 = StartAdvertising(
|
||||
&client1_, advertising_connection_listener_, advertising_options);
|
||||
|
||||
StopAdvertising(&client1_);
|
||||
StartDiscovery(&client1_, discovery_listener_);
|
||||
|
||||
Endpoint advertising_endpoint_2 = StartAdvertising(
|
||||
&client1_, advertising_connection_listener_, advertising_options);
|
||||
|
||||
EXPECT_NE(advertising_endpoint_1.id, advertising_endpoint_2.id);
|
||||
}
|
||||
|
||||
// Tests the low visibility mode with bluetooth disabled advertisment.
|
||||
TEST_F(ClientProxyTest,
|
||||
EndpointIdRotateWhenLowVizAdvertisementWithBluetoothDisabled) {
|
||||
ConnectionOptions advertising_options{.strategy = strategy_,
|
||||
.allowed = {
|
||||
.bluetooth = false,
|
||||
}};
|
||||
|
||||
Endpoint advertising_endpoint_1 = StartAdvertising(
|
||||
&client1_, advertising_connection_listener_, advertising_options);
|
||||
|
||||
StopAdvertising(&client1_);
|
||||
|
||||
Endpoint advertising_endpoint_2 = StartAdvertising(
|
||||
&client1_, advertising_connection_listener_, advertising_options);
|
||||
|
||||
EXPECT_NE(advertising_endpoint_1.id, advertising_endpoint_2.id);
|
||||
}
|
||||
|
||||
// Tests the low visibility mode with low power advertisment.
|
||||
TEST_F(ClientProxyTest, EndpointIdRotateWhenLowVizAdvertisementWithLowPower) {
|
||||
ConnectionOptions advertising_options{.strategy = strategy_,
|
||||
.low_power = true};
|
||||
|
||||
Endpoint advertising_endpoint_1 = StartAdvertising(
|
||||
&client1_, advertising_connection_listener_, advertising_options);
|
||||
|
||||
StopAdvertising(&client1_);
|
||||
|
||||
Endpoint advertising_endpoint_2 = StartAdvertising(
|
||||
&client1_, advertising_connection_listener_, advertising_options);
|
||||
|
||||
EXPECT_NE(advertising_endpoint_1.id, advertising_endpoint_2.id);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
|
||||
Reference in New Issue
Block a user