From 3e40931286fe063467ca3bcfc36f77afc00ae7fc Mon Sep 17 00:00:00 2001 From: hai007 Date: Mon, 23 Aug 2021 21:53:33 -0700 Subject: [PATCH] No changes to public files PiperOrigin-RevId: 392585608 --- cpp/analytics/BUILD | 58 ++ cpp/analytics/analytics_recorder.cc | 493 +++++++++++++++++ cpp/analytics/analytics_recorder.h | 170 ++++++ cpp/analytics/analytics_recorder_test.cc | 464 ++++++++++++++++ cpp/core/BUILD | 7 +- cpp/core/core.h | 5 + cpp/core/event_logger.h | 6 +- cpp/core/internal/BUILD | 2 + cpp/core/internal/base_pcp_handler.cc | 36 +- cpp/core/internal/base_pcp_handler.h | 8 + cpp/core/internal/client_proxy.cc | 40 +- cpp/core/internal/client_proxy.h | 14 +- cpp/core/internal/client_proxy_test.cc | 7 +- cpp/core/internal/endpoint_manager.cc | 7 +- cpp/core/internal/endpoint_manager.h | 3 +- cpp/core/internal/endpoint_manager_test.cc | 3 +- .../service_controller_router_test.cc | 3 +- cpp/platform/base/BUILD | 1 + cpp/platform/impl/g3/BUILD | 1 + cpp/platform/impl/windows/BUILD | 2 + .../impl/windows/bluetooth_adapter.cc | 320 +++++++++++ cpp/platform/impl/windows/bluetooth_adapter.h | 48 +- .../impl/windows/bluetooth_classic_medium.cc | 13 +- .../impl/windows/bluetooth_classic_medium.h | 6 +- .../bluetooth_classic_server_socket.cc | 9 +- .../windows/bluetooth_classic_server_socket.h | 3 +- cpp/platform/impl/windows/generated/BUILD | 2 + cpp/platform/impl/windows/platform.cc | 3 +- cpp/platform/public/BUILD | 2 + proto/analytics/BUILD | 47 ++ proto/analytics/connections_log.proto | 357 +++++++++++++ proto/connections/offline_wire_formats.proto | 21 + proto/errorcode/BUILD | 34 ++ proto/errorcode/error_code_enums.proto | 500 ++++++++++++++++++ 34 files changed, 2643 insertions(+), 52 deletions(-) create mode 100644 cpp/analytics/BUILD create mode 100644 cpp/analytics/analytics_recorder.cc create mode 100644 cpp/analytics/analytics_recorder.h create mode 100644 cpp/analytics/analytics_recorder_test.cc create mode 100644 cpp/platform/impl/windows/bluetooth_adapter.cc create mode 100644 proto/analytics/BUILD create mode 100644 proto/analytics/connections_log.proto create mode 100644 proto/errorcode/BUILD create mode 100644 proto/errorcode/error_code_enums.proto diff --git a/cpp/analytics/BUILD b/cpp/analytics/BUILD new file mode 100644 index 00000000..0f0d0fbf --- /dev/null +++ b/cpp/analytics/BUILD @@ -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", + ], +) diff --git a/cpp/analytics/analytics_recorder.cc b/cpp/analytics/analytics_recorder.cc new file mode 100644 index 00000000..c3a0a261 --- /dev/null +++ b/cpp/analytics/analytics_recorder.cc @@ -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 +#include + +#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 &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(); + 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 &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(); + 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()); + 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()); + 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(); + 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 diff --git a/cpp/analytics/analytics_recorder.h b/cpp/analytics/analytics_recorder.h new file mode 100644 index 00000000..b06f3ab1 --- /dev/null +++ b/cpp/analytics/analytics_recorder.h @@ -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 + +#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> + incoming_connection_requests_ ABSL_GUARDED_BY(mutex_); + absl::btree_map> + outgoing_connection_requests_ ABSL_GUARDED_BY(mutex_); +}; + +} // namespace analytics +} // namespace nearby +} // namespace location + +#endif // ANALYTICS_ANALYTICS_RECORDER_H_ diff --git a/cpp/analytics/analytics_recorder_test.cc b/cpp/analytics/analytics_recorder_test.cc new file mode 100644 index 00000000..dc16d19f --- /dev/null +++ b/cpp/analytics/analytics_recorder_test.cc @@ -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 +#include + +#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 GetLoggedEventTypes() { return logged_event_types_; } + + private: + int logged_client_session_count_ = 0; + CountDownLatch& client_session_done_latch_; + ClientSession logged_client_session_; + std::vector 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 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 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 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 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 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 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 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 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 diff --git a/cpp/core/BUILD b/cpp/core/BUILD index 12456f0b..16256a98 100644 --- a/cpp/core/BUILD +++ b/cpp/core/BUILD @@ -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", ], ) diff --git a/cpp/core/core.h b/cpp/core/core.h index ea18667c..1166ad48 100644 --- a/cpp/core/core.h +++ b/cpp/core/core.h @@ -15,10 +15,12 @@ #ifndef CORE_CORE_H_ #define CORE_CORE_H_ +#include #include #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&&); diff --git a/cpp/core/event_logger.h b/cpp/core/event_logger.h index 763eb1e3..80ec2ee7 100644 --- a/cpp/core/event_logger.h +++ b/cpp/core/event_logger.h @@ -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 diff --git a/cpp/core/internal/BUILD b/cpp/core/internal/BUILD index d039f3b1..c5e42de9 100644 --- a/cpp/core/internal/BUILD +++ b/cpp/core/internal/BUILD @@ -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", diff --git a/cpp/core/internal/base_pcp_handler.cc b/cpp/core/internal/base_pcp_handler.cc index 603afc16..6b3813d9 100644 --- a/cpp/core/internal/base_pcp_handler.cc +++ b/cpp/core/internal/base_pcp_handler.cc @@ -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 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 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 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 diff --git a/cpp/core/internal/base_pcp_handler.h b/cpp/core/internal/base_pcp_handler.h index 976704eb..63015f6d 100644 --- a/cpp/core/internal/base_pcp_handler.h +++ b/cpp/core/internal/base_pcp_handler.h @@ -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 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 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* future); diff --git a/cpp/core/internal/client_proxy.cc b/cpp/core/internal/client_proxy.cc index 861b5361..c2fb61eb 100644 --- a/cpp/core/internal/client_proxy.cc +++ b/cpp/core/internal/client_proxy.cc @@ -15,7 +15,9 @@ #include "core/internal/client_proxy.h" #include +#include #include +#include #include #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(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 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 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 { diff --git a/cpp/core/internal/client_proxy.h b/cpp/core/internal/client_proxy.h index fa258924..e6a580c5 100644 --- a/cpp/core/internal/client_proxy.h +++ b/cpp/core/internal/client_proxy.h @@ -16,9 +16,11 @@ #define CORE_INTERNAL_CLIENT_PROXY_H_ #include +#include #include #include +#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 default_cancellation_flag_ = std::make_unique(true); + + // An analytics logger with |EventLogger| provided by client, which is default + // nullptr as no-op. + std::unique_ptr analytics_recorder_; }; } // namespace connections diff --git a/cpp/core/internal/client_proxy_test.cc b/cpp/core/internal/client_proxy_test.cc index d3e71683..7be90ad2 100644 --- a/cpp/core/internal/client_proxy_test.cc +++ b/cpp/core/internal/client_proxy_test.cc @@ -131,10 +131,11 @@ class ClientProxyTest : public ::testing::TestWithParam { 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)); } diff --git a/cpp/core/internal/endpoint_manager.cc b/cpp/core/internal/endpoint_manager.cc index 2bb966cc..987c79ff 100644 --- a/cpp/core/internal/endpoint_manager.cc +++ b/cpp/core/internal/endpoint_manager.cc @@ -353,7 +353,8 @@ void EndpointManager::RegisterEndpoint(ClientProxy* client, const ConnectionResponseInfo& info, const ConnectionOptions& options, std::unique_ptr 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(); diff --git a/cpp/core/internal/endpoint_manager.h b/cpp/core/internal/endpoint_manager.h index 320b4227..36646e90 100644 --- a/cpp/core/internal/endpoint_manager.h +++ b/cpp/core/internal/endpoint_manager.h @@ -103,7 +103,8 @@ class EndpointManager { const ConnectionResponseInfo& info, const ConnectionOptions& options, std::unique_ptr 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); diff --git a/cpp/core/internal/endpoint_manager_test.cc b/cpp/core/internal/endpoint_manager_test.cc index 43094824..f08468f9 100644 --- a/cpp/core/internal/endpoint_manager_test.cc +++ b/cpp/core/internal/endpoint_manager_test.cc @@ -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()}; }; diff --git a/cpp/core/internal/service_controller_router_test.cc b/cpp/core/internal/service_controller_router_test.cc index 9f6f8b8d..74ed7c2c 100644 --- a/cpp/core/internal/service_controller_router_test.cc +++ b/cpp/core/internal/service_controller_router_test.cc @@ -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)); } diff --git a/cpp/platform/base/BUILD b/cpp/platform/base/BUILD index a9d8b18d..72e8feb0 100644 --- a/cpp/platform/base/BUILD +++ b/cpp/platform/base/BUILD @@ -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__", diff --git a/cpp/platform/impl/g3/BUILD b/cpp/platform/impl/g3/BUILD index b6d98452..7f1a661f 100644 --- a/cpp/platform/impl/g3/BUILD +++ b/cpp/platform/impl/g3/BUILD @@ -106,6 +106,7 @@ cc_library( "platform.cc", ], visibility = [ + "//third_party/nearby_connections/cpp/analytics:__subpackages__", "//core:__subpackages__", "//platform:__subpackages__", ], diff --git a/cpp/platform/impl/windows/BUILD b/cpp/platform/impl/windows/BUILD index 01e2f7b8..a3410d27 100644 --- a/cpp/platform/impl/windows/BUILD +++ b/cpp/platform/impl/windows/BUILD @@ -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", diff --git a/cpp/platform/impl/windows/bluetooth_adapter.cc b/cpp/platform/impl/windows/bluetooth_adapter.cc new file mode 100644 index 00000000..961a4974 --- /dev/null +++ b/cpp/platform/impl/windows/bluetooth_adapter.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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 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 diff --git a/cpp/platform/impl/windows/bluetooth_adapter.h b/cpp/platform/impl/windows/bluetooth_adapter.h index 60a7e8eb..ccf84be8 100644 --- a/cpp/platform/impl/windows/bluetooth_adapter.h +++ b/cpp/platform/impl/windows/bluetooth_adapter.h @@ -18,47 +18,67 @@ #include #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 diff --git a/cpp/platform/impl/windows/bluetooth_classic_medium.cc b/cpp/platform/impl/windows/bluetooth_classic_medium.cc index c58e2a0c..fa90fe93 100644 --- a/cpp/platform/impl/windows/bluetooth_classic_medium.cc +++ b/cpp/platform/impl/windows/bluetooth_classic_medium.cc @@ -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(bluetoothAdapter)) { InitializeCriticalSection(&critical_section_); InitializeDeviceWatcher(); @@ -253,7 +257,12 @@ BluetoothClassicMedium::ListenForService(const std::string& service_name, auto bluetooth_server_socket = std::make_unique(); - 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); } diff --git a/cpp/platform/impl/windows/bluetooth_classic_medium.h b/cpp/platform/impl/windows/bluetooth_classic_medium.h index befe187a..b5c0a5b5 100644 --- a/cpp/platform/impl/windows/bluetooth_classic_medium.h +++ b/cpp/platform/impl/windows/bluetooth_classic_medium.h @@ -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 diff --git a/cpp/platform/impl/windows/bluetooth_classic_server_socket.cc b/cpp/platform/impl/windows/bluetooth_classic_server_socket.cc index 191ad17b..5af12b69 100644 --- a/cpp/platform/impl/windows/bluetooth_classic_server_socket.cc +++ b/cpp/platform/impl/windows/bluetooth_classic_server_socket.cc @@ -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 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(), true); + stream_socket_listener_.as(), radioDiscoverable); } catch (std::exception exception) { // We will log and eat the exception since the caller // expects nullptr if it fails diff --git a/cpp/platform/impl/windows/bluetooth_classic_server_socket.h b/cpp/platform/impl/windows/bluetooth_classic_server_socket.h index 8b10b25a..bbb274af 100644 --- a/cpp/platform/impl/windows/bluetooth_classic_server_socket.h +++ b/cpp/platform/impl/windows/bluetooth_classic_server_socket.h @@ -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, diff --git a/cpp/platform/impl/windows/generated/BUILD b/cpp/platform/impl/windows/generated/BUILD index e23d4d29..66124c3a 100644 --- a/cpp/platform/impl/windows/generated/BUILD +++ b/cpp/platform/impl/windows/generated/BUILD @@ -30,6 +30,8 @@ cc_library( "uuid.lib", "winmm.lib", "winspool.lib", + "comsuppwd.lib", + "setupapi.lib", ], textual_hdrs = glob(["**/*.h"]), visibility = [ diff --git a/cpp/platform/impl/windows/platform.cc b/cpp/platform/impl/windows/platform.cc index 99172c00..aa62ec58 100644 --- a/cpp/platform/impl/windows/platform.cc +++ b/cpp/platform/impl/windows/platform.cc @@ -122,7 +122,8 @@ ImplementationPlatform::CreateBluetoothAdapter() { std::unique_ptr ImplementationPlatform::CreateBluetoothClassicMedium( BluetoothAdapter& adapter) { - return absl::make_unique(); + return absl::make_unique( + adapter); } // TODO(b/184975123): replace with real implementation. diff --git a/cpp/platform/public/BUILD b/cpp/platform/public/BUILD index 45951c4a..29932a43 100644 --- a/cpp/platform/public/BUILD +++ b/cpp/platform/public/BUILD @@ -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__", ], diff --git a/proto/analytics/BUILD b/proto/analytics/BUILD new file mode 100644 index 00000000..75daa408 --- /dev/null +++ b/proto/analytics/BUILD @@ -0,0 +1,47 @@ +# 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. + +load("//tools/build_defs/proto/cpp:cc_proto_library.bzl", "cc_proto_library") + +licenses(["notice"]) + +package(default_visibility = ["//visibility:public"]) + +proto_library( + name = "connections_log_proto", + srcs = [ + "connections_log.proto", + ], + cc_api_version = 2, + compatible_with = ["//buildenv/target:non_prod"], + deps = [ + "//storage/datapol/annotations/proto:datapol_annotations", + "//proto:connections_enums_proto", + "//third_party/nearby_connections/proto/errorcode:error_code_enums_proto", + ], +) + +cc_proto_library( + name = "connections_log_cc_proto", + compatible_with = ["//buildenv/target:non_prod"], + visibility = [ + "//third_party/nearby_connections:__subpackages__", + ], + deps = [":connections_log_proto"], +) + +java_lite_proto_library( + name = "connections_log_java_proto_lite", + deps = [":connections_log_proto"], +) diff --git a/proto/analytics/connections_log.proto b/proto/analytics/connections_log.proto new file mode 100644 index 00000000..0db581e0 --- /dev/null +++ b/proto/analytics/connections_log.proto @@ -0,0 +1,357 @@ +// 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. + +syntax = "proto2"; + +package location.nearby.analytics.proto; + +import "storage/datapol/annotations/proto/semantic_annotations.proto"; +import "third_party/nearby_connections/proto/connections_enums.proto"; +import "third_party/nearby_connections/proto/errorcode/error_code_enums.proto"; + +option optimize_for = LITE_RUNTIME; +option java_package = "com.google.location.nearby.analytics.proto"; +option java_outer_classname = "ConnectionsLogProto"; +option objc_class_prefix = "GNCP"; + +// Top-level log proto for Nearby Connections. +// LINT.IfChange(ConnectionsLog) +message ConnectionsLog { + // The type of this log. + optional location.nearby.proto.connections.EventType event_type = 1; + + // Non-null for EventType.CLIENT_SESSION. + // Encapsulates all client activity between connecting to and disconnecting + // from the Nearby Connections API via Client. + optional ClientSession client_session = 2; + + // The version of Nearby Connections. E.g. "v1.0.4". + optional string version = 3 [(datapol.semantic_type) = ST_SOFTWARE_ID]; + + // for EventType.ERROR_CODE + optional ErrorCode error_code = 4; + + // Encapsulates one session of a client connected to Nearby Connections API. + message ClientSession { + // Elapsed time in milliseconds between Client connect and + // disconnect. + optional int64 duration_millis = 1; + + // Zero or more StrategySessions. + repeated StrategySession strategy_session = 2; + } + + // One round of a particular Strategy done by a client. + message StrategySession { + // Elapsed time in milliseconds between a call to startAdvertising/Discovery + // and the end of this particular Strategy. A StrategySession may end due to + // - the client disconnecting from Client; + // - a call to stopAllEndpoints, which disconnects all endpoints and + // stops any advertising/discovery; + // - a new call to startAdvertising/Discovery. + optional int64 duration_millis = 1; + + // The Strategy used for this session. + optional location.nearby.proto.connections.ConnectionsStrategy strategy = 2; + + // The role(s) played by this device during this StrategySession. + repeated location.nearby.proto.connections.SessionRole role = 3; + + // One or more of the following *Phase is present, depending on the role(s). + + // Encapsulates discovery information. + repeated DiscoveryPhase discovery_phase = 4; + // Encapsulates advertising information. + repeated AdvertisingPhase advertising_phase = 5; + + // Attempts at establishing a connection to another device. + repeated ConnectionAttempt connection_attempt = 6; + + // Successful and accepted connections to another device. + repeated EstablishedConnection established_connection = 7; + + // Attempts to upgrade a connection from one medium to another. + repeated BandwidthUpgradeAttempt upgrade_attempt = 9; + } + + // Encapsulates activity during a period of discovery. + message DiscoveryPhase { + // Elapsed time in milliseconds between startDiscovery and stopDiscovery. + optional int64 duration_millis = 1; + + // The Medium(s) used for discovery. + repeated location.nearby.proto.connections.Medium medium = 2; + + // Discovered endpoints during this round of discovery. + repeated DiscoveredEndpoint discovered_endpoint = 3; + + // Attempted ConnectionRequests (requested by the client). They may or + // may not reach the other endpoint. + repeated ConnectionRequest sent_connection_request = 4; + + // UWB ranging related data during discovery (May range with multiple + // endpoints) + repeated UwbRangingProcess uwb_ranging = 5; + + // The SendingEvent flow id. + optional int64 client_flow_id = 6 [(datapol.semantic_type) = ST_SESSION_ID]; + } + + // An endpoint discovered on a particular medium during discovery. + message DiscoveredEndpoint { + // The medium on which this endpoint was discovered. + optional location.nearby.proto.connections.Medium medium = 1; + + // Elapsed time between the call to startDiscovery() and the time at which + // this endpoint was discovered. + optional int64 latency_millis = 2; + } + + // Encapsulates activity during UWB ranging. + message UwbRangingProcess { + // Elapsed time in milliseconds between startRanging and stopRanging. + optional int64 duration_millis = 1; + + // UWB raw ranging data received during discovery. This is optional. Only + // certain devices (Debug/Testing etc.) will log the raw data. + repeated RawUwbRangingEvent uwb_ranging_data = 2; + + // Number of ranging data received + optional int32 number_of_ranging_data = 3; + + // The minimum distance during a UWB ranging session + optional int32 distance_min = 4; + + // The maximum distance during a UWB ranging session + optional int32 distance_max = 5; + + // The average distance during a UWB ranging session + optional int32 distance_ave = 6; + + // The distance variance during a UWB ranging session + optional int32 distance_variance = 7; + + // The minimum AoA during a UWB ranging session + optional int32 azimuth_min = 8; + + // The maximum AoA during a UWB ranging session + optional int32 azimuth_max = 9; + + // The average AoA during a UWB ranging session + optional int32 azimuth_ave = 10; + + // The AoA variance during a UWB ranging session + optional int32 azimuth_variance = 11; + } + + // Ranging data received during discovery phase. + message RawUwbRangingEvent { + // Distance in cm + optional int32 distance = 1; + + // Azimuth angle in degree + optional int32 azimuth_angle = 2; + + // Polar angle in degree (0 if the device doesn't support it) + optional int32 polar_angle = 3; + } + + // Encapsulates activity during a period of advertising. + message AdvertisingPhase { + // Elapsed time in milliseconds between startAdvertising and + // stopAdvertising. + optional int64 duration_millis = 1; + + // The Medium(s) used for advertising. + repeated location.nearby.proto.connections.Medium medium = 2; + + // Received ConnectionRequests from remote endpoints. + repeated ConnectionRequest received_connection_request = 3; + + // The ReceivingEvent flow id. + optional int64 client_flow_id = 4 [(datapol.semantic_type) = ST_SESSION_ID]; + } + + // A request to connect, corresponding to the API's concept of + // request/accept/rejectConnection(). + message ConnectionRequest { + // Elapsed time in milliseconds between the connection request being + // initiated and the responses being received. + optional int64 duration_millis = 1; + + // Elapsed time in milliseconds between the start of the containing + // Advertising/DiscoveryPhase and the start of this ConnectionRequest, i.e. + // the time at which the request is sent (on the discoverer, at the request + // of the client) or received (on the advertiser, over the wire from the + // remote endpoint). + optional int64 request_delay_millis = 2; + + // The local endpoint's response to this connection request. + optional location.nearby.proto.connections.ConnectionRequestResponse + local_response = 3; + + // The remote endpoint's response to this connection request. + optional location.nearby.proto.connections.ConnectionRequestResponse + remote_response = 4; + + // The SendingEvent flow id. + optional int64 client_flow_id = 5 [(datapol.semantic_type) = ST_SESSION_ID]; + } + + // An attempt to connect to an endpoint over a particular medium. + message ConnectionAttempt { + // Elapsed time in milliseconds between starting the connection attempt + // and succeeding/failing. + optional int64 duration_millis = 1; + + // The type of connection attempt. + optional location.nearby.proto.connections.ConnectionAttemptType type = 2; + + // The direction (incoming vs outgoing) of this attempt. + optional location.nearby.proto.connections.ConnectionAttemptDirection + direction = 3; + + // The Medium of this connection attempt. + optional location.nearby.proto.connections.Medium medium = 4; + + // The result of the connection attempt. + optional location.nearby.proto.connections.ConnectionAttemptResult + attempt_result = 5; + + // The ReceivingEvent flow id. + optional int64 client_flow_id = 6 [(datapol.semantic_type) = ST_SESSION_ID]; + + // The token used to identify this connection pair. + optional string connection_token = 7 + [(datapol.semantic_type) = ST_SESSION_ID]; + } + + // A successfully-established connection over a particular medium. + message EstablishedConnection { + // Elapsed time in milliseconds that the connection is active. + optional int64 duration_millis = 1; + + // The Medium of this connection. + optional location.nearby.proto.connections.Medium medium = 2; + + // Payloads sent over this connection. + repeated Payload sent_payload = 3; + + // Payloads received over this connection. + repeated Payload received_payload = 4; + + // The reason this connection was disconnected. + optional location.nearby.proto.connections.DisconnectionReason + disconnection_reason = 5; + + // The SendingEvent flow id. + optional int64 client_flow_id = 6 [(datapol.semantic_type) = ST_SESSION_ID]; + + // The token use to identify this established connection. + optional string connection_token = 7 + [(datapol.semantic_type) = ST_SESSION_ID]; + } + + // A Payload transferred (or attempted to be transferred) between devices. + message Payload { + // Elapsed time in milliseconds that num_bytes_transferred took to transfer. + optional int64 duration_millis = 1; + + // The type of this payload. + optional location.nearby.proto.connections.PayloadType type = 2; + + // Total size of the payload in bytes. + optional int64 total_size_bytes = 3; + + // Total number of bytes transferred successfully. + optional int64 num_bytes_transferred = 4; + + // The number of chunks used to transfer num_bytes_transferred. + optional int32 num_chunks = 5; + + // The end status of the payload transfer. + optional location.nearby.proto.connections.PayloadStatus status = 6; + } + + // An attempt to upgrade an existing connection from one medium to another. + message BandwidthUpgradeAttempt { + // The direction (incoming vs outgoing) of the upgrade attempt. + optional location.nearby.proto.connections.ConnectionAttemptDirection + direction = 1; + + // Elapsed time in milliseconds of the upgrade attempt. + optional int64 duration_millis = 2; + + // The original medium (e.g. bluetooth). + optional location.nearby.proto.connections.Medium from_medium = 3; + + // The new medium that we're hoping to upgrade to (e.g. wifi). + optional location.nearby.proto.connections.Medium to_medium = 4; + + // The result of the upgrade attempt. + optional location.nearby.proto.connections.BandwidthUpgradeResult + upgrade_result = 5; + + // If upgrade_result is not success, the stage at which the error occurred. + optional location.nearby.proto.connections.BandwidthUpgradeErrorStage + error_stage = 6; + + // The SendingEvent flow id. + optional int64 client_flow_id = 7 [(datapol.semantic_type) = ST_SESSION_ID]; + + // The token used to identify this upgrade pair. + optional string connection_token = 8 + [(datapol.semantic_type) = ST_SESSION_ID]; + } + + // Next Id: 17 + message ErrorCode { + // The direction (incoming vs outgoing) of this error. + optional location.nearby.proto.connections.ConnectionAttemptDirection + direction = 1; + optional string service_id = 2; + // The error medium (e.g. bluetooth). + optional location.nearby.proto.connections.Medium medium = 3; + // The event which the error occurs on. + optional location.nearby.errorcode.proto.Event event = 4; + // The error description. + optional location.nearby.errorcode.proto.Description description = 5; + // The flow id which the error occurs on. + optional int64 flow_id = 6 [(datapol.semantic_type) = ST_SESSION_ID]; + + // Error code value + oneof ErrorCodeDetail { + location.nearby.errorcode.proto.CommonError common_error = 7; + location.nearby.errorcode.proto.StartAdvertisingError + start_advertising_error = 8; + location.nearby.errorcode.proto.StartDiscoveringError + start_discovering_error = 9; + location.nearby.errorcode.proto.StopAdvertisingError + stop_advertising_error = 10; + location.nearby.errorcode.proto.StopDiscoveringError + stop_discovering_error = 11; + location.nearby.errorcode.proto.StartListeningIncomingConnectionError + start_listening_incoming_connection_error = 12; + location.nearby.errorcode.proto.StopListeningIncomingConnectionError + stop_listening_incoming_connection_error = 13; + location.nearby.errorcode.proto.ConnectError connect_error = 14; + location.nearby.errorcode.proto.DisconnectError disconnect_error = 15; + } + + // The token use to identify this established connection. + optional string connection_token = 16 + [(datapol.semantic_type) = ST_SESSION_ID]; + } +} +// LINT.ThenChange() diff --git a/proto/connections/offline_wire_formats.proto b/proto/connections/offline_wire_formats.proto index 4ad85222..058266f0 100644 --- a/proto/connections/offline_wire_formats.proto +++ b/proto/connections/offline_wire_formats.proto @@ -303,6 +303,12 @@ message MediumMetadata { optional AvailableChannels available_channels = 7; // Usable WiFi Direct client channels on the local device. optional WifiDirectCliUsableChannels wifi_direct_cli_usable_channels = 8; + // Usable WiFi LAN channels on the local device. + optional WifiLanUsableChannels wifi_lan_usable_channels = 9; + // Usable WiFi Aware channels on the local device. + optional WifiAwareUsableChannels wifi_aware_usable_channels = 10; + // Usable WiFi Hotspot STA channels on the local device. + optional WifiHotspotStaUsableChannels wifi_hotspot_sta_usable_channels = 11; } // Available channels on the local device. @@ -315,6 +321,21 @@ message WifiDirectCliUsableChannels { repeated int32 channels = 1 [packed = true]; } +// Usable WiFi LAN channels on the local device. +message WifiLanUsableChannels { + repeated int32 channels = 1 [packed = true]; +} + +// Usable WiFi Aware channels on the local device. +message WifiAwareUsableChannels { + repeated int32 channels = 1 [packed = true]; +} + +// Usable WiFi Hotspot STA channels on the local device. +message WifiHotspotStaUsableChannels { + repeated int32 channels = 1 [packed = true]; +} + // LocationHint is used to specify a location as well as format. message LocationHint { // Location is the location, provided in the format specified by format. diff --git a/proto/errorcode/BUILD b/proto/errorcode/BUILD new file mode 100644 index 00000000..7f3ffe7a --- /dev/null +++ b/proto/errorcode/BUILD @@ -0,0 +1,34 @@ +# 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. + +# Proto for Nearby products + +licenses(["notice"]) + +package(default_visibility = ["//visibility:public"]) + +proto_library( + name = "error_code_enums_proto", + srcs = ["error_code_enums.proto"], + cc_api_version = 2, + compatible_with = ["//buildenv/target:non_prod"], + deps = [ + "//logs/proto/logs_annotations", + ], +) + +java_lite_proto_library( + name = "error_code_enums_java_proto_lite", + deps = [":error_code_enums_proto"], +) diff --git a/proto/errorcode/error_code_enums.proto b/proto/errorcode/error_code_enums.proto new file mode 100644 index 00000000..6c26d99d --- /dev/null +++ b/proto/errorcode/error_code_enums.proto @@ -0,0 +1,500 @@ +// 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. + +syntax = "proto2"; + +package location.nearby.errorcode.proto; + + + + +option optimize_for = LITE_RUNTIME; +option java_package = "com.google.location.nearby.errorcode.proto"; +option java_outer_classname = "ErrorCodeEnums"; +option objc_class_prefix = "GNCP"; + +// The type of the error. +// It help to sort error codes to different types to analyze and also impact the +// logcat print it as Warning or Severe. +enum ErrorType { + UNKNOWN_TYPE = 0; + + // The error should not happen on production, it's like the input is null or + // not invalid or it's a unexpected API call. For example, start advertising + // with empty service ID or start advertising with the same service ID twice. + DEVELOPING = 1; + + // It’s about the device's capabilities, some devices may not support the + // feature Nearby used. E.g. The device not support BLE advertising + DEVICE = 2; + + // The failure return from the system or library API we used to communicate + // with the medium. E.g. get null OS objects or call the API but get a + // negative return value which indicates that the system does not allow to do + // that now. + SYSTEM = 3; + + // The network related failure. E.g. get an EOF exception while reading pipe + // or fail to create connection. + NETWORK = 4; + + // This may not be a failure, it can be the things we are interested in, like + // to count how many BLE advertisements the device received in a specified + // period and how many different advertisements in it, it can help us to know + // the user under a clean or dirty environment. + OTHERS = 5; +} + +// The event which the error occurs on. +enum Event { + UNKNOWN_EVENT = 0; + START_ADVERTISING = 1; + STOP_ADVERTISING = 2; + START_LISTENING_INCOMING_CONNECTION = 3; + STOP_LISTENING_INCOMING_CONNECTION = 4; + START_DISCOVERING = 5; + STOP_DISCOVERING = 6; + CONNECT = 7; + DISCONNECT = 8; + ACCEPT_CONNECTION = 9; + REJECT_CONNECTION = 10; + SEND_PAYLOAD = 11; + CANCEL_PAYLOAD = 12; + RECEIVE_PAYLOAD = 13; +} + +// The error to identify the common failure for all mediums. The range between 0 +// and 30. +enum CommonError { + UNKNOWN_ERROR = 0; + + // The common error for all mediums, the range between 0 and 30. + + // Developing error, the input with invalid format or empty. + INVALID_PARAMETER = 1; + // Device error, the medium not available on this device. + MEDIUM_NOT_AVAILABLE = 2; + // System error, the medium in the unexpected state, e.g. we have check the + // medium is on, after then it suddently off and cause Nearby + // Connection failed. + UNEXPECTED_MEDIUM_STATE = 3; + // System error, the medim not available because the resource ran out. e.g. + // the Wi-Fi Direct initialized cause Wi-Fi Aware not available, or BLE + // connections hit the maximan number, or Wi-Fi Hotstop already created. + OUT_OF_RESOURCE = 4; + // Others error, the error happens when user cancel the flow, it's not a + // real failure. + FLOW_CANCELED = 5; + // Developing error, an unexpect call that the medium not ready, need to do + // something before this call. e.g. call WifiAwareImpli#connectToSocket but + // never join network before this call. + UNEXPECTED_CALL = 6; + + // Reserved 7 to 30 +} + +// The error for event START_ADVERTISING. The range between 31 and 99. +enum StartAdvertisingError { + reserved 37, 39; + + // Developing error, not allow to advertising fast pair model id and sharing + // fast advertisement at the same time, they are both use fast + // advertisement, and only allow 1 fast advertisement at the same time. + MULTIPLE_FAST_ADVERTISEMENT_NOT_ALLOWED = 31; + // System error, there's already someone advertising fast advertisement, not + // allow to start another one. + FAST_ADVERTISEMENT_ALREADY_ADVERTISED = 32; + // Developing error, this service ID already requested, should not request + // it again without stop advertising. + DUPLICATE_ADVERTISING_REQUESTED = 33; + // System error, failed to start GATT server + START_GATT_SERVER_FAILED = 34; + // System error, all advertising slot ran out, can't available for new + // regular advertisement. + BLE_MAX_GATT_ADVERTISEMENT_SLOT_REACHED = 35; + // System error, failed to start advertising for legacy advertisements on BLE + START_LEGACY_ADVERTISING_FAILED = 36; + // System error, failed to start advertising for extended advertisements on + // BLE + START_EXTENDED_ADVERTISING_FAILED = 38; + // System error, there's already someone advertising on Bluetooth, not allow + // to start another one. + BLUETOOTH_ALREADY_ADVERTISED = 40; + // System error, failed to modify the Bluetooth name. + MODIFY_BLUETOOTH_NAME_FAILED = 41; + // System error, failed to persist the original Bluetooth name into shared + // preference. + PERSIST_ORIGINAL_BLUETOOTH_NAME_FAILED = 42; + // System error, failed to start advertising. + START_ADVERTISING_FAILED = 43; + + // Developing error, not allow to advertising on Wi-Fi Lan(TDLS) without + // accetpting connections. The connection may comes in very quickly, so need + // to accetpt connections before advertising. + SHOULD_ACCEPT_CONNECTIONS_BEFORE_ADVERTISING_ON_WIFI_LAN = 44; + // System error, failed to acquire WifiAwareSession + ACQUIRE_WIFI_AWARE_SESSION_FAILED = 45; + + // The default value for clearcut + UNKNOWN_START_ADVERTISING_ERROR = 46; + + // System error, failed to update Aware PublishConfig + AWARE_UPDATE_PUBLISHING_FAILED = 47; + + // Next ID :48 +} + +// The error for event STOP_ADVERTISING. The range between 31 and 99. +enum StopAdvertisingError { + // System error, failed to stop advertising. + STOP_ADVERTISING_FAILED = 31; + // System error, failed to modify the Bluetooth name. + RESTORE_BLUETOOTH_NAME_FAILED = 32; + // System error, failed to stop advertising for BLE legacy advertisements. + STOP_LEGACY_ADVERTISING_FAILED = 33; + // System error, failed to stop advertising for BLE extended advertisements. + STOP_EXTENDED_ADVERTISING_FAILED = 34; + + // The default value for clearcut + UNKNOWN_STOP_ADVERTISING_ERROR = 35; + + // Next ID :36 +} + +// The error for event START_DISCOVERING. The range between 31 and 99. +enum StartDiscoveringError { + // Developing error, this service ID already requested, should not request it + // again without stop discovering. + DUPLICATE_DISCOVERING_REQUESTED = 31; + // System error, failed to start discovering for legacy advertisements on BLE + START_LEGACY_DISCOVERING_FAILED = 32; + // System error, failed to start discovering for extended advertisements on + // BLE + START_EXTENDED_DISCOVERING_FAILED = 33; + // System error, failed to start discovering. + START_DISCOVERING_FAILED = 34; + // Network error, invalid remote target info, discover the nearby devices but + // the information not valid. + INVALID_TARGET_INFO = 35; + // Network error, failed to fetch the advertisement from the remote devices. + FETCH_ADVERTISEMENT_FAILED = 36; + // Network error, failed to fetch the advertisement via GATT from the remote + // devices. + GATT_FETCH_ADVERTISEMENT_FAILED = 37; + // Network error, failed to fetch the advertisement via L2CAP from the remote + // devices. + L2CAP_FETCH_ADVERTISEMENT_FAILED = 38; + // System error, the medium not available when trying to fetch advertisements. + // e.g. fetch advertisements but BT disabled unexpectedly. + NOT_AVAILABLE_TO_FETCH_ADVERTISEMENT = 39; + // System error, failed to acquire WifiAwareSession + ACQUIRE_WIFI_AWARE_SESSION_FOR_DISCOVERING_FAILED = 40; + + // The default value for clearcut + UNKNOWN_START_DISCOVERING_ERROR = 41; + + // Next ID :42 +} + +// The error for event STOP_DISCOVERING. The range between 31 and 99. +enum StopDiscoveringError { + // System error, failed to stop discovering. + STOP_DISCOVERING_FAILED = 31; + // System error, failed to stop discovering for BLE legacy scanning. + STOP_LEGACY_DISCOVERING_FAILED = 32; + // System error, failed to stop discovering for BLE extended scanning. + STOP_EXTENDED_DISCOVERING_FAILED = 33; + + // The default value for clearcut + UNKNOWN_STOP_DISCOVERING_ERROR = 34; + + // Next ID :35 +} + +// The error for event START_LISTENING_INCOMING_CONNECTION. The range between 31 +// and 99. +enum StartListeningIncomingConnectionError { + // Developing error, this service ID already requested, should not request it + // again without stop accepting. + DUPLICATE_ACCEPTING_CONNECTION_REQUESTED = 31; + // System error, failed to open a GATT server for listening incoming GATT + // connection. + OPEN_GATT_SERVER_FAILED = 32; + // System error, failed to accept the incoming GATT connection + ACCEPT_GATT_CONNECTION_FAILED = 33; + // System error, failed to accept the incoming L2CAP connection + ACCEPT_L2CAP_CONNECTION_FAILED = 34; + // Network error, wait the GATT connection ready after the connection + // established but never. + CREATE_GATT_SERVER_SOCKET_NOT_READY = 35; + // System error, failed to accept the incoming connection + ACCEPT_CONNECTION_FAILED = 36; + // System error, failed to create a server socket for listening incoming + // connection. + CREATE_SERVER_SOCKET_FAILED = 37; + + // The default value for clearcut + UNKNOWN_START_LISTENING_INCOMING_CONNECTION_ERROR = 38; + + // Network error, failed to send L2 Message to the remote peer + ACCEPT_SEND_AWARE_L2_MESSAGE_FAILED = 39; + // Network error, failed to receive L2 Message from the remote peer + ACCEPT_RECEIVE_AWARE_L2_MESSAGE_FAILED = 40; + + // Next ID :41 +} + +// The error for event STOP_LISTENING_INCOMING_CONNECTION. The range between 31 +// and 99. +enum StopListeningIncomingConnectionError { + // System error, failed to stop accepting the incoming connection + STOP_ACCEPTING_CONNECTION_FAILED = 31; + + // The default value for clearcut + UNKNOWN_STOP_LISTENING_INCOMING_CONNECTION_ERROR = 32; + + // Next ID :33 +} + +// The error for event CONNECT. The range between 31 and 99. +enum ConnectError { + // Network error, failed to connect to remote device because we lost the + // target without MAC address to connect to. e.g. BLE cache MAC address in + // medium, it may lost when just try to connect. + UNEXPECT_TARGET_LOST = 31; + // System error, failed to establish connection on GATT + ESTABLISH_GATT_CONNECTION_FAILED = 32; + // System error, failed to establish connection on L2CAP + ESTABLISH_L2CAP_CONNECTION_FAILED = 33; + // Developing error, the MAC address not valid for connecting + INVALID_MAC_ADDRESS = 34; + // Others error, unexpected interrupt when sleep before connect GATT for + // waiting GATT server ready. Should not hapepen, it may be the process be + // killed. + SLEEP_BEFORE_CONNECT_GATT_INTERRUPTED = 35; + // Others error, unexpected interrupt when sleep after GATT connect to wait + // GATT connection ready to transfer data. Should not hapepen, it may be + // the process be killed. + SLEEP_AFTER_GATT_CONNECTED_INTERRUPTED = 36; + // Network error, failed to configure the GATT connection priority, it may + // failed when the connection still wait for the status update from network or + // just a RemoteException. + REQUEST_GATT_CONNECTION_PRIORITY_FAILED = 37; + // Network error, failed to change connection for data transferring on L2CAP + // connection. + L2CAP_SWITCH_TO_DATA_TRANSFERRING_FAILED = 38; + // Network error, failed to change connection for data transferring on GATT + // connection. + GATT_SWITCH_TO_DATA_TRANSFERRING_FAILED = 39; + // System error, failed to establish connection + ESTABLISH_CONNECTION_FAILED = 40; + // Developing error, this connection already established, should not request + // it again. + DUPLICATE_CONNECTION_REQUESTED = 41; + // Network error, the connection lost. + CONNECTION_LOST = 42; + // Network error, failed to connect to the network. e.g. an aware network, + // hotspot or a direct network. + CONNECT_TO_NETWORK_FAILED = 43; + + // The default value for clearcut + UNKNOWN_CONNECT_ERROR = 44; + + // Network error, failed to send L2 Message to the remote peer + CONNECT_SEND_AWARE_L2_MESSAGE_FAILED = 45; + // Network error, failed to send L2 Message to the remote peer + CONNECT_READ_AWARE_L2_MESSAGE_FAILED = 46; + + // Next ID :47 +} + +// The error for event DISCONNECT. The range between 31 and 99. +enum DisconnectError { + // System error, failed to disconnect the network. + DISCONNECT_NETWORK_FAILED = 31; + + // The default value for clearcut + UNKNOWN_DISCONNECT_ERROR = 32; + + // Next ID :33 +} + +enum Description { + reserved 28, 29; + + UNKNOWN = 0; + NULL_SERVICE_ID = 1; + NULL_ADVERTISEMENT_BYTES = 2; + CONNECTIONS_FEATURE_DISABLED = 3; + STALE_SDK_VERSION = 4; + FEATURE_BLUETOOTH_NOT_SUPPORTED = 5; + FEATURE_BLUETOOTH_LE_NOT_SUPPORTED = 6; + NULL_BLUETOOTH_MANAGER = 7; + NULL_BLUETOOTH_ADAPTER = 8; + INVALID_FAST_PAIR_MODEL_ID = 9; + INVALID_FAST_ADVERTISEMENT_DATA = 10; + INVALID_ADVERTISEMENT_HEADER_DATA = 11; + INVALID_REGULAR_ADVERTISEMENT_DATA = 12; + NULL_BLUETOOTH_LE_ADVERTISER_COMPAT = 13; + ADVERTISE_FAILED_ALREADY_STARTED = 14; + ADVERTISE_FAILED_DATA_TOO_LARGE = 15; + ADVERTISE_FAILED_FEATURE_UNSUPPORTED = 16; + ADVERTISE_FAILED_INTERNAL_ERROR = 17; + ADVERTISE_FAILED_TOO_MANY_ADVERTISERS = 18; + INTERRUPTED_EXCEPTION = 19; + EXECUTION_EXCEPTION = 20; + NULL_BLUETOOTH_DEVICE_NAME = 21; + SET_SCAN_MODE_FAILED = 22; + INVOKE_API_FAILED = 23; + TIMEOUT = 24; + NULL_NFC_TAG = 25; + FEATURE_NFC_NOT_SUPPORTED = 26; + FEATURE_NFC_HOST_CARD_EMULATION_NOT_SUPPORTED = 27; + MULTICAST_NOT_SUPPORTED = 30; + NSD_NOT_ENABLED = 31; + INVALID_PORT_NUMBER = 32; + NULL_SERVICE_NAME = 33; + NULL_SERVICE_TYPE = 34; + WITHOUT_CONNECTED_WIFI_NETWORK = 35; + FEATURE_WIFI_AWARE_NOT_SUPPORTED = 36; + NULL_CONNECTIVITY_MANAGER = 37; + NULL_WIFI_AWARE_MANAGER = 38; + STALE_ANDROID_VERSION = 39; + NULL_SERVICE_INFO = 40; + NULL_WORK_SOURCE = 41; + NULL_CALLBACK = 42; + NULL_BLUETOOTH_LE_SCANNER_COMPAT = 43; + EMPTY_WORK_SOURCE_CACHE = 44; + SCAN_FAILED_ALREADY_STARTED = 45; + SCAN_FAILED_APPLICATION_REGISTRATION_FAILED = 46; + SCAN_FAILED_INTERNAL_ERROR = 47; + SCAN_FAILED_FEATURE_UNSUPPORTED = 48; + SCAN_FAILED_BLUETOOTH_DISABLED = 49; + SCAN_FILTERS_NOT_ALLOWED_FOR_LOCATION = 50; + BLUETOOTH_SCAN_REJUVENATE_FAILED = 51; + NULL_BLE_PERIPHERAL = 52; + NULL_BLUETOOTH_GATT = 53; + UNEXPECTED_BLUETOOTH_STATE = 54; + REMOTE_EXCEPTION = 55; + INVALID_BLUETOOTH_SOCKET_STATE_BEFORE_CONNECT = 56; + BLUETOOTH_SOCKET_CLOSED_AFTER_CONNECTED = 57; + INVALID_BLUETOOTH_CHANNEL = 58; + NULL_BLUETOOTH_DEVICE = 59; + NULL_BLUETOOTH_PROXY = 60; + INVALID_PACKET_LENGTH = 61; + INVALID_PACKET_BYTES = 62; + UNEXPECTED_EOF_EXCEPTION = 63; + SOCKET_CLOSED_OR_TIMEOUT = 64; + INVALID_IPV4_ADDRESS = 65; + INVALID_IPV6_ADDRESS = 66; + NULL_ADDRESS = 67; + INVALID_VERSION = 68; + SET_CONNECTION_PRIORITY_FAILED = 69; + SET_CONNECTION_PRIORITY_INTERRUPTED = 70; + UNKNOWN_IO_EXCEPTION = 71; + READ_CHARACTERISTIC_FAILED = 72; + WIFI_HOTSPOT_ENABLED = 73; + AWARE_UNAVAILABLE = 74; + IN_BLACK_LIST = 75; + FEATURE_WIFI_NOT_SUPPORTED = 76; + NULL_WIFI_MANAGER = 77; + SOCKET_CLOSED = 78; + SOCKET_ALREADY_CONNECTED = 79; + NFC_TECH_NOT_SUPPORTED = 80; + NFC_SERVICE_DIED = 81; + BIND_NFC_SERVICE_FAILED = 82; + NFC_CREATE_SOCKET_FAILED = 83; + NULL_WIFI_AWARE_PEER = 84; + NETWORK_ALREADY_JOINED = 85; + JOIN_AWARE_NETWORK_CANCELLED = 86; + NETWORK_UNAVAILABLE = 87; + WITHOUT_ACTIVE_AWARE_NETWORK = 88; + WITHOUT_JOINED_AWARE_NETWORK = 89; + CONNET_TO_SOCKET_CANCELLED = 90; + NULL_SSID = 91; + NULL_PASSWORD = 92; + FEATURE_WIFI_DIRECT_NOT_SUPPORTED = 93; + NULL_WIFI_P2P_MANAGER = 94; + P2P_GROUP_FORMED = 95; + ACQUIRE_P2P_CHANNEL_FAILED = 96; + P2P_UNSUPPORTED = 97; + INTERNAL_ERROR = 98; + BUSY = 99; + REFLECTION_ERROR = 100; + NETWORK_ERROR_EHOSTUNREACH = 101; + NETWORK_ERROR_ENETUNREACH = 102; + ADD_NETWORK_FAILED = 103; + UPDATE_NETWORK_FAILED = 104; + ALREADY_IN_PROGRESS = 105; + INVALID_ARGS = 106; + NOT_AUTHORIZED = 107; + INVALID_NETWORK_ID = 108; + WIFI_MANAGER_ENABLE_NETWORK_FAILED = 109; + WIFI_MANAGER_RECONNECT_FAILED = 110; + WITHOUT_ACTIVE_NETWORK = 111; + WEBRTC_CONNECTION_FLOW_EXIST = 112; + NULL_DROID_GUARD_RESULT = 113; + TACHYON_SIGNALING_MESSENGER_EXIST = 114; + TACHYON_ALREADY_START_RECEIVE_MESSAGE = 115; + TACHYON_RECEIVE_MESSAGE_FAILED = 116; + TACHYON_RECEIVE_MESSAGE_INTERRUPTED = 117; + TACHYON_RECEIVE_MESSAGE_EXECUTION_EXCEPTION = 118; + TACHYON_RECEIVE_MESSAGE_TIMEOUT = 119; + TACHYON_RECEIVE_MESSAGE_AUTH_EXCEPTION = 120; + TACHYON_RECEIVE_MESSAGE_STATUS_EXCEPTION = 121; + TACHYON_SEND_MESSAGE_AUTH_EXCEPTION = 122; + TACHYON_SEND_MESSAGE_STATUS_EXCEPTION = 123; + TACHYON_GET_ICE_SERVER_AUTH_EXCEPTION = 124; + TACHYON_GET_ICE_SERVER_STATUS_EXCEPTION = 125; + EMPTY_TACHYON_ICE_SERVER = 126; + POTENTIAL_WEBRTC_LIB_LOADING_FAILURE = 127; + UNEXPECTED_GATT_DESCRIPTOR = 128; + FAIL_TO_RECEIVE_L2CAP_PACKET = 129; + WITHOUT_PSM_VALUE = 130; + SOCKET_BIND_LISTEN_FAILED = 131; + UNEXPECTED_PACKET_CONTENT = 132; + UNREGISTER_NSD_MANAGER_FAILED = 133; + PUBLISH_EMPTY_ADVERTISEMENT_FAILED = 134; + BLUETOOTH_SOCKET_NOT_IN_LISTENING_STATE = 135; + INVALID_BLUETOOTH_SOCKET_SIGNAL_SIZE = 136; + INVALID_BLUETOOTH_SOCKET_SIGNAL_STATUS = 137; + GET_ADDRESS_FAILED = 138; + NULL_LOCAL_ADDRESS = 139; + IS_LOOPBACK_ADDRESS = 140; + SOCKET_NOT_BOUND = 141; + INVALID_REMOTE_ADDRESS = 142; + SOCKET_ALREADY_BOUND = 143; + HOTSPOT_NOT_STARTED = 144; + WEBRTC_ALREADY_INITIALIZED = 145; + INVALID_WEBRTC_STATE = 146; + NULL_DATA_CHANNEL = 147; + CREATE_OFFER_FAILED = 148; + CLOSE_SERVER_SOCKET_FAILED = 149; + WIFI_AWARE_STARTED = 150; + AWARE_PUBLISH_SESSION_RAN_OUT = 151; + AWARE_SUBSCRIBE_SESSION_RAN_OUT = 152; + AWARE_DATA_PATH_RAN_OUT = 153; + WIFI_SIGNAL_STRENGTH_POOR = 154; + POTENTIAL_SRD_ISSUE = 155; + AWARE_L2_MESSAGE_HOST_NETWORK_ERROR = 156; + AWARE_L2_MESSAGE_NETWORK_AVAILABLE_ERROR = 157; + AWARE_L2_MESSAGE_IP_AVAILABLE_ERROR = 158; + AWARE_L2_MESSAGE_CANCELLATION_RECEIVED = 159; + TIE_BREAK_LOSER = 160; + SERVER_SOCKET_UNAVAILABLE = 161; + HOSTED_NETWORK_UNAVAILABLE = 162; + AWARE_UPDATE_PUBLISHING_CONFIG_FAILED = 163; + L2CAP_UNAVAILABLE = 164; + ALREADY_HAS_GATT_CONNECTION = 165; +}