Move Server Socket and its dependencies to their own files

PiperOrigin-RevId: 551330682
This commit is contained in:
Nick Bourdakos
2023-07-26 15:21:46 -07:00
committed by Copybara-Service
parent 2145ccce69
commit a42cdc7346
8 changed files with 625 additions and 0 deletions
+5
View File
@@ -570,6 +570,11 @@ let package = Package(
"connections/implementation/mediums/webrtc",
// This breaks the build, but seems to work fine without it?
"internal/platform/medium_environment.cc",
// Temporarily ignore BLEv2 source files.
// TODO(b/293283024): Stop ignoring these files when BLEv2 migration is complete.
"internal/platform/implementation/apple/ble_peripheral.mm",
"internal/platform/implementation/apple/ble_server_socket.mm",
"internal/platform/implementation/apple/ble_socket.mm",
],
sources: [
"compiled_proto",
@@ -104,6 +104,36 @@ objc_library(
],
)
objc_library(
name = "ble_v2",
srcs = [
"ble_peripheral.mm",
"ble_server_socket.mm",
"ble_socket.mm",
"ble_utils.mm",
"utils.mm",
],
hdrs = [
"ble_peripheral.h",
"ble_server_socket.h",
"ble_socket.h",
"ble_utils.h",
"utils.h",
],
# Prevent Objective-C++ headers from being pulled into swift.
aspect_hints = ["//tools/build_defs/swift:no_module"],
deps = [
"//internal/platform:base",
"//internal/platform/implementation:comm",
"//internal/platform/implementation/apple/Mediums",
"//third_party/apple_frameworks:CoreBluetooth",
"//third_party/apple_frameworks:Foundation",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/functional:any_invocable",
"@com_google_absl//absl/strings",
],
)
objc_library(
name = "Shared",
srcs = [
@@ -0,0 +1,54 @@
// Copyright 2023 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.
// Note: File language is detected using heuristics. Many Objective-C++ headers are incorrectly
// classified as C++ resulting in invalid linter errors. The use of "NSArray" and other Foundation
// classes like "NSData", "NSDictionary" and "NSUUID" are highly weighted for Objective-C and
// Objective-C++ scores. Oddly, "#import <Foundation/Foundation.h>" does not contribute any points.
// This comment alone should be enough to trick the IDE in to believing this is actually some sort
// of Objective-C file. See: cs/google3/devtools/search/lang/recognize_language_classifiers_data
#import <CoreBluetooth/CoreBluetooth.h>
#import <Foundation/Foundation.h>
#include <string>
#include "internal/platform/implementation/ble_v2.h"
namespace nearby {
namespace apple {
// Opaque wrapper over a CoreBluetooth peripheral. This can be used to uniquely
// identify a peripheral and connect to its GATT server.
class BlePeripheral : public api::ble_v2::BlePeripheral {
public:
explicit BlePeripheral(CBPeripheral *peripheral);
~BlePeripheral() override = default;
// Returns the hardware address of this peripheral.
//
// For example, "00:11:22:AA:BB:CC".
std::string GetAddress() const override;
// Returns an immutable unique identifier. The identifier does not change when
// the peripheral's address is rotated.
api::ble_v2::BlePeripheral::UniqueId GetUniqueId() const override;
private:
CBPeripheral *peripheral_;
api::ble_v2::BlePeripheral::UniqueId unique_id_;
};
} // namespace apple
} // namespace nearby
@@ -0,0 +1,38 @@
// Copyright 2023 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.
#import "internal/platform/implementation/apple/ble_peripheral.h"
#import <CoreBluetooth/CoreBluetooth.h>
#import <Foundation/Foundation.h>
#include <string>
#include "internal/platform/implementation/ble_v2.h"
#include "internal/platform/prng.h"
namespace nearby {
namespace apple {
BlePeripheral::BlePeripheral(CBPeripheral *peripheral)
: peripheral_(peripheral), unique_id_(Prng().NextInt64()) {}
std::string BlePeripheral::GetAddress() const {
return peripheral_.identifier.UUIDString.UTF8String;
}
api::ble_v2::BlePeripheral::UniqueId BlePeripheral::GetUniqueId() const { return unique_id_; }
} // namespace apple
} // namespace nearby
@@ -0,0 +1,76 @@
// Copyright 2023 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.
// Note: File language is detected using heuristics. Many Objective-C++ headers are incorrectly
// classified as C++ resulting in invalid linter errors. The use of "NSArray" and other Foundation
// classes like "NSData", "NSDictionary" and "NSUUID" are highly weighted for Objective-C and
// Objective-C++ scores. Oddly, "#import <Foundation/Foundation.h>" does not contribute any points.
// This comment alone should be enough to trick the IDE in to believing this is actually some sort
// of Objective-C file. See: cs/google3/devtools/search/lang/recognize_language_classifiers_data
// TODO(b/293336684): Remove this file when shared Weave is complete.
#import <Foundation/Foundation.h>
#include <memory>
#include "absl/functional/any_invocable.h"
#include "internal/platform/exception.h"
#include "internal/platform/implementation/ble_v2.h"
#import "internal/platform/implementation/apple/ble_socket.h"
namespace nearby {
namespace apple {
// A BLE server socket for listening for incoming Weave sockets.
class BleServerSocket : public api::ble_v2::BleServerSocket {
public:
BleServerSocket() = default;
~BleServerSocket() override;
// Wait for an available socket.
//
// Blocks until either:
// - at least one incoming connection request is available, or
// - ServerSocket is closed.
//
// On success, returns connected socket, ready to exchange data or nullptr on
// error. Once error is reported, it is permanent, and ServerSocket must be
// closed.
std::unique_ptr<api::ble_v2::BleSocket> Accept() override
ABSL_LOCKS_EXCLUDED(mutex_);
// Close the server socket.
//
// Returns Exception::kIo on error, otherwise Exception::kSuccess.
Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_);
bool Connect(std::unique_ptr<BleSocket> socket) ABSL_LOCKS_EXCLUDED(mutex_);
void SetCloseNotifier(absl::AnyInvocable<void()> notifier)
ABSL_LOCKS_EXCLUDED(mutex_);
private:
Exception DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
mutable absl::Mutex mutex_;
absl::CondVar cond_;
absl::flat_hash_set<std::unique_ptr<BleSocket>> pending_sockets_
ABSL_GUARDED_BY(mutex_);
absl::AnyInvocable<void()> close_notifier_ ABSL_GUARDED_BY(mutex_);
bool closed_ ABSL_GUARDED_BY(mutex_) = false;
};
} // namespace apple
} // namespace nearby
@@ -0,0 +1,82 @@
// Copyright 2023 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.
// TODO(b/293336684): Remove this file when shared Weave is complete.
#import "internal/platform/implementation/apple/ble_server_socket.h"
#import <Foundation/Foundation.h>
#include <memory>
#include <utility>
namespace nearby {
namespace apple {
BleServerSocket::~BleServerSocket() {
absl::MutexLock lock(&mutex_);
DoClose();
}
std::unique_ptr<api::ble_v2::BleSocket> BleServerSocket::Accept() {
absl::MutexLock lock(&mutex_);
while (!closed_ && pending_sockets_.empty()) {
cond_.Wait(&mutex_);
}
if (closed_) return {};
std::unique_ptr<BleSocket> remote_socket =
std::move(pending_sockets_.extract(pending_sockets_.begin()).value());
return std::move(remote_socket);
}
bool BleServerSocket::Connect(std::unique_ptr<BleSocket> socket) {
absl::MutexLock lock(&mutex_);
if (closed_) {
return false;
}
pending_sockets_.insert(std::move(socket));
cond_.SignalAll();
return !closed_;
}
void BleServerSocket::SetCloseNotifier(absl::AnyInvocable<void()> notifier) {
absl::MutexLock lock(&mutex_);
close_notifier_ = std::move(notifier);
}
Exception BleServerSocket::Close() {
absl::MutexLock lock(&mutex_);
return DoClose();
}
Exception BleServerSocket::DoClose() {
bool should_notify = !closed_;
closed_ = true;
if (should_notify) {
cond_.SignalAll();
if (close_notifier_) {
auto notifier = std::move(close_notifier_);
mutex_.Unlock();
// Notifier may contain calls to public API, and may cause deadlock, if
// mutex_ is held during the call.
notifier();
mutex_.Lock();
}
}
return {Exception::kSuccess};
}
} // namespace apple
} // namespace nearby
@@ -0,0 +1,130 @@
// Copyright 2023 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.
// Note: File language is detected using heuristics. Many Objective-C++ headers are incorrectly
// classified as C++ resulting in invalid linter errors. The use of "NSArray" and other Foundation
// classes like "NSData", "NSDictionary" and "NSUUID" are highly weighted for Objective-C and
// Objective-C++ scores. Oddly, "#import <Foundation/Foundation.h>" does not contribute any points.
// This comment alone should be enough to trick the IDE in to believing this is actually some sort
// of Objective-C file. See: cs/google3/devtools/search/lang/recognize_language_classifiers_data
// TODO(b/293336684): Remove this file when shared Weave is complete.
#import <Foundation/Foundation.h>
#include <memory>
#include "internal/platform/implementation/ble_v2.h"
#import "internal/platform/implementation/apple/ble_peripheral.h"
@class GNCMConnectionHandlers;
@protocol GNCMConnection;
namespace nearby {
namespace apple {
// A readable stream of bytes.
class BleInputStream : public InputStream {
public:
BleInputStream();
~BleInputStream() override;
// Reads at most `size` bytes from the input stream.
//
// Returns an empty byte array on end of file, or Exception::kIo on error.
ExceptionOr<ByteArray> Read(std::int64_t size) override;
// Closes the stream preventing further reads.
//
// Returns Exception::kIo on error, otherwise Exception::kSuccess.
Exception Close() override;
GNCMConnectionHandlers *GetConnectionHandlers() { return connectionHandlers_; }
private:
GNCMConnectionHandlers *connectionHandlers_;
NSMutableArray<NSData *> *newDataPackets_;
NSMutableData *accumulatedData_;
NSCondition *condition_;
};
// A writable stream of bytes.
class BleOutputStream : public OutputStream {
public:
explicit BleOutputStream(id<GNCMConnection> connection)
: connection_(connection), condition_([[NSCondition alloc] init]) {}
~BleOutputStream() override;
// Write the provided bytes to the output stream.
//
// Returns Exception::kIo on error, otherwise Exception::kSuccess.
Exception Write(const ByteArray &data) override;
// no-op
//
// Always returns Exception::kSuccess.
Exception Flush() override;
// Closes the stream preventing further writes.
//
// Returns Exception::kIo on error, otherwise Exception::kSuccess.
Exception Close() override;
private:
id<GNCMConnection> connection_;
NSCondition *condition_;
};
// A BLE Weave socket.
class BleSocket : public api::ble_v2::BleSocket {
public:
BleSocket(id<GNCMConnection> connection, BlePeripheral *peripheral);
~BleSocket() override;
// Returns the InputStream of the BleSocket.
// 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 BleSocket object is destroyed.
InputStream &GetInputStream() override { return *input_stream_; }
// Returns the OutputStream of the BleSocket.
// 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 BleSocket object is destroyed.
OutputStream &GetOutputStream() override { return *output_stream_; }
// Returns Exception::kIo on error, otherwise Exception::kSuccess.
Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_);
// Returns valid BlePeripheral pointer if there is a connection, and
// nullptr otherwise.
BlePeripheral *GetRemotePeripheral() override { return peripheral_; }
bool IsClosed() const ABSL_LOCKS_EXCLUDED(mutex_);
private:
void DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
mutable absl::Mutex mutex_;
bool closed_ ABSL_GUARDED_BY(mutex_) = false;
std::unique_ptr<BleInputStream> input_stream_;
std::unique_ptr<BleOutputStream> output_stream_;
BlePeripheral *peripheral_;
};
} // namespace apple
} // namespace nearby
@@ -0,0 +1,210 @@
// Copyright 2023 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.
#import "internal/platform/implementation/apple/ble_socket.h"
#include "internal/platform/implementation/ble_v2.h"
#import "internal/platform/implementation/apple/Mediums/Ble/GNCMBleConnection.h"
#import "internal/platform/implementation/apple/ble_peripheral.h"
#import "internal/platform/implementation/apple/ble_utils.h"
#import "internal/platform/implementation/apple/utils.h"
// TODO(b/293336684): Remove this file when shared Weave is complete.
namespace nearby {
namespace apple {
#pragma mark - BleInputStream
BleInputStream::BleInputStream()
: newDataPackets_([NSMutableArray array]),
accumulatedData_([NSMutableData data]),
condition_([[NSCondition alloc] init]) {
// Create the handlers of incoming data from the remote endpoint.
connectionHandlers_ = [GNCMConnectionHandlers
payloadHandler:^(NSData *data) {
[condition_ lock];
// Add the incoming data to the data packet array to be processed in read() below.
[newDataPackets_ addObject:data];
[condition_ broadcast];
[condition_ unlock];
}
disconnectedHandler:^{
[condition_ lock];
// Release the data packet array, meaning the stream has been closed or severed.
newDataPackets_ = nil;
[condition_ broadcast];
[condition_ unlock];
}];
}
BleInputStream::~BleInputStream() {
NSCAssert(!newDataPackets_, @"BleInputStream not closed before destruction");
}
ExceptionOr<ByteArray> BleInputStream::Read(std::int64_t size) {
// Block until either (a) the connection has been closed, (b) we have enough data to return.
NSData *dataToReturn;
[condition_ lock];
while (true) {
// Check if the stream has been closed or severed.
if (!newDataPackets_) break;
if (newDataPackets_.count > 0) {
// Add the packet data to the accumulated data.
for (NSData *data in newDataPackets_) {
if (data.length > 0) {
[accumulatedData_ appendData:data];
}
}
[newDataPackets_ removeAllObjects];
}
if ((size == -1) && (accumulatedData_.length > 0)) {
// Return all of the data.
dataToReturn = accumulatedData_;
accumulatedData_ = [NSMutableData data];
break;
} else if (accumulatedData_.length > 0) {
// Return up to |size| bytes of the data.
std::int64_t sizeToReturn = (accumulatedData_.length < size) ? accumulatedData_.length : size;
NSRange range = NSMakeRange(0, (NSUInteger)sizeToReturn);
dataToReturn = [accumulatedData_ subdataWithRange:range];
[accumulatedData_ replaceBytesInRange:range withBytes:nil length:0];
break;
}
[condition_ wait];
}
[condition_ unlock];
if (dataToReturn) {
NSLog(@"[NEARBY] Input stream: Received data of size: %lu", (unsigned long)dataToReturn.length);
return ExceptionOr<ByteArray>(ByteArrayFromNSData(dataToReturn));
} else {
return ExceptionOr<ByteArray>{Exception::kIo};
}
}
Exception BleInputStream::Close() {
// Unblock pending read operation.
[condition_ lock];
newDataPackets_ = nil;
[condition_ broadcast];
[condition_ unlock];
return {Exception::kSuccess};
}
#pragma mark - BleOutputStream
BleOutputStream::~BleOutputStream() {
NSCAssert(!connection_, @"BleOutputStream not closed before destruction");
}
Exception BleOutputStream::Write(const ByteArray &data) {
[condition_ lock];
NSLog(@"[NEARBY] Sending data of size: %lu", NSDataFromByteArray(data).length);
if (!connection_) {
[condition_ unlock];
return {Exception::kIo};
}
NSMutableData *packet = [NSMutableData dataWithData:NSDataFromByteArray(data)];
// Send the data, blocking until the completion handler is called.
__block bool isComplete = NO;
__block GNCMPayloadResult sendResult = GNCMPayloadResultFailure;
NSCondition *condition = condition_; // don't capture |this| in completion
[connection_ sendData:packet
progressHandler:^(size_t count) {
}
completion:^(GNCMPayloadResult result) {
[condition lock];
// Make sure we haven't already reported completion before. This prevents a crash
// where we try leaving a dispatch group more times than we entered it.
// b/79095653.
if (isComplete) {
[condition unlock];
return;
}
isComplete = YES;
sendResult = result;
[condition broadcast];
[condition unlock];
}];
while (connection_ && !isComplete) {
[condition_ wait];
}
if (sendResult == GNCMPayloadResultSuccess) {
[condition_ unlock];
return {Exception::kSuccess};
} else {
[condition_ unlock];
return {Exception::kIo};
}
}
Exception BleOutputStream::Flush() {
// The write() function blocks until the data is received by the remote endpoint, so there's
// nothing to do here.
return {Exception::kSuccess};
}
Exception BleOutputStream::Close() {
// Unblock pending write operation.
[condition_ lock];
connection_ = nil;
[condition_ broadcast];
[condition_ unlock];
return {Exception::kSuccess};
}
#pragma mark - BleSocket
BleSocket::BleSocket(id<GNCMConnection> connection, BlePeripheral *peripheral)
: input_stream_(new BleInputStream()),
output_stream_(new BleOutputStream(connection)),
peripheral_(peripheral) {}
BleSocket::~BleSocket() {
absl::MutexLock lock(&mutex_);
DoClose();
}
bool BleSocket::IsClosed() const {
absl::MutexLock lock(&mutex_);
return closed_;
}
Exception BleSocket::Close() {
absl::MutexLock lock(&mutex_);
DoClose();
return {Exception::kSuccess};
}
void BleSocket::DoClose() {
if (!closed_) {
input_stream_->Close();
output_stream_->Close();
closed_ = true;
}
}
} // namespace apple
} // namespace nearby