No changes to public files

PiperOrigin-RevId: 392585608
This commit is contained in:
hai007
2021-08-23 21:55:15 -07:00
committed by Copybara-Service
parent c73504e68d
commit 3e40931286
34 changed files with 2643 additions and 52 deletions
+58
View File
@@ -0,0 +1,58 @@
licenses(["notice"])
# Copyright 2020 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.
cc_library(
name = "analytics",
srcs = [
"analytics_recorder.cc",
],
hdrs = [
"analytics_recorder.h",
],
compatible_with = ["//buildenv/target:non_prod"],
visibility = [
"//core:__subpackages__",
],
deps = [
"//absl/container:btree",
"//absl/time",
"//core:core_types",
"//core:event_logger",
"//platform/base",
"//platform/public:logging",
"//platform/public:types",
"//proto:connections_enums_portable_proto",
"//third_party/nearby_connections/proto/analytics:connections_log_cc_proto",
],
)
cc_test(
name = "analytics_test",
size = "small",
srcs = [
"analytics_recorder_test.cc",
],
shard_count = 16,
deps = [
":analytics",
"//testing/base/public:gunit_main",
"//absl/time",
"//platform/impl/g3", # build_cleaner: keep
"//platform/public:logging",
"//platform/public:types",
"//proto:connections_enums_portable_proto",
"//third_party/nearby_connections/proto/analytics:connections_log_cc_proto",
],
)
+493
View File
@@ -0,0 +1,493 @@
// Copyright 2020 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 "third_party/nearby_connections/cpp/analytics/analytics_recorder.h"
#include <string>
#include <utility>
#include "absl/time/time.h"
#include "platform/public/logging.h"
#include "platform/public/mutex_lock.h"
#include "platform/public/system_clock.h"
#include "proto/analytics/connections_log.proto.h"
#include "proto/connections_enums.proto.h"
namespace location {
namespace nearby {
namespace analytics {
using ConnectionsLog = ::location::nearby::analytics::proto::ConnectionsLog;
using ClientSession = ConnectionsLog::ClientSession;
using StrategySession = ConnectionsLog::StrategySession;
using AdvertisingPhase = ConnectionsLog::AdvertisingPhase;
using DiscoveryPhase = ConnectionsLog::DiscoveryPhase;
using DiscoveredEndpoint = ConnectionsLog::DiscoveredEndpoint;
using ConnectionRequest = ConnectionsLog::ConnectionRequest;
using ConnectionRequestResponse =
::location::nearby::proto::connections::ConnectionRequestResponse;
using ConnectionStrategy =
::location::nearby::proto::connections::ConnectionsStrategy;
using EventType = ::location::nearby::proto::connections::EventType;
using Medium = ::location::nearby::proto::connections::Medium;
using SessionRole = ::location::nearby::proto::connections::SessionRole;
// These definitions are necessary before C++17.
constexpr absl::string_view AnalyticsRecorder::kVersion;
AnalyticsRecorder::AnalyticsRecorder(EventLogger *event_logger)
: event_logger_(event_logger) {
started_client_session_time_ = SystemClock::ElapsedRealtime();
NEARBY_LOGS(INFO) << "AnalyticsRecorder ctor event_logger_=" << event_logger_;
MutexLock lock(&mutex_);
if (CanRecordAnalyticsLocked("OnStartClientSession")) {
LogEvent(::location::nearby::proto::connections::START_CLIENT_SESSION);
}
}
AnalyticsRecorder::~AnalyticsRecorder() {
MutexLock lock(&mutex_);
incoming_connection_requests_.clear();
outgoing_connection_requests_.clear();
serial_executor_.Shutdown();
}
void AnalyticsRecorder::OnStartAdvertising(connections::Strategy strategy,
const std::vector<Medium> &mediums) {
MutexLock lock(&mutex_);
if (!CanRecordAnalyticsLocked("OnStartAdvertising")) {
return;
}
if (!strategy.IsValid()) {
NEARBY_LOGS(INFO) << "AnalyticsRecorder OnStartAdvertising with unknown "
"strategy, bail out.";
return;
}
// Initialize/update a StrategySession.
UpdateStrategySessionLocked(
strategy, ::location::nearby::proto::connections::ADVERTISER);
// Initialize and set a AdvertisingPhase.
started_advertising_phase_time_ = SystemClock::ElapsedRealtime();
current_advertising_phase_ = std::make_unique<AdvertisingPhase>();
absl::c_copy(mediums, RepeatedFieldBackInserter(
current_advertising_phase_->mutable_medium()));
}
void AnalyticsRecorder::OnStopAdvertising() {
MutexLock lock(&mutex_);
if (!CanRecordAnalyticsLocked("OnStopAdvertising")) {
return;
}
RecordAdvertisingPhaseDurationLocked();
}
void AnalyticsRecorder::OnStartDiscovery(connections::Strategy strategy,
const std::vector<Medium> &mediums) {
MutexLock lock(&mutex_);
if (!CanRecordAnalyticsLocked("OnStartDiscovery")) {
return;
}
if (!strategy.IsValid()) {
NEARBY_LOGS(INFO) << "AnalyticsRecorder OnStartDiscovery unknown "
"strategy enter, bail out.";
return;
}
// Initialize/update a StrategySession.
UpdateStrategySessionLocked(
strategy, ::location::nearby::proto::connections::DISCOVERER);
// Initialize and set a DiscoveryPhase.
started_discovery_phase_time_ = SystemClock::ElapsedRealtime();
current_discovery_phase_ = std::make_unique<DiscoveryPhase>();
for (auto medium : mediums) {
current_discovery_phase_->add_medium(medium);
}
}
void AnalyticsRecorder::OnStopDiscovery() {
MutexLock lock(&mutex_);
if (!CanRecordAnalyticsLocked("OnStopDiscovery")) {
return;
}
RecordDiscoveryPhaseDurationLocked();
}
void AnalyticsRecorder::OnEndpointFound(Medium medium) {
MutexLock lock(&mutex_);
if (!CanRecordAnalyticsLocked("OnEndpointFound")) {
return;
}
if (current_discovery_phase_ == nullptr) {
NEARBY_LOGS(INFO) << "Unable to record discovered endpoint due to null "
"current_discovery_phase_";
return;
}
DiscoveredEndpoint *discovered_endpoint =
current_discovery_phase_->add_discovered_endpoint();
discovered_endpoint->set_medium(medium);
discovered_endpoint->set_latency_millis(absl::ToInt64Milliseconds(
SystemClock::ElapsedRealtime() - started_discovery_phase_time_));
}
void AnalyticsRecorder::OnConnectionRequestReceived(
const std::string &remote_endpoint_id) {
MutexLock lock(&mutex_);
if (!CanRecordAnalyticsLocked("OnConnectionRequestReceived")) {
return;
}
absl::Time current_time = SystemClock::ElapsedRealtime();
auto connection_request(std::make_unique<ConnectionRequest>());
connection_request->set_duration_millis(absl::ToUnixMillis(current_time));
connection_request->set_request_delay_millis(absl::ToInt64Milliseconds(
current_time - started_advertising_phase_time_));
incoming_connection_requests_.emplace(remote_endpoint_id,
std::move(connection_request));
}
void AnalyticsRecorder::OnConnectionRequestSent(
const std::string &remote_endpoint_id) {
MutexLock lock(&mutex_);
if (!CanRecordAnalyticsLocked("OnConnectionRequestSent")) {
return;
}
absl::Time current_time = SystemClock::ElapsedRealtime();
auto connection_request(std::make_unique<ConnectionRequest>());
connection_request->set_duration_millis(absl::ToUnixMillis(current_time));
connection_request->set_request_delay_millis(
absl::ToInt64Milliseconds(current_time - started_discovery_phase_time_));
outgoing_connection_requests_.emplace(remote_endpoint_id,
std::move(connection_request));
}
void AnalyticsRecorder::OnRemoteEndpointAccepted(
const std::string &remote_endpoint_id) {
MutexLock lock(&mutex_);
if (!CanRecordAnalyticsLocked("OnRemoteEndpointAccepted")) {
return;
}
RemoteEndpointRespondedLocked(
remote_endpoint_id, ::location::nearby::proto::connections::ACCEPTED);
}
void AnalyticsRecorder::OnLocalEndpointAccepted(
const std::string &remote_endpoint_id) {
MutexLock lock(&mutex_);
if (!CanRecordAnalyticsLocked("OnLocalEndpointAccepted")) {
return;
}
LocalEndpointRespondedLocked(
remote_endpoint_id, ::location::nearby::proto::connections::ACCEPTED);
}
void AnalyticsRecorder::OnRemoteEndpointRejected(
const std::string &remote_endpoint_id) {
MutexLock lock(&mutex_);
if (!CanRecordAnalyticsLocked("OnRemoteEndpointRejected")) {
return;
}
RemoteEndpointRespondedLocked(
remote_endpoint_id, ::location::nearby::proto::connections::REJECTED);
}
void AnalyticsRecorder::OnLocalEndpointRejected(
const std::string &remote_endpoint_id) {
MutexLock lock(&mutex_);
if (!CanRecordAnalyticsLocked("OnLocalEndpointRejected")) {
return;
}
LocalEndpointRespondedLocked(
remote_endpoint_id, ::location::nearby::proto::connections::REJECTED);
}
void AnalyticsRecorder::LogSession() {
MutexLock lock(&mutex_);
if (!CanRecordAnalyticsLocked("LogSession")) {
return;
}
FinishStrategySessionLocked();
client_session_->set_duration_millis(absl::ToInt64Milliseconds(
SystemClock::ElapsedRealtime() - started_client_session_time_));
LogClientSession();
LogEvent(::location::nearby::proto::connections::STOP_CLIENT_SESSION);
session_was_logged_ = true;
}
bool AnalyticsRecorder::CanRecordAnalyticsLocked(
const std::string &method_name) {
NEARBY_LOGS(VERBOSE) << "AnalyticsRecorder LogEvent " << method_name
<< " is calling.";
if (event_logger_ == nullptr) {
NEARBY_LOGS(WARNING)
<< "AnalyticsRecorder CanRecordAnalytics Unexpected call "
<< method_name << " due to event_logger is null.";
return false;
}
if (session_was_logged_) {
NEARBY_LOGS(WARNING)
<< "AnalyticsRecorder CanRecordAnalytics Unexpected call "
<< method_name << " after session has already been logged.";
return false;
}
return true;
}
void AnalyticsRecorder::LogClientSession() {
serial_executor_.Execute(
"analytics-recorder", [this]() {
ConnectionsLog connections_log;
connections_log.set_event_type(
::location::nearby::proto::connections::CLIENT_SESSION);
connections_log.set_allocated_client_session(client_session_.release());
connections_log.set_version(kVersion);
NEARBY_LOGS(VERBOSE)
<< "AnalyticsRecorder LogClientSession connections_log="
<< connections_log.DebugString();
event_logger_->Log(connections_log);
});
}
void AnalyticsRecorder::LogEvent(EventType event_type) {
serial_executor_.Execute("analytics-recorder", [this, event_type]() {
ConnectionsLog connections_log;
connections_log.set_event_type(event_type);
connections_log.set_version(kVersion);
NEARBY_LOGS(VERBOSE) << "AnalyticsRecorder LogEvent connections_log="
<< connections_log.DebugString();
event_logger_->Log(connections_log);
});
}
void AnalyticsRecorder::UpdateStrategySessionLocked(
connections::Strategy strategy, SessionRole role) {
// If we're not switching strategies, just update the current StrategySession
// with the new role.
if (strategy == current_strategy_) {
if (absl::c_linear_search(current_strategy_session_->role(), role)) {
// We've already acted as this role before, so make sure we've finished
// recording the previous round.
switch (role) {
case ::location::nearby::proto::connections::ADVERTISER:
FinishAdvertisingPhaseLocked();
break;
case ::location::nearby::proto::connections::DISCOVERER:
FinishDiscoveryPhaseLocked();
break;
default:
break;
}
} else {
current_strategy_session_->add_role(role);
}
} else {
// Otherwise, we're starting a new Strategy.
current_strategy_ = strategy;
FinishStrategySessionLocked();
LogEvent(::location::nearby::proto::connections::START_STRATEGY_SESSION);
current_strategy_session_ = std::make_unique<StrategySession>();
started_strategy_session_time_ = SystemClock::ElapsedRealtime();
current_strategy_session_->set_strategy(
StrategyToConnectionStrategy(strategy));
current_strategy_session_->add_role(role);
}
}
void AnalyticsRecorder::RecordAdvertisingPhaseDurationLocked() const {
if (current_advertising_phase_ == nullptr) {
NEARBY_LOGS(INFO) << "Unable to record advertising phase duration due to "
"null current_advertising_phase_";
return;
}
if (!current_advertising_phase_->has_duration_millis()) {
current_advertising_phase_->set_duration_millis(absl::ToInt64Milliseconds(
SystemClock::ElapsedRealtime() - started_advertising_phase_time_));
}
}
void AnalyticsRecorder::FinishAdvertisingPhaseLocked() {
if (current_advertising_phase_ != nullptr) {
for (const auto &item : incoming_connection_requests_) {
// ConnectionRequests still pending have been ignored by the local or
// remote (or both) endpoints.
auto &connection_request = item.second;
MarkConnectionRequestIgnoredLocked(connection_request.get());
UpdateAdvertiserConnectionRequestLocked(connection_request.get());
}
RecordAdvertisingPhaseDurationLocked();
*current_strategy_session_->add_advertising_phase() =
*std::move(current_advertising_phase_);
}
incoming_connection_requests_.clear();
}
void AnalyticsRecorder::RecordDiscoveryPhaseDurationLocked() const {
if (current_discovery_phase_ == nullptr) {
NEARBY_LOGS(INFO) << "Unable to record discovery phase duration due to "
"null current_discovery_phase_";
return;
}
if (!current_discovery_phase_->has_duration_millis()) {
current_discovery_phase_->set_duration_millis(absl::ToInt64Milliseconds(
SystemClock::ElapsedRealtime() - started_discovery_phase_time_));
}
}
void AnalyticsRecorder::FinishDiscoveryPhaseLocked() {
if (current_discovery_phase_ != nullptr) {
for (const auto &item : outgoing_connection_requests_) {
// ConnectionRequests still pending have been ignored by the local or
// remote (or both) endpoints.
auto &connection_request = item.second;
MarkConnectionRequestIgnoredLocked(connection_request.get());
UpdateDiscovererConnectionRequestLocked(connection_request.get());
}
RecordDiscoveryPhaseDurationLocked();
*current_strategy_session_->add_discovery_phase() =
*std::move(current_discovery_phase_);
}
outgoing_connection_requests_.clear();
}
bool AnalyticsRecorder::UpdateAdvertiserConnectionRequestLocked(
ConnectionRequest *request) {
if (current_advertising_phase_ == nullptr) {
NEARBY_LOGS(INFO)
<< "Unable to record advertiser connection request due to null "
"current_advertising_phase_";
return false;
}
if (BothEndpointsRespondedLocked(request)) {
request->set_duration_millis(
absl::ToUnixMillis(SystemClock::ElapsedRealtime()) -
request->duration_millis());
*current_advertising_phase_->add_received_connection_request() =
*std::move(request);
return true;
}
return false;
}
bool AnalyticsRecorder::UpdateDiscovererConnectionRequestLocked(
ConnectionRequest *request) {
if (current_discovery_phase_ == nullptr) {
NEARBY_LOGS(INFO) << "Unable to record discoverer connection request due "
"to null current_discovery_phase_.";
return false;
}
if (BothEndpointsRespondedLocked(request) ||
request->local_response() ==
::location::nearby::proto::connections::NOT_SENT) {
request->set_duration_millis(
absl::ToUnixMillis(SystemClock::ElapsedRealtime()) -
request->duration_millis());
*current_discovery_phase_->add_sent_connection_request() =
*std::move(request);
return true;
}
return false;
}
bool AnalyticsRecorder::BothEndpointsRespondedLocked(
ConnectionRequest *request) {
return request->has_local_response() && request->has_remote_response();
}
void AnalyticsRecorder::LocalEndpointRespondedLocked(
const std::string &remote_endpoint_id, ConnectionRequestResponse response) {
auto out = outgoing_connection_requests_.find(remote_endpoint_id);
if (out != outgoing_connection_requests_.end()) {
ConnectionRequest *connection_request = out->second.get();
connection_request->set_local_response(response);
if (UpdateDiscovererConnectionRequestLocked(connection_request)) {
outgoing_connection_requests_.erase(out);
}
}
auto in = incoming_connection_requests_.find(remote_endpoint_id);
if (in != incoming_connection_requests_.end()) {
ConnectionRequest *connection_request = in->second.get();
connection_request->set_local_response(response);
if (UpdateAdvertiserConnectionRequestLocked(connection_request)) {
incoming_connection_requests_.erase(in);
}
}
}
void AnalyticsRecorder::RemoteEndpointRespondedLocked(
const std::string &remote_endpoint_id, ConnectionRequestResponse response) {
auto out = outgoing_connection_requests_.find(remote_endpoint_id);
if (out != outgoing_connection_requests_.end()) {
ConnectionRequest *connection_request = out->second.get();
connection_request->set_remote_response(response);
if (UpdateDiscovererConnectionRequestLocked(connection_request)) {
outgoing_connection_requests_.erase(out);
}
}
auto in = incoming_connection_requests_.find(remote_endpoint_id);
if (in != incoming_connection_requests_.end()) {
ConnectionRequest *connection_request = in->second.get();
connection_request->set_remote_response(response);
if (UpdateAdvertiserConnectionRequestLocked(connection_request)) {
incoming_connection_requests_.erase(in);
}
}
}
void AnalyticsRecorder::MarkConnectionRequestIgnoredLocked(
ConnectionRequest *request) {
if (!request->has_local_response()) {
request->set_local_response(
::location::nearby::proto::connections::IGNORED);
}
if (!request->has_remote_response()) {
request->set_remote_response(
::location::nearby::proto::connections::IGNORED);
}
}
void AnalyticsRecorder::FinishStrategySessionLocked() {
if (current_strategy_session_ != nullptr) {
FinishAdvertisingPhaseLocked();
FinishDiscoveryPhaseLocked();
// Add the StrategySession in ClientSession
current_strategy_session_->set_duration_millis(absl::ToInt64Milliseconds(
SystemClock::ElapsedRealtime() - SystemClock::ElapsedRealtime()));
*client_session_->add_strategy_session() =
*std::move(current_strategy_session_);
LogEvent(::location::nearby::proto::connections::STOP_STRATEGY_SESSION);
}
}
ConnectionStrategy AnalyticsRecorder::StrategyToConnectionStrategy(
connections::Strategy strategy) {
if (strategy == connections::Strategy::kP2pCluster) {
return ::location::nearby::proto::connections::P2P_CLUSTER;
}
if (strategy == connections::Strategy::kP2pStar) {
return ::location::nearby::proto::connections::P2P_STAR;
}
if (strategy == connections::Strategy::kP2pPointToPoint) {
return ::location::nearby::proto::connections::P2P_POINT_TO_POINT;
}
return ::location::nearby::proto::connections::UNKNOWN_STRATEGY;
}
} // namespace analytics
} // namespace nearby
} // namespace location
+170
View File
@@ -0,0 +1,170 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef ANALYTICS_ANALYTICS_RECORDER_H_
#define ANALYTICS_ANALYTICS_RECORDER_H_
#include <string>
#include "absl/container/btree_map.h"
#include "absl/time/time.h"
#include "core/event_logger.h"
#include "core/strategy.h"
#include "platform/public/mutex.h"
#include "platform/public/single_thread_executor.h"
#include "proto/analytics/connections_log.proto.h"
#include "proto/connections_enums.proto.h"
namespace location {
namespace nearby {
namespace analytics {
class AnalyticsRecorder {
public:
static constexpr absl::string_view kVersion = "v1.0.0";
explicit AnalyticsRecorder(EventLogger *event_logger);
virtual ~AnalyticsRecorder();
// Advertising phase
void OnStartAdvertising(
connections::Strategy strategy,
const std::vector<::location::nearby::proto::connections::Medium>
&mediums) ABSL_LOCKS_EXCLUDED(mutex_);
void OnStopAdvertising() ABSL_LOCKS_EXCLUDED(mutex_);
// Discovery phase
void OnStartDiscovery(
connections::Strategy strategy,
const std::vector<::location::nearby::proto::connections::Medium>
&mediums) ABSL_LOCKS_EXCLUDED(mutex_);
void OnStopDiscovery() ABSL_LOCKS_EXCLUDED(mutex_);
void OnEndpointFound(::location::nearby::proto::connections::Medium medium)
ABSL_LOCKS_EXCLUDED(mutex_);
// Connection request
void OnConnectionRequestReceived(const std::string &remote_endpoint_id)
ABSL_LOCKS_EXCLUDED(mutex_);
void OnConnectionRequestSent(const std::string &remote_endpoint_id)
ABSL_LOCKS_EXCLUDED(mutex_);
void OnRemoteEndpointAccepted(const std::string &remote_endpoint_id)
ABSL_LOCKS_EXCLUDED(mutex_);
void OnLocalEndpointAccepted(const std::string &remote_endpoint_id)
ABSL_LOCKS_EXCLUDED(mutex_);
void OnRemoteEndpointRejected(const std::string &remote_endpoint_id)
ABSL_LOCKS_EXCLUDED(mutex_);
void OnLocalEndpointRejected(const std::string &remote_endpoint_id)
ABSL_LOCKS_EXCLUDED(mutex_);
// Invokes event_logger_.Log() at the end of life of client. Log action is
// called in a separate thread to allow synchronous potentially lengthy
// execution.
void LogSession() ABSL_LOCKS_EXCLUDED(mutex_);
private:
bool CanRecordAnalyticsLocked(const std::string &method_name)
ABSL_SHARED_LOCKS_REQUIRED(mutex_);
// Callbacks the ConnectionsLog proto byte array data to the EventLogger with
// ClientSession sub-proto.
void LogClientSession();
// Callbacks the ConnectionsLog proto byte array data to the EventLogger.
void LogEvent(::location::nearby::proto::connections::EventType event_type);
void UpdateStrategySessionLocked(
connections::Strategy strategy,
::location::nearby::proto::connections::SessionRole role)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
void RecordAdvertisingPhaseDurationLocked() const
ABSL_SHARED_LOCKS_REQUIRED(mutex_);
void FinishAdvertisingPhaseLocked() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
void RecordDiscoveryPhaseDurationLocked() const
ABSL_SHARED_LOCKS_REQUIRED(mutex_);
void FinishDiscoveryPhaseLocked() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
bool UpdateAdvertiserConnectionRequestLocked(
::location::nearby::analytics::proto::ConnectionsLog::ConnectionRequest
*request) ABSL_SHARED_LOCKS_REQUIRED(mutex_);
bool UpdateDiscovererConnectionRequestLocked(
::location::nearby::analytics::proto::ConnectionsLog::ConnectionRequest
*request) ABSL_SHARED_LOCKS_REQUIRED(mutex_);
bool BothEndpointsRespondedLocked(
::location::nearby::analytics::proto::ConnectionsLog::ConnectionRequest
*request) ABSL_SHARED_LOCKS_REQUIRED(mutex_);
void LocalEndpointRespondedLocked(
const std::string &remote_endpoint_id,
::location::nearby::proto::connections::ConnectionRequestResponse
response) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
void RemoteEndpointRespondedLocked(
const std::string &remote_endpoint_id,
::location::nearby::proto::connections::ConnectionRequestResponse
response) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
void MarkConnectionRequestIgnoredLocked(
::location::nearby::analytics::proto::ConnectionsLog::ConnectionRequest
*request) ABSL_SHARED_LOCKS_REQUIRED(mutex_);
void FinishStrategySessionLocked() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
::location::nearby::proto::connections::ConnectionsStrategy
StrategyToConnectionStrategy(connections::Strategy strategy);
// Not owned by AnalyticsRecorder. Pointer must refer to a valid object
// that outlives the one constructed.
EventLogger *event_logger_;
SingleThreadExecutor serial_executor_;
// Protects all sub-protos reading and writing in ConnectionLog.
Mutex mutex_;
// ClientSession
std::unique_ptr<
::location::nearby::analytics::proto::ConnectionsLog::ClientSession>
client_session_ = std::make_unique<::location::nearby::analytics::proto::
ConnectionsLog::ClientSession>();
absl::Time started_client_session_time_;
bool session_was_logged_ ABSL_GUARDED_BY(mutex_) = false;
// Current StrategySession
connections::Strategy current_strategy_ ABSL_GUARDED_BY(mutex_) =
connections::Strategy::kNone;
std::unique_ptr<
::location::nearby::analytics::proto::ConnectionsLog::StrategySession>
current_strategy_session_ ABSL_GUARDED_BY(mutex_);
absl::Time started_strategy_session_time_ ABSL_GUARDED_BY(mutex_);
// Current AdvertisingPhase
std::unique_ptr<
::location::nearby::analytics::proto::ConnectionsLog::AdvertisingPhase>
current_advertising_phase_;
absl::Time started_advertising_phase_time_;
// Current DiscoveryPhase
std::unique_ptr<
::location::nearby::analytics::proto::ConnectionsLog::DiscoveryPhase>
current_discovery_phase_;
absl::Time started_discovery_phase_time_;
absl::btree_map<std::string,
std::unique_ptr<::location::nearby::analytics::proto::
ConnectionsLog::ConnectionRequest>>
incoming_connection_requests_ ABSL_GUARDED_BY(mutex_);
absl::btree_map<std::string,
std::unique_ptr<::location::nearby::analytics::proto::
ConnectionsLog::ConnectionRequest>>
outgoing_connection_requests_ ABSL_GUARDED_BY(mutex_);
};
} // namespace analytics
} // namespace nearby
} // namespace location
#endif // ANALYTICS_ANALYTICS_RECORDER_H_
+464
View File
@@ -0,0 +1,464 @@
// Copyright 2020 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 "third_party/nearby_connections/cpp/analytics/analytics_recorder.h"
#include <string>
#include <utility>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/time/time.h"
#include "platform/public/count_down_latch.h"
#include "platform/public/logging.h"
#include "proto/analytics/connections_log.proto.h"
#include "proto/connections_enums.proto.h"
namespace location {
namespace nearby {
namespace analytics {
namespace {
using ::location::nearby::proto::connections::ACCEPTED;
using ::location::nearby::proto::connections::BLE;
using ::location::nearby::proto::connections::BLUETOOTH;
using ::location::nearby::proto::connections::CLIENT_SESSION;
using ::location::nearby::proto::connections::ConnectionRequestResponse;
using ::location::nearby::proto::connections::EventType;
using ::location::nearby::proto::connections::IGNORED;
using ::location::nearby::proto::connections::Medium;
using ::location::nearby::proto::connections::REJECTED;
using ::location::nearby::proto::connections::START_STRATEGY_SESSION;
using ::location::nearby::proto::connections::STOP_CLIENT_SESSION;
using ::location::nearby::proto::connections::STOP_STRATEGY_SESSION;
using ::testing::Contains;
using ::testing::ElementsAre;
using ::testing::UnorderedElementsAreArray;
using ::location::nearby::analytics::proto::ConnectionsLog;
using ClientSession =
::location::nearby::analytics::proto::ConnectionsLog::ClientSession;
using StrategySession =
::location::nearby::analytics::proto::ConnectionsLog::StrategySession;
using AdvertisingPhase =
::location::nearby::analytics::proto::ConnectionsLog::AdvertisingPhase;
using DiscoveryPhase =
::location::nearby::analytics::proto::ConnectionsLog::DiscoveryPhase;
using DiscoveredEndpoint =
::location::nearby::analytics::proto::ConnectionsLog::DiscoveredEndpoint;
using ConnectionRequest =
::location::nearby::analytics::proto::ConnectionsLog::ConnectionRequest;
constexpr absl::Duration kDefaultTimeout = absl::Milliseconds(1000);
class FakeEventLogger : public EventLogger {
public:
explicit FakeEventLogger(CountDownLatch& client_session_done_latch)
: client_session_done_latch_(client_session_done_latch) {}
void Log(const ConnectionsLog& connections_log) override {
EventType event_type = connections_log.event_type();
logged_event_types_.push_back(event_type);
if (event_type == CLIENT_SESSION) {
logged_client_session_count_++;
logged_client_session_ = connections_log.client_session();
}
if (event_type == STOP_CLIENT_SESSION) {
client_session_done_latch_.CountDown();
}
}
int GetLoggedClientSessionCount() const {
return logged_client_session_count_;
}
const ClientSession& GetLoggedClientSession() {
return logged_client_session_;
}
std::vector<EventType> GetLoggedEventTypes() { return logged_event_types_; }
private:
int logged_client_session_count_ = 0;
CountDownLatch& client_session_done_latch_;
ClientSession logged_client_session_;
std::vector<EventType> logged_event_types_;
};
TEST(AnalyticsRecorderTest, SessionOnlyLoggedOnceWorks) {
CountDownLatch client_session_done_latch(1);
FakeEventLogger event_logger(client_session_done_latch);
AnalyticsRecorder analytics_recorder(&event_logger);
analytics_recorder.LogSession();
analytics_recorder.LogSession();
analytics_recorder.LogSession();
analytics_recorder.LogSession();
EXPECT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result());
// Only called once.
EXPECT_EQ(event_logger.GetLoggedClientSessionCount(), 1);
}
TEST(AnalyticsRecorderTest, SetFieldsCorrectlyForNestedAdvertisingCalls) {
connections::Strategy strategy = connections::Strategy::kP2pStar;
std::vector<Medium> mediums = {BLE, BLUETOOTH};
CountDownLatch client_session_done_latch(1);
FakeEventLogger event_logger(client_session_done_latch);
AnalyticsRecorder analytics_recorder(&event_logger);
analytics_recorder.OnStartAdvertising(strategy, mediums);
analytics_recorder.OnStopAdvertising();
analytics_recorder.OnStartAdvertising(strategy, {BLUETOOTH});
analytics_recorder.LogSession();
EXPECT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result());
const ClientSession& client_session = event_logger.GetLoggedClientSession();
ASSERT_EQ(client_session.strategy_session_size(), 1);
ASSERT_EQ(client_session.strategy_session(0).advertising_phase_size(), 2);
EXPECT_THAT(client_session.strategy_session(0).advertising_phase(0).medium(),
UnorderedElementsAreArray(mediums));
EXPECT_THAT(client_session.strategy_session(0).advertising_phase(1).medium(),
ElementsAre(BLUETOOTH));
}
TEST(AnalyticsRecorderTest, SetFieldsCorrectlyForNestedDiscoveryCalls) {
connections::Strategy strategy = connections::Strategy::kP2pStar;
std::vector<Medium> mediums = {BLE, BLUETOOTH};
CountDownLatch client_session_done_latch(1);
FakeEventLogger event_logger(client_session_done_latch);
AnalyticsRecorder analytics_recorder(&event_logger);
analytics_recorder.OnStartDiscovery(strategy, mediums);
analytics_recorder.OnStopDiscovery();
analytics_recorder.OnEndpointFound(BLUETOOTH);
analytics_recorder.OnEndpointFound(BLE);
analytics_recorder.OnStartDiscovery(strategy, {BLUETOOTH});
analytics_recorder.LogSession();
EXPECT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result());
const ClientSession& client_session = event_logger.GetLoggedClientSession();
EXPECT_EQ(client_session.strategy_session_size(), 1);
EXPECT_EQ(client_session.strategy_session(0).discovery_phase_size(), 2);
EXPECT_THAT(client_session.strategy_session(0).discovery_phase(0).medium(),
UnorderedElementsAreArray(mediums));
EXPECT_EQ(client_session.strategy_session(0)
.discovery_phase(0)
.discovered_endpoint_size(),
2);
EXPECT_EQ(client_session.strategy_session(0)
.discovery_phase(0)
.discovered_endpoint(0)
.medium(),
BLUETOOTH);
EXPECT_EQ(client_session.strategy_session(0)
.discovery_phase(0)
.discovered_endpoint(1)
.medium(),
BLE);
EXPECT_THAT(client_session.strategy_session(0).discovery_phase(1).medium(),
ElementsAre(BLUETOOTH));
}
TEST(AnalyticsRecorderTest,
OneStrategySessionForMultipleRoundsOfDiscoveryAdvertising) {
connections::Strategy strategy = connections::Strategy::kP2pStar;
std::vector<Medium> mediums = {BLE, BLUETOOTH};
CountDownLatch client_session_done_latch(1);
FakeEventLogger event_logger(client_session_done_latch);
AnalyticsRecorder analytics_recorder(&event_logger);
analytics_recorder.OnStartAdvertising(strategy, mediums);
analytics_recorder.OnStartDiscovery(strategy, mediums);
analytics_recorder.OnStopAdvertising();
analytics_recorder.OnStopDiscovery();
analytics_recorder.OnStartAdvertising(strategy, mediums);
analytics_recorder.OnStopAdvertising();
analytics_recorder.OnStartDiscovery(strategy, mediums);
analytics_recorder.OnStopDiscovery();
analytics_recorder.OnStartDiscovery(strategy, mediums);
analytics_recorder.OnStartAdvertising(strategy, mediums);
analytics_recorder.OnStopDiscovery();
analytics_recorder.OnStopAdvertising();
analytics_recorder.LogSession();
EXPECT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result());
std::vector<EventType> event_types = event_logger.GetLoggedEventTypes();
EXPECT_THAT(event_types, Contains(START_STRATEGY_SESSION).Times(1));
EXPECT_THAT(event_types, Contains(STOP_STRATEGY_SESSION).Times(1));
const ClientSession& client_session = event_logger.GetLoggedClientSession();
EXPECT_EQ(client_session.strategy_session_size(), 1);
EXPECT_EQ(client_session.strategy_session(0).advertising_phase_size(), 3);
EXPECT_EQ(client_session.strategy_session(0).discovery_phase_size(), 3);
}
TEST(AnalyticsRecorderTest, AdvertiserConnectionRequestsWorks) {
connections::Strategy strategy = connections::Strategy::kP2pStar;
std::vector<Medium> mediums = {BLE, BLUETOOTH};
std::string endpoint_id_0("endpoint_id_0");
std::string endpoint_id_1("endpoint_id_1");
std::string endpoint_id_2("endpoint_id_2");
std::string endpoint_id_3("endpoint_id_3");
CountDownLatch client_session_done_latch(1);
FakeEventLogger event_logger(client_session_done_latch);
AnalyticsRecorder analytics_recorder(&event_logger);
analytics_recorder.OnStartAdvertising(strategy, mediums);
analytics_recorder.OnConnectionRequestReceived(endpoint_id_0);
analytics_recorder.OnLocalEndpointAccepted(endpoint_id_0);
analytics_recorder.OnRemoteEndpointAccepted(endpoint_id_0);
analytics_recorder.OnConnectionRequestReceived(endpoint_id_1);
analytics_recorder.OnLocalEndpointAccepted(endpoint_id_1);
analytics_recorder.OnRemoteEndpointRejected(endpoint_id_1);
analytics_recorder.OnConnectionRequestReceived(endpoint_id_2);
analytics_recorder.OnLocalEndpointRejected(endpoint_id_2);
analytics_recorder.OnRemoteEndpointAccepted(endpoint_id_2);
analytics_recorder.OnConnectionRequestReceived(endpoint_id_3);
analytics_recorder.OnLocalEndpointRejected(endpoint_id_3);
analytics_recorder.OnRemoteEndpointRejected(endpoint_id_3);
analytics_recorder.LogSession();
EXPECT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result());
const ClientSession& client_session = event_logger.GetLoggedClientSession();
StrategySession strategy_session = client_session.strategy_session(0);
EXPECT_EQ(strategy_session.advertising_phase_size(), 1);
EXPECT_EQ(
strategy_session.advertising_phase(0).received_connection_request_size(),
4);
EXPECT_EQ(strategy_session.advertising_phase(0)
.received_connection_request(0)
.local_response(),
ACCEPTED);
EXPECT_EQ(strategy_session.advertising_phase(0)
.received_connection_request(0)
.remote_response(),
ACCEPTED);
EXPECT_EQ(strategy_session.advertising_phase(0)
.received_connection_request(1)
.local_response(),
ACCEPTED);
EXPECT_EQ(strategy_session.advertising_phase(0)
.received_connection_request(1)
.remote_response(),
REJECTED);
EXPECT_EQ(strategy_session.advertising_phase(0)
.received_connection_request(2)
.local_response(),
REJECTED);
EXPECT_EQ(strategy_session.advertising_phase(0)
.received_connection_request(2)
.remote_response(),
ACCEPTED);
EXPECT_EQ(strategy_session.advertising_phase(0)
.received_connection_request(3)
.local_response(),
REJECTED);
EXPECT_EQ(strategy_session.advertising_phase(0)
.received_connection_request(3)
.remote_response(),
REJECTED);
}
TEST(AnalyticsRecorderTest, DiscoveryConnectionRequestsWorks) {
connections::Strategy strategy = connections::Strategy::kP2pStar;
std::vector<Medium> mediums = {BLE, BLUETOOTH};
std::string endpoint_id_0("endpoint_id_0");
std::string endpoint_id_1("endpoint_id_1");
std::string endpoint_id_2("endpoint_id_2");
std::string endpoint_id_3("endpoint_id_3");
CountDownLatch client_session_done_latch(1);
FakeEventLogger event_logger(client_session_done_latch);
AnalyticsRecorder analytics_recorder(&event_logger);
analytics_recorder.OnStartDiscovery(strategy, mediums);
analytics_recorder.OnConnectionRequestSent(endpoint_id_0);
analytics_recorder.OnLocalEndpointAccepted(endpoint_id_0);
analytics_recorder.OnRemoteEndpointAccepted(endpoint_id_0);
analytics_recorder.OnConnectionRequestSent(endpoint_id_1);
analytics_recorder.OnLocalEndpointAccepted(endpoint_id_1);
analytics_recorder.OnRemoteEndpointRejected(endpoint_id_1);
analytics_recorder.OnConnectionRequestSent(endpoint_id_2);
analytics_recorder.OnLocalEndpointRejected(endpoint_id_2);
analytics_recorder.OnRemoteEndpointAccepted(endpoint_id_2);
analytics_recorder.OnConnectionRequestSent(endpoint_id_3);
analytics_recorder.OnLocalEndpointRejected(endpoint_id_3);
analytics_recorder.OnRemoteEndpointRejected(endpoint_id_3);
analytics_recorder.LogSession();
EXPECT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result());
const ClientSession& client_session = event_logger.GetLoggedClientSession();
StrategySession strategy_session = client_session.strategy_session(0);
EXPECT_EQ(strategy_session.discovery_phase_size(), 1);
EXPECT_EQ(strategy_session.discovery_phase(0).sent_connection_request_size(),
4);
auto& sent_connection_request_0 =
strategy_session.discovery_phase(0).sent_connection_request(0);
EXPECT_EQ(sent_connection_request_0.local_response(), ACCEPTED);
EXPECT_EQ(sent_connection_request_0.remote_response(), ACCEPTED);
auto& sent_connection_request_1 =
strategy_session.discovery_phase(0).sent_connection_request(1);
EXPECT_EQ(sent_connection_request_1.local_response(), ACCEPTED);
EXPECT_EQ(sent_connection_request_1.remote_response(), REJECTED);
auto& sent_connection_request_2 =
strategy_session.discovery_phase(0).sent_connection_request(2);
EXPECT_EQ(sent_connection_request_2.local_response(), REJECTED);
EXPECT_EQ(sent_connection_request_2.remote_response(), ACCEPTED);
auto& sent_connection_request_3 =
strategy_session.discovery_phase(0).sent_connection_request(3);
EXPECT_EQ(sent_connection_request_3.local_response(), REJECTED);
EXPECT_EQ(sent_connection_request_3.remote_response(), REJECTED);
}
TEST(AnalyticsRecorderTest,
AdvertiserUnfinishedConnectionRequestsIncludedAsIgnored) {
connections::Strategy strategy = connections::Strategy::kP2pStar;
std::vector<Medium> mediums = {BLE, BLUETOOTH};
std::string endpoint_id_0("endpoint_id_0");
std::string endpoint_id_1("endpoint_id_1");
std::string endpoint_id_2("endpoint_id_2");
CountDownLatch client_session_done_latch(1);
FakeEventLogger event_logger(client_session_done_latch);
AnalyticsRecorder analytics_recorder(&event_logger);
analytics_recorder.OnStartAdvertising(strategy, mediums);
// Ignored by local.
analytics_recorder.OnConnectionRequestReceived(endpoint_id_0);
analytics_recorder.OnRemoteEndpointAccepted(endpoint_id_0);
// Ignored by remote.
analytics_recorder.OnConnectionRequestReceived(endpoint_id_1);
analytics_recorder.OnLocalEndpointAccepted(endpoint_id_1);
// Ignored by both.
analytics_recorder.OnConnectionRequestReceived(endpoint_id_2);
analytics_recorder.LogSession();
EXPECT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result());
const ClientSession& client_session = event_logger.GetLoggedClientSession();
StrategySession strategy_session = client_session.strategy_session(0);
EXPECT_EQ(strategy_session.advertising_phase_size(), 1);
EXPECT_EQ(
strategy_session.advertising_phase(0).received_connection_request_size(),
3);
EXPECT_EQ(strategy_session.advertising_phase(0)
.received_connection_request(0)
.local_response(),
IGNORED);
EXPECT_EQ(strategy_session.advertising_phase(0)
.received_connection_request(0)
.remote_response(),
ACCEPTED);
EXPECT_EQ(strategy_session.advertising_phase(0)
.received_connection_request(1)
.local_response(),
ACCEPTED);
EXPECT_EQ(strategy_session.advertising_phase(0)
.received_connection_request(1)
.remote_response(),
IGNORED);
EXPECT_EQ(strategy_session.advertising_phase(0)
.received_connection_request(2)
.local_response(),
IGNORED);
EXPECT_EQ(strategy_session.advertising_phase(0)
.received_connection_request(2)
.remote_response(),
IGNORED);
}
TEST(AnalyticsRecorderTest,
DiscovererUnfinishedConnectionRequestsIncludedAsIgnored) {
connections::Strategy strategy = connections::Strategy::kP2pStar;
std::vector<Medium> mediums = {BLE, BLUETOOTH};
std::string endpoint_id_0("endpoint_id_0");
std::string endpoint_id_1("endpoint_id_1");
std::string endpoint_id_2("endpoint_id_2");
CountDownLatch client_session_done_latch(1);
FakeEventLogger event_logger(client_session_done_latch);
AnalyticsRecorder analytics_recorder(&event_logger);
analytics_recorder.OnStartDiscovery(strategy, mediums);
// Ignored by local.
analytics_recorder.OnConnectionRequestSent(endpoint_id_0);
analytics_recorder.OnRemoteEndpointAccepted(endpoint_id_0);
// Ignored by remote.
analytics_recorder.OnConnectionRequestSent(endpoint_id_1);
analytics_recorder.OnLocalEndpointAccepted(endpoint_id_1);
// Ignored by both.
analytics_recorder.OnConnectionRequestSent(endpoint_id_2);
analytics_recorder.LogSession();
EXPECT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result());
const ClientSession& client_session = event_logger.GetLoggedClientSession();
StrategySession strategy_session = client_session.strategy_session(0);
EXPECT_EQ(strategy_session.discovery_phase_size(), 1);
EXPECT_EQ(strategy_session.discovery_phase(0).sent_connection_request_size(),
3);
EXPECT_EQ(strategy_session.discovery_phase(0)
.sent_connection_request(0)
.local_response(),
IGNORED);
EXPECT_EQ(strategy_session.discovery_phase(0)
.sent_connection_request(0)
.remote_response(),
ACCEPTED);
EXPECT_EQ(strategy_session.discovery_phase(0)
.sent_connection_request(1)
.local_response(),
ACCEPTED);
EXPECT_EQ(strategy_session.discovery_phase(0)
.sent_connection_request(1)
.remote_response(),
IGNORED);
EXPECT_EQ(strategy_session.discovery_phase(0)
.sent_connection_request(2)
.local_response(),
IGNORED);
EXPECT_EQ(strategy_session.discovery_phase(0)
.sent_connection_request(2)
.remote_response(),
IGNORED);
}
} // namespace
} // namespace analytics
} // namespace nearby
} // namespace location
+6 -1
View File
@@ -55,6 +55,7 @@ cc_library(
compatible_with = ["//buildenv/target:non_prod"],
visibility = [
"//googlemac/iPhone/Shared/Nearby/Connections:__subpackages__",
"//third_party/nearby_connections/cpp/analytics:__subpackages__",
"//core:__subpackages__",
],
deps = [
@@ -72,8 +73,12 @@ cc_library(
hdrs = [
"event_logger.h",
],
compatible_with = ["//buildenv/target:non_prod"],
visibility = [
"//third_party/nearby_connections/cpp/analytics:__subpackages__",
],
deps = [
"//logs/proto/location/nearby:nearby_client_log_cc_proto",
"//third_party/nearby_connections/proto/analytics:connections_log_cc_proto",
],
)
+5
View File
@@ -15,10 +15,12 @@
#ifndef CORE_CORE_H_
#define CORE_CORE_H_
#include <functional>
#include <string>
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "core/event_logger.h"
#include "core/internal/client_proxy.h"
#include "core/internal/service_controller.h"
#include "core/internal/service_controller_router.h"
@@ -34,6 +36,9 @@ namespace connections {
class Core {
public:
explicit Core(ServiceControllerRouter* router);
// Client needs to call this constructor if analytics logger is needed.
Core(analytics::EventLogger* event_logger, ServiceControllerRouter* router)
: client_(event_logger), router_(router) {}
~Core();
Core(Core&&);
Core& operator=(Core&&);
+2 -4
View File
@@ -15,11 +15,10 @@
#ifndef CORE_EVENT_LOGGER_H_
#define CORE_EVENT_LOGGER_H_
#include "logs/proto/location/nearby/nearby_client_log.proto.h"
#include "proto/analytics/connections_log.proto.h"
namespace location {
namespace nearby {
namespace connections {
namespace analytics {
// Allows callers to log |ConnectionsLog| collected at Nearby Connections
@@ -30,11 +29,10 @@ class EventLogger {
// Logs |ConnectionsLog| details. Might block to do I/O, e.g. upload
// synchronously to some metrics server.
virtual void Log(const logs::ConnectionsLog& connections_log) = 0;
virtual void Log(const proto::ConnectionsLog& connections_log) = 0;
};
} // namespace analytics
} // namespace connections
} // namespace nearby
} // namespace location
+2
View File
@@ -99,6 +99,7 @@ cc_library(
"//absl/strings",
"//absl/time",
"//absl/types:span",
"//third_party/nearby_connections/cpp/analytics",
"//core:core_types",
"//core/internal/mediums",
"//core/internal/mediums:utils",
@@ -194,6 +195,7 @@ cc_test(
"//absl/synchronization",
"//absl/time",
"//absl/types:span",
"//third_party/nearby_connections/cpp/analytics",
"//core:core_types",
"//core/internal/mediums",
"//core/internal/mediums:utils",
+24 -12
View File
@@ -25,9 +25,11 @@
#include "absl/container/flat_hash_set.h"
#include "absl/strings/escaping.h"
#include "absl/types/span.h"
#include "core/internal/mediums/utils.h"
#include "core/internal/offline_frames.h"
#include "core/internal/pcp_handler.h"
#include "core/options.h"
#include "platform/base/base64_utils.h"
#include "platform/base/bluetooth_utils.h"
#include "platform/public/logging.h"
#include "platform/public/system_clock.h"
@@ -342,6 +344,7 @@ void BasePcpHandler::OnEncryptionSuccessRunnable(
}
connection_info.SetCryptoContext(std::move(ukey2));
connection_info.connection_token = GetHashedConnectionToken(raw_auth_token);
NEARBY_LOGS(INFO)
<< "Register encrypted connection; wait for response; endpoint_id="
<< endpoint_id;
@@ -382,7 +385,8 @@ void BasePcpHandler::OnEncryptionSuccessRunnable(
.keep_alive_timeout_millis =
connection_info.options.keep_alive_timeout_millis,
},
std::move(connection_info.channel), connection_info.listener);
std::move(connection_info.channel), connection_info.listener,
connection_info.connection_token);
if (auto future_status = connection_info.result.lock()) {
NEARBY_LOGS(INFO) << "Connection established; Finalising future OK.";
@@ -629,9 +633,12 @@ BasePcpHandler::GetDiscoveredEndpoints(
return result;
}
void BasePcpHandler::PendingConnectionInfo::SetCryptoContext(
std::unique_ptr<UKey2Handshake> ukey2) {
this->ukey2 = std::move(ukey2);
mediums::PeerId BasePcpHandler::CreatePeerIdFromAdvertisement(
const std::string& service_id, const std::string& endpoint_id,
const ByteArray& endpoint_info) {
std::string seed =
absl::StrCat(service_id, endpoint_id, std::string(endpoint_info));
return mediums::PeerId::FromSeed(ByteArray(std::move(seed)));
}
bool BasePcpHandler::HasOutgoingConnections(ClientProxy* client) const {
@@ -1415,8 +1422,21 @@ ExceptionOr<OfflineFrame> BasePcpHandler::ReadConnectionRequestFrame(
return wrapped_frame;
}
std::string BasePcpHandler::GetHashedConnectionToken(
const ByteArray& token_bytes) {
auto token = std::string(token_bytes);
return location::nearby::Base64Utils::Encode(
Utils::Sha256Hash(token, token.size()))
.substr(0, kConnectionTokenLength);
}
///////////////////// BasePcpHandler::PendingConnectionInfo ///////////////////
void BasePcpHandler::PendingConnectionInfo::SetCryptoContext(
std::unique_ptr<UKey2Handshake> ukey2) {
this->ukey2 = std::move(ukey2);
}
BasePcpHandler::PendingConnectionInfo::~PendingConnectionInfo() {
auto future_status = result.lock();
if (future_status && !future_status->IsSet()) {
@@ -1443,14 +1463,6 @@ void BasePcpHandler::PendingConnectionInfo::LocalEndpointRejectedConnection(
client->LocalEndpointRejectedConnection(endpoint_id);
}
mediums::PeerId BasePcpHandler::CreatePeerIdFromAdvertisement(
const std::string& service_id, const std::string& endpoint_id,
const ByteArray& endpoint_info) {
std::string seed =
absl::StrCat(service_id, endpoint_id, std::string(endpoint_info));
return mediums::PeerId::FromSeed(ByteArray(std::move(seed)));
}
} // namespace connections
} // namespace nearby
} // namespace location
+8
View File
@@ -348,6 +348,9 @@ class BasePcpHandler : public PcpHandler,
// accepted. Crypto context is passed over to channel_manager_ before
// switching to connected state, where Payload may be exchanged.
std::unique_ptr<securegcm::UKey2Handshake> ukey2;
// Used in AnalyticsRecorder for devices connection tracking.
std::string connection_token;
};
// @EncryptionRunnerThread
@@ -382,6 +385,7 @@ class BasePcpHandler : public PcpHandler,
absl::Seconds(2);
static constexpr absl::Duration kRejectedConnectionCloseDelay =
absl::Seconds(2);
static constexpr int kConnectionTokenLength = 8;
void OnConnectionResponse(ClientProxy* client, const std::string& endpoint_id,
const OfflineFrame& frame);
@@ -446,6 +450,10 @@ class BasePcpHandler : public PcpHandler,
ExceptionOr<OfflineFrame> ReadConnectionRequestFrame(
EndpointChannel* channel);
// Returns an 8 characters length hashed string generated via a token byte
// array.
std::string GetHashedConnectionToken(const ByteArray& token_bytes);
void WaitForLatch(const std::string& method_name, CountDownLatch* latch);
Status WaitForResult(const std::string& method_name, std::int64_t client_id,
Future<Status>* future);
+38 -2
View File
@@ -15,7 +15,9 @@
#include "core/internal/client_proxy.h"
#include <cstdlib>
#include <functional>
#include <limits>
#include <string>
#include <utility>
#include "absl/container/flat_hash_map.h"
@@ -41,7 +43,12 @@ constexpr char kEndpointIdChars[] = {
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'};
ClientProxy::ClientProxy() : client_id_(Prng().NextInt64()) {}
ClientProxy::ClientProxy(analytics::EventLogger* event_logger)
: client_id_(Prng().NextInt64()) {
NEARBY_LOGS(INFO) << "ClientProxy ctor event_logger=" << event_logger;
analytics_recorder_ =
std::make_unique<analytics::AnalyticsRecorder>(event_logger);
}
ClientProxy::~ClientProxy() { Reset(); }
@@ -58,6 +65,14 @@ std::string ClientProxy::GetLocalEndpointId() {
return local_endpoint_id_;
}
std::string ClientProxy::GetConnectionToken(const std::string& endpoint_id) {
Connection* item = LookupConnection(endpoint_id);
if (item != nullptr) {
return item->connection_token;
}
return {};
}
std::string ClientProxy::GenerateLocalEndpointId() {
if (high_vis_mode_) {
if (!local_high_vis_mode_cache_endpoint_id_.empty()) {
@@ -82,6 +97,7 @@ void ClientProxy::Reset() {
StoppedDiscovery();
RemoveAllEndpoints();
ExitHighVisibilityMode();
analytics_recorder_->LogSession();
}
void ClientProxy::StartedAdvertising(
@@ -104,6 +120,10 @@ void ClientProxy::StartedAdvertising(
advertising_info_ = {service_id, listener};
advertising_options_ = advertising_options;
const std::vector<proto::connections::Medium> medium_vector(mediums.begin(),
mediums.end());
analytics_recorder_->OnStartAdvertising(strategy, medium_vector);
}
void ClientProxy::StoppedAdvertising() {
@@ -113,6 +133,7 @@ void ClientProxy::StoppedAdvertising() {
if (IsAdvertising()) {
advertising_info_.Clear();
analytics_recorder_->OnStopAdvertising();
}
// advertising_options_ is purposefully not cleared here.
ResetLocalEndpointIdIfNeeded();
@@ -146,6 +167,10 @@ void ClientProxy::StartedDiscovery(
MutexLock lock(&mutex_);
discovery_info_ = DiscoveryInfo{service_id, listener};
discovery_options_ = discovery_options;
const std::vector<proto::connections::Medium> medium_vector(mediums.begin(),
mediums.end());
analytics_recorder_->OnStartDiscovery(strategy, medium_vector);
}
void ClientProxy::StoppedDiscovery() {
@@ -154,6 +179,7 @@ void ClientProxy::StoppedDiscovery() {
if (IsDiscovering()) {
discovered_endpoint_ids_.clear();
discovery_info_.Clear();
analytics_recorder_->OnStopDiscovery();
}
// discovery_options_ is purposefully not cleared here.
ResetLocalEndpointIdIfNeeded();
@@ -203,6 +229,7 @@ void ClientProxy::OnEndpointFound(const std::string& service_id,
discovered_endpoint_ids_.insert(endpoint_id);
discovery_info_.listener.endpoint_found_cb(endpoint_id, endpoint_info,
service_id);
analytics_recorder_->OnEndpointFound(medium);
}
void ClientProxy::OnEndpointLost(const std::string& service_id,
@@ -234,7 +261,8 @@ void ClientProxy::OnEndpointLost(const std::string& service_id,
void ClientProxy::OnConnectionInitiated(const std::string& endpoint_id,
const ConnectionResponseInfo& info,
const ConnectionOptions& options,
const ConnectionListener& listener) {
const ConnectionListener& listener,
const std::string& connection_token) {
MutexLock lock(&mutex_);
// Whether this is incoming or outgoing, the local and remote endpoints both
@@ -245,6 +273,7 @@ void ClientProxy::OnConnectionInitiated(const std::string& endpoint_id,
.is_incoming = info.is_incoming_connection,
.connection_listener = listener,
.connection_options = options,
.connection_token = connection_token,
});
// Instead of using structured binding which is nice, but banned
// (can not use c++17 features, until chromium does) we unpack manually.
@@ -265,6 +294,9 @@ void ClientProxy::OnConnectionInitiated(const std::string& endpoint_id,
if (info.is_incoming_connection) {
// Add CancellationFlag for advertisers once encryption succeeds.
AddCancellationFlag(endpoint_id);
analytics_recorder_->OnConnectionRequestReceived(endpoint_id);
} else {
analytics_recorder_->OnConnectionRequestSent(endpoint_id);
}
}
@@ -449,6 +481,7 @@ void ClientProxy::LocalEndpointAcceptedConnection(
if (item != nullptr) {
item->payload_listener = listener;
}
analytics_recorder_->OnLocalEndpointAccepted(endpoint_id);
}
void ClientProxy::LocalEndpointRejectedConnection(
@@ -463,6 +496,7 @@ void ClientProxy::LocalEndpointRejectedConnection(
}
AppendConnectionStatus(endpoint_id, Connection::kLocalEndpointRejected);
analytics_recorder_->OnLocalEndpointRejected(endpoint_id);
}
void ClientProxy::RemoteEndpointAcceptedConnection(
@@ -477,6 +511,7 @@ void ClientProxy::RemoteEndpointAcceptedConnection(
}
AppendConnectionStatus(endpoint_id, Connection::kRemoteEndpointAccepted);
analytics_recorder_->OnRemoteEndpointAccepted(endpoint_id);
}
void ClientProxy::RemoteEndpointRejectedConnection(
@@ -491,6 +526,7 @@ void ClientProxy::RemoteEndpointRejectedConnection(
}
AppendConnectionStatus(endpoint_id, Connection::kRemoteEndpointRejected);
analytics_recorder_->OnRemoteEndpointRejected(endpoint_id);
}
bool ClientProxy::IsConnectionAccepted(const std::string& endpoint_id) const {
+12 -2
View File
@@ -16,9 +16,11 @@
#define CORE_INTERNAL_CLIENT_PROXY_H_
#include <cstdint>
#include <functional>
#include <string>
#include <vector>
#include "third_party/nearby_connections/cpp/analytics/analytics_recorder.h"
#include "core/listeners.h"
#include "core/options.h"
#include "core/status.h"
@@ -46,7 +48,7 @@ class ClientProxy final {
static constexpr absl::Duration
kHighPowerAdvertisementEndpointIdCacheTimeout = absl::Seconds(30);
ClientProxy();
explicit ClientProxy(analytics::EventLogger* event_logger = nullptr);
~ClientProxy();
ClientProxy(ClientProxy&&) = default;
ClientProxy& operator=(ClientProxy&&) = default;
@@ -55,6 +57,8 @@ class ClientProxy final {
std::string GetLocalEndpointId();
std::string GetConnectionToken(const std::string& endpoint_id);
// Clears all the runtime state of this client.
void Reset();
@@ -98,7 +102,8 @@ class ClientProxy final {
void OnConnectionInitiated(const std::string& endpoint_id,
const ConnectionResponseInfo& info,
const ConnectionOptions& options,
const ConnectionListener& listener);
const ConnectionListener& listener,
const std::string& connection_token);
// Proxies to the client's ConnectionListener::OnAccepted() callback.
void OnConnectionAccepted(const std::string& endpoint_id);
@@ -205,6 +210,7 @@ class ClientProxy final {
ConnectionListener connection_listener;
PayloadListener payload_listener;
ConnectionOptions connection_options;
std::string connection_token;
};
struct AdvertisingInfo {
@@ -298,6 +304,10 @@ class ClientProxy final {
// A default cancellation flag with isCancelled set be true.
std::unique_ptr<CancellationFlag> default_cancellation_flag_ =
std::make_unique<CancellationFlag>(true);
// An analytics logger with |EventLogger| provided by client, which is default
// nullptr as no-op.
std::unique_ptr<analytics::AnalyticsRecorder> analytics_recorder_;
};
} // namespace connections
+4 -3
View File
@@ -131,10 +131,11 @@ class ClientProxyTest : public ::testing::TestWithParam<FeatureFlags> {
EXPECT_CALL(mock_discovery_connection_.initiated_cb, Call).Times(1);
const std::string auth_token{"auth_token"};
const ByteArray raw_auth_token{auth_token};
const std::string connection_token{"conntokn"};
advertising_connection_info_.remote_endpoint_info = endpoint.info;
client->OnConnectionInitiated(endpoint.id, advertising_connection_info_,
connection_options_,
discovery_connection_listener_);
client->OnConnectionInitiated(
endpoint.id, advertising_connection_info_, connection_options_,
discovery_connection_listener_, connection_token);
EXPECT_TRUE(client->HasPendingConnectionToEndpoint(endpoint.id));
}
+5 -2
View File
@@ -353,7 +353,8 @@ void EndpointManager::RegisterEndpoint(ClientProxy* client,
const ConnectionResponseInfo& info,
const ConnectionOptions& options,
std::unique_ptr<EndpointChannel> channel,
const ConnectionListener& listener) {
const ConnectionListener& listener,
const std::string& connection_token) {
CountDownLatch latch(1);
// NOTE (unique_ptr<> capture):
@@ -366,6 +367,7 @@ void EndpointManager::RegisterEndpoint(ClientProxy* client,
channel = channel.release(),
&endpoint_id, &info,
&options, &listener,
&connection_token,
&latch]() {
if (endpoints_.contains(endpoint_id)) {
NEARBY_LOGS(WARNING) << "Registering duplicate endpoint " << endpoint_id;
@@ -440,7 +442,8 @@ void EndpointManager::RegisterEndpoint(ClientProxy* client,
// It's now time to let the client know of this new connection so that
// they can accept or reject it.
client->OnConnectionInitiated(endpoint_id, info, options, listener);
client->OnConnectionInitiated(endpoint_id, info, options, listener,
connection_token);
latch.CountDown();
});
latch.Await();
+2 -1
View File
@@ -103,7 +103,8 @@ class EndpointManager {
const ConnectionResponseInfo& info,
const ConnectionOptions& options,
std::unique_ptr<EndpointChannel> channel,
const ConnectionListener& listener);
const ConnectionListener& listener,
const std::string& connection_token);
// Called when a client explicitly asks to disconnect from this endpoint. In
// this case, we do not notify the client of onDisconnected().
void UnregisterEndpoint(ClientProxy* client, const std::string& endpoint_id);
+2 -1
View File
@@ -108,7 +108,7 @@ class EndpointManagerTest : public ::testing::Test {
.WillRepeatedly(Return(start_time_));
EXPECT_CALL(mock_listener_.initiated_cb, Call).Times(1);
em_.RegisterEndpoint(&client_, endpoint_id_, info_, options_,
std::move(channel), listener_);
std::move(channel), listener_, connection_token);
if (should_close) {
EXPECT_TRUE(done.Await(absl::Milliseconds(1000)).result());
}
@@ -151,6 +151,7 @@ class EndpointManagerTest : public ::testing::Test {
.bandwidth_changed_cb =
mock_listener_.bandwidth_changed_cb.AsStdFunction(),
};
std::string connection_token = "conntokn";
absl::Time start_time_{absl::Now()};
};
@@ -148,8 +148,9 @@ class ServiceControllerRouterTest : public testing::Test {
.raw_authentication_token = ByteArray{"auth_token"},
.is_incoming_connection = true,
};
std::string connection_token{"conntokn"};
client->OnConnectionInitiated(endpoint_id, response_info, options,
request_info.listener);
request_info.listener, connection_token);
EXPECT_TRUE(client->HasPendingConnectionToEndpoint(endpoint_id));
}
+1
View File
@@ -43,6 +43,7 @@ cc_library(
compatible_with = ["//buildenv/target:non_prod"],
visibility = [
"//googlemac/iPhone/Shared/Nearby/Connections:__subpackages__",
"//third_party/nearby_connections/cpp/analytics:__subpackages__",
"//core:__subpackages__",
"//platform:__subpackages__",
"//platform/api:__subpackages__",
+1
View File
@@ -106,6 +106,7 @@ cc_library(
"platform.cc",
],
visibility = [
"//third_party/nearby_connections/cpp/analytics:__subpackages__",
"//core:__subpackages__",
"//platform:__subpackages__",
],
+2
View File
@@ -50,6 +50,7 @@ cc_library(
name = "comm",
hdrs = [
"ble.h",
"bluetooth_adapter.h",
"bluetooth_classic.h",
"bluetooth_classic_device.h",
"bluetooth_classic_medium.h",
@@ -87,6 +88,7 @@ cc_library(
cc_library(
name = "windows",
srcs = [
"bluetooth_adapter.cc",
"bluetooth_classic_device.cc",
"bluetooth_classic_medium.cc",
"bluetooth_classic_server_socket.cc",
@@ -0,0 +1,320 @@
// Copyright 2020 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 "platform/impl/windows/bluetooth_adapter.h"
#include <windows.h>
#include <stdio.h>
#include <winioctl.h>
#include <bthdef.h>
#include <bthioctl.h>
#include <cfgmgr32.h>
#include <devguid.h>
#include <initguid.h>
#include <objbase.h>
#include <setupapi.h>
#include <usbiodef.h>
#include <string>
#include "absl/strings/str_format.h"
#include "platform/impl/windows/generated/winrt/Windows.Foundation.h"
#include "platform/impl/windows/utils.h"
#include "platform/public/logging.h"
typedef std::basic_string<TCHAR> tstring;
// IOCTL to get local radio information
#define BTH_GET_DEVICE_INFO_IOCTL 0x411008
namespace location {
namespace nearby {
namespace windows {
BluetoothAdapter::BluetoothAdapter()
: windows_bluetooth_adapter_(winrt::Windows::Devices::Bluetooth::
BluetoothAdapter::GetDefaultAsync()
.get()) {
// Gets the radio represented by this Bluetooth adapter.
// https://docs.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothadapter.getradioasync?view=winrt-20348
windows_bluetooth_radio_ = windows_bluetooth_adapter_.GetRadioAsync().get();
}
// Synchronously sets the status of the BluetoothAdapter to 'status', and
// returns true if the operation was a success.
bool BluetoothAdapter::SetStatus(Status status) {
if (status == Status::kDisabled) {
// An asynchronous operation that attempts to set the state of the radio
// represented by this object.
// https://docs.microsoft.com/en-us/uwp/api/windows.devices.radios.radio.setstateasync?view=winrt-20348
windows_bluetooth_radio_.SetStateAsync(RadioState::Off).get();
} else {
windows_bluetooth_radio_.SetStateAsync(RadioState::On).get();
}
return true;
}
// Returns true if the BluetoothAdapter's current status is
// Status::Value::kEnabled.
bool BluetoothAdapter::IsEnabled() const {
// Gets the current state of the radio represented by this object.
// https://docs.microsoft.com/en-us/uwp/api/windows.devices.radios.radio.state?view=winrt-20348
return windows_bluetooth_radio_.State() == RadioState::On;
}
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#getScanMode()
//
// Returns ScanMode::kUnknown on error.
BluetoothAdapter::ScanMode BluetoothAdapter::GetScanMode() const {
return scan_mode_;
}
// Synchronously sets the scan mode of the adapter, and returns true if the
// operation was a success.
// TODO(jcarroll): Setup an event for this and hook the bluetooth medium into
// the event to allow for updates.
bool BluetoothAdapter::SetScanMode(ScanMode scan_mode) {
scan_mode_ = scan_mode;
return true;
}
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#getName()
// Returns an empty string on error
// TODO(b/184975123): replace with real implementation.
std::string BluetoothAdapter::GetName() const { return "Un-implemented"; }
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#setName(java.lang.String)
bool BluetoothAdapter::SetName(absl::string_view name) {
char *instanceID = GetGenericBluetoothAdapterInstanceID();
if (instanceID == NULL) {
NEARBY_LOGS(ERROR)
<< __func__ << ": Failed to get Generic Bluetooth Adapter InstanceID";
return false;
}
// Add 1 to length to get size (including null)
char *instanceIDModified = new char[(strlen(instanceID) + 1) * sizeof(char)];
absl::SNPrintF(instanceIDModified,
size_t((strlen(instanceID) + 1) * sizeof(char)), "%s",
instanceID);
find_and_replace(instanceIDModified, "\\", "#");
HANDLE hDevice;
char fileName[1024] = {0};
// defined in usbiodef.h
const GUID guid = GUID_DEVINTERFACE_USB_DEVICE;
OLECHAR guidOleStr[64];
int oleBufferLen = 64;
char guidStr[64];
int bufferLen = 64;
BOOL defaultCharUsed;
// Converts a globally unique identifier (GUID) into a string of printable
// characters.
// https://docs.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-stringfromguid2
auto conversionResult = StringFromGUID2(guid, guidOleStr, bufferLen);
if (conversionResult == 0) {
NEARBY_LOGS(ERROR) << __func__ << ": Failed to convert guid to string";
return false;
}
// Maps a UTF-16 (wide character) string to a new character string. The new
// character string is not necessarily from a multibyte character set.
// https://docs.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-widechartomultibyte
conversionResult =
WideCharToMultiByte(CP_UTF8, 0, guidOleStr, oleBufferLen, guidStr,
bufferLen, NULL, &defaultCharUsed);
if (conversionResult != 0) {
const char *errorResult = {};
switch (conversionResult) {
case ERROR_INSUFFICIENT_BUFFER:
errorResult =
"A supplied buffer size was not large enough, or it was "
"incorrectly set to NULL.";
break;
case ERROR_INVALID_FLAGS:
errorResult = "The values supplied for flags were not valid.";
break;
case ERROR_INVALID_PARAMETER:
errorResult = "Any of the parameter values was invalid.";
break;
case ERROR_NO_UNICODE_TRANSLATION:
errorResult = "Invalid Unicode was found in a string.";
break;
}
NEARBY_LOGS(ERROR) << __func__ << ": Failed to convert guid to string "
<< errorResult;
}
absl::SNPrintF(fileName, sizeof(fileName), "\\\\.\\%s%s#%s", fileName,
instanceIDModified, guidStr);
// Creates or opens a file or I/O device.
// https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
hDevice =
CreateFileA(fileName, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
delete[] instanceIDModified;
if (hDevice == INVALID_HANDLE_VALUE) {
NEARBY_LOGS(ERROR) << __func__ << ": Failed to open device. Error code: "
<< GetLastError();
return false;
}
// Change radio module local name in registry
HKEY hKey;
char rmLocalNameKey[1024] = {0};
LSTATUS ret;
absl::SNPrintF(rmLocalNameKey, sizeof(rmLocalNameKey),
"SYSTEM\\ControlSet001\\Enum\\%s\\Device Parameters",
instanceID);
// Opens the specified registry key. Note that key names are not case
// sensitive.
// https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regopenkeyexa
ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE, rmLocalNameKey, 0L, KEY_SET_VALUE,
&hKey);
if (ret != ERROR_SUCCESS) {
NEARBY_LOGS(ERROR) << __func__
<< ": Failed to open registry key. Error code: " << ret;
return false;
}
// Sets the data and type of a specified value under a registry key.
// https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regsetvalueexa
ret = RegSetValueExA(hKey, "Local Name", 0, REG_BINARY,
(LPBYTE)std::string(name).c_str(),
strlen(std::string(name).c_str()));
if (ret != ERROR_SUCCESS) {
NEARBY_LOGS(ERROR) << __func__
<< ": Failed to set registry key. Error code: " << ret;
return false;
}
// Closes a handle to the specified registry key.
// https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regclosekey
RegCloseKey(hKey);
// tells the control function to reset or reload or similar...
int32 reload = 4;
// merely a placeholder
DWORD bytes = 0;
// Send radio module driver command to update device information
// Sends a control code directly to a specified device driver, causing the
// corresponding device to perform the corresponding operation.
// https://docs.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-deviceiocontrol
if (!DeviceIoControl(hDevice, BTH_GET_DEVICE_INFO_IOCTL, &reload, 4, NULL, 0,
&bytes, NULL)) {
NEARBY_LOGS(ERROR)
<< __func__
<< ": Failed to update radio module local name. Error code: "
<< GetLastError();
return false;
}
return true;
}
void BluetoothAdapter::find_and_replace(char *source, const char *strFind,
const char *strReplace) {
std::string s = std::string(source);
std::string f = std::string(strFind);
std::string r = std::string(strReplace);
size_t j;
for (; (j = s.find(f)) != std::string::npos;) {
s.replace(j, f.length(), r);
}
memcpy(source, s.c_str(), s.size());
}
char *BluetoothAdapter::GetGenericBluetoothAdapterInstanceID(void) {
unsigned i;
CONFIGRET r;
HDEVINFO hDevInfo;
SP_DEVINFO_DATA DeviceInfoData;
char *deviceInstanceID = new char[MAX_DEVICE_ID_LEN];
// Find all bluetooth radio modules
// The SetupDiGetClassDevs function returns a handle to a device information
// set that contains requested device information elements for a local
// computer.
// https://docs.microsoft.com/en-us/windows/win32/api/setupapi/nf-setupapi-setupdigetclassdevsa
hDevInfo =
SetupDiGetClassDevsA(&GUID_DEVCLASS_BLUETOOTH, NULL, NULL, DIGCF_PRESENT);
if (hDevInfo == INVALID_HANDLE_VALUE) {
NEARBY_LOGS(ERROR) << __func__
<< ": Could not find BluetoothDevice on this machine";
return NULL;
}
// Get first Generic Bluetooth Adapter InstanceID
for (i = 0;; i++) {
DeviceInfoData.cbSize = sizeof(DeviceInfoData);
// The SetupDiEnumDeviceInfo function returns a SP_DEVINFO_DATA structure
// that specifies a device information element in a device information set.
// https://docs.microsoft.com/en-us/windows/win32/api/setupapi/nf-setupapi-setupdienumdeviceinfo
if (!SetupDiEnumDeviceInfo(hDevInfo, i, &DeviceInfoData)) break;
// The CM_Get_Device_ID function retrieves the device instance ID for a
// specified device instance on the local machine.
// https://docs.microsoft.com/en-us/windows/win32/api/cfgmgr32/nf-cfgmgr32-cm_get_device_ida
r = CM_Get_Device_IDA(DeviceInfoData.DevInst, deviceInstanceID,
MAX_DEVICE_ID_LEN, 0);
if (r != CR_SUCCESS) continue;
// With Windows, a Bluetooth radio can be packaged as an external dongle or
// embedded inside a computer but it must be connected to one of the
// computer's USB ports.
// https://docs.microsoft.com/en-us/windows-hardware/drivers/bluetooth/bluetooth-host-radio-support
if (strncmp("USB", deviceInstanceID, 3) == 0) {
return deviceInstanceID;
}
}
NEARBY_LOGS(ERROR) << __func__
<< ": Failed to get the generic bluetooth adapter id";
return NULL;
}
// Returns BT MAC address assigned to this adapter.
std::string BluetoothAdapter::GetMacAddress() const {
return uint64_to_mac_address_string(
windows_bluetooth_adapter_.BluetoothAddress());
}
} // namespace windows
} // namespace nearby
} // namespace location
+34 -14
View File
@@ -18,47 +18,67 @@
#include <string>
#include "platform/api/bluetooth_adapter.h"
#include "platform/impl/windows/generated/winrt/Windows.Devices.Bluetooth.h"
#include "platform/impl/windows/generated/winrt/Windows.Devices.Radios.h"
namespace location {
namespace nearby {
namespace windows {
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html
// Represents a Bluetooth adapter.
// https://docs.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothadapter?view=winrt-20348
using winrt::Windows::Devices::Bluetooth::IBluetoothAdapter;
// Represents a radio device on the system.
// https://docs.microsoft.com/en-us/uwp/api/windows.devices.radios.radio?view=winrt-20348
using winrt::Windows::Devices::Radios::IRadio;
// Enumeration that describes possible radio states.
// https://docs.microsoft.com/en-us/uwp/api/windows.devices.radios.radiostate?view=winrt-20348
using winrt::Windows::Devices::Radios::RadioState;
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html
class BluetoothAdapter : public api::BluetoothAdapter {
public:
BluetoothAdapter();
// TODO(b/184975123): replace with real implementation.
~BluetoothAdapter() override = default;
// Synchronously sets the status of the BluetoothAdapter to 'status', and
// returns true if the operation was a success.
// TODO(b/184975123): replace with real implementation.
bool SetStatus(Status status) override { return false; }
bool SetStatus(Status status) override;
// Returns true if the BluetoothAdapter's current status is
// Status::Value::kEnabled.
// TODO(b/184975123): replace with real implementation.
bool IsEnabled() const override { return false; }
bool IsEnabled() const override;
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#getScanMode()
//
// Returns ScanMode::kUnknown on error.
// TODO(b/184975123): replace with real implementation.
ScanMode GetScanMode() const override { return ScanMode::kUnknown; }
ScanMode GetScanMode() const override;
// Synchronously sets the scan mode of the adapter, and returns true if the
// operation was a success.
// TODO(b/184975123): replace with real implementation.
bool SetScanMode(ScanMode scan_mode) override { return false; }
bool SetScanMode(ScanMode scan_mode) override;
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#getName()
// Returns an empty string on error
// TODO(b/184975123): replace with real implementation.
std::string GetName() const override { return "Un-implemented"; }
std::string GetName() const override;
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#setName(java.lang.String)
// TODO(b/184975123): replace with real implementation.
bool SetName(absl::string_view name) override { return false; }
bool SetName(absl::string_view name) override;
// Returns BT MAC address assigned to this adapter.
// TODO(b/184975123): replace with real implementation.
std::string GetMacAddress() const override { return "Un-implemented"; }
std::string GetMacAddress() const override;
private:
IBluetoothAdapter
windows_bluetooth_adapter_;
IRadio windows_bluetooth_radio_;
char *GetGenericBluetoothAdapterInstanceID(void);
void find_and_replace(char *source, const char *strFind,
const char *strReplace);
ScanMode scan_mode_ = ScanMode::kNone;
};
} // namespace windows
@@ -21,6 +21,7 @@
#include "platform/base/cancellation_flag.h"
#include "platform/base/cancellation_flag_listener.h"
#include "platform/base/exception.h"
#include "platform/impl/windows/bluetooth_adapter.h"
#include "platform/impl/windows/bluetooth_classic_device.h"
#include "platform/impl/windows/bluetooth_classic_server_socket.h"
#include "platform/impl/windows/bluetooth_classic_socket.h"
@@ -36,7 +37,10 @@ namespace location {
namespace nearby {
namespace windows {
BluetoothClassicMedium::BluetoothClassicMedium() {
BluetoothClassicMedium::BluetoothClassicMedium(
const api::BluetoothAdapter& bluetoothAdapter)
: bluetooth_adapter_(
dynamic_cast<const BluetoothAdapter&>(bluetoothAdapter)) {
InitializeCriticalSection(&critical_section_);
InitializeDeviceWatcher();
@@ -253,7 +257,12 @@ BluetoothClassicMedium::ListenForService(const std::string& service_name,
auto bluetooth_server_socket =
std::make_unique<location::nearby::windows::BluetoothServerSocket>();
bluetooth_server_socket->StartListening(service_name, service_uuid);
bool radioDiscoverable =
bluetooth_adapter_.GetScanMode() ==
BluetoothAdapter::ScanMode::kConnectableDiscoverable;
bluetooth_server_socket->StartListening(service_name, service_uuid,
radioDiscoverable);
return std::move(bluetooth_server_socket);
}
@@ -22,6 +22,7 @@
#include "platform/impl/windows/bluetooth_classic_device.h"
#include "platform/impl/windows/bluetooth_classic_server_socket.h"
#include "platform/impl/windows/bluetooth_classic_socket.h"
#include "platform/impl/windows/bluetooth_adapter.h"
#include "platform/impl/windows/generated/winrt/Windows.Devices.Enumeration.h"
#include "platform/impl/windows/generated/winrt/Windows.Networking.Sockets.h"
#include "platform/impl/windows/generated/winrt/base.h"
@@ -86,7 +87,8 @@ using winrt::Windows::Storage::Streams::DataWriter;
// medium.
class BluetoothClassicMedium : public api::BluetoothClassicMedium {
public:
explicit BluetoothClassicMedium();
BluetoothClassicMedium() = default;
BluetoothClassicMedium(const api::BluetoothAdapter& bluetoothAdapter);
~BluetoothClassicMedium() override;
@@ -176,6 +178,8 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium {
// CRITICAL_SECTION is a lightweight synchronization mechanism
// https://docs.microsoft.com/en-us/windows/win32/sync/critical-section-objects
CRITICAL_SECTION critical_section_;
BluetoothAdapter bluetooth_adapter_;
};
} // namespace windows
@@ -26,7 +26,7 @@
namespace location {
namespace nearby {
namespace windows {
BluetoothServerSocket::BluetoothServerSocket() {
BluetoothServerSocket::BluetoothServerSocket() : rfcomm_provider_(nullptr) {
InitializeCriticalSection(&critical_section_);
}
@@ -61,8 +61,9 @@ std::unique_ptr<api::BluetoothSocket> BluetoothServerSocket::Accept() {
return nullptr;
}
Exception BluetoothServerSocket::StartListening(
const std::string& service_name, const std::string& service_uuid) {
Exception BluetoothServerSocket::StartListening(const std::string& service_name,
const std::string& service_uuid,
bool radioDiscoverable) {
EnterCriticalSection(&critical_section_);
winrt::guid service(service_uuid);
@@ -121,7 +122,7 @@ Exception BluetoothServerSocket::StartListening(
try {
rfcomm_provider_->StartAdvertising(
stream_socket_listener_.as<StreamSocketListener>(), true);
stream_socket_listener_.as<StreamSocketListener>(), radioDiscoverable);
} catch (std::exception exception) {
// We will log and eat the exception since the caller
// expects nullptr if it fails
@@ -88,7 +88,8 @@ class BluetoothServerSocket : public api::BluetoothServerSocket {
Exception Close() override;
Exception StartListening(const std::string& service_name,
const std::string& service_uuid);
const std::string& service_uuid,
bool radioDiscoverable);
private:
void InitializeServiceSdpAttributes(RfcommServiceProvider rfcommProvider,
@@ -30,6 +30,8 @@ cc_library(
"uuid.lib",
"winmm.lib",
"winspool.lib",
"comsuppwd.lib",
"setupapi.lib",
],
textual_hdrs = glob(["**/*.h"]),
visibility = [
+2 -1
View File
@@ -122,7 +122,8 @@ ImplementationPlatform::CreateBluetoothAdapter() {
std::unique_ptr<BluetoothClassicMedium>
ImplementationPlatform::CreateBluetoothClassicMedium(
BluetoothAdapter& adapter) {
return absl::make_unique<location::nearby::windows::BluetoothClassicMedium>();
return absl::make_unique<location::nearby::windows::BluetoothClassicMedium>(
adapter);
}
// TODO(b/184975123): replace with real implementation.
+2
View File
@@ -52,6 +52,7 @@ cc_library(
compatible_with = ["//buildenv/target:non_prod"],
visibility = [
"//googlemac/iPhone/Shared/Nearby/Connections:__subpackages__",
"//third_party/nearby_connections/cpp/analytics:__subpackages__",
"//core:__subpackages__",
"//platform/base:__pkg__",
"//platform/impl/windows:__subpackages__",
@@ -110,6 +111,7 @@ cc_library(
],
compatible_with = ["//buildenv/target:non_prod"],
visibility = [
"//third_party/nearby_connections/cpp/analytics:__subpackages__",
"//core:__subpackages__",
"//platform:__subpackages__",
],