Merge branch 'google3'

Change-Id: I2c5f890e76873090819e7569b214af829bfd0daf
This commit is contained in:
Alexey Polyudov
2020-06-24 10:57:51 -07:00
148 changed files with 8643 additions and 1188 deletions
+3
View File
@@ -11,6 +11,7 @@ cc_library(
"future.h",
"input_file.h",
"listenable_future.h",
"log_message.h",
"mutex.h",
"output_file.h",
"scheduled_executor.h",
@@ -62,12 +63,14 @@ cc_library(
"platform.h",
],
visibility = [
"//platform_v2/base:__pkg__",
"//platform_v2/impl:__subpackages__",
"//platform_v2/public:__pkg__",
],
deps = [
":comm",
":types",
"//platform_v2/base",
"//absl/strings",
"//absl/types:any",
],
+10 -10
View File
@@ -1,22 +1,22 @@
#ifndef PLATFORM_V2_API_ATOMIC_REFERENCE_H_
#define PLATFORM_V2_API_ATOMIC_REFERENCE_H_
#include <cstdint>
namespace location {
namespace nearby {
namespace api {
// An object reference that may be updated atomically.
//
// https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicReference.html
template <typename T>
class AtomicReference {
// Type that allows 32-bit atomic reads and writes.
class AtomicUint32 {
public:
virtual ~AtomicReference() = default;
virtual ~AtomicUint32() = default;
virtual T Get() const & = 0;
virtual T Get() && = 0;
virtual void Set(const T& value) = 0;
virtual void Set(T&& value) = 0;
// Atomically reads and returns stored value.
virtual std::uint32_t Get() const = 0;
// Atomically stores value.
virtual void Set(std::uint32_t value) = 0;
};
} // namespace api
+13 -3
View File
@@ -2,6 +2,7 @@
#define PLATFORM_V2_API_CONDITION_VARIABLE_H_
#include "platform_v2/base/exception.h"
#include "absl/time/clock.h"
namespace location {
namespace nearby {
@@ -15,10 +16,19 @@ class ConditionVariable {
public:
virtual ~ConditionVariable() {}
// https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#notify--
// Notifies all the waiters that condition state has changed.
virtual void Notify() = 0;
// https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#wait--
virtual Exception Wait() = 0; // throws Exception::kInterrupted
// Waits indefinitely for Notify to be called.
// May return prematurely in case of interrupt, if supported by platform.
// Returns kSuccess, or kInterrupted on interrupt.
virtual Exception Wait() = 0;
// Waits while timeout has not expired for Notify to be called.
// May return prematurely in case of interrupt, if supported by platform.
// Returns kSuccess, or kInterrupted on interrupt.
// If Timeout expired, and Notify was not called, returns kTimeout.
virtual Exception Wait(absl::Duration timeout) = 0;
};
} // namespace api
+41
View File
@@ -0,0 +1,41 @@
#ifndef PLATFORM_V2_API_LOG_MESSAGE_H_
#define PLATFORM_V2_API_LOG_MESSAGE_H_
#include <iostream>
namespace location {
namespace nearby {
namespace api {
// A log message that prints to appropraite destination when ~LogMessage() is
// called.
class LogMessage {
public:
enum class Severity {
kInfo = 0,
kWarning = 1,
kError = 2,
kFatal = 3, // Terminates the process after logging
};
// Configures minimum severity to be logged.
static void SetMinLogSeverity(Severity severity);
// Returns if a log with |severity| should be logged based on
// SetMinLogSeverity and additional platform requirements.
static bool ShouldCreateLogMessage(Severity severity);
virtual ~LogMessage() = default;
// Printf like logging.
virtual void Print(const char* format, ...) = 0;
// Returns a stream for std::cout like logging.
virtual std::ostream& Stream() = 0;
};
} // namespace api
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_API_LOG_MESSAGE_H_
+21 -7
View File
@@ -15,6 +15,7 @@
#include "platform_v2/api/count_down_latch.h"
#include "platform_v2/api/crypto.h"
#include "platform_v2/api/input_file.h"
#include "platform_v2/api/log_message.h"
#include "platform_v2/api/mutex.h"
#include "platform_v2/api/output_file.h"
#include "platform_v2/api/scheduled_executor.h"
@@ -25,8 +26,8 @@
#include "platform_v2/api/webrtc.h"
#include "platform_v2/api/wifi.h"
#include "platform_v2/api/wifi_lan.h"
#include "platform_v2/base/payload_id.h"
#include "absl/strings/string_view.h"
#include "absl/types/any.h"
namespace location {
namespace nearby {
@@ -44,18 +45,32 @@ class ImplementationPlatform {
// - Future<T> : to synchronize on Callable<T> schduled to execute.
// - CountDownLatch : to ensure at least N threads are waiting.
// - file I/O
static std::unique_ptr<AtomicReference<absl::any>> CreateAtomicReferenceAny(
absl::any initial_value);
static std::unique_ptr<SettableFuture<absl::any>> CreateSettableFutureAny();
// - Logging
// Atomics:
// =======
// Atomic boolean: special case. Uses native platform atomics.
// Does not use locking.
// Does not use dynamic memory allocations in operations.
static std::unique_ptr<AtomicBoolean> CreateAtomicBoolean(bool initial_value);
// Supports enums and integers up to 32-bit.
// Does not use locking, if platform supports 32-bit atimics natively.
// Does not use dynamic memory allocations in operations.
static std::unique_ptr<AtomicUint32>
CreateAtomicUint32(std::uint32_t value);
static std::unique_ptr<CountDownLatch> CreateCountDownLatch(
std::int32_t count);
static std::unique_ptr<Mutex> CreateMutex(Mutex::Mode mode);
static std::unique_ptr<ConditionVariable> CreateConditionVariable(
Mutex* mutex);
static std::unique_ptr<InputFile> CreateInputFile(std::int64_t payload_id,
static std::unique_ptr<InputFile> CreateInputFile(PayloadId payload_id,
std::int64_t total_size);
static std::unique_ptr<OutputFile> CreateOutputFile(std::int64_t payload_id);
static std::unique_ptr<OutputFile> CreateOutputFile(PayloadId payload_id);
static std::unique_ptr<LogMessage> CreateLogMessage(
const char* file, int line, LogMessage::Severity severity);
// Java-like Executors
static std::unique_ptr<SubmittableExecutor> CreateSingleThreadExecutor();
@@ -74,7 +89,6 @@ class ImplementationPlatform {
static std::unique_ptr<WifiMedium> CreateWifiMedium();
static std::unique_ptr<WifiLanMedium> CreateWifiLanMedium();
static std::unique_ptr<WebRtcMedium> CreateWebRtcMedium();
static std::string GetDeviceId();
};
} // namespace api
+9 -2
View File
@@ -16,8 +16,15 @@ class SettableFuture : public ListenableFuture<T> {
public:
~SettableFuture() override = default;
virtual bool Set(const T& value) = 0;
virtual bool Set(T&& value) = 0;
// Completes the future successfully. The value is returned to any waiters.
// Returns true, if value was set.
// Returns false, if Future is already in "done" state.
virtual bool Set(T value) = 0;
// Completes the future unsuccessfully. The exception value is returned to any
// waiters.
// Returns true, if exception was set.
// Returns false, if Future is already in "done" state.
virtual bool SetException(Exception exception) = 0;
};
+54 -34
View File
@@ -4,8 +4,8 @@
#include <string>
#include "platform_v2/base/byte_array.h"
#include "platform_v2/base/exception.h"
#include "platform_v2/base/input_stream.h"
#include "platform_v2/base/listeners.h"
#include "platform_v2/base/output_stream.h"
#include "absl/strings/string_view.h"
@@ -18,25 +18,33 @@ class WifiLanService {
public:
virtual ~WifiLanService() = default;
virtual std::string GetName() = 0;
virtual std::string GetName() const = 0;
};
class WifiLanSocket {
public:
virtual ~WifiLanSocket() = default;
// Returns the InputStream of the WifiLanSocket, empty std::unique_ptr<>
// on error.
virtual std::unique_ptr<InputStream> GetInputStream() = 0;
// Returns the InputStream of the WifiLanSocket.
// On error, returned stream will report Exception::kIo on any operation.
//
// The returned object is not owned by the caller, and can be invalidated once
// the WifiLanSocket object is destroyed.
virtual InputStream& GetInputStream() = 0;
// Returns the OutputStream of the WifiLanSocket, empty std::unique_ptr<>
// on error.
virtual std::unique_ptr<OutputStream> GetOutputStream() = 0;
// Returns the OutputStream of the WifiLanSocket.
// On error, returned stream will report Exception::kIo on any operation.
//
// The returned object is not owned by the caller, and can be invalidated once
// the WifiLanSocket object is destroyed.
virtual OutputStream& GetOutputStream() = 0;
// Returns Exception::kIo on error, Exception::kSuccess otherwise.
virtual Exception::Value Close() = 0;
virtual Exception Close() = 0;
virtual WifiLanService& GetRemoteWifiLanService() = 0;
// Returns valid WifiLanService pointer if there is a connection, and
// nullptr otherwise.
virtual WifiLanService* GetRemoteWifiLanService() = 0;
};
// Container of operations that can be performed over the WifiLan medium.
@@ -45,39 +53,51 @@ class WifiLanMedium {
virtual ~WifiLanMedium() = default;
virtual bool StartAdvertising(
absl::string_view service_id,
absl::string_view wifi_lan_service_info_name) = 0;
virtual void StopAdvertising(absl::string_view service_id) = 0;
const std::string& service_id,
const std::string& wifi_lan_service_info_name) = 0;
virtual bool StopAdvertising(const std::string& service_id) = 0;
// Callback for WifiLan discover results.
class DiscoveredServiceCallback {
public:
virtual ~DiscoveredServiceCallback() = default;
virtual void OnServiceDiscovered(WifiLanService* wifi_lan_service) = 0;
virtual void OnServiceLost(WifiLanService* wifi_lan_service) = 0;
struct DiscoveredServiceCallback {
// The WifiLanService* is not owned by callbacks.
// It is passed to give access to its non-const methods.
// It is guaranteed to be valid for the duration of call.
std::function<void(WifiLanService& wifi_lan_service,
const std::string& service_id)>
service_discovered_cb =
DefaultCallback<WifiLanService&, const std::string&>();
std::function<void(WifiLanService& wifi_lan_service,
const std::string& service_id)>
service_lost_cb =
DefaultCallback<WifiLanService&, const std::string&>();
};
virtual bool StartDiscovery(
absl::string_view service_id,
DiscoveredServiceCallback* discovered_service_callback) = 0;
virtual void StopDiscovery(absl::string_view service_id) = 0;
// Returns true once the WifiLan discovery has been initiated.
virtual bool StartDiscovery(const std::string& service_id,
DiscoveredServiceCallback callback) = 0;
class AcceptedConnectionCallback {
public:
virtual ~AcceptedConnectionCallback() = default;
// Returns true once WifiLan discovery for service_id is well and truly
// stopped; after this returns, there must be no more invocations of the
// DiscoveredServiceCallback passed in to StartDiscovery() for service_id.
virtual bool StopDiscovery(const std::string& service_id) = 0;
virtual void OnConnectionAccepted(WifiLanSocket* socket,
absl::string_view service_id) = 0;
// Callback that is invoked when a new connection is accepted.
struct AcceptedConnectionCallback {
std::function<void(WifiLanSocket& socket, const std::string& service_id)>
accepted_cb = DefaultCallback<WifiLanSocket&, const std::string&>();
};
// Returns true once WifiLan socket connection requests to service_id can be
// accepted.
virtual bool StartAcceptingConnections(
absl::string_view service_id,
AcceptedConnectionCallback* accepted_connection_callback) = 0;
virtual void StopAcceptingConnections(absl::string_view service_id) = 0;
const std::string& service_id,
AcceptedConnectionCallback callback) = 0;
virtual bool StopAcceptingConnections(const std::string& service_id) = 0;
virtual WifiLanSocket* Connect(WifiLanService* wifi_lan_service,
absl::string_view service_id) = 0;
// Connects to a WifiLan service.
// On success, returns a new WifiLanSocket.
// On error, returns nullptr.
virtual std::unique_ptr<WifiLanSocket> Connect(
WifiLanService& service, const std::string& service_id) = 0;
};
} // namespace api
+6 -1
View File
@@ -14,9 +14,11 @@ cc_library(
"input_stream.h",
"listeners.h",
"output_stream.h",
"payload_id.h",
"prng.h",
"runnable.h",
"socket.h",
"types.h",
],
visibility = [
"//core_v2:__subpackages__",
@@ -42,6 +44,7 @@ cc_library(
"base_pipe.h",
],
visibility = [
"//core_v2:__subpackages__",
"//platform_v2/impl:__subpackages__",
"//platform_v2/public:__pkg__",
],
@@ -61,7 +64,8 @@ cc_library(
"//platform_v2:__subpackages__",
],
deps = [
"//platform:logging",
"//platform_v2/api:platform",
"//platform_v2/api:types",
],
)
@@ -85,6 +89,7 @@ cc_library(
"//platform_v2/api:comm",
"//platform_v2/public:types",
"//absl/container:flat_hash_map",
"//absl/strings",
],
)
+1 -2
View File
@@ -27,13 +27,12 @@ class BaseInputStream : public InputStream {
std::uint16_t ReadUint16();
std::uint32_t ReadUint32();
std::uint64_t ReadUint64();
ByteArray ReadBytes(int size);
bool IsAvailable(int size) const {
return buffer_.size() - position_ >= size;
}
private:
ByteArray ReadBytes(int size);
ByteArray &buffer_;
int position_{0};
};
+20 -5
View File
@@ -1,10 +1,11 @@
#ifndef PLATFORM_V2_BASE_BYTE_ARRAY_H_
#define PLATFORM_V2_BASE_BYTE_ARRAY_H_
#include <array>
#include <cstdint>
#include <string>
#include "absl/strings/string_view.h"
#include <type_traits>
#include <utility>
namespace location {
namespace nearby {
@@ -13,13 +14,22 @@ class ByteArray {
public:
// Create an empty ByteArray
ByteArray() = default;
template <size_t N>
explicit ByteArray(const std::array<char, N>& data) {
SetData(data.data(), data.size());
}
ByteArray(const ByteArray&) = default;
ByteArray& operator=(const ByteArray&) = default;
ByteArray(ByteArray&&) = default;
ByteArray& operator=(ByteArray&&) = default;
// Create ByteArray from string.
explicit ByteArray(absl::string_view source) {
// Moves string out of temporary, allowing for a zero-copy constructions.
// This is an optimization for very large strings.
explicit ByteArray(std::string&& source) : data_(std::move(source)) {}
// Create ByteArray by copy of a std::string. This can't be a string_view,
// because it will conflict with std::string&& version of constructor.
explicit ByteArray(const std::string& source) {
SetData(source.data(), source.size());
}
@@ -59,7 +69,12 @@ class ByteArray {
friend bool operator!=(const ByteArray& lhs, const ByteArray& rhs);
friend bool operator<(const ByteArray& lhs, const ByteArray& rhs);
explicit operator std::string() const { return data_; }
// Returns a copy of internal representation as std::string.
explicit operator std::string() const& { return data_; }
// Moves string out of temporary ByteArray, allowing for a zero-copy
// operation.
explicit operator std::string() const&& { return std::move(data_); }
private:
std::string data_;
+8
View File
@@ -65,4 +65,12 @@ TEST(ByteArrayTest, SetExplicitData) {
EXPECT_EQ(0, memcmp(message, bytes.data(), kMessageSize));
}
TEST(ByteArrayTest, CreateFromNonNullTerminatedStdArray) {
constexpr static const std::array data{'a', '\x00', 'b'};
ByteArray bytes{data};
EXPECT_EQ(bytes.size(), 3);
EXPECT_EQ(bytes.size(), std::string(bytes).size());
EXPECT_EQ(std::string(bytes), std::string(data.data(), data.size()));
}
} // namespace
+55 -1
View File
@@ -1,6 +1,60 @@
#ifndef PLATFORM_V2_BASE_LOGGING_H_
#define PLATFORM_V2_BASE_LOGGING_H_
#include "platform/logging.h"
#include "platform_v2/api/log_message.h"
#include "platform_v2/api/platform.h"
namespace location {
namespace nearby {
// This class is used to explicitly ignore values in the conditional
// logging macros. This avoids compiler warnings like "value computed
// is not used" and "statement has no effect".
class LogMessageVoidify {
public:
LogMessageVoidify() = default;
// This has to be an operator with a precedence lower than << but
// higher than ?:
void operator&(std::ostream&) {}
};
} // namespace nearby
} // namespace location
// Severity enum conversion
#define NEARBY_SEVERITY_INFO location::nearby::api::LogMessage::Severity::kInfo
#define NEARBY_SEVERITY_WARNING \
location::nearby::api::LogMessage::Severity::kWarning
#define NEARBY_SEVERITY_ERROR \
location::nearby::api::LogMessage::Severity::kError
#define NEARBY_SEVERITY_FATAL \
location::nearby::api::LogMessage::Severity::kFatal
#define NEARBY_SEVERITY(severity) NEARBY_SEVERITY_##severity
// Log enabling
#define NEARBY_LOG_IS_ON(severity) \
location::nearby::api::LogMessage::ShouldCreateLogMessage( \
NEARBY_SEVERITY(severity))
#define NEARBY_LOG_SET_SEVERITY(severity) \
location::nearby::api::LogMessage::SetMinLogSeverity( \
NEARBY_SEVERITY(severity))
// Log message creation
#define NEARBY_LOG_MESSAGE(severity) \
location::nearby::api::ImplementationPlatform::CreateLogMessage( \
__FILE__, __LINE__, NEARBY_SEVERITY(severity))
// Public APIs
// The stream statement must come last or otherwise it won't compile.
#define NEARBY_LOGS(severity) \
!(NEARBY_LOG_IS_ON(severity)) ? (void)0 \
: location::nearby::LogMessageVoidify() & \
NEARBY_LOG_MESSAGE(severity)->Stream()
#define NEARBY_LOG(severity, ...) \
NEARBY_LOG_IS_ON(severity) \
? NEARBY_LOG_MESSAGE(severity)->Print(__VA_ARGS__) : (void)0
#endif // PLATFORM_V2_BASE_LOGGING_H_
+146 -13
View File
@@ -7,6 +7,7 @@
#include "platform_v2/api/bluetooth_adapter.h"
#include "platform_v2/api/bluetooth_classic.h"
#include "platform_v2/api/wifi_lan.h"
#include "platform_v2/base/logging.h"
#include "platform_v2/public/count_down_latch.h"
@@ -40,6 +41,7 @@ void MediumEnvironment::Reset() {
NEARBY_LOG(INFO, "MediumEnvironment::Reset()");
bluetooth_adapters_.clear();
bluetooth_mediums_.clear();
wifi_lan_mediums_.clear();
});
Sync();
}
@@ -77,7 +79,7 @@ void MediumEnvironment::OnBluetoothAdapterChangedState(
if (info.adapter == &adapter) continue;
NEARBY_LOG(INFO, "[adapter=%p, device=%p] notify: adapter=%p", &adapter,
&adapter_device, info.adapter);
OnDeviceStateChanged(info, adapter_device, name, mode, enabled);
OnBluetoothDeviceStateChanged(info, adapter_device, name, mode, enabled);
}
// We don't care if there is an adapter already since all we store is a
// pointer. Pointer must remain valid for the duration of a Core session
@@ -87,16 +89,17 @@ void MediumEnvironment::OnBluetoothAdapterChangedState(
});
}
void MediumEnvironment::OnDeviceStateChanged(
void MediumEnvironment::OnBluetoothDeviceStateChanged(
BluetoothMediumContext& info, api::BluetoothDevice& device,
const std::string& name, api::BluetoothAdapter::ScanMode mode,
bool enabled) {
if (!enabled_) return;
auto item = info.devices.find(&device);
if (item == info.devices.end()) {
NEARBY_LOG(
INFO, "G3 OnDeviceStateChanged [device impl=%p]: new device; notify=%d",
&device, enable_notifications_.load());
NEARBY_LOG(INFO,
"G3 OnBluetoothDeviceStateChanged [device impl=%p]: new device; "
"notify=%d",
&device, enable_notifications_.load());
if (mode == api::BluetoothAdapter::ScanMode::kConnectableDiscoverable &&
enabled) {
// New device is turned on, and is in discoverable state.
@@ -108,10 +111,10 @@ void MediumEnvironment::OnDeviceStateChanged(
}
}
} else {
NEARBY_LOG(
INFO,
"G3 OnDeviceStateChanged [device impl=%p]: exisitng device; notify=%d",
&device, enable_notifications_.load());
NEARBY_LOG(INFO,
"G3 OnBluetoothDeviceStateChanged [device impl=%p]: exisitng "
"device; notify=%d",
&device, enable_notifications_.load());
auto& discovered_name = item->second;
if (mode == api::BluetoothAdapter::ScanMode::kConnectableDiscoverable &&
enabled) {
@@ -145,6 +148,39 @@ void MediumEnvironment::OnDeviceStateChanged(
}
}
void MediumEnvironment::OnWifiLanServiceStateChanged(
WifiLanMediumContext& info, api::WifiLanService& service,
const std::string& service_id, bool enabled) {
if (!enabled_) return;
auto item = info.services.find(&service);
if (item == info.services.end()) {
NEARBY_LOG(INFO,
"G3 OnWifiLanServiceStateChanged [service impl=%p]: new service",
&service);
info.services.emplace(&service, service.GetName());
if (enabled) {
RunOnMediumEnvironmentThread([&info, &service, service_id]() {
info.discovery_callback.service_discovered_cb(service, service_id);
});
}
} else {
NEARBY_LOG(INFO,
"G3 OnWifiLanServiceStateChanged [service impl=%p]: exisitng "
"service",
&service);
if (enabled) {
RunOnMediumEnvironmentThread([&info, &service, service_id]() {
info.discovery_callback.service_discovered_cb(service, service_id);
});
} else {
RunOnMediumEnvironmentThread([&info, &service, service_id]() {
info.discovery_callback.service_lost_cb(service, service_id);
});
info.services.erase(item);
}
}
}
void MediumEnvironment::RunOnMediumEnvironmentThread(
std::function<void()> runnable) {
job_count_++;
@@ -167,8 +203,9 @@ void MediumEnvironment::RegisterBluetoothMedium(
owned_adapter);
for (auto& [adapter, device] : bluetooth_adapters_) {
if (adapter == nullptr) continue;
OnDeviceStateChanged(context, *device, adapter->GetName(),
adapter->GetScanMode(), adapter->IsEnabled());
OnBluetoothDeviceStateChanged(context, *device, adapter->GetName(),
adapter->GetScanMode(),
adapter->IsEnabled());
}
});
}
@@ -190,8 +227,9 @@ void MediumEnvironment::UpdateBluetoothMedium(
owned_adapter->IsEnabled(), owned_adapter->GetScanMode());
for (auto& [adapter, device] : bluetooth_adapters_) {
if (adapter == nullptr) continue;
OnDeviceStateChanged(context, *device, adapter->GetName(),
adapter->GetScanMode(), adapter->IsEnabled());
OnBluetoothDeviceStateChanged(context, *device, adapter->GetName(),
adapter->GetScanMode(),
adapter->IsEnabled());
}
});
}
@@ -208,5 +246,100 @@ void MediumEnvironment::UnregisterBluetoothMedium(
});
}
void MediumEnvironment::RegisterWebRtcSignalingMessenger(
absl::string_view self_id, OnSignalingMessageCallback callback) {
if (!enabled_) return;
RunOnMediumEnvironmentThread(
[this, self_id{std::string(self_id)}, callback{std::move(callback)}]() {
webrtc_signaling_callback_[self_id] = std::move(callback);
NEARBY_LOG(INFO, "Registered signaling message callback for id = %s",
self_id.c_str());
});
}
void MediumEnvironment::UnregisterWebRtcSignalingMessenger(
absl::string_view self_id) {
if (!enabled_) return;
RunOnMediumEnvironmentThread([this, self_id{std::string(self_id)}]() {
auto item = webrtc_signaling_callback_.extract(self_id);
if (item.empty()) return;
NEARBY_LOG(INFO, "Unregistered signaling message callback for id = %s",
self_id.c_str());
});
}
void MediumEnvironment::SendWebRtcSignalingMessage(absl::string_view peer_id,
const ByteArray& message) {
if (!enabled_) return;
RunOnMediumEnvironmentThread(
[this, peer_id{std::string(peer_id)}, message]() {
auto item = webrtc_signaling_callback_.find(peer_id);
if (item == webrtc_signaling_callback_.end()) {
NEARBY_LOG(WARNING, "No callback registered for peer id = %s",
peer_id.c_str());
return;
}
item->second(message);
});
}
void MediumEnvironment::RegisterWifiLanMedium(api::WifiLanMedium& medium) {
if (!enabled_) return;
RunOnMediumEnvironmentThread([this, &medium]() {
wifi_lan_mediums_.insert({&medium, WifiLanMediumContext{}});
NEARBY_LOG(INFO, "Registered: medium=%p", &medium);
});
}
void MediumEnvironment::UpdateWifiLanMediumForDiscovery(
api::WifiLanMedium& medium, api::WifiLanService& service,
const std::string& service_id, WifiLanDiscoveredServiceCallback callback,
bool enabled) {
if (!enabled_) return;
RunOnMediumEnvironmentThread([this, &medium, &service, service_id,
callback = std::move(callback), enabled]() {
auto item = wifi_lan_mediums_.find(&medium);
if (item == wifi_lan_mediums_.end()) {
NEARBY_LOG(
INFO, "Update WifiLan medium failed. There is no medium registered.");
return;
}
auto& context = item->second;
context.discovery_callback = std::move(callback);
NEARBY_LOG(INFO, "Updated: this=%p; medium=%p", this, &medium);
OnWifiLanServiceStateChanged(context, service, service_id, enabled);
});
}
void MediumEnvironment::UpdateWifiLanMediumForAcceptedConnection(
api::WifiLanMedium& medium, const std::string& service_id,
WifiLanAcceptedConnectionCallback accepted_connection_callback) {
if (!enabled_) return;
RunOnMediumEnvironmentThread([this, &medium,
accepted_connection_callback =
std::move(accepted_connection_callback)]() {
auto item = wifi_lan_mediums_.find(&medium);
if (item == wifi_lan_mediums_.end()) {
NEARBY_LOG(
INFO, "Update WifiLan medium failed. There is no medium registered.");
return;
}
auto& context = item->second;
context.accepted_connection_callback =
std::move(accepted_connection_callback);
NEARBY_LOG(INFO, "Updated: this=%p; medium=%p", this, &medium);
});
}
void MediumEnvironment::UnregisterWifiLanMedium(api::WifiLanMedium& medium) {
if (!enabled_) return;
RunOnMediumEnvironmentThread([this, &medium]() {
auto item = wifi_lan_mediums_.extract(&medium);
if (item.empty()) return;
NEARBY_LOG(INFO, "Unregistered WifiLan medium");
});
}
} // namespace nearby
} // namespace location
+56 -4
View File
@@ -5,9 +5,12 @@
#include "platform_v2/api/bluetooth_adapter.h"
#include "platform_v2/api/bluetooth_classic.h"
#include "platform_v2/api/webrtc.h"
#include "platform_v2/base/byte_array.h"
#include "platform_v2/base/listeners.h"
#include "platform_v2/public/single_thread_executor.h"
#include "absl/container/flat_hash_map.h"
#include "absl/strings/string_view.h"
namespace location {
namespace nearby {
@@ -21,6 +24,12 @@ class MediumEnvironment {
public:
using BluetoothDiscoveryCallback =
api::BluetoothClassicMedium::DiscoveryCallback;
using OnSignalingMessageCallback =
api::WebRtcSignalingMessenger::OnSignalingMessageCallback;
using WifiLanDiscoveredServiceCallback =
api::WifiLanMedium::DiscoveredServiceCallback;
using WifiLanAcceptedConnectionCallback =
api::WifiLanMedium::AcceptedConnectionCallback;
MediumEnvironment(const MediumEnvironment&) = delete;
MediumEnvironment& operator=(const MediumEnvironment&) = delete;
@@ -84,6 +93,28 @@ class MediumEnvironment {
// Removes medium-related info. This should correspond to device power off.
void UnregisterBluetoothMedium(api::BluetoothClassicMedium& medium);
// Registers |callback| to receive messages sent to device with id |self_id|.
void RegisterWebRtcSignalingMessenger(absl::string_view self_id,
OnSignalingMessageCallback callback);
// Unregisters the callback listening to incoming messages for |self_id|.
void UnregisterWebRtcSignalingMessenger(absl::string_view self_id);
// Simulates sending a signaling message |message| to device with id
// |peer_id|.
void SendWebRtcSignalingMessage(absl::string_view peer_id,
const ByteArray& message);
// Wifi-Lan medium registration/update calls.
void RegisterWifiLanMedium(api::WifiLanMedium& medium);
void UpdateWifiLanMediumForDiscovery(
api::WifiLanMedium& medium, api::WifiLanService& service,
const std::string& service_id,
WifiLanDiscoveredServiceCallback discovery_callback, bool enabled);
void UpdateWifiLanMediumForAcceptedConnection(
api::WifiLanMedium& medium, const std::string& service_id,
WifiLanAcceptedConnectionCallback accepted_connection_callback);
void UnregisterWifiLanMedium(api::WifiLanMedium& medium);
private:
struct BluetoothMediumContext {
BluetoothDiscoveryCallback callback;
@@ -92,6 +123,13 @@ class MediumEnvironment {
absl::flat_hash_map<api::BluetoothDevice*, std::string> devices;
};
struct WifiLanMediumContext {
WifiLanDiscoveredServiceCallback discovery_callback;
WifiLanAcceptedConnectionCallback accepted_connection_callback;
// discovered service vs service name map.
absl::flat_hash_map<api::WifiLanService*, std::string> services;
};
// This is a singleton object, for which destructor will never be called.
// Constructor will be invoked once from Instance() static method.
// Object is create in-place (with a placement new) to guarantee that
@@ -99,10 +137,17 @@ class MediumEnvironment {
MediumEnvironment() = default;
~MediumEnvironment() = default;
void OnDeviceStateChanged(BluetoothMediumContext& info,
api::BluetoothDevice& device,
const std::string& name,
api::BluetoothAdapter::ScanMode mode, bool enabled);
void OnBluetoothDeviceStateChanged(BluetoothMediumContext& info,
api::BluetoothDevice& device,
const std::string& name,
api::BluetoothAdapter::ScanMode mode,
bool enabled);
void OnWifiLanServiceStateChanged(WifiLanMediumContext& info,
api::WifiLanService& service,
const std::string& service_id,
bool enabled);
void RunOnMediumEnvironmentThread(std::function<void()> runnable);
std::atomic_bool enabled_ = true;
@@ -116,6 +161,13 @@ class MediumEnvironment {
bluetooth_adapters_;
absl::flat_hash_map<api::BluetoothClassicMedium*, BluetoothMediumContext>
bluetooth_mediums_;
// Maps peer id to callback for receiving signaling messages.
absl::flat_hash_map<std::string, OnSignalingMessageCallback>
webrtc_signaling_callback_;
absl::flat_hash_map<api::WifiLanMedium*, WifiLanMediumContext>
wifi_lan_mediums_;
};
} // namespace nearby
+14
View File
@@ -0,0 +1,14 @@
#ifndef PLATFORM_V2_BASE_PAYLOAD_ID_H_
#define PLATFORM_V2_BASE_PAYLOAD_ID_H_
#include <cstdint>
namespace location {
namespace nearby {
using PayloadId = std::int64_t;
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_BASE_PAYLOAD_ID_H_
+1 -1
View File
@@ -38,7 +38,7 @@ std::uint32_t Prng::NextUint32() {
std::int64_t Prng::NextInt64() {
return (static_cast<std::int64_t>(NextInt32()) << 32) |
(static_cast<std::int64_t>(NextInt32()));
(static_cast<std::int64_t>(NextUint32()));
}
} // namespace nearby
+50
View File
@@ -5,6 +5,13 @@
namespace location {
namespace nearby {
enum class TestMode {
kUpperHalfOfInt64,
kLowerHalfOfInt64,
kInt32,
kUint32,
};
TEST(PrngTest, NextInt32) {
std::int32_t i = Prng().NextInt32();
EXPECT_LE(i, std::numeric_limits<std::int32_t>::max());
@@ -23,5 +30,48 @@ TEST(PrngTest, NextInt64) {
EXPECT_GE(i, std::numeric_limits<std::int64_t>::min());
}
void ValidateRandom(TestMode mode) {
int count_all_zeros = 0;
int count_all_ones = 0;
std::uint32_t i;
Prng prng;
for (int count = 0; count < 100; ++count) {
switch (mode) {
case TestMode::kUpperHalfOfInt64:
i = static_cast<std::uint32_t>(prng.NextInt64() >> 32);
break;
case TestMode::kLowerHalfOfInt64:
i = static_cast<std::uint32_t>(prng.NextInt64());
break;
case TestMode::kInt32:
i = static_cast<std::uint32_t>(prng.NextInt32());
break;
case TestMode::kUint32:
i = static_cast<std::uint32_t>(prng.NextUint32());
break;
}
if (!i) count_all_zeros++;
if (i == 0xFFFFFFFF) count_all_ones++;
}
EXPECT_LE(count_all_zeros, 1);
EXPECT_LE(count_all_ones, 1);
}
TEST(PrngTest, ValidateUpperHalfOfInt64) {
ValidateRandom(TestMode::kUpperHalfOfInt64);
}
TEST(PrngTest, ValidateLowerHalfOfInt64) {
ValidateRandom(TestMode::kLowerHalfOfInt64);
}
TEST(PrngTest, ValidateInt32) {
ValidateRandom(TestMode::kInt32);
}
TEST(PrngTest, ValidateUint32) {
ValidateRandom(TestMode::kUint32);
}
} // namespace nearby
} // namespace location
+29
View File
@@ -0,0 +1,29 @@
#ifndef PLATFORM_V2_BASE_TYPES_H_
#define PLATFORM_V2_BASE_TYPES_H_
#include <type_traits>
namespace location {
namespace nearby {
// Similar to static_cast, but will assert that Derived is a derived type of
// Base.
// Usage:
// class A {};
// class B : public A {};
// class C {};
// B b;
// A* a = &b;
// B* b2 = down_cast<B*>(a); // This is OK.
// C* c = down_cast<C*>(a); // This will fail to compile.
template <typename Derived, typename Base>
inline Derived down_cast(Base* value) {
using DerivedType = typename std::remove_pointer<Derived>::type;
static_assert(std::is_base_of<Base, DerivedType>::value);
return static_cast<Derived>(value);
}
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_BASE_TYPES_H_
+7 -2
View File
@@ -2,25 +2,27 @@ cc_library(
name = "types",
testonly = True,
srcs = [
"log_message.cc",
"scheduled_executor.cc",
"system_clock.cc",
],
hdrs = [
"atomic_boolean.h",
"atomic_reference_any.h",
"atomic_reference.h",
"condition_variable.h",
"count_down_latch.h",
"log_message.h",
"multi_thread_executor.h",
"mutex.h",
"pipe.h",
"scheduled_executor.h",
"settable_future_any.h",
"single_thread_executor.h",
],
visibility = [
"//platform_v2/impl/g3:__pkg__",
],
deps = [
"//base",
"//platform_v2/api:platform",
"//platform_v2/api:types",
"//platform_v2/base",
@@ -41,11 +43,13 @@ cc_library(
"bluetooth_adapter.cc",
"bluetooth_classic.cc",
"webrtc.cc",
"wifi_lan.cc",
],
hdrs = [
"bluetooth_adapter.h",
"bluetooth_classic.h",
"webrtc.h",
"wifi_lan.h",
],
visibility = [
"//platform_v2/impl/g3:__pkg__",
@@ -105,6 +109,7 @@ cc_library(
"//platform_v2/impl/shared:file",
"//absl/base:core_headers",
"//absl/memory",
"//absl/strings",
"//absl/time",
],
)
@@ -0,0 +1,33 @@
#ifndef PLATFORM_V2_IMPL_G3_ATOMIC_REFERENCE_H_
#define PLATFORM_V2_IMPL_G3_ATOMIC_REFERENCE_H_
#include <atomic>
#include <cstdint>
#include "platform_v2/api/atomic_reference.h"
namespace location {
namespace nearby {
namespace g3 {
class AtomicUint32 : public api::AtomicUint32 {
public:
explicit AtomicUint32(std::int32_t value) : value_(value) {}
~AtomicUint32() override = default;
std::uint32_t Get() const override {
return value_;
}
void Set(std::uint32_t value) override {
value_ = value;
}
private:
std::atomic<std::uint32_t> value_;
};
} // namespace g3
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_IMPL_G3_ATOMIC_REFERENCE_H_
@@ -1,46 +0,0 @@
#ifndef PLATFORM_V2_IMPL_G3_ATOMIC_REFERENCE_ANY_H_
#define PLATFORM_V2_IMPL_G3_ATOMIC_REFERENCE_ANY_H_
#include "platform_v2/api/atomic_reference.h"
#include "absl/base/integral_types.h"
#include "absl/synchronization/mutex.h"
#include "absl/types/any.h"
namespace location {
namespace nearby {
namespace g3 {
// Provide implementation for absl::any.
class AtomicReferenceAny : public api::AtomicReference<absl::any> {
public:
explicit AtomicReferenceAny(absl::any initial_value)
: value_(std::move(initial_value)) {}
~AtomicReferenceAny() override = default;
absl::any Get() const & override {
absl::MutexLock lock(&mutex_);
return value_;
}
absl::any Get() && override {
absl::MutexLock lock(&mutex_);
return std::move(value_);
}
void Set(const absl::any& value) override {
absl::MutexLock lock(&mutex_);
value_ = value;
}
void Set(absl::any&& value) override {
absl::MutexLock lock(&mutex_);
value_ = std::move(value);
}
private:
mutable absl::Mutex mutex_;
absl::any value_;
};
} // namespace g3
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_IMPL_G3_ATOMIC_REFERENCE_ANY_H_
+22 -16
View File
@@ -13,9 +13,15 @@ namespace location {
namespace nearby {
namespace g3 {
BluetoothSocket::~BluetoothSocket() {
absl::MutexLock lock(&mutex_);
DoClose();
}
void BluetoothSocket::Connect(BluetoothSocket& other) {
absl::MutexLock lock(&mutex_);
remote_socket_ = &other;
input_ = other.output_;
}
bool BluetoothSocket::IsConnected() const {
@@ -29,7 +35,7 @@ bool BluetoothSocket::IsClosed() const {
}
bool BluetoothSocket::IsConnectedLocked() const {
return remote_socket_ != nullptr;
return input_ != nullptr;
}
InputStream& BluetoothSocket::GetInputStream() {
@@ -44,31 +50,31 @@ OutputStream& BluetoothSocket::GetOutputStream() {
InputStream& BluetoothSocket::GetLocalInputStream() {
absl::MutexLock lock(&mutex_);
return output_.GetInputStream();
return output_->GetInputStream();
}
OutputStream& BluetoothSocket::GetLocalOutputStream() {
absl::MutexLock lock(&mutex_);
return output_.GetOutputStream();
return output_->GetOutputStream();
}
Exception BluetoothSocket::Close() {
BluetoothSocket* remote_socket = nullptr;
{
absl::MutexLock lock(&mutex_);
if (!closed_) {
remote_socket = remote_socket_;
output_.GetOutputStream().Close();
output_.GetInputStream().Close();
closed_ = true;
}
}
if (remote_socket != nullptr) {
remote_socket->Close();
}
absl::MutexLock lock(&mutex_);
DoClose();
return {Exception::kSuccess};
}
void BluetoothSocket::DoClose() {
if (!closed_) {
remote_socket_ = nullptr;
output_->GetOutputStream().Close();
output_->GetInputStream().Close();
input_->GetOutputStream().Close();
input_->GetInputStream().Close();
closed_ = true;
}
}
BluetoothSocket* BluetoothSocket::GetRemoteSocket() {
absl::MutexLock lock(&mutex_);
return remote_socket_;
+5 -2
View File
@@ -25,7 +25,7 @@ class BluetoothSocket : public api::BluetoothSocket {
public:
BluetoothSocket() = default;
explicit BluetoothSocket(BluetoothAdapter* adapter) : adapter_(adapter) {}
~BluetoothSocket() override = default;
~BluetoothSocket() override;
// Connects to another BluetoothSocket, to form a functional low-level
// channel. From this point on, and until Close is called, connection exists.
@@ -64,6 +64,8 @@ class BluetoothSocket : public api::BluetoothSocket {
BluetoothDevice* GetRemoteDevice() override ABSL_LOCKS_EXCLUDED(mutex_);
private:
void DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Returns true if connection exists to the (possibly closed) remote socket.
bool IsConnectedLocked() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
@@ -80,7 +82,8 @@ class BluetoothSocket : public api::BluetoothSocket {
// Output pipe is initialized by constructor, it remains always valid, until
// it is closed. it represents output part of a local socket. Input part of a
// local socket comes from the peer socket, after connection.
Pipe output_;
std::shared_ptr<Pipe> output_ {new Pipe};
std::shared_ptr<Pipe> input_;
mutable absl::Mutex mutex_;
BluetoothAdapter* adapter_ = nullptr; // Our Adapter. Read only.
BluetoothSocket* remote_socket_ ABSL_GUARDED_BY(mutex_) = nullptr;
@@ -19,6 +19,11 @@ class ConditionVariable : public api::ConditionVariable {
cond_var_.Wait(mutex_);
return {Exception::kSuccess};
}
Exception Wait(absl::Duration timeout) override {
return cond_var_.WaitWithTimeout(mutex_, timeout)
? Exception{Exception::kTimeout}
: Exception{Exception::kSuccess};
}
void Notify() override { cond_var_.SignalAll(); }
private:
+56
View File
@@ -0,0 +1,56 @@
#include "platform_v2/impl/g3/log_message.h"
#include <algorithm>
#include "base/stringprintf.h"
namespace location {
namespace nearby {
namespace g3 {
api::LogMessage::Severity g_min_log_severity = api::LogMessage::Severity::kInfo;
inline absl::LogSeverity ConvertSeverity(api::LogMessage::Severity severity) {
switch (severity) {
case api::LogMessage::Severity::kInfo:
return absl::LogSeverity::kInfo;
case api::LogMessage::Severity::kWarning:
return absl::LogSeverity::kWarning;
case api::LogMessage::Severity::kError:
return absl::LogSeverity::kError;
case api::LogMessage::Severity::kFatal:
return absl::LogSeverity::kFatal;
}
}
LogMessage::LogMessage(const char* file, int line, Severity severity)
: log_streamer_(ConvertSeverity(severity), file, line) {}
LogMessage::~LogMessage() = default;
void LogMessage::Print(const char* format, ...) {
va_list ap;
va_start(ap, format);
std::string result;
StringAppendV(&result, format, ap);
log_streamer_.stream() << result;
va_end(ap);
}
std::ostream& LogMessage::Stream() { return log_streamer_.stream(); }
} // namespace g3
namespace api {
void LogMessage::SetMinLogSeverity(Severity severity) {
g3::g_min_log_severity = severity;
}
bool LogMessage::ShouldCreateLogMessage(Severity severity) {
return severity >= g3::g_min_log_severity;
}
} // namespace api
} // namespace nearby
} // namespace location
+29
View File
@@ -0,0 +1,29 @@
#ifndef PLATFORM_V2_IMPL_G3_LOG_MESSAGE_H_
#define PLATFORM_V2_IMPL_G3_LOG_MESSAGE_H_
#include "base/logging.h"
#include "platform_v2/api/log_message.h"
namespace location {
namespace nearby {
namespace g3 {
// See documentation in cpp/platform_v2/api/log_message.h
class LogMessage : public api::LogMessage {
public:
LogMessage(const char* file, int line, Severity severity);
~LogMessage() override;
void Print(const char* format, ...) override;
std::ostream& Stream() override;
private:
absl::LogStreamer log_streamer_;
};
} // namespace g3
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_IMPL_G3_LOG_MESSAGE_H_
+18 -21
View File
@@ -11,28 +11,30 @@
#include "platform_v2/api/bluetooth_classic.h"
#include "platform_v2/api/condition_variable.h"
#include "platform_v2/api/count_down_latch.h"
#include "platform_v2/api/log_message.h"
#include "platform_v2/api/mutex.h"
#include "platform_v2/api/scheduled_executor.h"
#include "platform_v2/api/server_sync.h"
#include "platform_v2/api/settable_future.h"
#include "platform_v2/api/submittable_executor.h"
#include "platform_v2/api/webrtc.h"
#include "platform_v2/api/wifi.h"
#include "platform_v2/impl/g3/atomic_boolean.h"
#include "platform_v2/impl/g3/atomic_reference_any.h"
#include "platform_v2/impl/g3/atomic_reference.h"
#include "platform_v2/impl/g3/bluetooth_adapter.h"
#include "platform_v2/impl/g3/bluetooth_classic.h"
#include "platform_v2/impl/g3/condition_variable.h"
#include "platform_v2/impl/g3/count_down_latch.h"
#include "platform_v2/impl/g3/log_message.h"
#include "platform_v2/impl/g3/multi_thread_executor.h"
#include "platform_v2/impl/g3/mutex.h"
#include "platform_v2/impl/g3/scheduled_executor.h"
#include "platform_v2/impl/g3/settable_future_any.h"
#include "platform_v2/impl/g3/single_thread_executor.h"
#include "platform_v2/impl/g3/webrtc.h"
#include "platform_v2/impl/g3/wifi_lan.h"
#include "platform_v2/impl/shared/file.h"
#include "absl/base/integral_types.h"
#include "absl/memory/memory.h"
#include "absl/strings/str_cat.h"
#include "absl/time/time.h"
namespace location {
@@ -40,8 +42,8 @@ namespace nearby {
namespace api {
namespace {
std::string GetPayloadPath(std::int64_t payload_id) {
return "/tmp/" + std::to_string(payload_id);
std::string GetPayloadPath(PayloadId payload_id) {
return absl::StrCat("/tmp/", payload_id);
}
} // namespace
@@ -60,14 +62,9 @@ ImplementationPlatform::CreateScheduledExecutor() {
return absl::make_unique<g3::ScheduledExecutor>();
}
std::unique_ptr<AtomicReference<absl::any>>
ImplementationPlatform::CreateAtomicReferenceAny(absl::any initial_value) {
return absl::make_unique<g3::AtomicReferenceAny>(initial_value);
}
std::unique_ptr<SettableFuture<absl::any>>
ImplementationPlatform::CreateSettableFutureAny() {
return absl::make_unique<g3::SettableFutureAny>();
std::unique_ptr<AtomicUint32>
ImplementationPlatform::CreateAtomicUint32(std::uint32_t value) {
return absl::make_unique<g3::AtomicUint32>(value);
}
std::unique_ptr<BluetoothAdapter>
@@ -86,16 +83,21 @@ std::unique_ptr<AtomicBoolean> ImplementationPlatform::CreateAtomicBoolean(
}
std::unique_ptr<InputFile> ImplementationPlatform::CreateInputFile(
std::int64_t payload_id, std::int64_t total_size) {
PayloadId payload_id, std::int64_t total_size) {
return absl::make_unique<shared::InputFile>(GetPayloadPath(payload_id),
total_size);
}
std::unique_ptr<OutputFile> ImplementationPlatform::CreateOutputFile(
std::int64_t payload_id) {
PayloadId payload_id) {
return absl::make_unique<shared::OutputFile>(GetPayloadPath(payload_id));
}
std::unique_ptr<LogMessage> ImplementationPlatform::CreateLogMessage(
const char* file, int line, LogMessage::Severity severity) {
return absl::make_unique<g3::LogMessage>(file, line, severity);
}
std::unique_ptr<BluetoothClassicMedium>
ImplementationPlatform::CreateBluetoothClassicMedium(
api::BluetoothAdapter& adapter) {
@@ -122,7 +124,7 @@ std::unique_ptr<WifiMedium> ImplementationPlatform::CreateWifiMedium() {
}
std::unique_ptr<WifiLanMedium> ImplementationPlatform::CreateWifiLanMedium() {
return std::unique_ptr<WifiLanMedium>();
return absl::make_unique<g3::WifiLanMedium>();
}
std::unique_ptr<WebRtcMedium> ImplementationPlatform::CreateWebRtcMedium() {
@@ -142,11 +144,6 @@ ImplementationPlatform::CreateConditionVariable(Mutex* mutex) {
new g3::ConditionVariable(static_cast<g3::Mutex*>(mutex)));
}
std::string ImplementationPlatform::GetDeviceId() {
// TODO(alexchau): Get deviceId from base
return "google3";
}
} // namespace api
} // namespace nearby
} // namespace location
@@ -1,104 +0,0 @@
#ifndef PLATFORM_V2_IMPL_G3_SETTABLE_FUTURE_ANY_H_
#define PLATFORM_V2_IMPL_G3_SETTABLE_FUTURE_ANY_H_
#include <utility>
#include "platform_v2/api/platform.h"
#include "platform_v2/api/settable_future.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/clock.h"
#include "absl/types/any.h"
namespace location {
namespace nearby {
namespace g3 {
class SettableFutureAny : public api::SettableFuture<absl::any> {
public:
SettableFutureAny() = default;
~SettableFutureAny() override = default;
bool Set(const absl::any& value) override {
absl::MutexLock lock(&mutex_);
if (!done_) {
value_ = value;
done_ = true;
exception_ = {Exception::kSuccess};
completed_.SignalAll();
}
return true;
}
bool Set(absl::any&& value) override {
absl::MutexLock lock(&mutex_);
if (!done_) {
value_ = std::move(value);
done_ = true;
exception_ = {Exception::kSuccess};
completed_.SignalAll();
}
return true;
}
bool SetException(Exception exception) override {
absl::MutexLock lock(&mutex_);
return SetExceptionLocked(exception);
}
void AddListener(Runnable runnable, api::Executor* executor) override {}
ExceptionOr<std::any> Get() override {
absl::MutexLock lock(&mutex_);
while (!done_) {
completed_.Wait(&mutex_);
}
return exception_.value != Exception::kSuccess
? ExceptionOr<std::any>{exception_.value}
: ExceptionOr<std::any>{value_};
}
ExceptionOr<std::any> Get(absl::Duration timeout) override {
absl::MutexLock lock(&mutex_);
while (!done_) {
absl::Time start_time = absl::Now();
if (completed_.WaitWithTimeout(&mutex_, timeout)) {
SetExceptionLocked({Exception::kTimeout});
break;
}
absl::Duration spent = absl::Now() - start_time;
if (spent < timeout) {
timeout -= spent;
} else if (!done_) {
SetExceptionLocked({Exception::kTimeout});
break;
}
}
return exception_.value != Exception::kSuccess
? ExceptionOr<std::any>{exception_.value}
: ExceptionOr<std::any>{value_};
}
private:
bool SetExceptionLocked(Exception exception) {
if (!done_) {
exception_ = exception.value != Exception::kSuccess
? exception
: Exception{Exception::kFailed};
done_ = true;
completed_.SignalAll();
}
return true;
}
absl::Mutex mutex_;
absl::CondVar completed_;
bool done_{false};
absl::any value_;
Exception exception_{Exception::kFailed};
};
} // namespace g3
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_IMPL_G3_SETTABLE_FUTURE_ANY_H_
+30 -6
View File
@@ -1,24 +1,49 @@
#include "platform_v2/impl/g3/webrtc.h"
#include <memory>
#include "platform_v2/base/medium_environment.h"
#include "webrtc/api/task_queue/default_task_queue_factory.h"
namespace location {
namespace nearby {
namespace g3 {
WebRtcSignalingMessenger::WebRtcSignalingMessenger(absl::string_view self_id)
: self_id_(self_id) {}
bool WebRtcSignalingMessenger::SendMessage(absl::string_view peer_id,
const ByteArray& message) {
auto& env = MediumEnvironment::Instance();
env.SendWebRtcSignalingMessage(peer_id, message);
return true;
}
bool WebRtcSignalingMessenger::StartReceivingMessages(
OnSignalingMessageCallback listener) {
auto& env = MediumEnvironment::Instance();
env.RegisterWebRtcSignalingMessenger(self_id_, listener);
return true;
}
void WebRtcSignalingMessenger::StopReceivingMessages() {
auto& env = MediumEnvironment::Instance();
env.UnregisterWebRtcSignalingMessenger(self_id_);
}
void WebRtcMedium::CreatePeerConnection(
webrtc::PeerConnectionObserver* observer, PeerConnectionCallback callback) {
webrtc::PeerConnectionInterface::RTCConfiguration rtc_config;
webrtc::PeerConnectionDependencies dependencies(observer);
std::unique_ptr<rtc::Thread> signaling_thread = rtc::Thread::Create();
signaling_thread->SetName("signaling_thread", nullptr);
RTC_CHECK(signaling_thread->Start()) << "Failed to start thread";
signaling_thread_ = rtc::Thread::Create();
signaling_thread_->SetName("signaling_thread", nullptr);
RTC_CHECK(signaling_thread_->Start()) << "Failed to start thread";
webrtc::PeerConnectionFactoryDependencies factory_dependencies;
factory_dependencies.task_queue_factory =
webrtc::CreateDefaultTaskQueueFactory();
factory_dependencies.signaling_thread = signaling_thread.release();
factory_dependencies.signaling_thread = signaling_thread_.get();
callback(webrtc::CreateModularPeerConnectionFactory(
std::move(factory_dependencies))
@@ -27,8 +52,7 @@ void WebRtcMedium::CreatePeerConnection(
std::unique_ptr<api::WebRtcSignalingMessenger>
WebRtcMedium::GetSignalingMessenger(absl::string_view self_id) {
// TODO(bfranz): Implement
return nullptr;
return std::make_unique<WebRtcSignalingMessenger>(self_id);
}
} // namespace g3
+19
View File
@@ -11,6 +11,23 @@ namespace location {
namespace nearby {
namespace g3 {
class WebRtcSignalingMessenger : public api::WebRtcSignalingMessenger {
public:
using OnSignalingMessageCallback =
api::WebRtcSignalingMessenger::OnSignalingMessageCallback;
explicit WebRtcSignalingMessenger(absl::string_view self_id);
~WebRtcSignalingMessenger() override = default;
bool SendMessage(absl::string_view peer_id,
const ByteArray& message) override;
bool StartReceivingMessages(OnSignalingMessageCallback listener) override;
void StopReceivingMessages() override;
private:
absl::string_view self_id_;
};
class WebRtcMedium : public api::WebRtcMedium {
public:
using PeerConnectionCallback = api::WebRtcMedium::PeerConnectionCallback;
@@ -26,6 +43,8 @@ class WebRtcMedium : public api::WebRtcMedium {
// Returns a signaling messenger for sending WebRTC signaling messages.
std::unique_ptr<api::WebRtcSignalingMessenger> GetSignalingMessenger(
absl::string_view self_id) override;
private:
std::unique_ptr<rtc::Thread> signaling_thread_;
};
} // namespace g3
+114
View File
@@ -0,0 +1,114 @@
#include "platform_v2/impl/g3/wifi_lan.h"
#include <memory>
#include <string>
#include "platform_v2/api/wifi_lan.h"
#include "platform_v2/base/logging.h"
#include "platform_v2/base/medium_environment.h"
#include "absl/synchronization/mutex.h"
namespace location {
namespace nearby {
namespace g3 {
InputStream& WifiLanSocket::GetInputStream() {
absl::MutexLock lock(&mutex_);
return pipe_.GetInputStream();
}
OutputStream& WifiLanSocket::GetOutputStream() {
absl::MutexLock lock(&mutex_);
return pipe_.GetOutputStream();
}
Exception WifiLanSocket::Close() {
absl::MutexLock lock(&mutex_);
pipe_.GetOutputStream().Close();
pipe_.GetInputStream().Close();
return {Exception::kSuccess};
}
WifiLanService* WifiLanSocket::GetRemoteWifiLanService() {
absl::MutexLock lock(&mutex_);
return service_;
}
WifiLanMedium::WifiLanMedium() {
auto& env = MediumEnvironment::Instance();
env.RegisterWifiLanMedium(*this);
}
WifiLanMedium::~WifiLanMedium() {
auto& env = MediumEnvironment::Instance();
env.UnregisterWifiLanMedium(*this);
}
bool WifiLanMedium::StartAdvertising(
const std::string& service_id,
const std::string& wifi_lan_service_info_name) {
// TODO(edwinwu): Integrate medium_environment.
// steps:
// 1. create wifi_lan_service as the parameter to create wifi_lan_socket
// auto service = std::make_unique<WifiLanService>();
// auto socket = std::make_unique<WifiLanSocket>(service);
// 2. callback for accepting connection; otherwise don't callback if not
// accepted connection.
// accepted_connection_callback_.accepted_cb(socket, service_id);
return true;
}
bool WifiLanMedium::StopAdvertising(const std::string& service_id) {
// TODO(edwinwu): Integrate medium_environment.
return true;
}
bool WifiLanMedium::StartDiscovery(const std::string& service_id,
DiscoveredServiceCallback callback) {
auto& env = MediumEnvironment::Instance();
NEARBY_LOG(INFO, "G3 StartDiscovery: service_id=%s", service_id.c_str());
env.UpdateWifiLanMediumForDiscovery(*this, service_, service_id,
std::move(callback), true);
return true;
}
bool WifiLanMedium::StopDiscovery(const std::string& service_id) {
auto& env = MediumEnvironment::Instance();
env.UpdateWifiLanMediumForDiscovery(*this, service_, service_id, {}, false);
return true;
}
bool WifiLanMedium::StartAcceptingConnections(
const std::string& service_id, AcceptedConnectionCallback callback) {
// TODO(edwinwu): Integrate medium_environment.
// steps:
auto& env = MediumEnvironment::Instance();
env.UpdateWifiLanMediumForAcceptedConnection(*this, service_id, callback);
return true;
}
bool WifiLanMedium::StopAcceptingConnections(const std::string& service_id) {
// TODO(edwinwu): Integrate medium_environment.
auto& env = MediumEnvironment::Instance();
env.UpdateWifiLanMediumForAcceptedConnection(*this, service_id, {});
return true;
}
std::unique_ptr<api::WifiLanSocket> WifiLanMedium::Connect(
api::WifiLanService& service, const std::string& service_id) {
auto socket = std::make_unique<WifiLanSocket>();
NEARBY_LOG(INFO, "G3 Connect: medium=%p, service_id=%s", this,
service_id.c_str());
return socket;
// TODO(edwinwu): Integrate medium_environment.
// steps:
// Request a connection, and block until the socket is provided via the
// callback.
// 1. connection = wifi_lan_service.requestConnection_();
// 2. create wifi_lan_socket with wifi_lan_service and connection
// return wifi_lan_socket;
}
} // namespace g3
} // namespace nearby
} // namespace location
+109
View File
@@ -0,0 +1,109 @@
#ifndef PLATFORM_V2_IMPL_G3_WIFI_LAN_H_
#define PLATFORM_V2_IMPL_G3_WIFI_LAN_H_
#include <string>
#include "platform_v2/api/wifi_lan.h"
#include "platform_v2/base/byte_array.h"
#include "platform_v2/base/input_stream.h"
#include "platform_v2/base/output_stream.h"
#include "platform_v2/impl/g3/pipe.h"
#include "absl/container/flat_hash_map.h"
#include "absl/synchronization/mutex.h"
namespace location {
namespace nearby {
namespace g3 {
// Opaque wrapper over a WifiLan service which contains encoded WifiLan service
// info name.
class WifiLanService : public api::WifiLanService {
public:
explicit WifiLanService(std::string name) : name_(std::move(name)) {}
~WifiLanService() override = default;
void SetName(std::string name) { name_ = std::move(name); }
std::string GetName() const override { return name_; }
private:
std::string name_;
};
class WifiLanSocket : public api::WifiLanSocket {
public:
WifiLanSocket() = default;
explicit WifiLanSocket(WifiLanService* service) : service_(service) {}
~WifiLanSocket() override = default;
// Connect to another WifiLanSocket, to form a functional low-level channel.
// from this point on, and until Close is called, connection exists.
void ConnectTo(WifiLanSocket* other) ABSL_LOCKS_EXCLUDED(mutex_);
// Returns the InputStream of this connected WifiLanSocket.
InputStream& GetInputStream() override ABSL_LOCKS_EXCLUDED(mutex_);
// Returns the OutputStream of this connected WifiLanSocket.
// This stream is for local side to write.
OutputStream& GetOutputStream() override ABSL_LOCKS_EXCLUDED(mutex_);
// Returns Exception::kIo on error, Exception::kSuccess otherwise.
Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_);
// Returns valid WifiLanService pointer if there is a connection, and
// nullptr otherwise.
WifiLanService* GetRemoteWifiLanService() override
ABSL_LOCKS_EXCLUDED(mutex_);
private:
Pipe pipe_;
WifiLanService* service_;
mutable absl::Mutex mutex_;
};
// Container of operations that can be performed over the WifiLan medium.
class WifiLanMedium : public api::WifiLanMedium {
public:
WifiLanMedium();
~WifiLanMedium() override;
bool StartAdvertising(const std::string& service_id,
const std::string& wifi_lan_service_info_name) override
ABSL_LOCKS_EXCLUDED(mutex_);
bool StopAdvertising(const std::string& service_id) override
ABSL_LOCKS_EXCLUDED(mutex_);
// Returns true once the WifiLan discovery has been initiated.
bool StartDiscovery(const std::string& service_id,
DiscoveredServiceCallback callback) override
ABSL_LOCKS_EXCLUDED(mutex_);
// Returns true once WifiLan discovery for service_id is well and truly
// stopped; after this returns, there must be no more invocations of the
// DiscoveredServiceCallback passed in to StartDiscovery() for service_id.
bool StopDiscovery(const std::string& service_id) override
ABSL_LOCKS_EXCLUDED(mutex_);
// Returns true once WifiLan socket connection requests to service_id can be
// accepted.
bool StartAcceptingConnections(const std::string& service_id,
AcceptedConnectionCallback callback) override
ABSL_LOCKS_EXCLUDED(mutex_);
bool StopAcceptingConnections(const std::string& service_id) override
ABSL_LOCKS_EXCLUDED(mutex_);
// Returns a new WifiLanSocket. On Success, WifiLanSocket::IsValid()
// returns true.
std::unique_ptr<api::WifiLanSocket> Connect(
api::WifiLanService& service, const std::string& service_id) override
ABSL_LOCKS_EXCLUDED(mutex_);
private:
absl::Mutex mutex_;
WifiLanService service_{"wifi_lan_service_info_name"};
};
} // namespace g3
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_IMPL_G3_WIFI_LAN_H_
+7
View File
@@ -13,11 +13,13 @@ cc_library(
"crypto.h",
"file.h",
"future.h",
"logging.h",
"multi_thread_executor.h",
"mutex.h",
"mutex_lock.h",
"pipe.h",
"scheduled_executor.h",
"settable_future.h",
"single_thread_executor.h",
"submittable_executor.h",
"system_clock.h",
@@ -32,6 +34,7 @@ cc_library(
"//platform_v2/api:platform",
"//platform_v2/api:types",
"//platform_v2/base",
"//platform_v2/base:logging",
"//platform_v2/base:util",
"//absl/base:core_headers",
"//absl/container:flat_hash_map",
@@ -44,11 +47,13 @@ cc_library(
name = "comm",
srcs = [
"bluetooth_classic.cc",
"wifi_lan.cc",
],
hdrs = [
"bluetooth_adapter.h",
"bluetooth_classic.h",
"webrtc.h",
"wifi_lan.h",
],
visibility = [
"//core_v2:__subpackages__",
@@ -89,6 +94,7 @@ cc_test(
"atomic_reference_test.cc",
"bluetooth_adapter_test.cc",
"bluetooth_classic_test.cc",
"condition_variable_test.cc",
"count_down_latch_test.cc",
"crypto_test.cc",
"future_test.cc",
@@ -98,6 +104,7 @@ cc_test(
"pipe_test.cc",
"scheduled_executor_test.cc",
"single_thread_executor_test.cc",
"wifi_lan_test.cc",
],
shard_count = 16,
deps = [
+48 -13
View File
@@ -2,36 +2,71 @@
#define PLATFORM_V2_PUBLIC_ATOMIC_REFERENCE_H_
#include <memory>
#include <type_traits>
#include "platform_v2/api/atomic_reference.h"
#include "platform_v2/api/platform.h"
#include "platform_v2/public/mutex.h"
#include "platform_v2/public/mutex_lock.h"
#include "absl/types/any.h"
namespace location {
namespace nearby {
// An object reference that may be updated atomically.
//
// https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicReference.html
template <typename, typename = void>
class AtomicReference;
// Platform-based atomic type, for something convertible to std::uint32_t.
template <typename T>
class AtomicReference final : public api::AtomicReference<T> {
class AtomicReference<T, std::enable_if_t<sizeof(T) <= sizeof(std::uint32_t) &&
std::is_trivially_copyable_v<T>,
void>>
final {
public:
using Platform = api::ImplementationPlatform;
explicit AtomicReference(const T& value)
: impl_(Platform::CreateAtomicReferenceAny(value)) {}
explicit AtomicReference(T&& value)
: impl_(Platform::CreateAtomicReferenceAny(std::move(value))) {}
~AtomicReference() override = default;
explicit AtomicReference(T value)
: impl_(Platform::CreateAtomicUint32(static_cast<std::uint32_t>(value))) {
}
~AtomicReference() = default;
AtomicReference(AtomicReference&&) = default;
AtomicReference& operator=(AtomicReference&&) = default;
T Get() const& override { return absl::any_cast<T>(impl_->Get()); }
T Get() && override { return absl::any_cast<T>(std::move(impl_->Get())); }
void Set(const T& value) override { impl_->Set(absl::any(value)); }
void Set(T&& value) override { impl_->Set(absl::any(value)); }
T Get() const { return static_cast<T>(impl_->Get()); }
void Set(T value) { impl_->Set(static_cast<std::uint32_t>(value)); }
private:
std::unique_ptr<api::AtomicReference<absl::any>> impl_;
std::unique_ptr<api::AtomicUint32> impl_;
};
// Atomic type that is using Platform mutex to provide atomicity.
// Supports any copyable type.
template <typename T>
class AtomicReference<T, std::enable_if_t<(sizeof(T) > sizeof(std::uint32_t) ||
!std::is_trivially_copyable_v<T>),
void>>
final {
public:
explicit AtomicReference(T value) {
MutexLock lock(&mutex_);
value_ = std::move(value);
}
void Set(T value) {
MutexLock lock(&mutex_);
value_ = std::move(value);
}
T Get() const& {
MutexLock lock(&mutex_);
return value_;
}
T&& Get() const&& {
MutexLock lock(&mutex_);
return std::move(value_);
}
private:
mutable Mutex mutex_;
T value_;
};
} // namespace nearby
@@ -19,6 +19,7 @@ class BluetoothClassicMediumTest : public ::testing::Test {
protected:
using DiscoveryCallback = BluetoothClassicMedium::DiscoveryCallback;
BluetoothClassicMediumTest() {
env_.Start();
env_.Reset();
adapter_a_ = std::make_unique<BluetoothAdapter>();
adapter_b_ = std::make_unique<BluetoothAdapter>();
@@ -40,6 +41,7 @@ class BluetoothClassicMediumTest : public ::testing::Test {
adapter_a_.reset();
adapter_b_.reset();
env_.Reset();
env_.Stop();
}
MediumEnvironment& env_{MediumEnvironment::Instance()};
+1 -2
View File
@@ -21,10 +21,9 @@ class ConditionVariable final {
ConditionVariable(ConditionVariable&&) = default;
ConditionVariable& operator=(ConditionVariable&&) = default;
// https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#notify--
void Notify() { impl_->Notify(); }
// https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#wait--
Exception Wait() { return impl_->Wait(); }
Exception Wait(absl::Duration timeout) { return impl_->Wait(timeout); }
private:
std::unique_ptr<api::ConditionVariable> impl_;
@@ -0,0 +1,62 @@
#include "platform_v2/public/condition_variable.h"
#include "platform_v2/public/logging.h"
#include "platform_v2/public/mutex.h"
#include "platform_v2/public/single_thread_executor.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/time/time.h"
namespace location {
namespace nearby {
namespace {
TEST(ConditionVariableTest, CanCreate) {
Mutex mutex;
ConditionVariable cond{&mutex};
}
TEST(ConditionVariableTest, CanWakeupWaiter) {
Mutex mutex;
ConditionVariable cond{&mutex};
bool done = false;
bool waiting = false;
NEARBY_LOG(INFO, "At start; done=%d", done);
{
SingleThreadExecutor executor;
executor.Execute([&cond, &mutex, &done, &waiting]() {
MutexLock lock(&mutex);
NEARBY_LOG(INFO, "Before cond.Wait(); done=%d", done);
waiting = true;
cond.Wait();
waiting = false;
done = true;
NEARBY_LOG(INFO, "After cond.Wait(); done=%d", done);
});
while (true) {
{
MutexLock lock(&mutex);
if (waiting) break;
}
SystemClock::Sleep(absl::Milliseconds(100));
}
{
MutexLock lock(&mutex);
cond.Notify();
EXPECT_FALSE(done);
}
}
NEARBY_LOG(INFO, "After executor shutdown: done=%d", done);
EXPECT_TRUE(done);
}
TEST(ConditionVariableTest, WaitTerminatesOnTimeoutWithoutNotify) {
Mutex mutex;
ConditionVariable cond{&mutex};
MutexLock lock(&mutex);
EXPECT_EQ(cond.Wait(absl::Milliseconds(100)), Exception{Exception::kTimeout});
}
} // namespace
} // namespace nearby
} // namespace location
+61 -17
View File
@@ -10,45 +10,89 @@
#include "platform_v2/api/platform.h"
#include "platform_v2/base/byte_array.h"
#include "platform_v2/base/exception.h"
#include "platform_v2/base/input_stream.h"
#include "platform_v2/base/output_stream.h"
namespace location {
namespace nearby {
class InputFile final : public api::InputFile {
class InputFile final {
public:
using Platform = api::ImplementationPlatform;
InputFile(std::int64_t payload_id, std::int64_t size)
: impl_(Platform::CreateInputFile(payload_id, size)) {}
~InputFile() override = default;
InputFile(PayloadId payload_id, std::int64_t size)
: impl_(Platform::CreateInputFile(payload_id, size)), id_(payload_id) {}
~InputFile() = default;
InputFile(InputFile&&) = default;
InputFile& operator=(InputFile&&) = default;
ExceptionOr<ByteArray> Read(std::int64_t size) override {
return impl_->Read(size);
}
std::string GetFilePath() const override { return impl_->GetFilePath(); }
std::int64_t GetTotalSize() const override { return impl_->GetTotalSize(); }
Exception Close() override { return impl_->Close(); }
// Reads up to size bytes and returns as a ByteArray object wrapped by
// ExceptionOr.
// Returns Exception::kIo on error, or end of file.
ExceptionOr<ByteArray> Read(std::int64_t size) { return impl_->Read(size); }
// Returns a string that uniqely identifies this file.
std::string GetFilePath() const { return impl_->GetFilePath(); }
// Returns total size of this file in bytes.
std::int64_t GetTotalSize() const { return impl_->GetTotalSize(); }
// Disallows further reads from the file and frees system resources,
// associated with it.
Exception Close() { return impl_->Close(); }
// Returns a handle to the underlying input stream.
//
// Returned handle will remain valid even if InputFile is moved, for as long
// as original InputFile lifetime continues.
// Side effects of any non-const operation invoked for InputFile (such as
// Read, or Close will be observable through InputStream& handle, and vice
// versa.
InputStream& GetInputStream() { return *impl_; }
// Returns payload id of this file. The closest "file" equivalent is inode.
PayloadId GetPayloadId() const { return id_; }
private:
std::unique_ptr<api::InputFile> impl_;
PayloadId id_;
};
class OutputFile final : public api::OutputFile {
class OutputFile final {
public:
using Platform = api::ImplementationPlatform;
explicit OutputFile(std::int64_t payload_id)
: impl_(Platform::CreateOutputFile(payload_id)) {}
~OutputFile() override = default;
explicit OutputFile(PayloadId payload_id)
: impl_(Platform::CreateOutputFile(payload_id)), id_(payload_id) {}
~OutputFile() = default;
OutputFile(OutputFile&&) = default;
OutputFile& operator=(OutputFile&&) = default;
Exception Write(const ByteArray& data) override { return impl_->Write(data); }
Exception Flush() override { return impl_->Flush(); }
Exception Close() override { return impl_->Close(); }
// Writes all data from ByteArray object to the underlying stream.
// Returns Exception::kIo on error, Exception::kSuccess otherwise.
Exception Write(const ByteArray& data) { return impl_->Write(data); }
// Ensures that all data written by previous calls to Write() is passed
// down to the applicable transport layer.
Exception Flush() { return impl_->Flush(); }
// Disallows further writes to the file and frees system resources,
// associated with it.
Exception Close() { return impl_->Close(); }
// Returns a handle to the underlying output stream.
//
// Returned handle will remain valid even if OutputFile is moved, for as long
// as original OutputFile lifetime continues.
// Side effects of any non-const operation invoked for OutputFile (such as
// Write, or Close will be observable through OutputStream& handle, and vice
// versa.
OutputStream& GetOutputStream() { return *impl_; }
// Returns payload id of this file. The closest "file" equivalent is inode.
PayloadId GetPayloadId() const { return id_; }
private:
std::unique_ptr<api::OutputFile> impl_;
PayloadId id_;
};
} // namespace nearby
+20 -42
View File
@@ -1,60 +1,38 @@
#ifndef PLATFORM_V2_PUBLIC_FUTURE_H_
#define PLATFORM_V2_PUBLIC_FUTURE_H_
#include "platform_v2/api/executor.h"
#include "platform_v2/api/platform.h"
#include "platform_v2/api/settable_future.h"
#include "platform_v2/base/exception.h"
#include "platform_v2/base/runnable.h"
#include "absl/time/time.h"
#include "absl/types/any.h"
#include "platform_v2/public/settable_future.h"
namespace location {
namespace nearby {
template <typename T>
class Future final : public api::SettableFuture<T> {
class Future final {
public:
using Platform = api::ImplementationPlatform;
~Future() override = default;
Future() : impl_(Platform::CreateSettableFutureAny().release()) {}
Future(Future&& other) = default;
Future& operator=(Future&& other) = default;
void AddListener(Runnable runnable, api::Executor* executor) override {
impl_->AddListener(runnable, executor);
}
bool Set(const T& value) override { return impl_->Set(absl::any(value)); }
bool Set(T&& value) override { return impl_->Set(absl::any(value)); }
bool SetException(Exception exception) override {
virtual bool Set(T value) { return impl_->Set(std::move(value)); }
virtual bool SetException(Exception exception) {
return impl_->SetException(exception);
}
// throws Exception::kInterrupted, Exception::kExecution
ExceptionOr<T> Get() override {
auto ret_val = impl_->Get();
if (ret_val.ok()) {
T result = absl::any_cast<T>(ret_val.result());
return ExceptionOr<T>{result};
} else {
return ExceptionOr<T>{ret_val.exception()};
}
virtual ExceptionOr<T> Get() { return impl_->Get(); }
virtual ExceptionOr<T> Get(absl::Duration timeout) {
return impl_->Get(timeout);
}
// throws Exception::kInterrupted, Exception::kExecution
// throws Exception::kTimeout if timeout is exceeded while waiting for
// result.
ExceptionOr<T> Get(absl::Duration timeout) override {
auto ret_val = impl_->Get(timeout);
if (ret_val.ok()) {
T result = absl::any_cast<T>(ret_val.result());
return ExceptionOr<T>{result};
} else {
return ExceptionOr<T>{ret_val.exception()};
}
void AddListener(Runnable runnable, api::Executor* executor) {
impl_->AddListener(std::move(runnable), executor);
}
private:
std::unique_ptr<api::SettableFuture<absl::any>> impl_;
// Instance of future implementation is wrapped in shared_ptr<> to make
// it possible to pass Future by value and share the implementation.
// This allows for the following constructions:
// 1)
// Future<bool> future;
// RunOnXyzThread([future]() { future.Set(DoTheJobAndReport()); });
// if (future.Get().Ok()) { /*...*/ }
// 2)
// Future<bool> future = DoSomeAsyncWork(); // Returns future, but keeps copy.
// if (future.Get().Ok()) { /*...*/ }
std::shared_ptr<SettableFuture<T>> impl_{new SettableFuture<T>()};
};
} // namespace nearby
+27 -1
View File
@@ -6,7 +6,33 @@
namespace {
TEST(LoggingTest, CanLog) {
NEARBY_LOG(INFO, "message");
NEARBY_LOG_SET_SEVERITY(INFO);
int num = 42;
NEARBY_LOG(INFO, "The answer to everything: %d", num++);
EXPECT_EQ(num, 43);
}
TEST(LoggingTest, CanLog_LoggingDisabled) {
NEARBY_LOG_SET_SEVERITY(ERROR);
int num = 42;
NEARBY_LOG(INFO, "The answer to everything: %d", num++);
// num++ should not be evaluated
EXPECT_EQ(num, 42);
}
TEST(LoggingTest, CanStream) {
NEARBY_LOG_SET_SEVERITY(INFO);
int num = 42;
NEARBY_LOGS(INFO) << "The answer to everything: " << num++;
EXPECT_EQ(num, 43);
}
TEST(LoggingTest, CanStream_LoggingDisabled) {
NEARBY_LOG_SET_SEVERITY(ERROR);
int num = 42;
NEARBY_LOGS(INFO) << "The answer to everything: " << num++;
// num++ should not be evaluated
EXPECT_EQ(num, 42);
}
} // namespace
+1 -1
View File
@@ -27,7 +27,7 @@ class MutexTest : public testing::Test {
protected:
SingleThreadExecutor executor_;
const absl::Duration kTimeToWait = absl::Milliseconds(200);
const absl::Duration kTimeToWait = absl::Milliseconds(500);
std::atomic_int step_ = 0;
absl::Mutex step_mutex_;
absl::CondVar step_cond_;
+1 -2
View File
@@ -7,8 +7,7 @@ namespace location {
namespace nearby {
// See for details:
// TODO(apolyudov): replace with cs/ link once it becomes available.
// https://critique-ng.corp.google.com/cl/310492721/depot/google3/platform_v2/base/base_pipe.h
// http://google3/platform_v2/base/base_pipe.h
class Pipe final : public BasePipe {
public:
Pipe();
@@ -12,6 +12,14 @@
namespace location {
namespace nearby {
// kShortDelay must be significant enough to guarantee that OS under heavy load
// should be able to execute the non-blocking test paths within this time.
absl::Duration kShortDelay = absl::Milliseconds(100);
// kLongDelay must be long enough to make sure that under OS under heavy load
// will let kShortDelay fire and jobs scheduled before the kLongDelay fires.
absl::Duration kLongDelay = 10 * kShortDelay;
TEST(ScheduledExecutorTest, ConsructorDestructorWorks) {
ScheduledExecutor executor;
}
@@ -28,7 +36,7 @@ TEST(ScheduledExecutorTest, CanExecute) {
{
absl::MutexLock lock(&mutex);
if (!done) {
cond.WaitWithTimeout(&mutex, absl::Seconds(1));
cond.WaitWithTimeout(&mutex, kLongDelay);
}
}
EXPECT_TRUE(done);
@@ -39,25 +47,25 @@ TEST(ScheduledExecutorTest, CanSchedule) {
std::atomic_int value = 0;
absl::Mutex mutex;
absl::CondVar cond;
// schedule job due in 100 ms.
// schedule job due in kLongDelay.
executor.Schedule(
[&value, &cond]() {
EXPECT_EQ(value, 1);
value = 5;
cond.Signal();
},
absl::Milliseconds(100));
// schedule job due in 10 ms; must fire before the first one.
kLongDelay);
// schedule job due in kShortDelay; must fire before the first one.
executor.Schedule(
[&value]() {
EXPECT_EQ(value, 0);
value = 1;
},
absl::Milliseconds(10));
kShortDelay);
{
// wait for the final job to unblock us.
// wait for the final job to unblock us; wait longer than kLongDelay.
absl::MutexLock lock(&mutex);
cond.WaitWithTimeout(&mutex, absl::Milliseconds(1000));
cond.WaitWithTimeout(&mutex, 2 * kLongDelay);
}
EXPECT_EQ(value, 5);
}
@@ -66,10 +74,10 @@ TEST(ScheduledExecutorTest, CanCancel) {
ScheduledExecutor executor;
std::atomic_int value = 0;
Cancelable cancelable =
executor.Schedule([&value]() { value += 1; }, absl::Milliseconds(10));
executor.Schedule([&value]() { value += 1; }, kShortDelay);
EXPECT_EQ(value, 0);
EXPECT_TRUE(cancelable.Cancel());
absl::SleepFor(absl::Milliseconds(500));
absl::SleepFor(kLongDelay);
EXPECT_EQ(value, 0);
}
@@ -78,17 +86,17 @@ TEST(ScheduledExecutorTest, FailToCancel) {
absl::CondVar cond;
ScheduledExecutor executor;
std::atomic_int value = 0;
// Schedule job in 10ms, which will we will attempt to cancel later.
// Schedule job in kShortDelay, which will we will attempt to cancel later.
Cancelable cancelable =
executor.Schedule([&value]() { value += 1; }, absl::Milliseconds(10));
// schedule another job to test results of the first one, in 50ms from now.
executor.Schedule([&value]() { value += 1; }, kShortDelay);
// schedule another job to test results of the first one, in kLongDelay.
executor.Schedule(
[&cancelable, &cond]() {
EXPECT_FALSE(cancelable.Cancel());
// Wake up main thread.
cond.Signal();
},
absl::Milliseconds(50));
kLongDelay);
{
absl::MutexLock lock(&mutex);
cond.Wait(&mutex);
+108
View File
@@ -0,0 +1,108 @@
#ifndef PLATFORM_V2_PUBLIC_SETTABLE_FUTURE_H_
#define PLATFORM_V2_PUBLIC_SETTABLE_FUTURE_H_
#include <utility>
#include "platform_v2/public/condition_variable.h"
#include "platform_v2/public/mutex.h"
#include "platform_v2/public/mutex_lock.h"
#include "platform_v2/public/system_clock.h"
namespace location {
namespace nearby {
template <typename T>
class SettableFuture : public api::SettableFuture<T> {
public:
SettableFuture() = default;
~SettableFuture() override = default;
bool Set(T value) override {
MutexLock lock(&mutex_);
if (!done_) {
value_ = std::move(value);
done_ = true;
exception_ = {Exception::kSuccess};
completed_.Notify();
InvokeAllLocked();
}
return true;
}
void AddListener(Runnable runnable, api::Executor* executor) override {
MutexLock lock(&mutex_);
if (done_) {
executor->Execute(std::move(runnable));
} else {
listeners_.emplace_back(std::make_pair(executor, std::move(runnable)));
}
}
bool SetException(Exception exception) override {
MutexLock lock(&mutex_);
return SetExceptionLocked(exception);
}
ExceptionOr<T> Get() override {
MutexLock lock(&mutex_);
while (!done_) {
completed_.Wait();
}
return exception_.value != Exception::kSuccess
? ExceptionOr<T>{exception_.value}
: ExceptionOr<T>{value_};
}
ExceptionOr<T> Get(absl::Duration timeout) override {
MutexLock lock(&mutex_);
while (!done_) {
absl::Time start_time = SystemClock::ElapsedRealtime();
if (completed_.Wait(timeout).Raised(Exception::kTimeout)) {
SetExceptionLocked({Exception::kTimeout});
break;
}
absl::Duration spent = SystemClock::ElapsedRealtime() - start_time;
if (spent < timeout) {
timeout -= spent;
} else if (!done_) {
SetExceptionLocked({Exception::kTimeout});
break;
}
}
return exception_.value != Exception::kSuccess
? ExceptionOr<T>{exception_.value}
: ExceptionOr<T>{value_};
}
private:
bool SetExceptionLocked(Exception exception) {
if (!done_) {
exception_ = exception.value != Exception::kSuccess
? exception
: Exception{Exception::kFailed};
done_ = true;
completed_.Notify();
InvokeAllLocked();
}
return true;
}
void InvokeAllLocked() {
for (auto& item : listeners_) {
item.first->Execute(std::move(item.second));
}
listeners_.clear();
}
Mutex mutex_;
ConditionVariable completed_{&mutex_};
std::vector<std::pair<api::Executor*, std::function<void()>>> listeners_;
bool done_{false};
T value_;
Exception exception_{Exception::kFailed};
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_PUBLIC_SETTABLE_FUTURE_H_
+31 -2
View File
@@ -10,6 +10,34 @@
namespace location {
namespace nearby {
class WebRtcSignalingMessenger final {
public:
using OnSignalingMessageCallback =
api::WebRtcSignalingMessenger::OnSignalingMessageCallback;
explicit WebRtcSignalingMessenger(
std::unique_ptr<api::WebRtcSignalingMessenger> messenger)
: impl_(std::move(messenger)) {}
~WebRtcSignalingMessenger() = default;
WebRtcSignalingMessenger(WebRtcSignalingMessenger&&) = default;
WebRtcSignalingMessenger operator=(WebRtcSignalingMessenger&&) = delete;
bool SendMessage(absl::string_view peer_id, const ByteArray& message) {
return impl_->SendMessage(peer_id, message);
}
bool StartReceivingMessages(OnSignalingMessageCallback listener) {
return impl_->StartReceivingMessages(listener);
}
void StopReceivingMessages() { impl_->StopReceivingMessages(); }
bool IsValid() const { return impl_ != nullptr; }
private:
std::unique_ptr<api::WebRtcSignalingMessenger> impl_;
};
class WebRtcMedium final {
public:
using PeerConnectionCallback = api::WebRtcMedium::PeerConnectionCallback;
@@ -27,9 +55,10 @@ class WebRtcMedium final {
}
// Returns a signaling messenger for sending WebRTC signaling messages.
std::unique_ptr<api::WebRtcSignalingMessenger> GetSignalingMessenger(
std::unique_ptr<WebRtcSignalingMessenger> GetSignalingMessenger(
absl::string_view self_id) {
return impl_->GetSignalingMessenger(self_id);
return std::make_unique<WebRtcSignalingMessenger>(
impl_->GetSignalingMessenger(self_id));
}
bool IsValid() const { return impl_ != nullptr; }
+120
View File
@@ -0,0 +1,120 @@
#include "platform_v2/public/wifi_lan.h"
#include "platform_v2/public/logging.h"
#include "platform_v2/public/mutex_lock.h"
namespace location {
namespace nearby {
bool WifiLanMedium::StartAdvertising(
const std::string& service_id,
const std::string& wifi_lan_service_info_name) {
return impl_->StartAdvertising(service_id, wifi_lan_service_info_name);
}
bool WifiLanMedium::StopAdvertising(const std::string& service_id) {
return impl_->StopAdvertising(service_id);
}
bool WifiLanMedium::StartDiscovery(const std::string& service_id,
DiscoveredServiceCallback callback) {
{
MutexLock lock(&mutex_);
discovered_service_callback_ = std::move(callback);
services_.clear();
}
return impl_->StartDiscovery(
service_id,
{
.service_discovered_cb =
[this](api::WifiLanService& service,
const std::string& service_id) {
MutexLock lock(&mutex_);
auto pair = services_.emplace(
&service, absl::make_unique<ServiceDiscoveryInfo>());
auto& context = *pair.first->second;
if (!pair.second) {
NEARBY_LOG(INFO, "Adding (again) service=%p, impl=%p",
&context.service, &service);
return;
}
context.service = WifiLanService(&service);
NEARBY_LOG(INFO, "Adding service=%p, impl=%p", &context.service,
&service);
discovered_service_callback_.service_discovered_cb(
context.service, service_id);
},
.service_lost_cb =
[this](api::WifiLanService& service,
const std::string& service_id) {
MutexLock lock(&mutex_);
auto item = services_.extract(&service);
auto& context = *item.mapped();
NEARBY_LOG(INFO, "Removing service=%p, impl=%p",
&context.service, &service);
discovered_service_callback_.service_lost_cb(context.service,
service_id);
},
});
}
bool WifiLanMedium::StopDiscovery(const std::string& service_id) {
{
MutexLock lock(&mutex_);
discovered_service_callback_ = {};
services_.clear();
NEARBY_LOG(INFO, "WifiLan Discovery disabled: impl=%p", &GetImpl());
}
return impl_->StopDiscovery(service_id);
}
bool WifiLanMedium::StartAcceptingConnections(
const std::string& service_id, AcceptedConnectionCallback callback) {
{
MutexLock lock(&mutex_);
accepted_connection_callback_ = std::move(callback);
}
return impl_->StartAcceptingConnections(
service_id,
{
.accepted_cb =
[this](api::WifiLanSocket& socket,
const std::string& service_id) {
MutexLock lock(&mutex_);
auto pair = sockets_.emplace(
&socket, absl::make_unique<AcceptedConnectionInfo>());
auto& context = *pair.first->second;
if (!pair.second) {
NEARBY_LOG(INFO, "Adding (again) socket=%p, impl=%p",
&context.socket, &socket);
return;
}
context.socket = WifiLanSocket(&socket);
NEARBY_LOG(INFO, "Adding socket=%p, impl=%p", &context.socket,
&socket);
accepted_connection_callback_.accepted_cb(context.socket,
service_id);
},
});
}
bool WifiLanMedium::StopAcceptingConnections(const std::string& service_id) {
{
MutexLock lock(&mutex_);
accepted_connection_callback_ = {};
sockets_.clear();
NEARBY_LOG(INFO, "WifiLan accepted connection disabled: impl=%p",
&GetImpl());
}
return impl_->StopDiscovery(service_id);
}
WifiLanSocket WifiLanMedium::Connect(WifiLanService& service,
const std::string& service_id) {
NEARBY_LOG(INFO, "WifiLanMedium::Connect: service=%p [impl=%p]", &service,
&service.GetImpl());
return WifiLanSocket(impl_->Connect(service.GetImpl(), service_id));
}
} // namespace nearby
} // namespace location
+160
View File
@@ -0,0 +1,160 @@
#ifndef PLATFORM_V2_PUBLIC_WIFI_LAN_H_
#define PLATFORM_V2_PUBLIC_WIFI_LAN_H_
#include "platform_v2/api/platform.h"
#include "platform_v2/api/wifi_lan.h"
#include "platform_v2/base/byte_array.h"
#include "platform_v2/base/input_stream.h"
#include "platform_v2/base/output_stream.h"
#include "platform_v2/public/mutex.h"
#include "absl/container/flat_hash_map.h"
namespace location {
namespace nearby {
// Opaque wrapper over a WifiLan service which contains encoded service name.
class WifiLanService final {
public:
WifiLanService() = default;
WifiLanService(const WifiLanService&) = default;
WifiLanService& operator=(const WifiLanService&) = default;
explicit WifiLanService(api::WifiLanService* service) : impl_(service) {}
~WifiLanService() = default;
std::string GetName() const { return impl_->GetName(); }
api::WifiLanService& GetImpl() { return *impl_; }
bool IsValid() const { return impl_ != nullptr; }
private:
api::WifiLanService* impl_;
};
class WifiLanSocket final {
public:
WifiLanSocket() = default;
WifiLanSocket(const WifiLanSocket&) = default;
WifiLanSocket& operator=(const WifiLanSocket&) = default;
explicit WifiLanSocket(api::WifiLanSocket* socket) : impl_(socket) {}
explicit WifiLanSocket(std::unique_ptr<api::WifiLanSocket> socket)
: impl_(socket.release()) {}
~WifiLanSocket() = default;
// Returns the InputStream of the WifiLanSocket.
// On error, returned stream will report Exception::kIo on any operation.
//
// The returned object is not owned by the caller, and can be invalidated once
// the WifiLanSocket object is destroyed.
InputStream& GetInputStream() { return impl_->GetInputStream(); }
// Returns the OutputStream of the WifiLanSocket.
// On error, returned stream will report Exception::kIo on any operation.
//
// The returned object is not owned by the caller, and can be invalidated once
// the WifiLanSocket object is destroyed.
OutputStream& GetOutputStream() { return impl_->GetOutputStream(); }
// Returns Exception::kIo on error, Exception::kSuccess otherwise.
Exception Close() { return impl_->Close(); }
WifiLanService GetRemoteWifiLanService() {
return WifiLanService(impl_->GetRemoteWifiLanService());
}
// Returns true if a socket is usable. If this method returns false,
// it is not safe to call any other method.
// NOTE(socket validity):
// Socket created by a default public constructor is not valid, because
// it is missing platform implementation.
// The only way to obtain a valid socket is through connection, such as
// an object returned by WifiLanMedium::Connect
// These methods may also return an invalid socket if connection failed for
// any reason.
bool IsValid() const { return impl_ != nullptr; }
// Returns reference to platform implementation.
// This is used to communicate with platform code, and for debugging purposes.
// Returned reference will remain valid for while WifiLanSocket object is
// itself valid. Typically WifiLanSocket lifetime matches duration of the
// connection, and is controlled by end user, since they hold the instance.
api::WifiLanSocket& GetImpl() { return *impl_; }
private:
std::shared_ptr<api::WifiLanSocket> impl_;
};
// Container of operations that can be performed over the WifiLan medium.
class WifiLanMedium final {
public:
using Platform = api::ImplementationPlatform;
struct DiscoveredServiceCallback {
std::function<void(WifiLanService& wifi_lan_service,
const std::string& service_id)>
service_discovered_cb =
DefaultCallback<WifiLanService&, const std::string&>();
std::function<void(WifiLanService& wifi_lan_service,
const std::string& service_id)>
service_lost_cb =
DefaultCallback<WifiLanService&, const std::string&>();
};
struct ServiceDiscoveryInfo {
WifiLanService service;
};
struct AcceptedConnectionCallback {
std::function<void(WifiLanSocket& socket, const std::string& service_id)>
accepted_cb = DefaultCallback<WifiLanSocket&, const std::string&>();
};
struct AcceptedConnectionInfo {
WifiLanSocket socket;
};
WifiLanMedium() : impl_(Platform::CreateWifiLanMedium()) {}
~WifiLanMedium() = default;
bool StartAdvertising(const std::string& service_id,
const std::string& wifi_lan_service_info_name);
bool StopAdvertising(const std::string& service_id);
// Returns true once the WifiLan discovery has been initiated.
bool StartDiscovery(const std::string& service_id,
DiscoveredServiceCallback callback);
// Returns true once WifiLan discovery for service_id is well and truly
// stopped; after this returns, there must be no more invocations of the
// DiscoveredServiceCallback passed in to StartDiscovery() for service_id.
bool StopDiscovery(const std::string& service_id);
// Returns true once WifiLan socket connection requests to service_id can be
// accepted.
bool StartAcceptingConnections(const std::string& service_id,
AcceptedConnectionCallback callback);
bool StopAcceptingConnections(const std::string& service_id);
// Returns a new WifiLanSocket. On Success, WifiLanSocket::IsValid()
// returns true.
WifiLanSocket Connect(WifiLanService& service, const std::string& service_id);
bool IsValid() const { return impl_ != nullptr; }
api::WifiLanMedium& GetImpl() { return *impl_; }
private:
Mutex mutex_;
std::unique_ptr<api::WifiLanMedium> impl_;
absl::flat_hash_map<api::WifiLanService*,
std::unique_ptr<ServiceDiscoveryInfo>>
services_ ABSL_GUARDED_BY(mutex_);
absl::flat_hash_map<api::WifiLanSocket*,
std::unique_ptr<AcceptedConnectionInfo>>
sockets_ ABSL_GUARDED_BY(mutex_);
DiscoveredServiceCallback discovered_service_callback_
ABSL_GUARDED_BY(mutex_);
AcceptedConnectionCallback accepted_connection_callback_
ABSL_GUARDED_BY(mutex_);
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_PUBLIC_WIFI_LAN_H_
+102
View File
@@ -0,0 +1,102 @@
#include "platform_v2/public/wifi_lan.h"
#include <memory>
#include "platform_v2/base/medium_environment.h"
#include "platform_v2/public/count_down_latch.h"
#include "platform_v2/public/logging.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace location {
namespace nearby {
namespace {
constexpr absl::string_view kServiceID{"com.google.location.nearby.apps.test"};
class WifiLanMediumTest : public ::testing::Test {
protected:
using DiscoveredServiceCallback = WifiLanMedium::DiscoveredServiceCallback;
WifiLanMediumTest() { env_.Stop(); }
MediumEnvironment& env_{MediumEnvironment::Instance()};
};
TEST_F(WifiLanMediumTest, ConstructorDestructorWorks) {
env_.Start();
WifiLanMedium medium_a;
WifiLanMedium medium_b;
// Make sure we can create functional mediums.
ASSERT_TRUE(medium_a.IsValid());
ASSERT_TRUE(medium_b.IsValid());
// Make sure we can create 2 distinct mediums.
EXPECT_NE(&medium_a.GetImpl(), &medium_b.GetImpl());
env_.Stop();
}
TEST_F(WifiLanMediumTest, CanStartDiscoveryAndServiceIndeedDiscovered) {
env_.Start();
WifiLanMedium medium;
CountDownLatch found_latch(1);
CountDownLatch lost_latch(1);
medium.StartDiscovery(std::string(kServiceID),
DiscoveredServiceCallback{
.service_discovered_cb =
[&found_latch](WifiLanService& service,
const std::string& service_id) {
NEARBY_LOG(INFO, "Service discovered: %s",
service.GetName().c_str());
EXPECT_EQ(kServiceID, service_id);
found_latch.CountDown();
},
.service_lost_cb =
[&lost_latch](WifiLanService& service,
const std::string& service_id) {
NEARBY_LOG(INFO, "Service lost: %s",
service.GetName().c_str());
EXPECT_EQ(kServiceID, service_id);
lost_latch.CountDown();
},
});
EXPECT_TRUE(found_latch.Await(absl::Milliseconds(1000)).result());
env_.Stop();
}
TEST_F(WifiLanMediumTest, CanStopDiscovery) {
env_.Start();
WifiLanMedium medium;
CountDownLatch found_latch(1);
CountDownLatch lost_latch(1);
medium.StartDiscovery(std::string(kServiceID),
DiscoveredServiceCallback{
.service_discovered_cb =
[&found_latch](WifiLanService& service,
const std::string& service_id) {
NEARBY_LOG(INFO, "Service discovered: %s",
service.GetName().c_str());
EXPECT_EQ(kServiceID, service_id);
found_latch.CountDown();
},
.service_lost_cb =
[&lost_latch](WifiLanService& service,
const std::string& service_id) {
NEARBY_LOG(INFO, "Service lost: %s",
service.GetName().c_str());
EXPECT_EQ(kServiceID, service_id);
lost_latch.CountDown();
},
});
EXPECT_TRUE(found_latch.Await(absl::Milliseconds(1000)).result());
bool stop = medium.StopDiscovery(std::string(kServiceID));
EXPECT_TRUE(stop);
env_.Stop();
}
} // namespace
} // namespace nearby
} // namespace location