| // 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 <functional> |
| #include <string> |
| #include <vector> |
| |
| #include "connections/advertising_options.h" |
| #include "connections/discovery_options.h" |
| #include "connections/listeners.h" |
| #include "connections/status.h" |
| #include "connections/strategy.h" |
| #include "internal/platform/byte_array.h" |
| #include "internal/platform/cancellation_flag.h" |
| #include "internal/platform/error_code_recorder.h" |
| #include "internal/platform/prng.h" |
| #include "internal/platform/cancelable_alarm.h" |
| #include "internal/platform/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 AdvertisingOptions& advertising_options = AdvertisingOptions{}); |
| // 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 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; |
| |
| // 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& 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 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(); |
| AdvertisingOptions GetAdvertisingOptions() const; |
| DiscoveryOptions 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; |
| DiscoveryOptions discovery_options; |
| AdvertisingOptions advertising_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.) |
| 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_; |
| |
| // 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_ |