Internal change

PiperOrigin-RevId: 360321561
This commit is contained in:
hai007
2021-03-01 18:10:48 -08:00
committed by Copybara-Service
parent d5976e6857
commit 0c70dc054a
5 changed files with 274 additions and 18 deletions
+16 -3
View File
@@ -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,
+6
View File
@@ -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_;
+81 -12
View File
@@ -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
+33 -1
View File
@@ -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.
+138 -2
View File
@@ -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