Merge remote-tracking branch 'nearby/main'

# Conflicts:
#	connections/implementation/bwu_manager_test.cc
#	sharing/BUILD
#	sharing/certificates/fake_nearby_share_certificate_manager.cc
#	sharing/certificates/fake_nearby_share_certificate_manager.h
#	sharing/internal/base/utf_string_conversions.h
This commit is contained in:
Lasan Mahaliyana
2026-07-26 22:46:37 +05:30
155 changed files with 5257 additions and 2018 deletions
-4
View File
@@ -80,8 +80,6 @@ cc_library(
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/functional:any_invocable",
"@com_google_absl//absl/meta:type_traits",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/strings:string_view",
@@ -323,7 +321,6 @@ cc_library(
":types",
":uuid",
"//connections/implementation/flags:connections_flags",
"//internal/base",
"//internal/flags:nearby_flags",
"//internal/platform/implementation:comm",
"//internal/platform/implementation:platform",
@@ -374,7 +371,6 @@ cc_library(
":mac_address",
":types",
":uuid",
"//internal/base",
"//internal/platform/implementation:comm",
"//internal/platform/implementation:webrtc_platform",
"//internal/platform/implementation:wifi_utils",
-73
View File
@@ -19,7 +19,6 @@
#include <utility>
#include "absl/container/flat_hash_map.h"
#include "absl/strings/string_view.h"
#include "internal/platform/bluetooth_adapter.h"
#include "internal/platform/cancellation_flag.h"
#include "internal/platform/implementation/bluetooth_classic.h"
@@ -57,11 +56,6 @@ MediumSocket* BluetoothSocket::CreateVirtualSocket(
}
BluetoothClassicMedium::~BluetoothClassicMedium() {
LOG(INFO) << "~BluetoothClassicMedium: observer_list_ size: "
<< observer_list_.size();
if (!observer_list_.empty()) {
impl_->RemoveObserver(this);
}
StopDiscovery();
LOG(INFO) << "eof ~BluetoothClassicMedium";
}
@@ -160,71 +154,4 @@ bool BluetoothClassicMedium::StopDiscovery() {
return impl_->StopDiscovery();
}
void BluetoothClassicMedium::AddObserver(Observer* observer) {
LOG(INFO) << "BT AddObserver; impl=" << &GetImpl();
MutexLock lock(&mutex_);
if (observer_list_.empty()) {
impl_->AddObserver(this);
}
observer_list_.AddObserver(observer);
LOG(INFO) << "BT AddObserver done";
}
void BluetoothClassicMedium::RemoveObserver(Observer* observer) {
LOG(INFO) << "BT RemoveObserver; impl=" << &GetImpl();
MutexLock lock(&mutex_);
observer_list_.RemoveObserver(observer);
if (observer_list_.empty()) {
impl_->RemoveObserver(this);
}
LOG(INFO) << "BT RemoveObserver done";
}
// api::BluetoothClassicMedium::Observer methods
void BluetoothClassicMedium::DeviceAdded(api::BluetoothDevice& device) {
VLOG(1) << "BT DeviceAdded; name=" << device.GetName()
<< ", address=" << device.GetMacAddress().ToString();
BluetoothDevice bt_device(&device);
for (auto* observer : observer_list_.GetObservers()) {
observer->DeviceAdded(bt_device);
}
}
void BluetoothClassicMedium::DeviceRemoved(api::BluetoothDevice& device) {
VLOG(1) << "BT DeviceRemoved; name=" << device.GetName()
<< ", address=" << device.GetMacAddress().ToString();
BluetoothDevice bt_device(&device);
for (auto* observer : observer_list_.GetObservers()) {
observer->DeviceRemoved(bt_device);
}
}
void BluetoothClassicMedium::DeviceAddressChanged(
api::BluetoothDevice& device, absl::string_view old_address) {
VLOG(1) << "BT DeviceAddressChanged; name=" << device.GetName()
<< ", address=" << device.GetMacAddress().ToString()
<< ", old_address=" << old_address;
BluetoothDevice bt_device(&device);
for (auto* observer : observer_list_.GetObservers()) {
observer->DeviceAddressChanged(bt_device, old_address);
}
}
void BluetoothClassicMedium::DevicePairedChanged(api::BluetoothDevice& device,
bool new_paired_status) {
VLOG(1) << "BT DevicePairedChanged; name=" << device.GetName()
<< ", address=" << device.GetMacAddress().ToString()
<< ", status=" << new_paired_status;
BluetoothDevice bt_device(&device);
for (auto* observer : observer_list_.GetObservers()) {
observer->DevicePairedChanged(bt_device, new_paired_status);
}
}
void BluetoothClassicMedium::DeviceConnectedStateChanged(
api::BluetoothDevice& device, bool connected) {
VLOG(1) << "BT DeviceConnectedStateChanged: name=" << device.GetName()
<< ", address=" << device.GetMacAddress().ToString()
<< ", connected=" << connected;
BluetoothDevice bt_device(&device);
for (auto* observer : observer_list_.GetObservers()) {
observer->DeviceConnectedStateChanged(bt_device, connected);
}
}
} // namespace nearby
+3 -50
View File
@@ -26,7 +26,6 @@
#include "absl/container/flat_hash_map.h"
#include "absl/functional/any_invocable.h"
#include "absl/strings/string_view.h"
#include "internal/base/observer_list.h"
#include "internal/platform/blocking_queue_stream.h"
#include "internal/platform/bluetooth_adapter.h"
#include "internal/platform/byte_array.h"
@@ -157,7 +156,7 @@ class BluetoothServerSocket final {
BluetoothServerSocket& operator=(const BluetoothServerSocket&) = default;
~BluetoothServerSocket() = default;
explicit BluetoothServerSocket(
std::unique_ptr<api::BluetoothServerSocket> socket)
std::shared_ptr<api::BluetoothServerSocket> socket)
: impl_(std::move(socket)) {}
// https://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html#accept()
@@ -223,7 +222,7 @@ class BluetoothPairing final {
// Container of operations that can be performed over the Bluetooth Classic
// medium.
class BluetoothClassicMedium : public api::BluetoothClassicMedium::Observer {
class BluetoothClassicMedium {
public:
using Platform = api::ImplementationPlatform;
struct DiscoveryCallback {
@@ -246,39 +245,11 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium::Observer {
BluetoothDevice device;
};
class Observer {
public:
virtual ~Observer() = default;
// Called when a new `device` is added. The `device` parameter becomes
// invalid after the call.
virtual void DeviceAdded(BluetoothDevice& device) {}
// Called when `device` is removed. The `device` parameter becomes invalid
// after the call.
virtual void DeviceRemoved(BluetoothDevice& device) {}
// Called when the address of `device` changed due to pairing. The
// `device` parameter becomes invalid after the call.
virtual void DeviceAddressChanged(BluetoothDevice& device,
absl::string_view old_address) {}
// Called when the paired property of `device` changed. The `device`
// parameter becomes invalid after the call.
virtual void DevicePairedChanged(BluetoothDevice& device,
bool new_paired_status) {}
// Called when `device` has connected or disconnected. The `device`
// parameter becomes invalid after the call.
virtual void DeviceConnectedStateChanged(BluetoothDevice& device,
bool connected) {}
};
explicit BluetoothClassicMedium(BluetoothAdapter& adapter)
: impl_(Platform::CreateBluetoothClassicMedium(adapter.GetImpl())),
adapter_(adapter) {}
~BluetoothClassicMedium() override;
virtual ~BluetoothClassicMedium();
// NOTE(DiscoveryCallback):
// BluetoothDevice is a proxy object created as a result of BT discovery.
@@ -353,23 +324,6 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium::Observer {
return BluetoothDevice(impl_->GetRemoteDevice(mac_address));
}
// Adds an observer. `observer` must be valid until RemoveObserver is called,
// or BluetoothClassicMedium is destroyed.
void AddObserver(Observer* observer);
// Removes an observer. It's OK to remove an unregistered observer.
void RemoveObserver(Observer* observer);
// api::BluetoothClassicMedium::Observer methods
void DeviceAdded(api::BluetoothDevice& device) override;
void DeviceRemoved(api::BluetoothDevice& device) override;
void DeviceAddressChanged(api::BluetoothDevice& device,
absl::string_view old_address) override;
void DevicePairedChanged(api::BluetoothDevice& device,
bool new_paired_status) override;
void DeviceConnectedStateChanged(api::BluetoothDevice& device,
bool connected) override;
private:
Mutex mutex_;
std::unique_ptr<api::BluetoothClassicMedium> impl_;
@@ -379,7 +333,6 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium::Observer {
devices_ ABSL_GUARDED_BY(mutex_);
DiscoveryCallback discovery_callback_ ABSL_GUARDED_BY(mutex_);
bool discovery_enabled_ ABSL_GUARDED_BY(mutex_) = false;
ObserverList<Observer> observer_list_;
};
} // namespace nearby
@@ -49,39 +49,6 @@ constexpr FeatureFlags kTestCases[] = {
},
};
class BluetoothClassicMediumObserver
: public BluetoothClassicMedium ::Observer {
public:
explicit BluetoothClassicMediumObserver(
CountDownLatch* device_added_latch, CountDownLatch* device_removed_latch,
CountDownLatch* device_paired_changed_latch)
: device_added_latch_(device_added_latch),
device_removed_latch_(device_removed_latch),
device_paired_changed_latch_(device_paired_changed_latch) {}
void DeviceAdded(BluetoothDevice& device) override {
if (!device_added_latch_) return;
device_added_latch_->CountDown();
}
void DeviceRemoved(BluetoothDevice& device) override {
if (!device_removed_latch_) return;
device_removed_latch_->CountDown();
}
void DevicePairedChanged(BluetoothDevice& device,
bool new_paired_status) override {
if (!device_paired_changed_latch_) return;
paired_status_ = new_paired_status;
device_paired_changed_latch_->CountDown();
}
CountDownLatch* device_added_latch_;
CountDownLatch* device_removed_latch_;
CountDownLatch* device_paired_changed_latch_;
bool paired_status_ = false;
};
class BluetoothClassicMediumTest
: public ::testing::TestWithParam<FeatureFlags> {
protected:
@@ -341,11 +308,6 @@ TEST_F(BluetoothClassicMediumTest, CanStartDiscovery) {
adapter_a_->SetScanMode(BluetoothAdapter::ScanMode::kConnectable);
CountDownLatch found_latch(1);
CountDownLatch lost_latch(1);
CountDownLatch device_added_latch(1);
CountDownLatch device_removed_latch(1);
BluetoothClassicMediumObserver observer(&device_added_latch,
&device_removed_latch, nullptr);
bt_a_->AddObserver(&observer);
bt_a_->StartDiscovery(DiscoveryCallback{
.device_discovered_cb =
@@ -365,21 +327,15 @@ TEST_F(BluetoothClassicMediumTest, CanStartDiscovery) {
EXPECT_EQ(adapter_b_->GetScanMode(),
BluetoothAdapter::ScanMode::kConnectableDiscoverable);
EXPECT_TRUE(found_latch.Await(absl::Milliseconds(1000)).result());
EXPECT_TRUE(device_added_latch.Await(absl::Milliseconds(1000)).result());
adapter_b_->SetStatus(BluetoothAdapter::Status::kDisabled);
EXPECT_FALSE(adapter_b_->IsEnabled());
EXPECT_TRUE(lost_latch.Await(absl::Milliseconds(1000)).result());
EXPECT_TRUE(device_removed_latch.Await(absl::Milliseconds(1000)).result());
}
TEST_F(BluetoothClassicMediumTest, DiscoveryCallbackAfterStopDiscovery) {
SingleThreadExecutor executor;
adapter_a_->SetScanMode(BluetoothAdapter::ScanMode::kConnectable);
CountDownLatch found_latch(1);
CountDownLatch device_added_latch(1);
BluetoothClassicMediumObserver observer(&device_added_latch, nullptr,
nullptr);
bt_a_->AddObserver(&observer);
bt_a_->StartDiscovery(DiscoveryCallback{
.device_discovered_cb =
@@ -397,7 +353,6 @@ TEST_F(BluetoothClassicMediumTest, DiscoveryCallbackAfterStopDiscovery) {
BluetoothAdapter::ScanMode::kConnectableDiscoverable);
bt_a_->StopDiscovery();
EXPECT_TRUE(found_latch.Await(absl::Milliseconds(1000)).result());
EXPECT_TRUE(device_added_latch.Await(absl::Milliseconds(1000)).result());
executor.Shutdown();
}
@@ -491,10 +446,6 @@ TEST_F(BluetoothClassicMediumTest, BluetoothPairingSuccess) {
CountDownLatch paired_latch(1);
CountDownLatch initiated_latch(1);
CountDownLatch error_latch(1);
CountDownLatch device_paired_latch(1);
BluetoothClassicMediumObserver observer(nullptr, nullptr,
&device_paired_latch);
bt_a_->AddObserver(&observer);
EXPECT_TRUE(bluetooth_pairing->InitiatePairing({
.on_paired_cb = [&]() { paired_latch.CountDown(); },
.on_pairing_error_cb =
@@ -520,8 +471,6 @@ TEST_F(BluetoothClassicMediumTest, BluetoothPairingSuccess) {
// Finishes pairing with remote device.
EXPECT_TRUE(bluetooth_pairing->FinishPairing(received_passkey));
paired_latch.Await();
device_paired_latch.Await();
EXPECT_TRUE(observer.paired_status_);
EXPECT_TRUE(bluetooth_pairing->IsPaired());
// Unpairs with remote device.
+3
View File
@@ -190,6 +190,9 @@ cc_library(
],
deps = [
] + select({
"@platforms//os:platform_macos": [
"//internal/platform/implementation/apple",
],
"@platforms//os:windows": [
"//internal/platform/implementation/windows",
],
@@ -221,6 +221,7 @@ objc_library(
"//internal/platform/implementation/apple/Mediums/BLE/Sockets:Peripheral",
"//third_party/apple_frameworks:CoreBluetooth",
"//third_party/apple_frameworks:Foundation",
"@com_google_absl//absl/algorithm:container",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/functional:any_invocable",
"@com_google_absl//absl/strings",
@@ -386,6 +386,10 @@ static const int kMaxAdvertisementLengthOnIOS = 23;
[_peripheralManager respondToRequest:request withResult:CBATTErrorAttributeNotFound];
return;
}
if (request.offset > value.length) {
[_peripheralManager respondToRequest:request withResult:CBATTErrorInvalidOffset];
return;
}
request.value =
[value subdataWithRange:NSMakeRange(request.offset, value.length - request.offset)];
[_peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
@@ -24,6 +24,7 @@
#import "internal/platform/implementation/apple/Mediums/BLE/GNCMConnection.h"
enum { kL2CAPPacketLength = 4 };
static const NSUInteger kGNCBLEL2CAPMaxFrameLength = 5 * 1024 * 1024; // 5 MB
static const CGFloat kRequestDataConnectionDelayInSeconds = 0.0;
static const UInt8 kRequestDataConnectionTimeoutInSeconds = 5;
@@ -266,6 +267,20 @@ static NSData *PrefixLengthData(NSData *data) {
}
_expectedDataLength = CFSwapInt32BigToHost(
*(int *)([[data subdataWithRange:NSMakeRange(0, kL2CAPPacketLength)] bytes]));
if (_expectedDataLength == 0 || _expectedDataLength > kGNCBLEL2CAPMaxFrameLength) {
GNCLoggerError(@"[NEARBY] Rejecting L2CAP frame: declared length %lu out of range "
@"(max %lu); closing.",
(unsigned long)_expectedDataLength,
(unsigned long)kGNCBLEL2CAPMaxFrameLength);
_expectedDataLength = 0;
[_stream close];
if (_connectionHandlers.disconnectedHandler) {
dispatch_async(_callbackQueue, ^{
_connectionHandlers.disconnectedHandler();
});
}
return nil;
}
}
NSUInteger realDataLength = data.length - kL2CAPPacketLength;
if (realDataLength < _expectedDataLength) {
@@ -74,6 +74,9 @@ enum { READ_BUFFER_SIZE = 409600 };
/// Whether the stream is closed.
BOOL _closed;
/// Buffer for reading data from the input stream.
uint8_t _readBuffer[READ_BUFFER_SIZE];
}
#pragma mark Public
@@ -319,12 +322,11 @@ enum { READ_BUFFER_SIZE = 409600 };
/// Receives data from device and invokes |_receivedDataBlock|.
- (void)receiveStreamData {
dispatch_assert_queue_debug(_streamQueue);
uint8_t readBuffer[READ_BUFFER_SIZE];
NSInteger bytesRead = [self.inputStream read:readBuffer maxLength:READ_BUFFER_SIZE];
NSInteger bytesRead = [self.inputStream read:_readBuffer maxLength:READ_BUFFER_SIZE];
if (bytesRead > 0) {
NSMutableData *data = [NSMutableData data];
[data appendBytes:readBuffer length:(NSUInteger)bytesRead];
[data appendBytes:_readBuffer length:(NSUInteger)bytesRead];
if (_verboseLoggingEnabled) {
GNCLoggerDebug(@"[NEARBY] Stream data from device of length %@", @(data.length));
@@ -140,7 +140,11 @@ NS_ASSUME_NONNULL_BEGIN
return;
}
if (![[data subdataWithRange:NSMakeRange(0, prefixLength)] isEqual:_serviceIDHash]) {
// IntroductionFrame.service_id_hash. We MUST bounds-check before
// -subdataWithRange:, otherwise a short follow-up packet throws
// NSRangeException on CoreBluetooth's dispatch queue -> objc_terminate.
if (data.length < prefixLength ||
![[data subdataWithRange:NSMakeRange(0, prefixLength)] isEqual:_serviceIDHash]) {
GNCLoggerInfo(@"[NEARBY] Input stream: Received wrong data packet and discarded");
return;
}
@@ -78,6 +78,11 @@ NSData *_Nullable GNCMParseBLEFramesIntroductionPacket(NSData *_Nullable data) {
::location::nearby::mediums::SocketVersion::V2 &&
socket_control_frame.introduction().has_service_id_hash()) {
std::string service_id_hash = socket_control_frame.introduction().service_id_hash();
// service_id_hash is attacker-supplied; clamp to the protocol-defined
// 3-byte length so it cannot be used to inflate prefixLength downstream.
if (service_id_hash.size() != GNCMBleAdvertisementLengthServiceIDHash) {
return nil;
}
return [NSData dataWithBytes:service_id_hash.data() length:service_id_hash.length()];
}
}
@@ -111,15 +111,17 @@ static NSTimeInterval gKBTCrashLoopMaxTimeBetweenResetting = 15.f;
- (void)addPeripheralServiceManager:(GNSPeripheralServiceManager *)peripheralServiceManager
bleServiceAddedCompletion:(GNSErrorHandler)completion {
[_peripheralServiceManagers setObject:peripheralServiceManager
forKey:peripheralServiceManager.serviceUUID];
[peripheralServiceManager addedToPeripheralManager:self bleServiceAddedCompletion:completion];
if (_started) {
[self addBleServiceForServiceManager:peripheralServiceManager];
}
// Update all advertised services to make sure that the right services are advertised in case
// all BLE services were already added.
[self updateAdvertisedServices];
dispatch_async(_queue, ^{
[self->_peripheralServiceManagers setObject:peripheralServiceManager
forKey:peripheralServiceManager.serviceUUID];
[peripheralServiceManager addedToPeripheralManager:self bleServiceAddedCompletion:completion];
if (self->_started) {
[self addBleServiceForServiceManager:peripheralServiceManager];
}
// Update all advertised services to make sure that the right services are advertised in case
// all BLE services were already added.
[self updateAdvertisedServices];
});
}
- (void)start {
@@ -211,18 +213,24 @@ static NSTimeInterval gKBTCrashLoopMaxTimeBetweenResetting = 15.f;
- (void)removePeripheralServiceManagerForServiceUUID:(CBUUID *)serviceUUID
bleServiceRemovedCompletion:(GNSErrorHandler)completion {
GNSPeripheralServiceManager *peripheralServiceManager =
[_peripheralServiceManagers objectForKey:serviceUUID];
if (peripheralServiceManager == nil) {
completion(nil);
return;
}
[_cbPeripheralManager removeService:peripheralServiceManager.cbService];
[_peripheralServiceManagers removeObjectForKey:serviceUUID];
[peripheralServiceManager didRemoveCBService];
dispatch_async(_queue, ^{
GNSPeripheralServiceManager *peripheralServiceManager =
[self->_peripheralServiceManagers objectForKey:serviceUUID];
if (peripheralServiceManager == nil) {
if (completion) {
completion(nil);
}
return;
}
[self->_cbPeripheralManager removeService:peripheralServiceManager.cbService];
[self->_peripheralServiceManagers removeObjectForKey:serviceUUID];
[peripheralServiceManager didRemoveCBService];
[self updateAdvertisedServices];
completion(nil);
[self updateAdvertisedServices];
if (completion) {
completion(nil);
}
});
}
- (void)removeAllBleServices {
File diff suppressed because it is too large Load Diff
@@ -140,6 +140,32 @@ static const NSTimeInterval kTestTimeout = 1.0;
XCTAssertNil(realData);
}
- (void)testExtractRealDataFromData_oversizedData {
_connection = [self createConnectionWithIncoming:YES];
XCTestExpectation *disconnectionExpectation =
[self expectationWithDescription:@"disconnection handler"];
_connection.connectionHandlers = [GNCMConnectionHandlers
payloadHandler:^(NSData *data) {
XCTFail(@"Unexpected payload");
}
disconnectedHandler:^{
[disconnectionExpectation fulfill];
}];
// 6 MB frame length
uint32_t oversizedLength = 6 * 1024 * 1024;
uint32_t lengthBigEndian = CFSwapInt32HostToBig(oversizedLength);
NSMutableData *prefixData = [NSMutableData dataWithCapacity:sizeof(uint32_t)];
[prefixData appendBytes:&lengthBigEndian length:sizeof(uint32_t)];
NSData *realData = [_connection extractRealDataFromData:prefixData];
XCTAssertNil(realData);
XCTAssertEqual(_connection.expectedDataLength, 0);
[self waitForExpectationsWithTimeout:kTestTimeout handler:nil];
}
- (void)testExtractRealDataFromData_moreThanExpectedData {
_connection = [self createConnectionWithIncoming:YES];
NSData *testData = [self createDataWithLength:10];
@@ -47,6 +47,9 @@ NS_ASSUME_NONNULL_BEGIN
/** Expectation fulfilled when peripheral responds to a request with an error. */
@property(nonatomic, readonly) XCTestExpectation *respondToRequestErrorExpectation;
/** The last response result. */
@property(nonatomic, assign) CBATTError lastResponseResult;
/** Expectation fulfilled when peripheral unpublishes an L2CAP channel. */
@property(nonatomic, readonly) XCTestExpectation *unpublishExpectation;
@@ -117,6 +120,20 @@ NS_ASSUME_NONNULL_BEGIN
- (void)simulatePeripheralManagerDidReceiveReadRequestForService:(CBUUID *)service
characteristic:(CBUUID *)characteristic;
/**
* Simulates a read request event with an offset.
*
* Creates a fake read request with the given offset for the given service and characteristic UUIDs
* and calls the @c gnc_peripheralManager:didReceiveReadRequest: delegate method.
*
* @param service The service UUID of the characteristic to read from.
* @param characteristic The characteristic UUID to read from.
* @param offset The offset to read from.
*/
- (void)simulatePeripheralManagerDidReceiveReadRequestForService:(CBUUID *)service
characteristic:(CBUUID *)characteristic
offset:(NSUInteger)offset;
@end
NS_ASSUME_NONNULL_END
@@ -37,6 +37,9 @@
// Keep a strong reference to the service.
@property(readwrite, nonatomic) CBService *service;
// Change property to readwrite for tests.
@property(readwrite, nonatomic) NSUInteger offset;
- (instancetype)initWithService:(CBUUID *)service characteristic:(CBUUID *)characteristic;
@end
@@ -115,6 +118,7 @@ static const uint16_t kPSM = 192;
}
- (void)respondToRequest:(CBATTRequest *)request withResult:(CBATTError)result {
self.lastResponseResult = result;
if (result == CBATTErrorSuccess) {
[_respondToRequestSuccessExpectation fulfill];
return;
@@ -176,6 +180,15 @@ static const uint16_t kPSM = 192;
[_peripheralDelegate gnc_peripheralManager:self didReceiveReadRequest:request];
}
- (void)simulatePeripheralManagerDidReceiveReadRequestForService:(CBUUID *)service
characteristic:(CBUUID *)characteristic
offset:(NSUInteger)offset {
CBATTRequest *request = [[CBATTRequest alloc] initWithService:service
characteristic:characteristic];
request.offset = offset;
[_peripheralDelegate gnc_peripheralManager:self didReceiveReadRequest:request];
}
- (void)setDelegate:(id<CBPeripheralManagerDelegate>)delegate {
self.peripheralDelegate = (id<GNCPeripheralManagerDelegate>)delegate;
}
@@ -241,4 +241,33 @@ static const NSTimeInterval kTimeout = 1.0;
[self waitForExpectationsWithTimeout:kTimeout handler:nil];
}
- (void)testReceiveShortDataPacketAfterIntro {
_connection = [GNCMBleConnection connectionWithSocket:(GNSSocket *)_fakeSocket
serviceID:nil
expectedIntroPacket:YES
callbackQueue:_callbackQueue];
NSData *introPacket = GNCMGenerateBLEFramesIntroductionPacket(GNCMServiceIDHash(kServiceID));
// Receive the intro packet first to set `_serviceIDHash`.
[_fakeSocket simulateSocketDidReceiveData:introPacket];
// Receive a data packet that is shorter than the service ID hash length.
// This should not crash; it should just be discarded.
NSData *shortPacket = [@"ab" dataUsingEncoding:NSUTF8StringEncoding];
XCTestExpectation *expectation = [self expectationWithDescription:@"Payload handler not called"];
expectation.inverted = YES;
GNCMConnectionHandlers *handlers = [[GNCMConnectionHandlers alloc] init];
handlers.payloadHandler = ^(NSData *data) {
[expectation fulfill];
};
_connection.connectionHandlers = handlers;
[_fakeSocket simulateSocketDidReceiveData:shortPacket];
[self waitForExpectationsWithTimeout:kTimeout handler:nil];
}
@end
@@ -45,6 +45,18 @@ static const NSTimeInterval kWaitForConnectionTimeout = 6.0; // Allow for the 5
XCTAssertEqualObjects(parsedHash, serviceIDHash);
}
- (void)testParseBLEFramesIntroductionPacketFailure_InvalidHashLength {
// Too long hash (4 bytes, protocol expects 3 bytes)
NSData *longHash = [@"1234" dataUsingEncoding:NSUTF8StringEncoding];
NSData *longPacket = GNCMGenerateBLEFramesIntroductionPacket(longHash);
XCTAssertNil(GNCMParseBLEFramesIntroductionPacket(longPacket));
// Too short hash (2 bytes, protocol expects 3 bytes)
NSData *shortHash = [@"12" dataUsingEncoding:NSUTF8StringEncoding];
NSData *shortPacket = GNCMGenerateBLEFramesIntroductionPacket(shortHash);
XCTAssertNil(GNCMParseBLEFramesIntroductionPacket(shortPacket));
}
- (void)testParseBLEFramesIntroductionPacketFailure_NilData {
NSData *parsedHash = GNCMParseBLEFramesIntroductionPacket(nil);
XCTAssertNil(parsedHash);
@@ -57,7 +57,10 @@ NS_ASSUME_NONNULL_BEGIN
- (nullable NSString *)getBonjourServiceNameFromEndpoint:(nw_endpoint_t)endpoint {
const char *name = nw_endpoint_get_bonjour_service_name(endpoint);
return name ? @(name) : nil;
if (name == NULL) return nil;
// @() returns nil on non-UTF-8 input; the wire format does not guarantee UTF-8.
// Round-trip through Latin-1 so callers always get a non-nil NSString.
return @(name) ?: [NSString stringWithCString:name encoding:NSISOLatin1StringEncoding];
}
@end
@@ -63,11 +63,14 @@ NSDictionary<NSString *, NSString *> *GNCTXTRecordForBrowseResult(nw_browse_resu
block:^bool(const char *key, const nw_txt_record_find_key_t found,
const uint8_t *value, const size_t value_len) {
if (found == nw_txt_record_find_key_non_empty_value) {
NSString *keyString = @(key);
NSString *valueString =
[[NSString alloc] initWithBytes:value
length:value_len
encoding:NSUTF8StringEncoding];
[txtRecords setValue:valueString forKey:@(key)];
if (keyString != nil && valueString != nil) {
[txtRecords setObject:valueString forKey:keyString];
}
}
return YES;
}];
@@ -231,6 +234,11 @@ NSDictionary<NSString *, NSString *> *GNCTXTRecordForBrowseResult(nw_browse_resu
[browseResultWrapper copyEndpointFromResult:new_result];
NSString *name = [browseResultWrapper
getBonjourServiceNameFromEndpoint:endpoint];
if (name == nil) {
GNCLoggerInfo(
@"Dropping mDNS result with unrepresentable name.");
break;
}
NSDictionary<NSString *, NSString *> *txtRecords =
GNCTXTRecordForBrowseResult(new_result);
serviceFoundHandler(name, txtRecords);
@@ -250,6 +258,11 @@ NSDictionary<NSString *, NSString *> *GNCTXTRecordForBrowseResult(nw_browse_resu
[browseResultWrapper copyEndpointFromResult:old_result];
NSString *oldName = [browseResultWrapper
getBonjourServiceNameFromEndpoint:old_endpoint];
if (oldName == nil) {
GNCLoggerInfo(
@"Dropping mDNS result with unrepresentable old name.");
break;
}
NSDictionary<NSString *, NSString *> *oldTXTRecords =
GNCTXTRecordForBrowseResult(old_result);
serviceLostHandler(oldName, oldTXTRecords);
@@ -258,6 +271,11 @@ NSDictionary<NSString *, NSString *> *GNCTXTRecordForBrowseResult(nw_browse_resu
[browseResultWrapper copyEndpointFromResult:new_result];
NSString *newName = [browseResultWrapper
getBonjourServiceNameFromEndpoint:new_endpoint];
if (newName == nil) {
GNCLoggerInfo(
@"Dropping mDNS result with unrepresentable new name.");
break;
}
NSDictionary<NSString *, NSString *> *newTXTRecords =
GNCTXTRecordForBrowseResult(new_result);
serviceFoundHandler(newName, newTXTRecords);
@@ -276,6 +294,11 @@ NSDictionary<NSString *, NSString *> *GNCTXTRecordForBrowseResult(nw_browse_resu
[browseResultWrapper copyEndpointFromResult:old_result];
NSString *name = [browseResultWrapper
getBonjourServiceNameFromEndpoint:endpoint];
if (name == nil) {
GNCLoggerInfo(
@"Dropping mDNS result with unrepresentable name.");
break;
}
NSDictionary<NSString *, NSString *> *txtRecords =
GNCTXTRecordForBrowseResult(old_result);
serviceLostHandler(name, txtRecords);
@@ -31,6 +31,7 @@ NS_ASSUME_NONNULL_BEGIN
@property(nonatomic) nw_browse_result_change_t getChangesFromResult;
@property(nonatomic, nullable) nw_endpoint_t endpointFromResultResult;
@property(nonatomic, nullable) NSString *getBonjourServiceNameFromEndpointResult;
@property(nonatomic) BOOL returnNilServiceName;
@end
@@ -67,6 +67,9 @@ NS_ASSUME_NONNULL_BEGIN
}
- (nullable NSString *)getBonjourServiceNameFromEndpoint:(nw_endpoint_t)endpoint {
if (self.returnNilServiceName) {
return nil;
}
return self.getBonjourServiceNameFromEndpointResult ?: @"FakeService";
}
@@ -53,16 +53,16 @@ NS_ASSUME_NONNULL_BEGIN
* @param serviceName The name of the service found.
* @param txtRecords The TXT records of the service found.
*/
- (void)triggerServiceFound:(NSString*)serviceName
txtRecords:(NSDictionary<NSString*, NSString*>*)txtRecords;
- (void)triggerServiceFound:(nullable NSString*)serviceName
txtRecords:(nullable NSDictionary<NSString*, NSString*>*)txtRecords;
/**
* Triggers the service lost handler with the given service info.
*
* @param serviceName The name of the service lost.
* @param txtRecords The TXT records of the service lost.
*/
- (void)triggerServiceLost:(NSString*)serviceName
txtRecords:(NSDictionary<NSString*, NSString*>*)txtRecords;
- (void)triggerServiceLost:(nullable NSString*)serviceName
txtRecords:(nullable NSDictionary<NSString*, NSString*>*)txtRecords;
@end
@@ -122,15 +122,15 @@
return serverSocket;
}
- (void)triggerServiceFound:(NSString *)serviceName
txtRecords:(NSDictionary<NSString *, NSString *> *)txtRecords {
- (void)triggerServiceFound:(nullable NSString *)serviceName
txtRecords:(nullable NSDictionary<NSString *, NSString *> *)txtRecords {
if (self.serviceFoundHandler) {
self.serviceFoundHandler(serviceName, txtRecords);
}
}
- (void)triggerServiceLost:(NSString *)serviceName
txtRecords:(NSDictionary<NSString *, NSString *> *)txtRecords {
- (void)triggerServiceLost:(nullable NSString *)serviceName
txtRecords:(nullable NSDictionary<NSString *, NSString *> *)txtRecords {
if (self.serviceLostHandler) {
self.serviceLostHandler(serviceName, txtRecords);
}
@@ -67,4 +67,17 @@
XCTAssertEqualObjects(results[@"key2"], @"value2");
}
- (void)testGetBonjourServiceName_InvalidUTF8 {
GNCNWBrowseResultImpl *browseResult = [[GNCNWBrowseResultImpl alloc] init];
const char *raw_invalid = "\xc3\x28"
"abc";
nw_endpoint_t endpoint =
nw_endpoint_create_bonjour_service(raw_invalid, "_servicetype._tcp", "local.");
XCTAssertNotNil(endpoint);
NSString *serviceName = [browseResult getBonjourServiceNameFromEndpoint:endpoint];
XCTAssertNotNil(serviceName);
XCTAssertEqualObjects(serviceName, @"(abc");
}
@end
@@ -61,7 +61,6 @@ static NSString *const kHostAddress = @"127.0.0.1";
_mockConnectionImpl = OCMClassMock([GNCNWConnectionImpl class]);
}
- (void)testGNCNWFrameworkCanBeInstantiated {
GNCNWFramework *framework = [[GNCNWFramework alloc] init];
XCTAssertNotNil(framework);
@@ -272,6 +271,49 @@ static NSString *const kHostAddress = @"127.0.0.1";
XCTAssertEqualObjects(foundTXTRecords, @{@"key" : @"value"});
}
- (void)testStartDiscoveryForServiceTypeNilName API_AVAILABLE(ios(13.0)) {
GNCNWFramework *framework = [[GNCNWFramework alloc] init];
GNCFakeNWBrowser *fakeBrowser = [[GNCFakeNWBrowser alloc] init];
fakeBrowser.createWithDescriptorResult = (nw_browser_t)fakeBrowser;
OCMStub([_mockBrowserImpl alloc]).andReturn(fakeBrowser);
XCTestExpectation *serviceFoundExpectation = [self expectationWithDescription:@"Service found"];
serviceFoundExpectation.inverted = YES;
NSError *error = nil;
BOOL result = [framework startDiscoveryForServiceType:kServiceType
serviceFoundHandler:^(NSString *serviceName,
NSDictionary<NSString *, NSString *> *txtRecords) {
[serviceFoundExpectation fulfill];
}
serviceLostHandler:^(NSString *serviceName,
NSDictionary<NSString *, NSString *> *txtRecords) {
}
includePeerToPeer:NO
error:&error];
XCTAssertTrue(result);
XCTAssertNil(error);
// Simulate a service being found with nil name.
GNCFakeNWBrowseResult *fakeBrowseResult = [[GNCFakeNWBrowseResult alloc] init];
fakeBrowseResult.txtRecord = @{@"key" : @"value"};
fakeBrowseResult.getChangesFromResult = nw_browse_result_change_result_added;
nw_endpoint_t fakeEndpoint =
nw_endpoint_create_host("localhost", [[NSString stringWithFormat:@"%ld", kPort] UTF8String]);
fakeBrowseResult.endpointFromResultResult = fakeEndpoint;
fakeBrowseResult.returnNilServiceName = YES;
OCMStub([_mockBrowseResultImpl sharedInstance]).andReturn(fakeBrowseResult);
if (fakeBrowser.browseResultsChangedHandler) {
GNCFakeNWBrowseResult *oldFakeBrowseResult = [[GNCFakeNWBrowseResult alloc] init];
fakeBrowser.browseResultsChangedHandler((nw_browse_result_t)oldFakeBrowseResult,
(nw_browse_result_t)fakeBrowseResult, true);
}
[self waitForExpectations:@[ serviceFoundExpectation ] timeout:0.1];
}
- (void)testStartDiscoveryForServiceTypeDuplicate API_AVAILABLE(ios(13.0)) {
GNCNWFramework *framework = [[GNCNWFramework alloc] init];
GNCFakeNWBrowser *fakeBrowser = [[GNCFakeNWBrowser alloc] init];
@@ -449,12 +491,13 @@ static NSString *const kHostAddress = @"127.0.0.1";
// TODO: b/377543997 - Migrate to dependency injection and remove mocks.
OCMStub([_mockBrowserImpl alloc]).andReturn(fakeBrowser);
__block BOOL serviceFound = NO;
XCTestExpectation *serviceFoundExpectation = [self expectationWithDescription:@"Service found"];
serviceFoundExpectation.inverted = YES;
NSError *error = nil;
[framework startDiscoveryForServiceType:kServiceType
serviceFoundHandler:^(NSString *serviceName,
NSDictionary<NSString *, NSString *> *txtRecords) {
serviceFound = YES;
[serviceFoundExpectation fulfill];
}
serviceLostHandler:^(NSString *serviceName,
NSDictionary<NSString *, NSString *> *txtRecords) {
@@ -478,14 +521,7 @@ static NSString *const kHostAddress = @"127.0.0.1";
(nw_browse_result_t)fakeBrowseResult, true);
}
// Allow async blocks to run.
XCTestExpectation *delay = [[XCTestExpectation alloc] initWithDescription:@"delay"];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[delay fulfill];
});
[self waitForExpectations:@[ delay ] timeout:0.5];
XCTAssertFalse(serviceFound);
[self waitForExpectations:@[ serviceFoundExpectation ] timeout:0.1];
}
- (void)testStartDiscoveryIgnoresLoopbackRemove API_AVAILABLE(ios(13.0)) {
@@ -495,7 +531,8 @@ static NSString *const kHostAddress = @"127.0.0.1";
// TODO: b/377543997 - Migrate to dependency injection and remove mocks.
OCMStub([_mockBrowserImpl alloc]).andReturn(fakeBrowser);
__block BOOL serviceLost = NO;
XCTestExpectation *serviceLostExpectation = [self expectationWithDescription:@"Service lost"];
serviceLostExpectation.inverted = YES;
NSError *error = nil;
[framework startDiscoveryForServiceType:kServiceType
serviceFoundHandler:^(NSString *serviceName,
@@ -503,7 +540,7 @@ static NSString *const kHostAddress = @"127.0.0.1";
}
serviceLostHandler:^(NSString *serviceName,
NSDictionary<NSString *, NSString *> *txtRecords) {
serviceLost = YES;
[serviceLostExpectation fulfill];
}
includePeerToPeer:NO
error:&error];
@@ -524,14 +561,7 @@ static NSString *const kHostAddress = @"127.0.0.1";
(nw_browse_result_t)newFakeBrowseResult, true);
}
// Allow async blocks to run.
XCTestExpectation *delay = [[XCTestExpectation alloc] initWithDescription:@"delay"];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[delay fulfill];
});
[self waitForExpectations:@[ delay ] timeout:0.5];
XCTAssertFalse(serviceLost);
[self waitForExpectations:@[ serviceLostExpectation ] timeout:0.1];
}
- (void)testStopDiscoveryForServiceType API_AVAILABLE(ios(13.0)) {
@@ -722,8 +752,8 @@ static NSString *const kHostAddress = @"127.0.0.1";
GNCNWFrameworkSocket *socket = [framework connectToHost:address
port:kPort
includePeerToPeer:NO
cancelSource:nil
queue:nil
cancelSource:nil
queue:nil
error:&error];
XCTAssertNotNil(socket);
@@ -742,8 +772,8 @@ static NSString *const kHostAddress = @"127.0.0.1";
GNCNWFrameworkSocket *socket = [framework connectToHost:address
port:kPort
includePeerToPeer:NO
cancelSource:nil
queue:nil
cancelSource:nil
queue:nil
error:&error];
XCTAssertNil(socket);
@@ -208,7 +208,7 @@ static const char *const kTestServiceID = "TestServiceID";
}
- (void)testStartMultipleServicesScanning_Success {
std::vector<nearby::Uuid> service_uuids = {nearby::Uuid(0, 0)};
std::vector<nearby::Uuid> service_uuids = {nearby::Uuid(0x0000FE2C00001000, 0x800000805F9B34FB)};
nearby::api::ble::TxPowerLevel tx_power_level = nearby::api::ble::TxPowerLevel::kUltraLow;
bool result = _medium->StartMultipleServicesScanning(service_uuids, tx_power_level, {});
@@ -217,7 +217,7 @@ static const char *const kTestServiceID = "TestServiceID";
}
- (void)testStartMultipleServicesScanning_Failure {
std::vector<nearby::Uuid> service_uuids = {nearby::Uuid(0, 0)};
std::vector<nearby::Uuid> service_uuids = {nearby::Uuid(0x0000FE2C00001000, 0x800000805F9B34FB)};
nearby::api::ble::TxPowerLevel tx_power_level = nearby::api::ble::TxPowerLevel::kUltraLow;
_fakeGNCBLEMedium.startScanningError = [NSError errorWithDomain:@"test" code:0 userInfo:nil];
@@ -300,8 +300,8 @@ static const char *const kTestServiceID = "TestServiceID";
const nearby::api::ble::BleAdvertisementData &advertisement) {
[expectation fulfill];
})};
_medium->StartScanning(nearby::Uuid(0, 0), nearby::api::ble::TxPowerLevel::kUltraLow,
std::move(callback));
_medium->StartScanning(nearby::Uuid(0x0000FE2C00001000, 0x800000805F9B34FB),
nearby::api::ble::TxPowerLevel::kUltraLow, std::move(callback));
if (_fakeGNCBLEMedium.advertisementFoundHandler) {
_fakeGNCBLEMedium.advertisementFoundHandler(fakePeripheral, serviceData);
}
@@ -326,8 +326,8 @@ static const char *const kTestServiceID = "TestServiceID";
const nearby::api::ble::BleAdvertisementData &advertisement) {
[expectation fulfill];
})};
_medium->StartScanning(nearby::Uuid(0, 0), nearby::api::ble::TxPowerLevel::kUltraLow,
std::move(callback));
_medium->StartScanning(nearby::Uuid(0x0000FE2C00001000, 0x800000805F9B34FB),
nearby::api::ble::TxPowerLevel::kUltraLow, std::move(callback));
if (_fakeGNCBLEMedium.advertisementFoundHandler) {
_fakeGNCBLEMedium.advertisementFoundHandler(fakePeripheral, serviceData);
}
@@ -385,8 +385,8 @@ static const char *const kTestServiceID = "TestServiceID";
const nearby::api::ble::BleAdvertisementData &advertisement) {
[expectation fulfill];
})};
_medium->StartScanning(nearby::Uuid(0, 0), nearby::api::ble::TxPowerLevel::kUltraLow,
std::move(callback));
_medium->StartScanning(nearby::Uuid(0x0000FE2C00001000, 0x800000805F9B34FB),
nearby::api::ble::TxPowerLevel::kUltraLow, std::move(callback));
if (_fakeGNCBLEMedium.advertisementFoundHandler) {
_fakeGNCBLEMedium.advertisementFoundHandler(fakePeripheral, serviceData);
}
@@ -413,8 +413,8 @@ static const char *const kTestServiceID = "TestServiceID";
const nearby::api::ble::BleAdvertisementData &advertisement) {
[expectation fulfill];
})};
_medium->StartScanning(nearby::Uuid(0, 0), nearby::api::ble::TxPowerLevel::kUltraLow,
std::move(callback));
_medium->StartScanning(nearby::Uuid(0x0000FE2C00001000, 0x800000805F9B34FB),
nearby::api::ble::TxPowerLevel::kUltraLow, std::move(callback));
if (_fakeGNCBLEMedium.advertisementFoundHandler) {
_fakeGNCBLEMedium.advertisementFoundHandler(fakePeripheral, serviceData);
}
@@ -450,8 +450,8 @@ static const char *const kTestServiceID = "TestServiceID";
const nearby::api::ble::BleAdvertisementData &advertisement) {
[expectation fulfill];
})};
_medium->StartScanning(nearby::Uuid(0, 0), nearby::api::ble::TxPowerLevel::kUltraLow,
std::move(callback));
_medium->StartScanning(nearby::Uuid(0x0000FE2C00001000, 0x800000805F9B34FB),
nearby::api::ble::TxPowerLevel::kUltraLow, std::move(callback));
if (_fakeGNCBLEMedium.advertisementFoundHandler) {
_fakeGNCBLEMedium.advertisementFoundHandler(fakePeripheral, serviceData);
}
@@ -483,8 +483,8 @@ static const char *const kTestServiceID = "TestServiceID";
const nearby::api::ble::BleAdvertisementData &advertisement) {
[expectation fulfill];
})};
_medium->StartScanning(nearby::Uuid(0, 0), nearby::api::ble::TxPowerLevel::kUltraLow,
std::move(callback));
_medium->StartScanning(nearby::Uuid(0x0000FE2C00001000, 0x800000805F9B34FB),
nearby::api::ble::TxPowerLevel::kUltraLow, std::move(callback));
if (_fakeGNCBLEMedium.advertisementFoundHandler) {
_fakeGNCBLEMedium.advertisementFoundHandler(fakePeripheral, serviceData);
}
@@ -650,6 +650,239 @@ static const char *const kTestServiceID = "TestServiceID";
XCTAssertEqual(server_socket.get(), nullptr);
}
- (void)testOpenServerSocket_Cleanup_InitialState {
id mockFeatureFlags = OCMClassMock([GNCFeatureFlags class]);
OCMStub([mockFeatureFlags fixBleServerSocketDeadlockEnabled]).andReturn(YES);
id mockPeripheralManager = OCMClassMock([GNSPeripheralManager class]);
__block BOOL added = NO;
OCMStub([mockPeripheralManager addPeripheralServiceManager:[OCMArg any]
bleServiceAddedCompletion:[OCMArg any]])
.andDo(^(GNSPeripheralManager *localSelf, GNSPeripheralServiceManager *manager,
void (^completion)(NSError *error)) {
added = YES;
completion(nil);
});
__block BOOL removed = NO;
OCMStub([mockPeripheralManager removePeripheralServiceManagerForServiceUUID:[OCMArg any]
bleServiceRemovedCompletion:[OCMArg any]])
.andDo(^(GNSPeripheralManager *localSelf, CBUUID *serviceUUID,
void (^completion)(NSError *error)) {
removed = YES;
completion(nil);
});
nearby::apple::BleMediumPeer::SetPeripheralManagerFactory(_medium.get(), ^() {
return mockPeripheralManager;
});
// Open the server socket.
auto server_socket = _medium->OpenServerSocket(kTestServiceID);
XCTAssertNotEqual(server_socket.get(), nullptr);
XCTAssertTrue(added);
XCTAssertFalse(removed);
server_socket->Close();
}
- (void)testOpenServerSocket_Cleanup_AcceptConnection {
id mockFeatureFlags = OCMClassMock([GNCFeatureFlags class]);
OCMStub([mockFeatureFlags fixBleServerSocketDeadlockEnabled]).andReturn(YES);
id mockPeripheralManager = OCMClassMock([GNSPeripheralManager class]);
OCMStub([mockPeripheralManager addPeripheralServiceManager:[OCMArg any]
bleServiceAddedCompletion:[OCMArg any]])
.andDo(^(GNSPeripheralManager *localSelf, GNSPeripheralServiceManager *manager,
void (^completion)(NSError *error)) {
completion(nil);
});
__block BOOL removed = NO;
OCMStub([mockPeripheralManager removePeripheralServiceManagerForServiceUUID:[OCMArg any]
bleServiceRemovedCompletion:[OCMArg any]])
.andDo(^(GNSPeripheralManager *localSelf, CBUUID *serviceUUID,
void (^completion)(NSError *error)) {
removed = YES;
completion(nil);
});
nearby::apple::BleMediumPeer::SetPeripheralManagerFactory(_medium.get(), ^() {
return mockPeripheralManager;
});
__block BOOL (^capturedHandler)(GNSSocket *) = nil;
id mockServiceManagerClass = OCMClassMock([GNSPeripheralServiceManager class]);
OCMStub([mockServiceManagerClass alloc]).andReturn(mockServiceManagerClass);
OCMStub([mockServiceManagerClass initWithBleServiceUUID:[OCMArg any]
addPairingCharacteristic:NO
shouldAcceptSocketHandler:[OCMArg any]])
.andDo(^(NSInvocation *invocation) {
BOOL (^handler)(GNSSocket *);
[invocation getArgument:&handler atIndex:4];
capturedHandler = handler;
})
.andReturn(mockServiceManagerClass);
auto server_socket = _medium->OpenServerSocket(kTestServiceID);
XCTAssertNotEqual(server_socket.get(), nullptr);
XCTAssertNotNil(capturedHandler);
// Simulate connection accepted -> client socket is created.
GNCFakeSocket *fakeSocket = [[GNCFakeSocket alloc] init];
BOOL result = capturedHandler((GNSSocket *)fakeSocket);
XCTAssertTrue(result);
XCTAssertFalse(removed); // Accepting socket shouldn't remove service manager.
server_socket->Close();
}
- (void)testOpenServerSocket_Cleanup_CloseClientSocket {
id mockFeatureFlags = OCMClassMock([GNCFeatureFlags class]);
OCMStub([mockFeatureFlags fixBleServerSocketDeadlockEnabled]).andReturn(YES);
id mockPeripheralManager = OCMClassMock([GNSPeripheralManager class]);
OCMStub([mockPeripheralManager addPeripheralServiceManager:[OCMArg any]
bleServiceAddedCompletion:[OCMArg any]])
.andDo(^(GNSPeripheralManager *localSelf, GNSPeripheralServiceManager *manager,
void (^completion)(NSError *error)) {
completion(nil);
});
__block BOOL removed = NO;
OCMStub([mockPeripheralManager removePeripheralServiceManagerForServiceUUID:[OCMArg any]
bleServiceRemovedCompletion:[OCMArg any]])
.andDo(^(GNSPeripheralManager *localSelf, CBUUID *serviceUUID,
void (^completion)(NSError *error)) {
removed = YES;
completion(nil);
});
nearby::apple::BleMediumPeer::SetPeripheralManagerFactory(_medium.get(), ^() {
return mockPeripheralManager;
});
__block BOOL (^capturedHandler)(GNSSocket *) = nil;
id mockServiceManagerClass = OCMClassMock([GNSPeripheralServiceManager class]);
OCMStub([mockServiceManagerClass alloc]).andReturn(mockServiceManagerClass);
OCMStub([mockServiceManagerClass initWithBleServiceUUID:[OCMArg any]
addPairingCharacteristic:NO
shouldAcceptSocketHandler:[OCMArg any]])
.andDo(^(NSInvocation *invocation) {
BOOL (^handler)(GNSSocket *);
[invocation getArgument:&handler atIndex:4];
capturedHandler = handler;
})
.andReturn(mockServiceManagerClass);
auto server_socket = _medium->OpenServerSocket(kTestServiceID);
XCTAssertNotEqual(server_socket.get(), nullptr);
GNCFakeSocket *fakeSocket = [[GNCFakeSocket alloc] init];
capturedHandler((GNSSocket *)fakeSocket);
[fakeSocket simulateSocketDidConnect];
__block std::unique_ptr<nearby::api::ble::BleSocket> client_socket = nullptr;
XCTestExpectation *acceptExpectation = [self expectationWithDescription:@"Accept connection"];
nearby::api::ble::BleServerSocket *raw_server_socket = server_socket.get();
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
client_socket = raw_server_socket->Accept();
[acceptExpectation fulfill];
});
[self waitForExpectations:@[ acceptExpectation ] timeout:1.0];
XCTAssertTrue(client_socket != nullptr);
// Close the client socket.
client_socket->Close();
// Wait a bit for any async callbacks
XCTestExpectation *expectation2 = [self expectationWithDescription:@"Wait after client close"];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
[expectation2 fulfill];
});
[self waitForExpectations:@[ expectation2 ] timeout:1.0];
XCTAssertFalse(removed); // Closing client socket shouldn't remove service manager!
server_socket->Close();
}
- (void)testOpenServerSocket_Cleanup_CloseServerSocket {
id mockFeatureFlags = OCMClassMock([GNCFeatureFlags class]);
OCMStub([mockFeatureFlags fixBleServerSocketDeadlockEnabled]).andReturn(YES);
id mockPeripheralManager = OCMClassMock([GNSPeripheralManager class]);
OCMStub([mockPeripheralManager addPeripheralServiceManager:[OCMArg any]
bleServiceAddedCompletion:[OCMArg any]])
.andDo(^(GNSPeripheralManager *localSelf, GNSPeripheralServiceManager *manager,
void (^completion)(NSError *error)) {
completion(nil);
});
__block BOOL removed = NO;
OCMStub([mockPeripheralManager removePeripheralServiceManagerForServiceUUID:[OCMArg any]
bleServiceRemovedCompletion:[OCMArg any]])
.andDo(^(GNSPeripheralManager *localSelf, CBUUID *serviceUUID,
void (^completion)(NSError *error)) {
removed = YES;
completion(nil);
});
nearby::apple::BleMediumPeer::SetPeripheralManagerFactory(_medium.get(), ^() {
return mockPeripheralManager;
});
__block BOOL (^capturedHandler)(GNSSocket *) = nil;
id mockServiceManagerClass = OCMClassMock([GNSPeripheralServiceManager class]);
OCMStub([mockServiceManagerClass alloc]).andReturn(mockServiceManagerClass);
OCMStub([mockServiceManagerClass initWithBleServiceUUID:[OCMArg any]
addPairingCharacteristic:NO
shouldAcceptSocketHandler:[OCMArg any]])
.andDo(^(NSInvocation *invocation) {
BOOL (^handler)(GNSSocket *);
[invocation getArgument:&handler atIndex:4];
capturedHandler = handler;
})
.andReturn(mockServiceManagerClass);
auto server_socket = _medium->OpenServerSocket(kTestServiceID);
XCTAssertNotEqual(server_socket.get(), nullptr);
GNCFakeSocket *fakeSocket = [[GNCFakeSocket alloc] init];
capturedHandler((GNSSocket *)fakeSocket);
[fakeSocket simulateSocketDidConnect];
__block std::unique_ptr<nearby::api::ble::BleSocket> client_socket = nullptr;
XCTestExpectation *acceptExpectation = [self expectationWithDescription:@"Accept connection"];
nearby::api::ble::BleServerSocket *raw_server_socket = server_socket.get();
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
client_socket = raw_server_socket->Accept();
[acceptExpectation fulfill];
});
[self waitForExpectations:@[ acceptExpectation ] timeout:1.0];
// Close the server socket.
server_socket->Close();
// Wait a bit for any async callbacks
XCTestExpectation *expectation3 = [self expectationWithDescription:@"Wait after server close"];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
[expectation3 fulfill];
});
[self waitForExpectations:@[ expectation3 ] timeout:1.0];
XCTAssertTrue(removed); // Closing server socket MUST remove service manager!
}
#pragma mark - Other Tests
- (void)testIsExtendedAdvertisementsAvailable {
@@ -669,8 +902,8 @@ static const char *const kTestServiceID = "TestServiceID";
const nearby::api::ble::BleAdvertisementData &advertisement) {
[expectation fulfill];
})};
_medium->StartScanning(nearby::Uuid(0, 0), nearby::api::ble::TxPowerLevel::kUltraLow,
std::move(callback));
_medium->StartScanning(nearby::Uuid(0x0000FE2C00001000, 0x800000805F9B34FB),
nearby::api::ble::TxPowerLevel::kUltraLow, std::move(callback));
if (_fakeGNCBLEMedium.advertisementFoundHandler) {
_fakeGNCBLEMedium.advertisementFoundHandler(fakePeripheral, serviceData);
}
@@ -717,8 +950,8 @@ static const char *const kTestServiceID = "TestServiceID";
[expectation fulfill];
})};
_medium->StartScanning(nearby::Uuid(0, 0), nearby::api::ble::TxPowerLevel::kUltraLow,
std::move(callback));
_medium->StartScanning(nearby::Uuid(0x0000FE2C00001000, 0x800000805F9B34FB),
nearby::api::ble::TxPowerLevel::kUltraLow, std::move(callback));
if (_fakeGNCBLEMedium.advertisementFoundHandler) {
_fakeGNCBLEMedium.advertisementFoundHandler(fakePeripheral, serviceData);
@@ -742,8 +975,8 @@ static const char *const kTestServiceID = "TestServiceID";
const nearby::api::ble::BleAdvertisementData &advertisement) {
[expectation1 fulfill];
})};
_medium->StartScanning(nearby::Uuid(0, 0), nearby::api::ble::TxPowerLevel::kUltraLow,
std::move(callback1));
_medium->StartScanning(nearby::Uuid(0x0000FE2C00001000, 0x800000805F9B34FB),
nearby::api::ble::TxPowerLevel::kUltraLow, std::move(callback1));
if (_fakeGNCBLEMedium.advertisementFoundHandler) {
_fakeGNCBLEMedium.advertisementFoundHandler(fakePeripheral, serviceData1);
}
@@ -766,8 +999,8 @@ static const char *const kTestServiceID = "TestServiceID";
serviceData2[[CBUUID UUIDWithString:kTestServiceUUIDString]]);
[expectation2 fulfill];
})};
_medium->StartScanning(nearby::Uuid(0, 0), nearby::api::ble::TxPowerLevel::kUltraLow,
std::move(callback2));
_medium->StartScanning(nearby::Uuid(0x0000FE2C00001000, 0x800000805F9B34FB),
nearby::api::ble::TxPowerLevel::kUltraLow, std::move(callback2));
if (_fakeGNCBLEMedium.advertisementFoundHandler) {
_fakeGNCBLEMedium.advertisementFoundHandler(fakePeripheral, serviceData2);
@@ -799,8 +1032,8 @@ static const char *const kTestServiceID = "TestServiceID";
[expectation2 fulfill];
}
})};
_medium->StartScanning(nearby::Uuid(0, 0), nearby::api::ble::TxPowerLevel::kUltraLow,
std::move(callback));
_medium->StartScanning(nearby::Uuid(0x0000FE2C00001000, 0x800000805F9B34FB),
nearby::api::ble::TxPowerLevel::kUltraLow, std::move(callback));
if (_fakeGNCBLEMedium.advertisementFoundHandler) {
_fakeGNCBLEMedium.advertisementFoundHandler(fakePeripheral, serviceData);
}
@@ -826,8 +1059,8 @@ static const char *const kTestServiceID = "TestServiceID";
const nearby::api::ble::BleAdvertisementData &advertisement) {
[expectation1 fulfill];
})};
_medium->StartScanning(nearby::Uuid(0, 0), nearby::api::ble::TxPowerLevel::kUltraLow,
std::move(callback1));
_medium->StartScanning(nearby::Uuid(0x0000FE2C00001000, 0x800000805F9B34FB),
nearby::api::ble::TxPowerLevel::kUltraLow, std::move(callback1));
if (_fakeGNCBLEMedium.advertisementFoundHandler) {
_fakeGNCBLEMedium.advertisementFoundHandler(fakePeripheral, serviceData);
}
@@ -843,8 +1076,8 @@ static const char *const kTestServiceID = "TestServiceID";
const nearby::api::ble::BleAdvertisementData &advertisement) {
[expectation2 fulfill];
})};
_medium->StartScanning(nearby::Uuid(0, 0), nearby::api::ble::TxPowerLevel::kUltraLow,
std::move(callback2));
_medium->StartScanning(nearby::Uuid(0x0000FE2C00001000, 0x800000805F9B34FB),
nearby::api::ble::TxPowerLevel::kUltraLow, std::move(callback2));
if (_fakeGNCBLEMedium.advertisementFoundHandler) {
_fakeGNCBLEMedium.advertisementFoundHandler(fakePeripheral, serviceData);
@@ -157,4 +157,30 @@ static NSString *const kServiceType = @"_test._tcp";
[self waitForExpectationsWithTimeout:1.0 handler:nil];
}
- (void)testStartDiscoveryCallbacksWithNilName {
XCTestExpectation *foundExpectation = [self expectationWithDescription:@"Service found callback"];
XCTestExpectation *lostExpectation = [self expectationWithDescription:@"Service lost callback"];
nearby::apple::network_utils::NetworkDiscoveredServiceCallback callback;
callback.network_service_discovered_cb = [&](const nearby::NsdServiceInfo &service_info) {
XCTAssertEqual(service_info.GetServiceName(), std::string(""));
XCTAssertEqual(service_info.GetServiceType(), kServiceType.UTF8String);
[foundExpectation fulfill];
};
callback.network_service_lost_cb = [&](const nearby::NsdServiceInfo &service_info) {
XCTAssertEqual(service_info.GetServiceName(), std::string(""));
XCTAssertEqual(service_info.GetServiceType(), kServiceType.UTF8String);
[lostExpectation fulfill];
};
BOOL result = nearby::apple::network_utils::StartDiscovery(
_fakeNWFramework, kServiceType.UTF8String, std::move(callback), YES);
XCTAssertTrue(result);
[_fakeNWFramework triggerServiceFound:nil txtRecords:@{}];
[_fakeNWFramework triggerServiceLost:nil txtRecords:@{}];
[self waitForExpectationsWithTimeout:1.0 handler:nil];
}
@end
@@ -247,6 +247,7 @@ class BleMedium : public api::ble::BleMedium {
GNSPeripheralManager *socketPeripheralManager_;
absl::Mutex scanning_mutex_;
std::vector<Uuid> scanning_service_uuids_ ABSL_GUARDED_BY(scanning_mutex_);
GNSCentralManager *socketCentralManager_ ABSL_GUARDED_BY(scanning_mutex_);
// Used for the blocking version of StartAdvertising and only has an advertisement found callback.
@@ -12,11 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "absl/algorithm/container.h"
#import "internal/platform/implementation/apple/ble_medium.h"
#import <CoreBluetooth/CoreBluetooth.h>
#import <Foundation/Foundation.h>
#include <algorithm>
#include <memory>
#include <string>
#include <utility>
@@ -157,8 +159,17 @@ void BleMedium::HandleAdvertisementFound(id<GNCPeripheral> peripheral,
return;
}
std::vector<Uuid> scanning_uuids;
{
absl::MutexLock lock(&scanning_mutex_);
scanning_uuids = scanning_service_uuids_;
}
for (CBUUID *key in serviceData.allKeys) {
data.service_data[CPPUUIDFromObjC(key)] = ByteArrayFromNSData(serviceData[key]);
Uuid cpp_uuid = CPPUUIDFromObjC(key);
if (absl::c_find(scanning_uuids, cpp_uuid) == scanning_uuids.end()) {
continue;
}
data.service_data[cpp_uuid] = ByteArrayFromNSData(serviceData[key]);
}
// Add the peripheral to the map if we haven't discovered it yet.
@@ -197,6 +208,7 @@ std::unique_ptr<api::ble::BleMedium::ScanningSession> BleMedium::StartScanning(
{
absl::MutexLock lock(&scanning_mutex_);
scanning_cb_ = std::make_shared<api::ble::BleMedium::ScanningCallback>(std::move(callback));
scanning_service_uuids_ = {service_uuid};
if (central_manager_factory_) {
socketCentralManager_ = central_manager_factory_(serviceUUID);
@@ -273,6 +285,7 @@ bool BleMedium::StartScanning(const Uuid &service_uuid, api::ble::TxPowerLevel t
{
absl::MutexLock lock(&scanning_mutex_);
scan_cb_ = std::make_shared<api::ble::BleMedium::ScanCallback>(std::move(callback));
scanning_service_uuids_ = {service_uuid};
if (central_manager_factory_) {
socketCentralManager_ = central_manager_factory_(serviceUUID);
@@ -331,6 +344,7 @@ bool BleMedium::StartMultipleServicesScanning(const std::vector<Uuid> &service_u
{
absl::MutexLock lock(&scanning_mutex_);
scan_cb_ = std::make_shared<api::ble::BleMedium::ScanCallback>(std::move(callback));
scanning_service_uuids_ = service_uuids;
if (central_manager_factory_) {
socketCentralManager_ = central_manager_factory_(serviceUUIDs[0]);
@@ -379,6 +393,7 @@ bool BleMedium::StopScanning() {
[socketCentralManager_ stopNoScanMode];
scan_cb_ = nullptr;
scanning_cb_ = nullptr;
scanning_service_uuids_.clear();
}
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
@@ -534,6 +549,13 @@ std::unique_ptr<api::ble::BleServerSocket> BleMedium::OpenServerSocketWithDeadlo
server_socket->SetCloseNotifier([this]() {
absl::MutexLock lock(server_socket_mutex_);
server_socket_ptr_ = nullptr;
if (socketPeripheralManager_ != nil && socketPeripheralServiceManager_ != nil) {
[socketPeripheralManager_
removePeripheralServiceManagerForServiceUUID:socketPeripheralServiceManager_.serviceUUID
bleServiceRemovedCompletion:^(NSError *_Nullable error) {
GNCLoggerInfo(@"Weave service removed from peripheral manager.");
}];
}
});
socketPeripheralServiceManager_ = [[GNSPeripheralServiceManager alloc]
@@ -549,15 +571,6 @@ std::unique_ptr<api::ble::BleServerSocket> BleMedium::OpenServerSocketWithDeadlo
callbackQueue:connection_callback_queue_];
auto socket_wrapper = std::make_unique<BleSocket>(connection);
socket_wrapper->SetCloseNotifier(
[socketPeripheralManager = socketPeripheralManager_,
serviceUUID = socketPeripheralServiceManager_.serviceUUID]() {
[socketPeripheralManager
removePeripheralServiceManagerForServiceUUID:serviceUUID
bleServiceRemovedCompletion:^(NSError *_Nullable error) {
GNCLoggerInfo(@"BleSocket is removed peripheral manager.");
}];
});
connection.connectionHandlers = socket_wrapper->GetInputStream().GetConnectionHandlers();
@@ -612,6 +625,16 @@ std::unique_ptr<api::ble::BleServerSocket> BleMedium::OpenServerSocketLegacy(
// Raw pointer for closure capture in the legacy path (risks use-after-free).
BleServerSocket *server_socket_ptr = server_socket.get();
server_socket->SetCloseNotifier([this]() {
if (socketPeripheralManager_ != nil && socketPeripheralServiceManager_ != nil) {
[socketPeripheralManager_
removePeripheralServiceManagerForServiceUUID:socketPeripheralServiceManager_.serviceUUID
bleServiceRemovedCompletion:^(NSError *_Nullable error) {
GNCLoggerInfo(@"Weave service removed from peripheral manager.");
}];
}
});
socketPeripheralServiceManager_ = [[GNSPeripheralServiceManager alloc]
initWithBleServiceUUID:[CBUUID UUIDWithString:kWeaveServiceUUID]
addPairingCharacteristic:NO
@@ -627,14 +650,6 @@ std::unique_ptr<api::ble::BleServerSocket> BleMedium::OpenServerSocketLegacy(
callbackQueue:connection_callback_queue_];
auto socket = std::make_unique<BleSocket>(connection);
socket->SetCloseNotifier([socketPeripheralManager = socketPeripheralManager_,
serviceUUID = socketPeripheralServiceManager_.serviceUUID]() {
[socketPeripheralManager
removePeripheralServiceManagerForServiceUUID:serviceUUID
bleServiceRemovedCompletion:^(NSError *_Nullable error) {
GNCLoggerInfo(@"BleSocket is removed peripheral manager.");
}];
});
connection.connectionHandlers = socket->GetInputStream().GetConnectionHandlers();
if (server_socket_ptr) {
@@ -63,7 +63,7 @@ bool StartDiscovery(GNCNWFramework* medium, const std::string& service_type,
serviceFoundHandler:^(NSString* name, NSDictionary<NSString*, NSString*>* txtRecords) {
NsdServiceInfo nsd_service_info;
nsd_service_info.SetServiceType([serviceType UTF8String]);
nsd_service_info.SetServiceName([name UTF8String]);
nsd_service_info.SetServiceName(name ? [name UTF8String] : "");
[txtRecords
enumerateKeysAndObjectsUsingBlock:[nsd_service_info = &nsd_service_info](
NSString* key, NSString* val, BOOL* stop) {
@@ -74,7 +74,7 @@ bool StartDiscovery(GNCNWFramework* medium, const std::string& service_type,
serviceLostHandler:^(NSString* name, NSDictionary<NSString*, NSString*>* txtRecords) {
NsdServiceInfo nsd_service_info;
nsd_service_info.SetServiceType([serviceType UTF8String]);
nsd_service_info.SetServiceName([name UTF8String]);
nsd_service_info.SetServiceName(name ? [name UTF8String] : "");
[txtRecords
enumerateKeysAndObjectsUsingBlock:[nsd_service_info = &nsd_service_info](
NSString* key, NSString* val, BOOL* stop) {
@@ -51,8 +51,12 @@ namespace api {
std::string ImplementationPlatform::GetCustomSavePath(const std::string& parent_folder,
const std::string& file_name) {
// Collapse any path escaping characters.
NSString* parentFolder = [@(parent_folder.c_str()) stringByReplacingOccurrencesOfString:@"../"
withString:@""];
NSString* parentFolderRaw = @(parent_folder.c_str());
if (parentFolderRaw == nil) {
return std::string();
}
NSString* parentFolder = [parentFolderRaw stringByReplacingOccurrencesOfString:@"../"
withString:@""];
NSURL* parentFolderURL = [NSURL fileURLWithPath:parentFolder];
// The only reserved character in a file name on macOS is the forward-slash. It's unclear if iOS
@@ -66,8 +70,12 @@ std::string ImplementationPlatform::GetCustomSavePath(const std::string& parent_
// """
//
// See: https://en.wikipedia.org/wiki/Filename
NSString* fileName = [@(file_name.c_str()) stringByReplacingOccurrencesOfString:@"/"
withString:@":"];
NSString* fileNameRaw = @(file_name.c_str());
if (fileNameRaw == nil) {
return std::string();
}
NSString* fileName = [fileNameRaw stringByReplacingOccurrencesOfString:@"/"
withString:@":"];
NSString* baseName = [fileName stringByDeletingPathExtension];
NSString* extension = [fileName pathExtension];
@@ -86,8 +94,12 @@ std::string ImplementationPlatform::GetCustomSavePath(const std::string& parent_
std::string ImplementationPlatform::GetDownloadPath(const std::string& parent_folder,
const std::string& file_name) {
NSString* parentFolderRaw = @(parent_folder.c_str());
if (parentFolderRaw == nil) {
return std::string();
}
NSString* customSavePath =
[NSTemporaryDirectory() stringByAppendingPathComponent:@(parent_folder.c_str())];
[NSTemporaryDirectory() stringByAppendingPathComponent:parentFolderRaw];
return GetCustomSavePath(customSavePath.UTF8String, file_name);
}
@@ -21,6 +21,7 @@
#include <utility>
#include "absl/time/time.h"
#import "internal/platform/implementation/apple/Log/GNCLogger.h"
#include "internal/platform/runnable.h"
// Defines the state of a scheduled task. This enum is at global scope
@@ -93,7 +94,23 @@ class ExecutorCancelable : public nearby::api::Cancelable {
namespace nearby {
namespace apple {
namespace {
void ExecuteRunnable(Runnable &runnable) {
@try {
try {
runnable();
} catch (const std::exception &e) {
GNCLoggerError(@"Runnable threw C++ exception: %s", e.what());
} catch (...) {
GNCLoggerError(@"Runnable threw unknown C++ exception");
}
} @catch (NSException *e) {
GNCLoggerError(@"Runnable threw ObjC exception: %@: %@", e.name, e.reason);
}
}
} // namespace
ScheduledExecutor::ScheduledExecutor() { impl_ = [GNCOperationQueueImpl implWithMaxConcurrency:1]; }
@@ -106,7 +123,6 @@ ScheduledExecutor::~ScheduledExecutor() {
impl_ = nil;
}
std::shared_ptr<api::Cancelable> ScheduledExecutor::Schedule(Runnable &&runnable,
absl::Duration duration) {
if (impl_.shuttingDown) return std::shared_ptr<api::Cancelable>(nullptr);
@@ -133,7 +149,7 @@ std::shared_ptr<api::Cancelable> ScheduledExecutor::Schedule(Runnable &&runnable
}
GNCScheduledTaskState expected = GNCScheduledTaskState::kScheduled;
if (task->_state.compare_exchange_strong(expected, GNCScheduledTaskState::kRunning)) {
task->_runnable();
ExecuteRunnable(task->_runnable);
task->_state.store(GNCScheduledTaskState::kDone);
}
}];
@@ -152,7 +168,7 @@ bool ScheduledExecutor::DoSubmit(Runnable &&runnable) {
// Submit the runnable to the queue.
__block Runnable local_runnable = std::move(runnable);
[impl_.queue addOperationWithBlock:^{
local_runnable();
ExecuteRunnable(local_runnable);
}];
return true;
}
@@ -19,7 +19,6 @@
#include <optional>
#include <string>
#include "absl/base/attributes.h"
#include "absl/functional/any_invocable.h"
#include "absl/strings/string_view.h"
#include "internal/platform/cancellation_flag.h"
@@ -29,8 +28,7 @@
#include "internal/platform/mac_address.h"
#include "internal/platform/output_stream.h"
namespace nearby {
namespace api {
namespace nearby::api {
// https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html.
class BluetoothDevice {
@@ -213,29 +211,6 @@ class BluetoothClassicMedium {
DefaultCallback<BluetoothDevice&>();
};
class Observer {
public:
virtual ~Observer() = default;
// Called when a new `device` is added to the adapter.
virtual void DeviceAdded(BluetoothDevice& device) {}
// Called when `device` is removed from the adapter.
virtual void DeviceRemoved(BluetoothDevice& device) {}
// Called when the address of `device` changed due to pairing.
virtual void DeviceAddressChanged(BluetoothDevice& device,
absl::string_view old_address) {}
// Called when the paired property of `device` changed.
virtual void DevicePairedChanged(BluetoothDevice& device,
bool new_paired_status) {}
// Called when `device` has connected or disconnected.
virtual void DeviceConnectedStateChanged(BluetoothDevice& device,
bool connected) {}
};
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#startDiscovery()
//
// Returns true once the process of discovery has been initiated.
@@ -273,7 +248,7 @@ class BluetoothClassicMedium {
// UUID.
//
// Returns nullptr error.
virtual std::unique_ptr<BluetoothServerSocket> ListenForService(
virtual std::shared_ptr<BluetoothServerSocket> ListenForService(
const std::string& service_name, const std::string& service_uuid) = 0;
// https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#createBond()
@@ -285,12 +260,8 @@ class BluetoothClassicMedium {
BluetoothDevice& remote_device) = 0;
virtual BluetoothDevice* GetRemoteDevice(MacAddress mac_address) = 0;
virtual void AddObserver(Observer* observer) = 0;
virtual void RemoveObserver(Observer* observer) = 0;
};
} // namespace api
} // namespace nearby
} // namespace nearby::api
#endif // PLATFORM_API_BLUETOOTH_CLASSIC_H_
@@ -16,6 +16,7 @@
#define PLATFORM_API_DEVICE_INFO_H_
#include <cstddef>
#include <cstdint>
#include <functional>
#include <optional>
#include <string>
@@ -39,6 +40,7 @@ class DeviceInfo {
kWindows,
kMacOS
};
enum class SuspendResumeEvent { kSuspend, kResume };
virtual ~DeviceInfo() = default;
@@ -69,6 +71,14 @@ class DeviceInfo {
// Control device sleep
virtual bool PreventSleep() = 0;
virtual bool AllowSleep() = 0;
// Monitor suspend/resume events.
// Returns a listener id that can be used to unregister the listener.
virtual int64_t RegisterSuspendResumeListener(
std::function<void(SuspendResumeEvent)> callback) {
return 0;
}
virtual void UnregisterSuspendResumeListener(int64_t listener_id) {}
};
template <typename Sink>
+19 -4
View File
@@ -59,10 +59,11 @@ cc_library(
"//internal/platform/implementation:types",
"//internal/platform/implementation/shared:count_down_latch",
"//internal/platform/implementation/shared:posix_mutex",
"//internal/test",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/base:nullability",
"@com_google_absl//absl/container:btree",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/functional:any_invocable",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/synchronization",
@@ -151,13 +152,13 @@ cc_test(
"//internal/platform:test_util",
"//internal/platform:uuid",
"//internal/platform/implementation:comm",
"//third_party/gloop/thread/fiber",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/status",
"@com_google_absl//absl/strings:string_view",
"@com_google_absl//absl/synchronization",
"@com_google_absl//absl/time",
"@com_google_googletest//:gtest_main",
"@com_google_nisaba//nisaba/port:thread_pool/fiber",
],
)
@@ -187,13 +188,11 @@ cc_library(
visibility = [
"//connections:__subpackages__",
"//connections:partners",
"//internal/account:__subpackages__",
"//internal/auth:__subpackages__",
"//internal/crypto:__subpackages__",
"//internal/data:__subpackages__",
"//internal/network:__subpackages__",
"//internal/platform:__subpackages__",
"//internal/preferences:__subpackages__",
"//internal/proto/analytics:__subpackages__",
"//internal/weave:__subpackages__",
"//location/nearby/sharing/sdk:__subpackages__",
@@ -217,6 +216,7 @@ cc_library(
"//internal/platform/implementation/shared:file",
"//third_party/gloop/thread",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/base:no_destructor",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
@@ -237,3 +237,18 @@ cc_library(
"@nlohmann_json//:json",
],
)
cc_test(
name = "scheduled_executor_test",
srcs = ["scheduled_executor_test.cc"],
deps = [
":g3",
":types",
"//internal/platform:base",
"//internal/platform:test_util",
"//internal/platform:types",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/time",
"@com_google_googletest//:gtest_main",
],
)
@@ -19,11 +19,11 @@
#include "gtest/gtest.h"
#include "absl/synchronization/notification.h"
#include "absl/time/time.h"
#include "third_party/gloop/thread/fiber/fiber.h"
#include "internal/platform/cancellation_flag.h"
#include "internal/platform/implementation/awdl.h"
#include "internal/platform/medium_environment.h"
#include "internal/platform/nsd_service_info.h"
#include "thread/fiber/fiber.h"
namespace nearby {
namespace g3 {
@@ -240,10 +240,10 @@ std::unique_ptr<api::BluetoothSocket> BluetoothClassicMedium::ConnectToService(
return socket;
}
std::unique_ptr<api::BluetoothServerSocket>
std::shared_ptr<api::BluetoothServerSocket>
BluetoothClassicMedium::ListenForService(const std::string& service_name,
const std::string& service_uuid) {
auto socket = std::make_unique<BluetoothServerSocket>(GetAdapter());
auto socket = std::make_shared<BluetoothServerSocket>(GetAdapter());
socket->SetCloseNotifier([this, uuid = service_uuid]() {
absl::MutexLock lock(mutex_);
sockets_.erase(uuid);
@@ -264,15 +264,5 @@ api::BluetoothDevice* BluetoothClassicMedium::GetRemoteDevice(
return MediumEnvironment::Instance().FindBluetoothDevice(mac_address);
}
void BluetoothClassicMedium::AddObserver(
api::BluetoothClassicMedium::Observer* observer) {
MediumEnvironment::Instance().AddObserver(observer);
}
void BluetoothClassicMedium::RemoveObserver(
api::BluetoothClassicMedium::Observer* observer) {
MediumEnvironment::Instance().RemoveObserver(observer);
}
} // namespace g3
} // namespace nearby
@@ -195,7 +195,7 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium {
// UUID.
//
// Returns nullptr on error.
std::unique_ptr<api::BluetoothServerSocket> ListenForService(
std::shared_ptr<api::BluetoothServerSocket> ListenForService(
const std::string& service_name, const std::string& service_uuid) override
ABSL_LOCKS_EXCLUDED(mutex_);
@@ -206,9 +206,6 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium {
api::BluetoothDevice* GetRemoteDevice(MacAddress mac_address) override;
void AddObserver(Observer* observer) override;
void RemoveObserver(Observer* observer) override;
private:
absl::Mutex mutex_;
BluetoothAdapter* adapter_; // Our device adapter; read-only.
@@ -20,6 +20,7 @@
#include <string>
#include "absl/base/attributes.h"
#include "absl/base/no_destructor.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
@@ -239,7 +240,8 @@ ImplementationPlatform::CreateConditionVariable(Mutex* mutex) {
}
std::unique_ptr<Timer> ImplementationPlatform::CreateTimer() {
return std::make_unique<g3::Timer>();
static absl::NoDestructor<g3::ScheduledExecutor> timer_executor;
return std::make_unique<g3::Timer>(timer_executor.get());
}
std::unique_ptr<nearby::api::DeviceInfo>
@@ -0,0 +1,133 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "internal/platform/scheduled_executor.h"
#include <atomic>
#include "gtest/gtest.h"
#include "absl/time/time.h"
#include "internal/platform/cancelable.h"
#include "internal/platform/count_down_latch.h"
#include "internal/platform/medium_environment.h"
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(200);
// 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, SimulatedClockCanSchedule) {
MediumEnvironment::Instance().Start({.use_simulated_clock = true});
ScheduledExecutor executor;
std::atomic_int value = 0;
CountDownLatch first_task_latch(1);
CountDownLatch second_task_latch(1);
// schedule job due in kLongDelay.
executor.Schedule(
[&]() {
EXPECT_EQ(value, 1);
value = 5;
first_task_latch.CountDown();
},
kLongDelay);
// schedule job due in kShortDelay; must fire before the first one.
executor.Schedule(
[&]() {
EXPECT_EQ(value, 0);
value = 1;
second_task_latch.CountDown();
},
kShortDelay);
EXPECT_EQ(value, 0);
MediumEnvironment::Instance().FastForward(kShortDelay -
absl::Milliseconds(1));
EXPECT_EQ(value, 0);
MediumEnvironment::Instance().FastForward(absl::Milliseconds(1));
second_task_latch.Await();
EXPECT_EQ(value, 1);
MediumEnvironment::Instance().FastForward(kLongDelay - kShortDelay);
first_task_latch.Await();
EXPECT_EQ(value, 5);
// Very long sleep to make sure that the sleep is truly simulated.
MediumEnvironment::Instance().FastForward(absl::Minutes(30));
MediumEnvironment::Instance().Stop();
}
TEST(ScheduledExecutorTest,
DestroyExecutorWithSimulatedClockIgnoresPendingTasks) {
MediumEnvironment::Instance().Start({.use_simulated_clock = true});
{
ScheduledExecutor executor;
executor.Schedule(
[&]() {
// This task should never be executed.
EXPECT_TRUE(false);
},
kShortDelay);
}
MediumEnvironment::Instance().FastForward(absl::Minutes(30));
MediumEnvironment::Instance().Stop();
}
TEST(ScheduledExecutorTest, SimulatedClockCanScheduleRepeatedly) {
MediumEnvironment::Instance().Start({.use_simulated_clock = true});
ScheduledExecutor executor;
std::atomic_int value = 0;
std::atomic_int i = 0;
CountDownLatch latch[] = {CountDownLatch(1), CountDownLatch(1)};
Cancelable cancelable = executor.ScheduleRepeatedly(
[&]() {
value++;
latch[i.fetch_add(1)].CountDown();
},
kShortDelay);
EXPECT_EQ(value, 0);
// Advance to just before the first execution.
MediumEnvironment::Instance().FastForward(kShortDelay -
absl::Milliseconds(1));
EXPECT_EQ(value, 0);
// Advance past the first execution.
MediumEnvironment::Instance().FastForward(absl::Milliseconds(1));
latch[0].Await(absl::Seconds(1));
EXPECT_EQ(value, 1);
// Advance to just before the second execution.
MediumEnvironment::Instance().FastForward(kShortDelay -
absl::Milliseconds(1));
EXPECT_EQ(value, 1);
// Advance past the second execution.
MediumEnvironment::Instance().FastForward(absl::Milliseconds(1));
latch[1].Await(absl::Seconds(1));
EXPECT_EQ(value, 2);
// Cancel the task.
cancelable.Cancel();
// Advance a long time and make sure it doesn't run again.
MediumEnvironment::Instance().FastForward(kLongDelay * 5);
EXPECT_EQ(value, 2);
MediumEnvironment::Instance().Stop();
}
} // namespace nearby
+15 -6
View File
@@ -19,10 +19,13 @@
#include <memory>
#include <utility>
#include "absl/base/nullability.h"
#include "absl/base/thread_annotations.h"
#include "absl/functional/any_invocable.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/time.h"
#include "internal/platform/implementation/g3/scheduled_executor.h"
#include "internal/platform/implementation/cancelable.h"
#include "internal/platform/implementation/scheduled_executor.h"
#include "internal/platform/implementation/timer.h"
namespace nearby {
@@ -30,8 +33,11 @@ namespace g3 {
class Timer : public api::Timer {
public:
Timer() = default;
~Timer() override = default;
explicit Timer(api::ScheduledExecutor* absl_nonnull executor)
: executor_(executor) {};
~Timer() override {
Stop();
};
bool Create(int delay, int interval,
absl::AnyInvocable<void()> callback) override {
@@ -52,13 +58,16 @@ class Timer : public api::Timer {
task_.reset();
return result;
}
return false;
return true;
}
private:
bool Schedule(absl::Duration delay) {
absl::MutexLock lock(mutex_);
task_ = executor_.Schedule([this]() { TriggerCallback(); }, delay);
if (is_stopped_) {
return false;
}
task_ = executor_->Schedule([this]() { TriggerCallback(); }, delay);
return true;
}
@@ -77,7 +86,7 @@ class Timer : public api::Timer {
std::atomic_bool is_stopped_;
absl::Duration interval_;
std::shared_ptr<api::Cancelable> task_ ABSL_GUARDED_BY(mutex_);
ScheduledExecutor executor_;
api::ScheduledExecutor* absl_nonnull const executor_;
};
} // namespace g3
@@ -15,7 +15,6 @@
#ifndef PLATFORM_API_SCHEDULED_EXECUTOR_H_
#define PLATFORM_API_SCHEDULED_EXECUTOR_H_
#include <cstdint>
#include <memory>
#include "absl/time/time.h"
@@ -72,7 +72,13 @@ cc_library(
"timer.h",
"utils.h",
],
defines = ["_SILENCE_CLANG_COROUTINE_MESSAGE"],
defines = [
"_SILENCE_CLANG_COROUTINE_MESSAGE",
"_WIN32_WINNT=_WIN32_WINNT_WIN10",
],
linkopts = [
"powrprof.lib",
],
tags = ["windows"],
visibility = ["//visibility:private"],
deps = [
@@ -32,6 +32,7 @@
#include "internal/platform/implementation/bluetooth_classic.h"
#include "internal/platform/implementation/windows/bluetooth_adapter.h"
#include "internal/platform/implementation/windows/bluetooth_classic_device.h"
#include "internal/platform/implementation/windows/bluetooth_classic_server_socket.h"
#include "internal/platform/implementation/windows/bluetooth_classic_socket.h"
#include "internal/platform/implementation/windows/bluetooth_pairing.h"
#include "internal/platform/implementation/windows/generated/winrt/Windows.Devices.Bluetooth.Rfcomm.h"
@@ -43,8 +44,7 @@
#include "internal/platform/logging.h"
#include "internal/platform/mac_address.h"
namespace nearby {
namespace windows {
namespace nearby::windows {
namespace {
using ::winrt::Windows::Devices::Bluetooth::Rfcomm::RfcommDeviceService;
using ::winrt::Windows::Devices::Bluetooth::Rfcomm::RfcommServiceId;
@@ -124,7 +124,13 @@ BluetoothClassicMedium::BluetoothClassicMedium(
&BluetoothClassicMedium::OnScanModeChanged, this, std::placeholders::_1));
}
BluetoothClassicMedium::~BluetoothClassicMedium() {}
BluetoothClassicMedium::~BluetoothClassicMedium() {
// Clear the close notifier to prevent UAF if the server_socket_ outlives
// the BluetoothClassicMedium.
if (raw_server_socket_ != nullptr) {
raw_server_socket_->SetCloseNotifier(nullptr);
}
}
bool BluetoothClassicMedium::StartDiscovery(
BluetoothClassicMedium::DiscoveryCallback discovery_callback) {
@@ -259,7 +265,7 @@ std::unique_ptr<api::BluetoothSocket> BluetoothClassicMedium::ConnectToService(
// UUID.
//
// Returns nullptr error.
std::unique_ptr<api::BluetoothServerSocket>
std::shared_ptr<api::BluetoothServerSocket>
BluetoothClassicMedium::ListenForService(const std::string& service_name,
const std::string& service_uuid) {
VLOG(1) << "ListenForService is called with service name: " << service_name
@@ -283,14 +289,24 @@ BluetoothClassicMedium::ListenForService(const std::string& service_name,
bool radio_discoverable =
scan_mode_ == BluetoothAdapter::ScanMode::kConnectableDiscoverable;
bool result = StartAdvertising(radio_discoverable);
if (rfcomm_provider_ != nullptr &&
is_radio_discoverable_ == radio_discoverable) {
LOG(WARNING) << __func__
<< ": Ignore StartAdvertising due to no change to "
"current advertising.";
return server_socket_;
}
if (!result) {
auto server_socket = StartAdvertising(radio_discoverable);
if (!server_socket) {
LOG(ERROR) << __func__ << ": Failed to start listening.";
return nullptr;
}
return std::move(server_socket_);
raw_server_socket_ = server_socket.get();
server_socket_ = std::move(server_socket);
return server_socket_;
}
api::BluetoothDevice* BluetoothClassicMedium::GetRemoteDevice(
@@ -685,9 +701,6 @@ winrt::fire_and_forget BluetoothClassicMedium::DeviceWatcher_Added(
if (discovery_callback_.device_discovered_cb != nullptr) {
discovery_callback_.device_discovered_cb(*device);
}
for (auto& observer : observers_.GetObservers()) {
observer->DeviceAdded(*device);
}
return winrt::fire_and_forget();
}
@@ -753,9 +766,6 @@ winrt::fire_and_forget BluetoothClassicMedium::DeviceWatcher_Updated(
LOG(INFO) << __func__
<< ": Notifying device paired changed: " << std::boolalpha
<< new_paired_status;
for (auto& observer : observers_.GetObservers()) {
observer->DevicePairedChanged(*device, new_paired_status);
}
}
return winrt::fire_and_forget();
@@ -803,10 +813,6 @@ winrt::fire_and_forget BluetoothClassicMedium::DeviceWatcher_Removed(
discovery_callback_.device_lost_cb(*device);
}
for (auto& observer : observers_.GetObservers()) {
observer->DeviceRemoved(*device);
}
RemoveRemoteDevice(mac_address);
return winrt::fire_and_forget();
@@ -833,25 +839,19 @@ bool BluetoothClassicMedium::IsWatcherRunning() {
(status == DeviceWatcherStatus::Stopping);
}
bool BluetoothClassicMedium::StartAdvertising(bool radio_discoverable) {
std::shared_ptr<BluetoothServerSocket>
BluetoothClassicMedium::StartAdvertising(bool radio_discoverable) {
LOG(INFO) << __func__
<< ": StartAdvertising is called with radio_discoverable: "
<< radio_discoverable << ".";
std::shared_ptr<BluetoothServerSocket> server_socket;
try {
if (rfcomm_provider_ != nullptr &&
is_radio_discoverable_ == radio_discoverable) {
LOG(WARNING) << __func__
<< ": Ignore StartAdvertising due to no change to "
"current advertising.";
return true;
}
if (rfcomm_provider_ != nullptr && !StopAdvertising()) {
LOG(WARNING) << __func__
<< ": Failed to StartAdvertising due to cannot stop "
"running advertising.";
return false;
return nullptr;
}
rfcomm_provider_ =
@@ -859,79 +859,57 @@ bool BluetoothClassicMedium::StartAdvertising(bool radio_discoverable) {
RfcommServiceId::FromUuid(winrt::guid(service_uuid_)))
.get();
server_socket_ = std::make_unique<BluetoothServerSocket>(
server_socket = BluetoothServerSocket::Create(
winrt::to_string(rfcomm_provider_.ServiceId().AsString()));
raw_server_socket_ = server_socket_.get();
if (!server_socket_->listen()) {
if (!server_socket->listen()) {
LOG(ERROR) << __func__
<< ": Failed to StartAdvertising due to cannot start socket.";
server_socket_->Close();
server_socket_ = nullptr;
rfcomm_provider_ = nullptr;
return false;
return nullptr;
}
server_socket_->SetCloseNotifier([&]() { StopAdvertising(); });
server_socket->SetCloseNotifier([&]() { StopAdvertising(); });
// Set the SDP attributes and start Bluetooth advertising
InitializeServiceSdpAttributes(rfcomm_provider_, service_name_);
// Start to advertising.
rfcomm_provider_.StartAdvertising(server_socket_->stream_socket_listener(),
rfcomm_provider_.StartAdvertising(server_socket->stream_socket_listener(),
radio_discoverable);
is_radio_discoverable_ = radio_discoverable;
LOG(INFO) << ": StartListening completed successfully.";
return true;
return server_socket;
} catch (std::exception exception) {
// We will log and eat the exception since the caller
// expects nullptr if it fails
LOG(ERROR) << __func__
<< ": Exception setting up for listen: " << exception.what();
if (server_socket_ != nullptr) {
server_socket_->Close();
server_socket_ = nullptr;
}
if (rfcomm_provider_ != nullptr) {
rfcomm_provider_ = nullptr;
}
return false;
return nullptr;
} catch (const winrt::hresult_error& ex) {
LOG(ERROR) << __func__ << ": Exception setting up for listen: " << ex.code()
<< ": " << winrt::to_string(ex.message());
if (server_socket_ != nullptr) {
server_socket_->Close();
server_socket_ = nullptr;
}
if (rfcomm_provider_ != nullptr) {
rfcomm_provider_ = nullptr;
}
return false;
return nullptr;
} catch (...) {
LOG(ERROR) << __func__ << ": Unknown exception.";
if (server_socket_ != nullptr) {
server_socket_->Close();
server_socket_ = nullptr;
}
if (rfcomm_provider_ != nullptr) {
rfcomm_provider_ = nullptr;
}
return false;
return nullptr;
}
}
bool BluetoothClassicMedium::StopAdvertising() {
VLOG(1) << __func__ << ": StopAdvertising is called";
bool result = false;
try {
if (rfcomm_provider_ == nullptr) {
LOG(ERROR) << __func__
@@ -940,12 +918,9 @@ bool BluetoothClassicMedium::StopAdvertising() {
}
rfcomm_provider_.StopAdvertising();
rfcomm_provider_ = nullptr;
raw_server_socket_ = nullptr;
server_socket_ = nullptr;
LOG(INFO) << ": StopAdvertising completed successfully.";
return true;
result = true;
} catch (std::exception exception) {
LOG(ERROR) << __func__
<< ": StopAdvertising exception: " << exception.what();
@@ -957,9 +932,9 @@ bool BluetoothClassicMedium::StopAdvertising() {
}
rfcomm_provider_ = nullptr;
raw_server_socket_ = nullptr;
server_socket_ = nullptr;
return false;
raw_server_socket_ = nullptr;
return result;
}
bool BluetoothClassicMedium::InitializeServiceSdpAttributes(
@@ -988,5 +963,4 @@ bool BluetoothClassicMedium::InitializeServiceSdpAttributes(
}
}
} // namespace windows
} // namespace nearby
} // namespace nearby::windows
@@ -21,7 +21,6 @@
#include "absl/base/thread_annotations.h"
#include "absl/container/flat_hash_map.h"
#include "absl/synchronization/mutex.h"
#include "internal/base/observer_list.h"
#include "internal/platform/cancellation_flag.h"
#include "internal/platform/implementation/bluetooth_adapter.h"
#include "internal/platform/implementation/bluetooth_classic.h"
@@ -34,8 +33,7 @@
#include "internal/platform/implementation/windows/generated/winrt/base.h"
#include "internal/platform/mac_address.h"
namespace nearby {
namespace windows {
namespace nearby::windows {
// Container of operations that can be performed over the Bluetooth Classic
// medium.
@@ -80,7 +78,7 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium {
// UUID.
//
// Returns nullptr error.
std::unique_ptr<api::BluetoothServerSocket> ListenForService(
std::shared_ptr<api::BluetoothServerSocket> ListenForService(
const std::string& service_name,
const std::string& service_uuid) override;
@@ -91,19 +89,11 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium {
std::unique_ptr<api::BluetoothPairing> CreatePairing(
api::BluetoothDevice& remote_device) override;
void AddObserver(Observer* observer) override {
observers_.AddObserver(observer);
}
// Removes an observer. It's OK to remove an unregistered observer.
void RemoveObserver(Observer* observer) override {
observers_.RemoveObserver(observer);
}
private:
bool StartScanning();
bool StopScanning();
bool StartAdvertising(bool radio_discoverable);
std::shared_ptr<BluetoothServerSocket> StartAdvertising(
bool radio_discoverable);
bool StopAdvertising();
bool InitializeServiceSdpAttributes(
::winrt::Windows::Devices::Bluetooth::Rfcomm::RfcommServiceProvider
@@ -185,13 +175,14 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium {
// Used for advertising.
::winrt::Windows::Devices::Bluetooth::Rfcomm::RfcommServiceProvider
rfcomm_provider_ = nullptr;
std::unique_ptr<BluetoothServerSocket> server_socket_ = nullptr;
std::shared_ptr<api::BluetoothServerSocket> server_socket_;
// Raw pointer to the BluetoothServerSocket impl class that is held by the
// shared_ptr server_socket_. The lifetime of this pointer is guaranteed by
// the shared_ptr.
BluetoothServerSocket* raw_server_socket_ = nullptr;
bool is_radio_discoverable_ = false;
ObserverList<Observer> observers_;
};
} // namespace windows
} // namespace nearby
} // namespace nearby::windows
#endif // PLATFORM_IMPL_WINDOWS_BLUETOOTH_CLASSIC_MEDIUM_H_
@@ -26,10 +26,10 @@
#include "internal/platform/exception.h"
#include "internal/platform/implementation/bluetooth_classic.h"
#include "internal/platform/implementation/windows/bluetooth_classic_socket.h"
#include "internal/platform/implementation/windows/generated/winrt/Windows.Networking.Sockets.h"
#include "internal/platform/logging.h"
namespace nearby {
namespace windows {
namespace nearby::windows {
namespace {
using ::winrt::Windows::Networking::Sockets::SocketProtectionLevel;
using ::winrt::Windows::Networking::Sockets::SocketQualityOfService;
@@ -132,7 +132,8 @@ bool BluetoothServerSocket::listen() {
// Setup socket event of ConnectionReceived.
listener_event_token_ = stream_socket_listener_.ConnectionReceived(
{this, &BluetoothServerSocket::Listener_ConnectionReceived});
{shared_from_this(),
&BluetoothServerSocket::Listener_ConnectionReceived});
stream_socket_listener_
.BindServiceNameAsync(winrt::to_hstring(service_name_),
@@ -167,5 +168,4 @@ bool BluetoothServerSocket::listen() {
return ::winrt::fire_and_forget{};
}
} // namespace windows
} // namespace nearby
} // namespace nearby::windows
@@ -15,25 +15,30 @@
#ifndef PLATFORM_IMPL_WINDOWS_BLUETOOTH_CLASSIC_SERVER_SOCKET_H_
#define PLATFORM_IMPL_WINDOWS_BLUETOOTH_CLASSIC_SERVER_SOCKET_H_
#include <Windows.h>
#include <windows.h>
#include <memory>
#include <queue>
#include <string>
#include "absl/functional/any_invocable.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
#include "internal/platform/exception.h"
#include "internal/platform/implementation/bluetooth_classic.h"
#include "internal/platform/implementation/windows/bluetooth_classic_socket.h"
#include "internal/platform/implementation/windows/generated/winrt/Windows.Networking.Sockets.h"
#include "internal/platform/implementation/windows/generated/winrt/base.h"
namespace nearby {
namespace windows {
namespace nearby::windows {
class BluetoothServerSocket : public api::BluetoothServerSocket {
class BluetoothServerSocket
: public api::BluetoothServerSocket,
public std::enable_shared_from_this<BluetoothServerSocket> {
public:
explicit BluetoothServerSocket(absl::string_view service_name);
static std::shared_ptr<BluetoothServerSocket> Create(
absl::string_view service_name) {
return std::shared_ptr<BluetoothServerSocket>(
new BluetoothServerSocket(service_name));
}
~BluetoothServerSocket() override;
@@ -65,6 +70,9 @@ class BluetoothServerSocket : public api::BluetoothServerSocket {
}
private:
// BluetoothServerSocket must be created as a shared_ptr.
explicit BluetoothServerSocket(absl::string_view service_name);
// The listener is accepting incoming connections
::winrt::fire_and_forget Listener_ConnectionReceived(
::winrt::Windows::Networking::Sockets::StreamSocketListener listener,
@@ -93,7 +101,6 @@ class BluetoothServerSocket : public api::BluetoothServerSocket {
bool closed_ = false;
};
} // namespace windows
} // namespace nearby
} // namespace nearby::windows
#endif // PLATFORM_IMPL_WINDOWS_BLUETOOTH_CLASSIC_SERVER_SOCKET_H_
@@ -14,13 +14,19 @@
#include "internal/platform/implementation/windows/device_info.h"
// clang-format off
#include <shlobj_core.h>
#include <windows.h>
#include <wtsapi32.h>
#include <powrprof.h>
#include <powerbase.h>
// clang-format on
#include <cstdint>
#include <functional>
#include <optional>
#include <string>
#include <utility>
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
@@ -30,10 +36,19 @@
#include "internal/platform/implementation/windows/device_paths.h"
#include "internal/platform/implementation/windows/string_utils.h"
#include "internal/platform/implementation/windows/utils.h"
#include "internal/platform/logging.h"
namespace nearby::windows {
namespace {
using ::nearby::windows::string_utils::WideStringToString;
} // namespace
DeviceInfo::~DeviceInfo() {
if (suspend_resume_notification_handle_ != nullptr) {
PowerUnregisterSuspendResumeNotification(
suspend_resume_notification_handle_);
}
}
std::optional<std::string> DeviceInfo::GetOsDeviceName() const {
std::optional<std::wstring> device_name = GetDnsHostName();
@@ -113,4 +128,61 @@ bool DeviceInfo::AllowSleep() {
return session_manager_.AllowSleep();
}
ULONG DeviceInfo::PowerSuspendResumeCallback(PVOID context, ULONG type,
PVOID setting) {
api::DeviceInfo::SuspendResumeEvent event;
switch (type) {
case PBT_APMSUSPEND:
event = api::DeviceInfo::SuspendResumeEvent::kSuspend;
break;
case PBT_APMRESUMESUSPEND:
event = api::DeviceInfo::SuspendResumeEvent::kResume;
break;
default:
return 0;
}
DeviceInfo* device_info = static_cast<DeviceInfo*>(context);
device_info->OnSuspendResumeEvent(event);
return 0;
}
int64_t DeviceInfo::RegisterSuspendResumeListener(
std::function<void(api::DeviceInfo::SuspendResumeEvent)> callback) {
absl::MutexLock lock(suspend_resume_mutex_);
int64_t listener_id = ++next_suspend_resume_listener_id_;
suspend_resume_listeners_.emplace(listener_id, std::move(callback));
if (suspend_resume_listeners_.size() == 1) {
DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS subscribe_params;
subscribe_params.Callback = PowerSuspendResumeCallback;
subscribe_params.Context = this;
PowerRegisterSuspendResumeNotification(
DEVICE_NOTIFY_CALLBACK, &subscribe_params,
&suspend_resume_notification_handle_);
}
return listener_id;
}
void DeviceInfo::UnregisterSuspendResumeListener(int64_t listener_id) {
absl::MutexLock lock(suspend_resume_mutex_);
suspend_resume_listeners_.erase(listener_id);
if (suspend_resume_listeners_.empty()) {
if (suspend_resume_notification_handle_ != nullptr) {
PowerUnregisterSuspendResumeNotification(
suspend_resume_notification_handle_);
}
suspend_resume_notification_handle_ = nullptr;
}
}
void DeviceInfo::OnSuspendResumeEvent(
api::DeviceInfo::SuspendResumeEvent event) {
LOG(INFO) << "OnSuspendResumeEvent: "
<< (event == DeviceInfo::SuspendResumeEvent::kSuspend ? "kSuspend"
: "kResume");
absl::MutexLock lock(suspend_resume_mutex_);
for (auto& it : suspend_resume_listeners_) {
it.second(event);
}
}
} // namespace nearby::windows
@@ -15,11 +15,19 @@
#ifndef PLATFORM_IMPL_WINDOWS_DEVICE_INFO_H_
#define PLATFORM_IMPL_WINDOWS_DEVICE_INFO_H_
// clang-format off
#include <windows.h>
#include <powerbase.h>
// clang-format on
#include <cstdint>
#include <functional>
#include <optional>
#include <string>
#include "absl/base/thread_annotations.h"
#include "absl/container/flat_hash_map.h"
#include "absl/functional/any_invocable.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
#include "internal/base/file_path.h"
@@ -31,7 +39,7 @@ namespace windows {
class DeviceInfo : public api::DeviceInfo {
public:
~DeviceInfo() override = default;
~DeviceInfo() override;
std::optional<std::string> GetOsDeviceName() const override;
api::DeviceInfo::DeviceType GetDeviceType() const override;
@@ -51,9 +59,26 @@ class DeviceInfo : public api::DeviceInfo {
bool PreventSleep() override;
bool AllowSleep() override;
int64_t RegisterSuspendResumeListener(
std::function<void(api::DeviceInfo::SuspendResumeEvent)> callback)
override;
void UnregisterSuspendResumeListener(int64_t listener_id) override;
private:
static ULONG PowerSuspendResumeCallback(PVOID context, ULONG type,
PVOID setting);
void OnSuspendResumeEvent(SuspendResumeEvent event);
mutable absl::Mutex mutex_;
SessionManager session_manager_ ABSL_GUARDED_BY(mutex_);
absl::Mutex suspend_resume_mutex_;
int64_t next_suspend_resume_listener_id_ ABSL_GUARDED_BY(
suspend_resume_mutex_) = 0;
absl::flat_hash_map<
int64_t, absl::AnyInvocable<void(SuspendResumeEvent)>>
suspend_resume_listeners_ ABSL_GUARDED_BY(suspend_resume_mutex_);
HPOWERNOTIFY suspend_resume_notification_handle_
ABSL_GUARDED_BY(suspend_resume_mutex_) = nullptr;
};
} // namespace windows
@@ -19,6 +19,7 @@
#include "absl/synchronization/mutex.h"
#include "internal/platform/implementation/mutex.h"
#include "internal/platform/condition_variable.h"
namespace nearby {
namespace windows {
@@ -239,7 +239,6 @@ class WifiDirectMedium : public api::WifiDirectMedium {
std::unique_ptr<api::WifiDirectServerSocket> ListenForService(
int port) override;
// Advertiser start WiFiDirect GO with specific Credentials.
bool StartWifiDirect(WifiDirectCredentials* wifi_direct_credentials) override;
// Advertiser stop the current WiFiDirect GO.
bool StopWifiDirect() override;
@@ -347,6 +346,7 @@ class WifiDirectMedium : public api::WifiDirectMedium {
std::string ip_address_local_;
std::string ip_address_remote_;
absl::CondVar is_ip_address_ready_;
std::string remote_device_name_;
WifiDirectServerSocket* server_socket_ptr_ ABSL_GUARDED_BY(mutex_) = nullptr;
SubmittableExecutor listener_executor_;
@@ -251,8 +251,10 @@ std::unique_ptr<api::WifiDirectServerSocket> WifiDirectMedium::ListenForService(
bool WifiDirectMedium::StartWifiDirect(
WifiDirectCredentials* wifi_direct_credentials) {
remote_device_name_ = wifi_direct_credentials->GetRemoteDeviceName();
LOG(INFO) << __func__ << ": remote_device_name from credentials: "
<< remote_device_name_;
absl::MutexLock lock(mutex_);
LOG(INFO) << __func__ << ": Start to create WiFiDirect.";
if (IsBeaconing()) {
LOG(WARNING) << "Cannot create WiFiDirect GO again when it is running.";
return true;
@@ -431,13 +433,19 @@ fire_and_forget WifiDirectMedium::OnConnectionRequested(
LOG(INFO) << "Receive connection request from: "
<< winrt::to_string(device_name)
<< "; device ID: " << winrt::to_string(device_id);
if (!remote_device_name_.empty() &&
!absl::EqualsIgnoreCase(remote_device_name_,
winrt::to_string(device_name))) {
LOG(INFO) << "Ignore the connection request from the unrelated device.";
return winrt::fire_and_forget();
}
DeviceInformation windows_device_info(connection_request.DeviceInformation());
auto deviceInfoP =
std::make_unique<WifiDirectDeviceDiscovered>(windows_device_info);
{
absl::MutexLock lock(&mutex_);
absl::MutexLock lock(mutex_);
connection_requested_devices_by_id_[device_id] = std::move(deviceInfoP);
}
@@ -500,7 +508,7 @@ fire_and_forget WifiDirectMedium::OnConnectionRequested(
std::string remote_ip =
winrt::to_string(pair.RemoteHostName().DisplayName());
absl::MutexLock lock(&mutex_);
absl::MutexLock lock(mutex_);
wifi_direct_device_ = device;
ip_address_local_ = local_ip;
ip_address_remote_ = remote_ip;
@@ -749,7 +757,7 @@ fire_and_forget WifiDirectMedium::Watcher_DeviceAdded(
<< "; device name: " << winrt::to_string(device_info.Name());
winrt::hstring device_id = device_info.Id();
{
absl::MutexLock lock(&mutex_);
absl::MutexLock lock(mutex_);
if (discovered_devices_by_id_.contains(device_id)) {
return winrt::fire_and_forget();
}
@@ -800,7 +808,7 @@ fire_and_forget WifiDirectMedium::Watcher_DeviceAdded(
// Create a WiFiDirectDevice out of this id
if (!is_paired) {
LOG(INFO) << "GC paired failed!";
absl::MutexLock lock(&mutex_);
absl::MutexLock lock(mutex_);
if (connection_latch_) {
connection_latch_->CountDown();
}
@@ -14,7 +14,6 @@
#include <windows.h>
#include <exception>
#include <memory>
#include <string>
#include <utility>
@@ -52,7 +51,7 @@ void WifiDirectServerSocket::SetIPAddress(std::string ip_address) {
std::unique_ptr<api::WifiDirectSocket> WifiDirectServerSocket::Accept() {
{
absl::MutexLock lock(&mutex_);
absl::MutexLock lock(mutex_);
if (closed_) return nullptr;
if (server_socket_accepted_connection_) {
LOG(INFO) << "Server socket has already accepted a connection. Return.";
@@ -74,7 +73,7 @@ std::unique_ptr<api::WifiDirectSocket> WifiDirectServerSocket::Accept() {
LOG(INFO) << "Start to accept connection from WiFiDirect client.";
auto client_socket = server_socket_.Accept();
absl::MutexLock lock(&mutex_);
absl::MutexLock lock(mutex_);
if (closed_ || client_socket == nullptr) {
LOG(INFO) << "Accept server socket failed or closed.";
return nullptr;
-31
View File
@@ -192,9 +192,6 @@ void MediumEnvironment::OnBluetoothDeviceStateChanged(
if (enable_notifications_) {
VLOG(1) << "Notify about new discovered device";
info.callback.device_discovered_cb(device);
for (auto& observer : observers_.GetObservers()) {
observer->DeviceAdded(device);
}
}
}
} else {
@@ -215,9 +212,6 @@ void MediumEnvironment::OnBluetoothDeviceStateChanged(
if (enable_notifications_) {
VLOG(1) << "Notify about existing discovered device";
info.callback.device_discovered_cb(device);
for (auto& observer : observers_.GetObservers()) {
observer->DeviceAdded(device);
}
}
}
}
@@ -227,9 +221,6 @@ void MediumEnvironment::OnBluetoothDeviceStateChanged(
if (enable_notifications_) {
VLOG(1) << "Notify about removed device";
info.callback.device_lost_cb(device);
for (auto& observer : observers_.GetObservers()) {
observer->DeviceRemoved(device);
}
}
info.devices.erase(item);
}
@@ -1291,11 +1282,6 @@ bool MediumEnvironment::SetPairingState(api::BluetoothDevice* device,
latch.CountDown();
});
latch.Await();
if (enable_notifications_) {
for (auto& observer : observers_.GetObservers()) {
observer->DevicePairedChanged(*device, true);
}
}
return updated;
}
@@ -1359,11 +1345,6 @@ bool MediumEnvironment::FinishPairing(api::BluetoothDevice* device) {
pairing_context->pairing_error.value());
} else {
pairing_context->is_paired = true;
if (enable_notifications_) {
for (auto& observer : observers_.GetObservers()) {
observer->DevicePairedChanged(*device, true);
}
}
pairing_context->pairing_callback.on_paired_cb();
}
return finshed;
@@ -1409,18 +1390,6 @@ void MediumEnvironment::ClearBluetoothDevicesForPairing() {
RunOnMediumEnvironmentThread([&]() { devices_pairing_contexts_.clear(); });
}
void MediumEnvironment::AddObserver(
api::BluetoothClassicMedium::Observer* observer) {
if (!enabled_) return;
observers_.AddObserver(observer);
}
void MediumEnvironment::RemoveObserver(
api::BluetoothClassicMedium::Observer* observer) {
if (!enabled_) return;
observers_.RemoveObserver(observer);
}
void MediumEnvironment::SetBleExtendedAdvertisementsAvailable(bool enabled) {
ble_extended_advertisements_available_ = enabled;
}
-5
View File
@@ -27,7 +27,6 @@
#include "absl/container/flat_hash_map.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "internal/base/observer_list.h"
#include "internal/platform/borrowable.h"
#include "internal/platform/implementation/awdl.h"
#include "internal/platform/implementation/ble.h"
@@ -377,9 +376,6 @@ class MediumEnvironment {
// Clears the map `devices_pairing_contexts_`.
void ClearBluetoothDevicesForPairing();
void AddObserver(api::BluetoothClassicMedium::Observer* observer);
void RemoveObserver(api::BluetoothClassicMedium::Observer* observer);
// Sets the availability of BLE extended advertisements. It is false by
// default.
void SetBleExtendedAdvertisementsAvailable(bool enabled);
@@ -514,7 +510,6 @@ class MediumEnvironment {
bool use_valid_peer_connection_ = true;
absl::Duration peer_connection_latency_ = absl::ZeroDuration();
std::shared_ptr<FakeClock> simulated_clock_ ABSL_GUARDED_BY(mutex_);
ObserverList<api::BluetoothClassicMedium::Observer> observers_;
bool ble_extended_advertisements_available_ = false;
};
+12 -4
View File
@@ -144,15 +144,23 @@ class ABSL_LOCKABLE ScheduledExecutor final : public Lockable {
return;
}
// Re-schedule the next execution before running the task to avoid clock
// slip as much as possible.
// Ideally the next event should be scheduled at
// (last scheduled time + delay). So that if the task takes too long to
// finish, we can still catch up the schedule.
// However, that would require changing the scheduler to allow scheduling
// an event at a specific time.
{
MutexLock lock(&executor_->mutex_);
ScheduleNextUnderLock();
}
(*cancellable_task_)();
if (cancelled_.Get()) {
return;
}
// Re-schedule the next execution.
MutexLock lock(&executor_->mutex_);
ScheduleNextUnderLock();
}
void ScheduleNextUnderLock()
@@ -24,7 +24,6 @@
#include "absl/time/time.h"
#include "internal/platform/cancelable.h"
#include "internal/platform/count_down_latch.h"
#include "internal/platform/medium_environment.h"
namespace nearby {
@@ -209,59 +208,6 @@ TEST(ScheduledExecutorTest, ExecuteDuringShutdownFails) {
executor.Shutdown();
}
TEST(ScheduledExecutorTest, SimulatedClockCanSchedule) {
MediumEnvironment::Instance().Start({.use_simulated_clock = true});
ScheduledExecutor executor;
std::atomic_int value = 0;
CountDownLatch first_task_latch(1);
CountDownLatch second_task_latch(1);
// schedule job due in kLongDelay.
executor.Schedule(
[&]() {
EXPECT_EQ(value, 1);
value = 5;
first_task_latch.CountDown();
},
kLongDelay);
// schedule job due in kShortDelay; must fire before the first one.
executor.Schedule(
[&]() {
EXPECT_EQ(value, 0);
value = 1;
second_task_latch.CountDown();
},
kShortDelay);
EXPECT_EQ(value, 0);
MediumEnvironment::Instance().FastForward(kShortDelay -
absl::Milliseconds(1));
EXPECT_EQ(value, 0);
MediumEnvironment::Instance().FastForward(absl::Milliseconds(1));
second_task_latch.Await();
EXPECT_EQ(value, 1);
MediumEnvironment::Instance().FastForward(kLongDelay - kShortDelay);
first_task_latch.Await();
EXPECT_EQ(value, 5);
// Very long sleep to make sure that the sleep is truly simulated.
MediumEnvironment::Instance().FastForward(absl::Minutes(30));
MediumEnvironment::Instance().Stop();
}
TEST(ScheduledExecutorTest,
DestroyExecutorWithSimulatedClockIgnoresPendingTasks) {
MediumEnvironment::Instance().Start({.use_simulated_clock = true});
{
ScheduledExecutor executor;
executor.Schedule(
[&]() {
// This task should never be executed.
EXPECT_TRUE(false);
},
kShortDelay);
}
MediumEnvironment::Instance().FastForward(absl::Minutes(30));
MediumEnvironment::Instance().Stop();
}
struct ScheduledThreadCheckTestClass {
ScheduledExecutor executor;
int value ABSL_GUARDED_BY(executor) = 0;
@@ -397,52 +343,4 @@ TEST(ScheduledExecutorTest, CanCancelOneOfTwoRepeatedTasks) {
cancelableB.Cancel();
}
TEST(ScheduledExecutorTest, SimulatedClockCanScheduleRepeatedly) {
MediumEnvironment::Instance().Start({.use_simulated_clock = true});
ScheduledExecutor executor;
std::atomic_int value = 0;
std::atomic_int i = 0;
CountDownLatch latch[] = {CountDownLatch(1), CountDownLatch(1)};
Cancelable cancelable = executor.ScheduleRepeatedly(
[&]() {
value++;
latch[i.fetch_add(1)].CountDown();
},
kShortDelay);
EXPECT_EQ(value, 0);
// Advance to just before the first execution.
MediumEnvironment::Instance().FastForward(kShortDelay -
absl::Milliseconds(1));
EXPECT_EQ(value, 0);
// Advance past the first execution.
MediumEnvironment::Instance().FastForward(absl::Milliseconds(1));
latch[0].Await(absl::Seconds(1));
EXPECT_EQ(value, 1);
// Wait for the second execution to schedule.
absl::SleepFor(kShortDelay);
// Advance to just before the second execution.
MediumEnvironment::Instance().FastForward(kShortDelay -
absl::Milliseconds(1));
EXPECT_EQ(value, 1);
// Advance past the second execution.
MediumEnvironment::Instance().FastForward(absl::Milliseconds(1));
latch[1].Await(absl::Seconds(1));
EXPECT_EQ(value, 2);
// Cancel the task.
cancelable.Cancel();
// Advance a long time and make sure it doesn't run again.
MediumEnvironment::Instance().FastForward(kLongDelay * 5);
EXPECT_EQ(value, 2);
MediumEnvironment::Instance().Stop();
}
} // namespace nearby
+35 -2
View File
@@ -14,11 +14,17 @@
#include "internal/platform/service_address.h"
#include <cstdint>
#include <string>
#include "absl/strings/string_view.h"
#include "connections/implementation/proto/offline_wire_formats.pb.h"
namespace nearby {
namespace {
constexpr absl::string_view kIpv6LoopbackAddress(
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1", 16);
} // namespace
void ServiceAddressToProto(
const ServiceAddress& service_address,
@@ -31,9 +37,9 @@ void ServiceAddressToProto(
bool ServiceAddressFromProto(
const location::nearby::connections::ServiceAddress& proto,
ServiceAddress& service_address) {
// Address must be either 4 or 16 bytes and port must be set.
// Address must be either 4 or 16 bytes and port must be valid (1 to 65535).
if ((proto.ip_address().size() != 16 && proto.ip_address().size() != 4) ||
proto.port() == 0) {
proto.port() <= 0 || proto.port() > 65535) {
return false;
}
service_address.address = {proto.ip_address().begin(),
@@ -42,4 +48,31 @@ bool ServiceAddressFromProto(
return true;
}
bool ServiceAddress::IsLoopbackAddress() const {
if (address.size() == 4) {
// IPv4 loopback: 127.0.0.0/8
return address[0] == 127;
} else if (address.size() == 16) {
// IPv6 loopback: ::1
return absl::string_view(address.data(), address.size()) ==
kIpv6LoopbackAddress;
}
return false;
}
bool ServiceAddress::IsLinkLocalAddress() const {
if (address.size() == 4) {
// IPv4 link-local: 169.254.0.0/16
uint8_t b0 = static_cast<uint8_t>(address[0]);
uint8_t b1 = static_cast<uint8_t>(address[1]);
return b0 == 169 && b1 == 254;
} else if (address.size() == 16) {
// IPv6 link-local: fe80::/10
uint8_t b0 = static_cast<uint8_t>(address[0]);
uint8_t b1 = static_cast<uint8_t>(address[1]);
return b0 == 0xfe && (b1 & 0xc0) == 0x80;
}
return false;
}
} // namespace nearby
+8 -1
View File
@@ -16,7 +16,6 @@
#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_SERVICE_ADDRESS_H_
#include <cstdint>
#include <ostream>
#include <string>
#include <vector>
@@ -33,6 +32,14 @@ struct ServiceAddress {
uint16_t port;
bool operator==(const ServiceAddress& other) const = default;
// Returns true if the address is a loopback address (IPv4 127.0.0.0/8 or
// IPv6 ::1).
bool IsLoopbackAddress() const;
// Returns true if the address is a link-local address (IPv4 169.254.0.0/16 or
// IPv6 fe80::/10).
bool IsLinkLocalAddress() const;
};
// Support logging of ServiceAddress.
+110 -1
View File
@@ -81,8 +81,18 @@ TEST(ServiceAddressTest, ServiceAddressFromProtoInvalidAddress) {
TEST(ServiceAddressTest, ServiceAddressFromProtoInvalidPort) {
ProtoServiceAddress proto;
proto.set_ip_address(std::string("\x7f\0\0\1", 4));
proto.set_port(0);
ServiceAddress service_address;
// Port 0
proto.set_port(0);
EXPECT_FALSE(ServiceAddressFromProto(proto, service_address));
// Port > 65535
proto.set_port(65536);
EXPECT_FALSE(ServiceAddressFromProto(proto, service_address));
// Port < 0
proto.set_port(-1);
EXPECT_FALSE(ServiceAddressFromProto(proto, service_address));
}
@@ -118,5 +128,104 @@ TEST(ServiceAddressTest, ServiceAddressEquality) {
EXPECT_EQ(service_address5, service_address6);
}
TEST(ServiceAddressTest, IsLoopbackAddressIPv4) {
ServiceAddress service_address = {
.address = {127, 0, 0, 1},
.port = 8080,
};
EXPECT_TRUE(service_address.IsLoopbackAddress());
ServiceAddress not_loopback = {
.address = {10, 0, 0, 1},
.port = 8080,
};
EXPECT_FALSE(not_loopback.IsLoopbackAddress());
}
TEST(ServiceAddressTest, IsLoopbackAddressIPv6) {
ServiceAddress service_address = {
.address = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
.port = 8080,
};
EXPECT_TRUE(service_address.IsLoopbackAddress());
ServiceAddress not_loopback = {
.address = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2},
.port = 8080,
};
EXPECT_FALSE(not_loopback.IsLoopbackAddress());
}
TEST(ServiceAddressTest, IsLinkLocalAddressIPv4) {
ServiceAddress service_address = {
.address = {static_cast<char>(169), static_cast<char>(254), 0, 1},
.port = 8080,
};
EXPECT_TRUE(service_address.IsLinkLocalAddress());
ServiceAddress not_link_local = {
.address = {169, static_cast<char>(253), 0, 1},
.port = 8080,
};
EXPECT_FALSE(not_link_local.IsLinkLocalAddress());
ServiceAddress not_link_local_b0 = {
.address = {100, static_cast<char>(254), 0, 1},
.port = 8080,
};
EXPECT_FALSE(not_link_local_b0.IsLinkLocalAddress());
}
TEST(ServiceAddressTest, IsLinkLocalAddressIPv6) {
ServiceAddress service_address = {
.address = {static_cast<char>(0xfe), static_cast<char>(0x80), 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
.port = 8080,
};
EXPECT_TRUE(service_address.IsLinkLocalAddress());
ServiceAddress not_link_local = {
.address = {static_cast<char>(0xfd), static_cast<char>(0x80), 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
.port = 8080,
};
EXPECT_FALSE(not_link_local.IsLinkLocalAddress());
ServiceAddress not_link_local_b1 = {
.address = {static_cast<char>(0xfe), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1},
.port = 8080,
};
EXPECT_FALSE(not_link_local_b1.IsLinkLocalAddress());
}
TEST(ServiceAddressTest, IsLoopbackAddressInvalidLength) {
ServiceAddress service_address = {
.address = {127, 0, 0},
.port = 8080,
};
EXPECT_FALSE(service_address.IsLoopbackAddress());
ServiceAddress empty_address = {
.address = {},
.port = 8080,
};
EXPECT_FALSE(empty_address.IsLoopbackAddress());
}
TEST(ServiceAddressTest, IsLinkLocalAddressInvalidLength) {
ServiceAddress service_address = {
.address = {static_cast<char>(169), static_cast<char>(254), 0},
.port = 8080,
};
EXPECT_FALSE(service_address.IsLinkLocalAddress());
ServiceAddress empty_address = {
.address = {},
.port = 8080,
};
EXPECT_FALSE(empty_address.IsLinkLocalAddress());
}
} // namespace
} // namespace nearby
+7
View File
@@ -123,6 +123,12 @@ class WifiDirectCredentials {
return technology_;
}
// Get/Set Remote Device Name.
std::string GetRemoteDeviceName() const { return remote_device_name_; }
void SetRemoteDeviceName(const std::string& remote_device_name) {
remote_device_name_ = remote_device_name;
}
private:
// There are 2 types of WifiDirectAuthType.
// 1. Without Service Discovery: the credentials are ssid/password.
@@ -137,6 +143,7 @@ class WifiDirectCredentials {
std::string gateway_ = "0.0.0.0";
int port_ = 0;
int frequency_ = -1;
std::string remote_device_name_;
location::nearby::proto::connections::ConnectionBand band_;
location::nearby::proto::connections::ConnectionTechnology technology_;
};