Files
nearby/connections/implementation/client_proxy.h
T
Johnson Lu ee9266b175 Implement Dynamic Role Switch
Implement dynamic role switch in bwu manager. When NC receive BWU request, it will check if it should switch role based on the device's capability.

PiperOrigin-RevId: 755703443
2025-05-06 23:38:16 -07:00

555 lines
24 KiB
C++

// Copyright 2021 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 CORE_INTERNAL_CLIENT_PROXY_H_
#define CORE_INTERNAL_CLIENT_PROXY_H_
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "absl/functional/any_invocable.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "connections/advertising_options.h"
#include "connections/connection_options.h"
#include "connections/discovery_options.h"
#include "connections/implementation/analytics/analytics_recorder.h"
#include "connections/implementation/proto/offline_wire_formats.pb.h"
#include "connections/listeners.h"
#include "connections/medium_selector.h"
#include "connections/payload.h"
#include "connections/status.h"
#include "connections/strategy.h"
#include "connections/v3/connection_listening_options.h"
#include "connections/v3/connections_device_provider.h"
#include "connections/v3/listeners.h"
#include "internal/analytics/event_logger.h"
#include "internal/interop/device.h"
#include "internal/interop/device_provider.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/cancelable_alarm.h"
#include "internal/platform/cancellation_flag.h"
#include "internal/platform/error_code_recorder.h"
#include "internal/platform/mutex.h"
// Prefer using absl:: versions of a set and a map; they tend to be more
// efficient: implementation is using open-addressing hash tables.
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/types/span.h"
#include "internal/platform/os_name.h"
#include "internal/platform/scheduled_executor.h"
#include "internal/proto/analytics/connections_log.pb.h"
namespace nearby {
namespace connections {
// ClientProxy is tracking state of client's connection, and serves as
// a proxy for notifications sent to this client.
class ClientProxy final {
public:
static constexpr int kEndpointIdLength = 4;
static constexpr absl::Duration
kHighPowerAdvertisementEndpointIdCacheTimeout = absl::Seconds(30);
explicit ClientProxy(
::nearby::analytics::EventLogger* event_logger = nullptr);
~ClientProxy();
ClientProxy(ClientProxy&&) = default;
ClientProxy& operator=(ClientProxy&&) = default;
std::int64_t GetClientId() const;
std::string GetLocalEndpointId();
std::string GetLocalEndpointInfo() { return local_endpoint_info_; }
analytics::AnalyticsRecorder& GetAnalyticsRecorder() const {
return *analytics_recorder_;
}
std::string GetConnectionToken(const std::string& endpoint_id);
std::optional<std::string> GetBluetoothMacAddress(
const std::string& endpoint_id);
void SetBluetoothMacAddress(const std::string& endpoint_id,
const std::string& bluetooth_mac_address);
const NearbyDevice* GetLocalDevice();
NearbyDeviceProvider* GetLocalDeviceProvider() {
if (external_device_provider_ != nullptr) {
return external_device_provider_;
}
return connections_device_provider_.get();
}
// Clears all the runtime state of this client.
void Reset();
// Marks this client as advertising with the given callbacks.
void StartedAdvertising(
const std::string& service_id, Strategy strategy,
const ConnectionListener& connection_lifecycle_listener,
absl::Span<location::nearby::proto::connections::Medium> mediums,
const std::vector<location::nearby::analytics::proto::ConnectionsLog::
OperationResultWithMedium>&
operation_result_with_medium,
const AdvertisingOptions& advertising_options = AdvertisingOptions{});
// Marks this client as not advertising.
void StoppedAdvertising();
bool IsAdvertising() const;
std::string GetAdvertisingServiceId() const;
// Marks this client as listening for incoming connections.
void StartedListeningForIncomingConnections(
absl::string_view service_id, Strategy strategy,
v3::ConnectionListener listener,
const v3::ConnectionListeningOptions& options);
void StoppedListeningForIncomingConnections();
bool IsListeningForIncomingConnections() const;
std::string GetListeningForIncomingConnectionsServiceId() const;
ConnectionListener GetAdvertisingOrIncomingConnectionListener();
// Marks this client as discovering with the given callback.
void StartedDiscovery(
const std::string& service_id, Strategy strategy,
DiscoveryListener discovery_listener,
absl::Span<location::nearby::proto::connections::Medium> mediums,
const std::vector<location::nearby::analytics::proto::ConnectionsLog::
OperationResultWithMedium>&
operation_result_with_medium,
const DiscoveryOptions& discovery_options = DiscoveryOptions{});
// Marks this client as not discovering at all.
void StoppedDiscovery();
bool IsDiscoveringServiceId(const std::string& service_id) const;
bool IsDiscovering() const;
std::string GetDiscoveryServiceId() const;
void UpdateLocalEndpointInfo(absl::string_view endpoint_info) {
MutexLock lock(&mutex_);
local_endpoint_info_ = std::string(endpoint_info);
}
void UpdateAdvertisingOptions(const AdvertisingOptions& advertising_options) {
MutexLock lock(&mutex_);
advertising_options_ = advertising_options;
}
void UpdateDiscoveryOptions(const DiscoveryOptions& discovery_options) {
MutexLock lock(&mutex_);
discovery_options_ = discovery_options;
}
// Proxies to the client's DiscoveryListener::OnEndpointFound() callback.
void OnEndpointFound(const std::string& service_id,
const std::string& endpoint_id,
const ByteArray& endpoint_info,
location::nearby::proto::connections::Medium medium);
// Proxies to the client's DiscoveryListener::OnEndpointLost() callback.
void OnEndpointLost(const std::string& service_id,
const std::string& endpoint_id);
// Triggered when client request connection to remote device.
void OnRequestConnection(const Strategy& strategy,
const std::string& endpoint_id,
const ConnectionOptions& connection_options);
// Proxies to the client's ConnectionListener::OnInitiated() callback.
void OnConnectionInitiated(const std::string& endpoint_id,
const ConnectionResponseInfo& info,
const ConnectionOptions& connection_options,
const ConnectionListener& listener,
const std::string& connection_token);
// Proxies to the client's ConnectionListener::OnAccepted() callback.
void OnConnectionAccepted(const std::string& endpoint_id);
// Proxies to the client's ConnectionListener::OnRejected() callback.
void OnConnectionRejected(const std::string& endpoint_id,
const Status& status);
void OnBandwidthChanged(const std::string& endpoint_id, Medium new_medium);
// Removes the endpoint from this client's list of connected endpoints. If
// notify is true, also calls the client's
// ConnectionListener.disconnected_cb() callback.
void OnDisconnected(const std::string& endpoint_id, bool notify);
// Returns the medium we're currently connected to the endpoint over, or
// UNKNOWN if we don't know or don't have a connection.
Medium GetConnectedMedium(const std::string& endpoint_id) const;
// Returns all mediums eligible for upgrade.
BooleanMediumSelector GetUpgradeMediums(const std::string& endpoint_id) const;
// Returns if this endpoint support 5G for WIFI.
bool Is5GHzSupported(const std::string& endpoint_id) const;
// Returns BSSID for this endpoint.
std::string GetBssid(const std::string& endpoint_id) const;
// Returns WIFI Frequency for this endpoint.
std::int32_t GetApFrequency(const std::string& endpoint_id) const;
// Returns IP Address in 4 bytes format for this endpoint.
std::string GetIPAddress(const std::string& endpoint_id) const;
// Returns true if it's safe to send payloads to this endpoint.
bool IsConnectedToEndpoint(const std::string& endpoint_id) const;
// Returns all endpoints that can safely be sent payloads.
std::vector<std::string> GetConnectedEndpoints() const;
// Returns all endpoints that are still awaiting acceptance.
std::vector<std::string> GetPendingConnectedEndpoints() const;
// Returns true if there is at least one connected connection or one pending
// connection.
bool HasOngoingConnection() const;
// Returns the number of endpoints that are connected and outgoing.
std::int32_t GetNumOutgoingConnections() const;
// Returns the number of endpoints that are connected and incoming.
std::int32_t GetNumIncomingConnections() const;
// Returns true if endpoint is incoming connection.
bool IsIncomingConnection(const std::string& endpoint_id) const;
// Returns true if endpoint is outgoing connection.
bool IsOutgoingConnection(const std::string& endpoint_id) const;
// If true, then we're in the process of approving (or rejecting) a
// connection. No payloads should be sent until isConnectedToEndpoint()
// returns true.
bool HasPendingConnectionToEndpoint(const std::string& endpoint_id) const;
// Returns true if the local endpoint has already marked itself as
// accepted/rejected.
bool HasLocalEndpointResponded(const std::string& endpoint_id) const;
// Returns true if the remote endpoint has already marked themselves as
// accepted/rejected.
bool HasRemoteEndpointResponded(const std::string& endpoint_id) const;
// Marks the local endpoint as having accepted the connection.
void LocalEndpointAcceptedConnection(const std::string& endpoint_id,
PayloadListener listener);
// Marks the local endpoint as having rejected the connection.
void LocalEndpointRejectedConnection(const std::string& endpoint_id);
// Marks the remote endpoint as having accepted the connection.
void RemoteEndpointAcceptedConnection(const std::string& endpoint_id);
// Marks the remote endpoint as having rejected the connection.
void RemoteEndpointRejectedConnection(const std::string& endpoint_id);
// Returns true if both the local endpoint and the remote endpoint have
// accepted the connection.
bool IsConnectionAccepted(const std::string& endpoint_id) const;
// Returns true if either the local endpoint or the remote endpoint has
// rejected the connection.
bool IsConnectionRejected(const std::string& endpoint_id) const;
// Returns true if the client should enforce topology constraints.
bool ShouldEnforceTopologyConstraints() const;
// Returns true, if connection party should attempt to upgrade itself to
// use a higher bandwidth medium, if it is available.
bool AutoUpgradeBandwidth() const;
// Proxies to the client's PayloadListener::OnPayload() callback.
void OnPayload(const std::string& endpoint_id, Payload payload);
// Proxies to the client's PayloadListener::OnPayloadProgress() callback.
void OnPayloadProgress(const std::string& endpoint_id,
const PayloadProgressInfo& info);
bool LocalConnectionIsAccepted(std::string endpoint_id) const;
bool RemoteConnectionIsAccepted(std::string endpoint_id) const;
// Adds a CancellationFlag for endpoint id.
void AddCancellationFlag(const std::string& endpoint_id);
// Returns the CancellationFlag for endpoint id,
CancellationFlag* GetCancellationFlag(const std::string& endpoint_id);
// Sets the CancellationFlag to true for endpoint id.
void CancelEndpoint(const std::string& endpoint_id);
// Cancels all CancellationFlags.
void CancelAllEndpoints();
AdvertisingOptions GetAdvertisingOptions() const;
DiscoveryOptions GetDiscoveryOptions() const;
v3::ConnectionListeningOptions GetListeningOptions() const;
// The endpoint id will be stable for 30 seconds after high visibility mode
// (high power and Bluetooth Classic) advertisement stops.
// If client re-enters high visibility mode within 30 seconds, he is going to
// have the same endpoint id.
void EnterHighVisibilityMode();
// Cleans up any modifications in high visibility mode. The endpoint id always
// rotates.
void ExitHighVisibilityMode();
// Enters stable endpoint ID mode.
void EnterStableEndpointIdMode();
// Cleans up any modifications in stable endpoint ID mode. The endpoint id
// always rotates.
void ExitStableEndpointIdMode();
std::string Dump();
virtual const location::nearby::connections::OsInfo& GetLocalOsInfo() const;
std::optional<location::nearby::connections::OsInfo> GetRemoteOsInfo(
absl::string_view endpoint_id) const;
void SetLocalOsType(
const location::nearby::connections::OsInfo::OsType& os_type);
void SetRemoteOsInfo(
absl::string_view endpoint_id,
const location::nearby::connections::OsInfo& remote_os_info);
void RegisterDeviceProvider(NearbyDeviceProvider* provider) {
external_device_provider_ = provider;
}
void RegisterConnectionsDeviceProvider(
std::unique_ptr<v3::ConnectionsDeviceProvider> provider) {
connections_device_provider_ = std::move(provider);
}
const bool& IsSupportSafeToDisconnect() const {
return supports_safe_to_disconnect_;
}
bool IsSupportAutoReconnect() const { return support_auto_reconnect_; }
const std::int32_t& GetLocalSafeToDisconnectVersion() const {
return local_safe_to_disconnect_version_;
}
std::optional<std::int32_t> GetRemoteSafeToDisconnectVersion(
absl::string_view endpoint_id) const;
void SetRemoteSafeToDisconnectVersion(
absl::string_view endpoint_id,
const std::int32_t& safe_to_disconnect_version);
bool IsSafeToDisconnectEnabled(absl::string_view endpoint_id);
bool IsAutoReconnectEnabled(absl::string_view endpoint_id);
bool IsPayloadReceivedAckEnabled(absl::string_view endpoint_id);
// Returns the multiplex socket supports status for local device.
std::int32_t GetLocalMultiplexSocketBitmask() const;
// Sets the multiplex socket supports status for remote device.
void SetRemoteMultiplexSocketBitmask(absl::string_view endpoint_id,
int remote_multiplex_socket_bitmask);
// Returns true if the multiplex socket is supported for the given medium.
bool IsLocalMultiplexSocketSupported(Medium medium);
// Gets the multiplex socket supports status for remote device.
std::optional<std::int32_t> GetRemoteMultiplexSocketBitmask(
absl::string_view endpoint_id) const;
// Returns true if the multiplex socket is supported for the given medium.
bool IsMultiplexSocketSupported(absl::string_view endpoint_id, Medium medium);
// Gets the WebRTC non cellular network status.
bool GetWebRtcNonCellular();
// Sets the WebRTC non cellular network status.
void SetWebRtcNonCellular(bool webrtc_non_cellular);
// Returns true if DCT advertising/scanning is enabled.
bool IsDctEnabled() const;
// Gets the DCT dedup value. This is used to dedup the same device name when
// scanning for multiple devices.
// It is 7 bits derived from the local endpoint ID.
uint8_t GetDctDedup() const;
// Updates the DCT device name before advertising.
void UpdateDctDeviceName(absl::string_view device_name);
std::optional<location::nearby::connections::MediumRole> GetMediumRole(
absl::string_view endpoint_id) const;
/** Bitmask for bt multiplex connection support. */
// Note. Deprecates the first and second bit of BT_MULTIPLEX_ENABLED and
// WIFI_LAN_MULTIPLEX_ENABLED and shift them to the third and the forth bit.
// The reason is we need to escape the (0, 1) bit which has been set in some
// devices without salt enabled. If accompany with the devices with salted
// enabled, the frames passed cannot be decrypted and the connection shall be
// failed. Please refer to b/295925531#comment#14 for the details.
enum MultiplexSocketBitmask : uint32_t {
kBtMultiplexEnabled = 1 << 2,
kWifiLanMultiplexEnabled = 1 << 3,
};
private:
struct Connection {
// Status: may be either:
// Connection::PENDING, or combination of
// Connection::LOCAL_ENDPOINT_ACCEPTED:
// Connection::LOCAL_ENDPOINT_REJECTED and
// Connection::REMOTE_ENDPOINT_ACCEPTED:
// Connection::REMOTE_ENDPOINT_REJECTED, or
// Connection::CONNECTED.
// Only when this is set to CONNECTED should you allow payload transfers.
//
// We want this enum to be implicitly convertible to int, because
// we perform bit operations on it.
enum Status : uint8_t {
kPending = 0,
kLocalEndpointAccepted = 1 << 0,
kLocalEndpointRejected = 1 << 1,
kRemoteEndpointAccepted = 1 << 2,
kRemoteEndpointRejected = 1 << 3,
kConnected = 1 << 4,
};
bool is_incoming{false};
Medium connected_medium{Medium::UNKNOWN_MEDIUM};
Status status{kPending};
ConnectionListener connection_listener;
ConnectionOptions connection_options;
DiscoveryOptions discovery_options;
AdvertisingOptions advertising_options;
std::string connection_token;
std::optional<location::nearby::connections::OsInfo> os_info;
std::int32_t safe_to_disconnect_version;
std::int32_t remote_multiplex_socket_bitmask;
};
using ConnectionPair = std::pair<Connection, PayloadListener>;
struct AdvertisingInfo {
std::string service_id;
ConnectionListener listener;
void Clear() { service_id.clear(); }
bool IsEmpty() const { return service_id.empty(); }
};
struct DiscoveryInfo {
std::string service_id;
DiscoveryListener listener;
void Clear() { service_id.clear(); }
bool IsEmpty() const { return service_id.empty(); }
};
struct ListeningInfo {
std::string service_id;
v3::ConnectionListener listener;
void Clear() { service_id.clear(); }
bool IsEmpty() const { return service_id.empty(); }
};
// `RemoveAllEndpoints` is expected to only be called during destruction of
// ClientProxy via `ClientProxy::Reset`, which makes destroying
// CancellationFlags safe here since we are destroying ClientProxy. Do not
// use CancellationFlags after `RemoveAllEndpoints` is called, since all
// flags now are referencing garbage memory.
void RemoveAllEndpoints();
void OnSessionComplete();
bool ConnectionStatusesContains(const std::string& endpoint_id,
Connection::Status status_to_match) const;
void AppendConnectionStatus(const std::string& endpoint_id,
Connection::Status status_to_append);
const ConnectionPair* LookupConnection(absl::string_view endpoint_id) const;
ConnectionPair* LookupConnection(absl::string_view endpoint_id);
bool ConnectionStatusMatches(const std::string& endpoint_id,
Connection::Status status) const;
std::vector<std::string> GetMatchingEndpoints(
absl::AnyInvocable<bool(const Connection&)> pred) const;
std::string GenerateLocalEndpointId();
void ScheduleClearCachedEndpointIdAlarm();
void CancelClearCachedEndpointIdAlarm();
location::nearby::connections::OsInfo::OsType OSNameToOsInfoType(
api::OSName osName);
std::string ToString(PayloadProgressInfo::Status status) const;
std::optional<std::string> GetEndpointIdForDct() const;
// The device name used for DCT advertising.
std::string dct_device_name_;
// The dedup value used for DCT advertising.
uint8_t dct_dedup_ = 0;
// The endpoint ID used for DCT advertising.
std::string dct_endpoint_id_;
mutable RecursiveMutex mutex_;
std::int64_t client_id_;
std::string local_endpoint_id_;
std::string local_endpoint_info_;
// If currently is advertising in high visibility mode is true: high power and
// Bluetooth Classic enabled. When high_visibility_mode_ is true, the endpoint
// id is stable for 30s. When high_visibility_mode_ is false, the endpoint id
// always rotates.
bool high_vis_mode_ = false;
// If advertising is in stable endpoint ID mode, the endpoint ID is stable
// for 30s after advertising or disconnection. When stable_endpoint_id_mode_
// is false, the endpoint id always rotates.
bool stable_endpoint_id_mode_ = false;
// Caches the endpoint id for stable endpoint ID mode.
std::string cached_endpoint_id_;
ScheduledExecutor single_thread_executor_;
std::unique_ptr<CancelableAlarm> cached_endpoint_id_alarm_;
// If not empty, we are currently advertising and accepting connection
// requests for the given service_id.
AdvertisingInfo advertising_info_;
// If not empty, we are currently discovering for the given service_id.
DiscoveryInfo discovery_info_;
// If not empty, we are currently listening for the given service_id.
ListeningInfo listening_info_;
// The active ClientProxy's advertising constraints. Empty()
// returns true if the client hasn't started advertising false otherwise.
// Note: this is not cleared when the client stops advertising because it
// might still be useful downstream of advertising (eg: establishing
// connections, performing bandwidth upgrades, etc.)
AdvertisingOptions advertising_options_;
// The active ClientProxy's discovery constraints. Null if the client
// hasn't started discovering. Note: this is not cleared when the client
// stops discovering because it might still be useful downstream of
// discovery (eg: connection speed, etc.)
DiscoveryOptions discovery_options_;
// The active ClientProxy's listening constraints.
v3::ConnectionListeningOptions listening_options_;
// Maps endpoint_id to endpoint connection state.
absl::flat_hash_map<std::string, ConnectionPair> connections_;
// Maps endpoint_id to Bluetooth Mac Addresses.
absl::flat_hash_map<std::string, std::string> bluetooth_mac_addresses_;
// A cache of endpoint ids that we've already notified the discoverer of. We
// check this cache before calling onEndpointFound() so that we don't notify
// the client multiple times for the same endpoint. This would otherwise
// happen because some mediums (like Bluetooth) repeatedly give us the same
// endpoints after each scan.
absl::flat_hash_set<std::string> discovered_endpoint_ids_;
// Maps endpoint_id to CancellationFlag. CancellationFlags are passed around
// as raw pointers to other classes in Nearby Connections, so it is important
// that objects in this map are not cleared, even if they are cancelled.
absl::flat_hash_map<std::string, std::unique_ptr<CancellationFlag>>
cancellation_flags_;
// A default cancellation flag with isCancelled set be true.
std::unique_ptr<CancellationFlag> default_cancellation_flag_ =
std::make_unique<CancellationFlag>(true);
// An analytics logger with |EventLogger| provided by client, which is default
// nullptr as no-op.
std::unique_ptr<analytics::AnalyticsRecorder> analytics_recorder_;
std::unique_ptr<ErrorCodeRecorder> error_code_recorder_;
// Local device OS information.
location::nearby::connections::OsInfo local_os_info_;
// For device providers not owned by Nearby connections (e.g. Nearby
// Presence's DeviceProvider.)
NearbyDeviceProvider* external_device_provider_ = nullptr;
// For Nearby Connections' own device provider.
std::unique_ptr<v3::ConnectionsDeviceProvider> connections_device_provider_;
bool supports_safe_to_disconnect_;
bool support_auto_reconnect_;
std::int32_t local_safe_to_disconnect_version_;
// Allowed to use WebRTC over non-cellular networks.
bool webrtc_non_cellular_ = false;
};
} // namespace connections
} // namespace nearby
#endif // CORE_INTERNAL_CLIENT_PROXY_H_