mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-14 22:56:12 -04:00
324 lines
14 KiB
C++
324 lines
14 KiB
C++
// 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 CORE_INTERNAL_CLIENT_PROXY_H_
|
|
#define CORE_INTERNAL_CLIENT_PROXY_H_
|
|
|
|
#include <cstdint>
|
|
#include <functional>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "core/listeners.h"
|
|
#include "core/options.h"
|
|
#include "core/status.h"
|
|
#include "core/strategy.h"
|
|
#include "platform/base/byte_array.h"
|
|
#include "platform/base/cancellation_flag.h"
|
|
#include "platform/base/error_code_recorder.h"
|
|
#include "platform/base/prng.h"
|
|
#include "platform/public/cancelable_alarm.h"
|
|
#include "platform/public/mutex.h"
|
|
#include "internal/analytics/analytics_recorder.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"
|
|
|
|
namespace location {
|
|
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(analytics::EventLogger* event_logger = nullptr);
|
|
~ClientProxy();
|
|
ClientProxy(ClientProxy&&) = default;
|
|
ClientProxy& operator=(ClientProxy&&) = default;
|
|
|
|
std::int64_t GetClientId() const;
|
|
|
|
std::string GetLocalEndpointId();
|
|
|
|
analytics::AnalyticsRecorder& GetAnalyticsRecorder() const {
|
|
return *analytics_recorder_;
|
|
}
|
|
|
|
std::string GetConnectionToken(const std::string& endpoint_id);
|
|
|
|
// 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<proto::connections::Medium> mediums,
|
|
const ConnectionOptions& advertising_options = ConnectionOptions{});
|
|
// Marks this client as not advertising.
|
|
void StoppedAdvertising();
|
|
bool IsAdvertising() const;
|
|
std::string GetAdvertisingServiceId() const;
|
|
|
|
// Get service ID of a surrently active link (either advertising, or
|
|
// discovering).
|
|
std::string GetServiceId() const;
|
|
|
|
// Marks this client as discovering with the given callback.
|
|
void StartedDiscovery(
|
|
const std::string& service_id, Strategy strategy,
|
|
const DiscoveryListener& discovery_listener,
|
|
absl::Span<proto::connections::Medium> mediums,
|
|
const ConnectionOptions& discovery_options = ConnectionOptions{});
|
|
// 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;
|
|
|
|
// Proxies to the client's DiscoveryListener::OnEndpointFound() callback.
|
|
void OnEndpointFound(const std::string& service_id,
|
|
const std::string& endpoint_id,
|
|
const ByteArray& endpoint_info,
|
|
proto::connections::Medium medium);
|
|
// Proxies to the client's DiscoveryListener::OnEndpointLost() callback.
|
|
void OnEndpointLost(const std::string& service_id,
|
|
const std::string& endpoint_id);
|
|
|
|
// Proxies to the client's ConnectionListener::OnInitiated() callback.
|
|
void OnConnectionInitiated(const std::string& endpoint_id,
|
|
const ConnectionResponseInfo& info,
|
|
const ConnectionOptions& 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 all mediums eligible for upgrade.
|
|
BooleanMediumSelector GetUpgradeMediums(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 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;
|
|
// 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,
|
|
const 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;
|
|
|
|
// 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();
|
|
ConnectionOptions GetAdvertisingOptions() const;
|
|
ConnectionOptions GetDiscoveryOptions() const;
|
|
|
|
// The endpoint id will be stable for 30 seconds after high visibility mode
|
|
// (high power and Bluetooth Classic) advertisement stops.
|
|
// If client re-enters high visibility mode within 30 seconds, he is going to
|
|
// have the same endpoint id.
|
|
void EnterHighVisibilityMode();
|
|
// Cleans up any modifications in high visibility mode. The endpoint id always
|
|
// rotates.
|
|
void ExitHighVisibilityMode();
|
|
|
|
private:
|
|
struct Connection {
|
|
// Status: may be either:
|
|
// 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};
|
|
Status status{kPending};
|
|
ConnectionListener connection_listener;
|
|
PayloadListener payload_listener;
|
|
ConnectionOptions connection_options;
|
|
std::string connection_token;
|
|
};
|
|
|
|
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(); }
|
|
};
|
|
|
|
void RemoveAllEndpoints();
|
|
void ResetLocalEndpointIdIfNeeded();
|
|
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 Connection* LookupConnection(const std::string& endpoint_id) const;
|
|
Connection* LookupConnection(const std::string& endpoint_id);
|
|
bool ConnectionStatusMatches(const std::string& endpoint_id,
|
|
Connection::Status status) const;
|
|
std::vector<std::string> GetMatchingEndpoints(
|
|
std::function<bool(const Connection&)> pred) const;
|
|
std::string GenerateLocalEndpointId();
|
|
|
|
void ScheduleClearLocalHighVisModeCacheEndpointIdAlarm();
|
|
void CancelClearLocalHighVisModeCacheEndpointIdAlarm();
|
|
|
|
std::string ToString(PayloadProgressInfo::Status status) const;
|
|
|
|
mutable RecursiveMutex mutex_;
|
|
Prng prng_;
|
|
std::int64_t client_id_;
|
|
std::string local_endpoint_id_;
|
|
// If currently is advertising in high visibility mode is true: high power and
|
|
// Bluetooth Classic enabled. When high_visibility_mode_ is true, the endpoint
|
|
// id is stable for 30s. When high_visibility_mode_ is false, the endpoint id
|
|
// always rotates.
|
|
bool high_vis_mode_{false};
|
|
// Caches the endpoint id when it is in high visibility mode advertisement for
|
|
// 30s. Currently, Nearby Connections keeps rotating endpoint id. The client
|
|
// (Nearby Share) treats different endpoints as different receivers, duplicate
|
|
// share targets for same devices occur on share sheet in this case.
|
|
// Therefore, we remember the high visibility mode advertisement endpoint id
|
|
// here. empty if 1) There is no high power advertisement before 2) The
|
|
// endpoint id cached here in previous high visibility mode advertisement
|
|
// expires.
|
|
std::string local_high_vis_mode_cache_endpoint_id_;
|
|
ScheduledExecutor single_thread_executor_;
|
|
CancelableAlarm clear_local_high_vis_mode_cache_endpoint_id_alarm_;
|
|
|
|
// If not empty, we are currently advertising and accepting connection
|
|
// requests for the given service_id.
|
|
AdvertisingInfo advertising_info_;
|
|
|
|
// If not empty, we are currently discovering for the given service_id.
|
|
DiscoveryInfo discovery_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.)
|
|
ConnectionOptions 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.)
|
|
ConnectionOptions discovery_options_;
|
|
|
|
// Maps endpoint_id to endpoint connection state.
|
|
absl::flat_hash_map<std::string, Connection> connections_;
|
|
|
|
// 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.
|
|
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_;
|
|
};
|
|
|
|
} // namespace connections
|
|
} // namespace nearby
|
|
} // namespace location
|
|
|
|
#endif // CORE_INTERNAL_CLIENT_PROXY_H_
|