mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-16 15:36:12 -04:00
252 lines
11 KiB
C++
252 lines
11 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_ENDPOINT_CHANNEL_MANAGER_H_
|
|
#define CORE_INTERNAL_ENDPOINT_CHANNEL_MANAGER_H_
|
|
|
|
#include <memory>
|
|
#include <string>
|
|
#include <utility>
|
|
|
|
#include "absl/base/thread_annotations.h"
|
|
#include "absl/container/flat_hash_map.h"
|
|
#include "absl/strings/string_view.h"
|
|
#include "absl/time/time.h"
|
|
#include "connections/implementation/analytics/analytics_recorder.h"
|
|
#include "connections/implementation/client_proxy.h"
|
|
#include "connections/implementation/endpoint_channel.h"
|
|
#include "internal/platform/mutex.h"
|
|
#include "proto/connections_enums.pb.h"
|
|
|
|
namespace nearby::connections {
|
|
using DisconnectionReason =
|
|
::location::nearby::proto::connections::DisconnectionReason;
|
|
using SafeDisconnectionResult = nearby::analytics::SafeDisconnectionResult;
|
|
|
|
// NOTE(std::string):
|
|
// All the strings in internal class public interfaces should be exchanged as
|
|
// const std::string& if they are immutable, and as std::string if they are
|
|
// mutable.
|
|
// This is to keep all the internal classes compatible with each other,
|
|
// and minimize resources spent on the type conversion.
|
|
// Project-wide, strings are either passed around as reference (which has
|
|
// zero maintenance costs, and sizeof(void*) memory usage => passed around in a
|
|
// CPU register), and whenever lifetime etension is required, it must be copied
|
|
// to std::string instance (which will again propagate as a const reference
|
|
// within it's lifetime domain).
|
|
|
|
// Manages the communication channels to all the remote endpoints with which we
|
|
// are interacting.
|
|
class EndpointChannelManager final {
|
|
public:
|
|
using EncryptionContext = EndpointChannel::EncryptionContext;
|
|
|
|
~EndpointChannelManager();
|
|
|
|
// Registers the initial EndpointChannel to be associated with an endpoint;
|
|
// if there already exists a previously-associated EndpointChannel, that will
|
|
// be closed before continuing the registration.
|
|
void RegisterChannelForEndpoint(ClientProxy* client,
|
|
const std::string& endpoint_id,
|
|
std::shared_ptr<EndpointChannel> channel)
|
|
ABSL_LOCKS_EXCLUDED(mutex_);
|
|
|
|
// Replaces the EndpointChannel to be associated with an endpoint from here on
|
|
// in, transferring the encryption context from the previous EndpointChannel
|
|
// to the newly-provided EndpointChannel.
|
|
void ReplaceChannelForEndpoint(ClientProxy* client,
|
|
const std::string& endpoint_id,
|
|
std::shared_ptr<EndpointChannel> channel,
|
|
bool enable_encryption)
|
|
ABSL_LOCKS_EXCLUDED(mutex_);
|
|
|
|
bool EncryptChannelForEndpoint(const std::string& endpoint_id,
|
|
std::unique_ptr<EncryptionContext> context)
|
|
ABSL_LOCKS_EXCLUDED(mutex_);
|
|
|
|
// NOTE(shared_ptr<> usage):
|
|
//
|
|
// EndpointChannelManager is holding an EndpointChannel instance;
|
|
// GetChannelForEndpoint() is passing ownership over to a worker thread.
|
|
// It is not a pointer passing but an ownership passing, to guarantee that
|
|
// channel instance will not disappear underneath the feet of a worker thread
|
|
// inside EndpointManager [ EndpointManager::EndpointChannelLoopRunnable() ].
|
|
// If it is just a pointer, Channel will get destroyed while in use by a
|
|
// worker thread. shared_ptr is a simple and reliable tool to avoid that.
|
|
//
|
|
// The reason why it can not be std::unique_ptr<> is: there are other code
|
|
// paths that expect to be able to read the pointer value multiple times, from
|
|
// multiple places (each of them needs "ownership" for the duration of their
|
|
// use). EndpointManager::SendTransferFrameBytes() is another such place.
|
|
// If EndpointChannelManager replaces the current channel, and any (or both)
|
|
// EndpointManager methods that use a channel are running, it is better to
|
|
// have a shared ownership.
|
|
std::shared_ptr<EndpointChannel> GetChannelForEndpoint(
|
|
absl::string_view endpoint_id) ABSL_LOCKS_EXCLUDED(mutex_);
|
|
|
|
// Returns true if 'endpoint_id' actually had a registered EndpointChannel.
|
|
// IOW, a return of false signifies a no-op.
|
|
bool UnregisterChannelForEndpoint(absl::string_view endpoint_id,
|
|
DisconnectionReason reason,
|
|
SafeDisconnectionResult result)
|
|
ABSL_LOCKS_EXCLUDED(mutex_);
|
|
|
|
int GetConnectedEndpointsCount() const ABSL_LOCKS_EXCLUDED(mutex_);
|
|
|
|
// Check if any endpoint uses WLAN Medium
|
|
bool isWifiLanConnected() const ABSL_LOCKS_EXCLUDED(mutex_);
|
|
void UpdateSafeToDisconnectForEndpoint(const std::string& endpoint_id,
|
|
bool safe_to_disconnect_enabled)
|
|
ABSL_LOCKS_EXCLUDED(mutex_);
|
|
bool CreateNewTimeoutDisconnectedState(absl::string_view endpoint_id,
|
|
absl::Duration timeout_millis)
|
|
ABSL_LOCKS_EXCLUDED(mutex_);
|
|
void MarkEndpointStopWaitToDisconnect(absl::string_view endpoint_id,
|
|
bool is_safe_to_disconnect,
|
|
bool notify_stop_waiting)
|
|
ABSL_LOCKS_EXCLUDED(mutex_);
|
|
|
|
bool IsSafeToDisconnect(absl::string_view endpoint_id)
|
|
ABSL_LOCKS_EXCLUDED(mutex_);
|
|
bool IsWaitingForSafeToDisconnectTimeoutForTesting(
|
|
absl::string_view endpoint_id) ABSL_LOCKS_EXCLUDED(mutex_);
|
|
void RemoveTimeoutDisconnectedState(absl::string_view endpoint_id)
|
|
ABSL_LOCKS_EXCLUDED(mutex_);
|
|
|
|
private:
|
|
// Tracks channel state for all endpoints. This includes what EndpointChannel
|
|
// the endpoint is currently using and whether or not the EndpointChannel has
|
|
// been encrypted yet.
|
|
class ChannelState {
|
|
public:
|
|
class EndpointData {
|
|
public:
|
|
EndpointData() = default;
|
|
EndpointData(EndpointData&&) = default;
|
|
EndpointData& operator=(EndpointData&&) = default;
|
|
~EndpointData() {
|
|
if (channel_ != nullptr) {
|
|
channel_->Close(disconnect_reason_);
|
|
}
|
|
}
|
|
|
|
// True if we have a 'context' for the endpoint.
|
|
bool IsEncrypted() const { return context_ != nullptr; }
|
|
|
|
void CreateNewTimeoutDisconnectedState(absl::Duration timeout_millis)
|
|
ABSL_LOCKS_EXCLUDED(timeout_to_disconnected_mutex_);
|
|
void MarkEndpointStopWaitToDisconnect(bool is_safe_to_disconnect,
|
|
bool notify_stop_waiting)
|
|
ABSL_LOCKS_EXCLUDED(timeout_to_disconnected_mutex_);
|
|
bool IsWaitingForSafeToDisconnectTimeout() const
|
|
ABSL_LOCKS_EXCLUDED(timeout_to_disconnected_mutex_);
|
|
bool IsSafeToDisconnect() const
|
|
ABSL_LOCKS_EXCLUDED(timeout_to_disconnected_mutex_);
|
|
void RemoveTimeoutDisconnectedState()
|
|
ABSL_LOCKS_EXCLUDED(timeout_to_disconnected_mutex_);
|
|
bool EncryptChannel();
|
|
|
|
std::shared_ptr<EndpointChannel> channel() const { return channel_; }
|
|
void set_channel(std::shared_ptr<EndpointChannel> channel) {
|
|
channel_ = std::move(channel);
|
|
}
|
|
|
|
std::shared_ptr<EncryptionContext> context() const { return context_; }
|
|
void set_context(std::shared_ptr<EncryptionContext> context) {
|
|
context_ = std::move(context);
|
|
}
|
|
|
|
void set_disconnect_reason(DisconnectionReason disconnect_reason) {
|
|
disconnect_reason_ = disconnect_reason;
|
|
}
|
|
|
|
bool safe_to_disconnect_enabled() const {
|
|
return safe_to_disconnect_enabled_;
|
|
}
|
|
void set_safe_to_disconnect_enabled(bool safe_to_disconnect_enabled) {
|
|
safe_to_disconnect_enabled_ = safe_to_disconnect_enabled;
|
|
}
|
|
|
|
private:
|
|
std::shared_ptr<EndpointChannel> channel_;
|
|
std::shared_ptr<EncryptionContext> context_;
|
|
DisconnectionReason disconnect_reason_ =
|
|
DisconnectionReason::UNKNOWN_DISCONNECTION_REASON;
|
|
bool safe_to_disconnect_enabled_ = false;
|
|
mutable Mutex timeout_to_disconnected_mutex_;
|
|
ConditionVariable timeout_to_disconnected_{
|
|
&timeout_to_disconnected_mutex_};
|
|
bool timeout_to_disconnected_enabled_
|
|
ABSL_GUARDED_BY(timeout_to_disconnected_mutex_) = false;
|
|
bool timeout_to_disconnected_notified_
|
|
ABSL_GUARDED_BY(timeout_to_disconnected_mutex_) = false;
|
|
bool is_safe_to_disconnect_
|
|
ABSL_GUARDED_BY(timeout_to_disconnected_mutex_) = false;
|
|
};
|
|
|
|
ChannelState() = default;
|
|
~ChannelState() { DestroyAll(); }
|
|
ChannelState(ChannelState&&) = default;
|
|
ChannelState& operator=(ChannelState&&) = default;
|
|
|
|
// Provides a way to destroy contents of a container, while holding a lock.
|
|
void DestroyAll();
|
|
|
|
// Return pointer to endpoint data, or nullptr, it not found.
|
|
std::shared_ptr<EndpointData> GetEndpointData(
|
|
absl::string_view endpoint_id);
|
|
|
|
// Registers a new endpoint id. This is the only spot EndpointData is
|
|
// created.
|
|
std::shared_ptr<EndpointData> RegisterEndpoint(
|
|
absl::string_view endpoint_id);
|
|
|
|
void UpdateSafeToDisconnectForEndpoint(const std::string& endpoint_id,
|
|
bool safe_to_disconnect_enabled);
|
|
|
|
// Removes all knowledge of this endpoint, cleaning up as necessary.
|
|
// Returns false if the endpoint was not found.
|
|
bool RemoveEndpoint(absl::string_view endpoint_id,
|
|
DisconnectionReason reason);
|
|
|
|
int GetConnectedEndpointsCount() const { return endpoints_.size(); }
|
|
bool isWifiLanConnected() const;
|
|
void MarkEndpointStopWaitToDisconnect(absl::string_view endpoint_id,
|
|
bool is_safe_to_disconnect,
|
|
bool notify_stop_waiting);
|
|
bool IsWaitingForSafeToDisconnectTimeoutForTesting(
|
|
absl::string_view endpoint_id);
|
|
bool IsSafeToDisconnect(absl::string_view endpoint_id);
|
|
void RemoveTimeoutDisconnectedState(absl::string_view endpoint_id);
|
|
|
|
private:
|
|
// Endpoint ID -> EndpointData. Contains everything we know about the
|
|
// endpoint.
|
|
absl::flat_hash_map<std::string, std::shared_ptr<EndpointData>> endpoints_;
|
|
};
|
|
|
|
void SetActiveEndpointChannel(
|
|
ClientProxy* client, const std::string& endpoint_id,
|
|
std::shared_ptr<ChannelState::EndpointData> endpoint,
|
|
std::shared_ptr<EndpointChannel> channel, bool enable_encryption)
|
|
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
|
|
|
mutable Mutex mutex_;
|
|
ChannelState channel_state_ ABSL_GUARDED_BY(mutex_);
|
|
};
|
|
|
|
} // namespace nearby::connections
|
|
|
|
#endif // CORE_INTERNAL_ENDPOINT_CHANNEL_MANAGER_H_
|