mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-16 15:36:12 -04:00
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:
@@ -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()];
|
||||
}
|
||||
}
|
||||
|
||||
+28
-20
@@ -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 {
|
||||
|
||||
+474
-372
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);
|
||||
|
||||
+1
@@ -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
|
||||
|
||||
|
||||
+3
@@ -67,6 +67,9 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
}
|
||||
|
||||
- (nullable NSString *)getBonjourServiceNameFromEndpoint:(nw_endpoint_t)endpoint {
|
||||
if (self.returnNilServiceName) {
|
||||
return nil;
|
||||
}
|
||||
return self.getBonjourServiceNameFromEndpointResult ?: @"FakeService";
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -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
|
||||
|
||||
|
||||
+4
-4
@@ -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);
|
||||
}
|
||||
|
||||
+13
@@ -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
|
||||
|
||||
+55
-25
@@ -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>
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user