mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-16 07:36:10 -04:00
merged from upstream.
This commit is contained in:
@@ -17,69 +17,6 @@ load("@rules_cc//cc:cc_test.bzl", "cc_test")
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
cc_library(
|
||||
name = "auth_status",
|
||||
hdrs = ["auth_status.h"],
|
||||
visibility = [
|
||||
"//internal/auth:__pkg__",
|
||||
"//internal/platform/implementation:__subpackages__",
|
||||
"//location/nearby/cpp/sharing/clients/cpp:__subpackages__",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "account_info",
|
||||
hdrs = ["account_info.h"],
|
||||
visibility = [
|
||||
"//internal/auth:__pkg__",
|
||||
"//internal/platform/implementation:__subpackages__",
|
||||
"//location/nearby/cpp/sharing/clients/cpp:__subpackages__",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "account_manager",
|
||||
hdrs = ["account_manager.h"],
|
||||
visibility = [
|
||||
"//internal/account:__pkg__",
|
||||
"//internal/platform:__pkg__",
|
||||
"//internal/platform/implementation:__subpackages__",
|
||||
"//internal/test:__subpackages__",
|
||||
"//location/nearby/cpp/sharing/clients/cpp:__subpackages__",
|
||||
"//location/nearby/sharing/lib:__subpackages__",
|
||||
"//location/nearby/sharing/sdk/quick_share_server:__pkg__",
|
||||
"//sharing:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
":account_info",
|
||||
":signin_attempt",
|
||||
"@com_google_absl//absl/functional:any_invocable",
|
||||
"@com_google_absl//absl/status",
|
||||
"@com_google_absl//absl/status:statusor",
|
||||
"@com_google_absl//absl/strings:string_view",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "signin_attempt",
|
||||
hdrs = ["signin_attempt.h"],
|
||||
visibility = [
|
||||
"//internal/account:__pkg__",
|
||||
"//internal/auth:__pkg__",
|
||||
"//internal/platform/implementation:__subpackages__",
|
||||
"//internal/test:__subpackages__",
|
||||
"//location/nearby/cpp/sharing/clients/cpp:__subpackages__",
|
||||
"//location/nearby/sharing/sdk/quick_share_server:__pkg__",
|
||||
"//sharing:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
":account_info",
|
||||
":auth_status",
|
||||
"@com_google_absl//absl/functional:any_invocable",
|
||||
"@com_google_absl//absl/strings:string_view",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "types",
|
||||
hdrs = [
|
||||
@@ -112,11 +49,12 @@ cc_library(
|
||||
"//internal/test:__subpackages__",
|
||||
"//location/nearby/analytics/cpp:__subpackages__",
|
||||
"//location/nearby/cpp/sharing:__subpackages__",
|
||||
"//presence:__subpackages__",
|
||||
"//sharing:__subpackages__",
|
||||
"//third_party/nearby/presence:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"//internal/base:file_path",
|
||||
"//internal/base:files",
|
||||
"//internal/crypto_cros",
|
||||
"//internal/platform:base",
|
||||
"//internal/platform:mac_address",
|
||||
@@ -149,6 +87,26 @@ cc_library(
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "webrtc_platform",
|
||||
hdrs = [
|
||||
"webrtc.h",
|
||||
"webrtc_platform.h",
|
||||
],
|
||||
compatible_with = ["//buildenv/target:non_prod"],
|
||||
visibility = [
|
||||
"//:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"//connections/implementation/proto:offline_wire_formats_cc_proto",
|
||||
"//internal/platform:base",
|
||||
"//third_party/webrtc/files/stable/webrtc/api:libjingle_peerconnection_api",
|
||||
"//third_party/webrtc/files/stable/webrtc/api:scoped_refptr",
|
||||
"@com_google_absl//absl/functional:any_invocable",
|
||||
"@com_google_absl//absl/strings:string_view",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "comm",
|
||||
hdrs = [
|
||||
@@ -162,30 +120,26 @@ cc_library(
|
||||
"http_loader.h",
|
||||
"psk_info.h",
|
||||
"upgrade_address_info.h",
|
||||
"webrtc.h",
|
||||
"wifi.h",
|
||||
"wifi_direct.h",
|
||||
"wifi_hotspot.h",
|
||||
"wifi_lan.h",
|
||||
],
|
||||
copts = ["-DNO_WEBRTC"],
|
||||
visibility = [
|
||||
"//connections/implementation:__subpackages__",
|
||||
"//internal/network:__subpackages__",
|
||||
"//internal/platform:__pkg__",
|
||||
"//internal/platform/implementation:__subpackages__",
|
||||
"//presence:__subpackages__",
|
||||
"//presence/implementation:__subpackages__",
|
||||
"//third_party/nearby/presence:__subpackages__",
|
||||
"//third_party/nearby/presence/implementation:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"//connections/implementation/proto:offline_wire_formats_cc_proto",
|
||||
"//internal/platform:base",
|
||||
"//internal/platform:cancellation_flag",
|
||||
"//internal/platform:mac_address",
|
||||
"//internal/platform:uuid",
|
||||
"//internal/proto:credential_cc_proto",
|
||||
"//internal/proto:local_credential_cc_proto",
|
||||
# TODO: Support WebRTC
|
||||
"@com_google_absl//absl/base:core_headers",
|
||||
"@com_google_absl//absl/container:flat_hash_map",
|
||||
"@com_google_absl//absl/functional:any_invocable",
|
||||
@@ -194,7 +148,6 @@ cc_library(
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_absl//absl/strings:str_format",
|
||||
"@com_google_absl//absl/time",
|
||||
"@com_google_absl//absl/types:optional",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -203,7 +156,6 @@ cc_library(
|
||||
hdrs = [
|
||||
"platform.h",
|
||||
],
|
||||
defines = ["NO_WEBRTC"],
|
||||
visibility = [
|
||||
"//connections/implementation:__subpackages__",
|
||||
"//internal:__subpackages__",
|
||||
@@ -213,6 +165,7 @@ cc_library(
|
||||
"//location/nearby/analytics/cpp:__subpackages__",
|
||||
"//location/nearby/apps/better_together/plugins/preferences_native:__subpackages__",
|
||||
"//location/nearby/cpp/sharing:__subpackages__",
|
||||
"//sharing/internal/impl/common:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
":comm",
|
||||
@@ -229,6 +182,8 @@ cc_library(
|
||||
tags = ["keep_dep"], # Prevent build_cleaner from removing the dependency.
|
||||
visibility = [
|
||||
"//:__subpackages__",
|
||||
"//location/nearby/analytics/cpp:__subpackages__",
|
||||
"//location/nearby/cpp:__subpackages__",
|
||||
"//location/nearby/sharing/lib:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
@@ -259,3 +214,16 @@ cc_test(
|
||||
"@com_google_googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "device_info_test",
|
||||
size = "small",
|
||||
timeout = "moderate",
|
||||
srcs = ["device_info_test.cc"],
|
||||
deps = [
|
||||
":types",
|
||||
"@com_github_protobuf_matchers//protobuf-matchers",
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
// Copyright 2024 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_ACCOUNT_INFO_H_
|
||||
#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_ACCOUNT_INFO_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace nearby {
|
||||
|
||||
// Describes a Nearby account. The account class will have more properties
|
||||
// and methods in the future based on the new feature added.
|
||||
struct AccountInfo {
|
||||
std::string id; // The unique identify of the account.
|
||||
std::string display_name;
|
||||
std::string family_name;
|
||||
std::string given_name;
|
||||
std::string picture_url;
|
||||
std::string email;
|
||||
};
|
||||
|
||||
} // namespace nearby
|
||||
|
||||
#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_ACCOUNT_INFO_H_
|
||||
@@ -1,89 +0,0 @@
|
||||
// Copyright 2022 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef PLATFORM_API_ACCOUNT_MANAGER_H_
|
||||
#define PLATFORM_API_ACCOUNT_MANAGER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/functional/any_invocable.h"
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "internal/platform/implementation/account_info.h"
|
||||
#include "internal/platform/implementation/signin_attempt.h"
|
||||
|
||||
namespace nearby {
|
||||
|
||||
// AccountManager manages the accounts are used to access Nearby backend.
|
||||
// In current design, AccountManager only support one active account.
|
||||
class AccountManager {
|
||||
public:
|
||||
using Account = AccountInfo;
|
||||
|
||||
// Observes the activity of the account manager.
|
||||
class Observer {
|
||||
public:
|
||||
virtual ~Observer() = default;
|
||||
|
||||
virtual void OnLoginSucceeded(absl::string_view account_id) = 0;
|
||||
// |credential_error| is true if the logout is due to critical auth error.
|
||||
virtual void OnLogoutSucceeded(absl::string_view account_id,
|
||||
bool credential_error) = 0;
|
||||
};
|
||||
|
||||
virtual ~AccountManager() = default;
|
||||
|
||||
// Gets current active account. If no login user, return std::nullopt.
|
||||
virtual std::optional<Account> GetCurrentAccount() = 0;
|
||||
|
||||
// Initializes the login process for a Google account from an oauth client.
|
||||
// |client_id| GCP client_id of the client
|
||||
// |client_secret| GCP client_secret of the client
|
||||
// Returns a SigninAttempt object that can be used to complete the login
|
||||
// process.
|
||||
virtual std::unique_ptr<SigninAttempt> Login(
|
||||
absl::string_view client_id, absl::string_view client_secret) = 0;
|
||||
|
||||
// Logs out current active account. |logout_callback| is called when logout is
|
||||
// completed.
|
||||
virtual void Logout(
|
||||
absl::AnyInvocable<void(absl::Status)> logout_callback) = 0;
|
||||
|
||||
// Gets access token for the active account.
|
||||
// |callback| is called with the access token or error status.
|
||||
//
|
||||
// Returns false if callback is null.
|
||||
virtual bool GetAccessToken(
|
||||
absl::AnyInvocable<void(absl::StatusOr<std::string>)> callback) = 0;
|
||||
|
||||
// Returns a pair containing the client id and client secret used in the most
|
||||
// recent Login request.
|
||||
// If no current user is logged in, returns empty string for both.
|
||||
virtual std::pair<std::string, std::string> GetOAuthClientCredential() = 0;
|
||||
|
||||
virtual void AddObserver(Observer* observer) = 0;
|
||||
virtual void RemoveObserver(Observer* observer) = 0;
|
||||
|
||||
virtual void SaveAccountPrefs(absl::string_view user_id,
|
||||
absl::string_view client_id,
|
||||
absl::string_view client_secret) = 0;
|
||||
};
|
||||
|
||||
} // namespace nearby
|
||||
|
||||
#endif // PLATFORM_API_ACCOUNT_MANAGER_H_
|
||||
@@ -51,6 +51,18 @@ objc_library(
|
||||
],
|
||||
)
|
||||
|
||||
objc_library(
|
||||
name = "apple_webrtc",
|
||||
srcs = [
|
||||
"webrtc_platform.mm",
|
||||
],
|
||||
deps = [
|
||||
"//connections/implementation/mediums/webrtc:webrtc_medium_impl",
|
||||
"//internal/platform/implementation:webrtc_platform",
|
||||
"//third_party/apple_frameworks:Foundation",
|
||||
],
|
||||
)
|
||||
|
||||
objc_library(
|
||||
name = "apple",
|
||||
srcs = [
|
||||
@@ -61,7 +73,6 @@ objc_library(
|
||||
"preferences_manager.mm",
|
||||
"scheduled_executor.mm",
|
||||
"timer.mm",
|
||||
"webrtc.mm",
|
||||
"wifi_hotspot.mm",
|
||||
"wifi_lan.mm",
|
||||
],
|
||||
@@ -70,7 +81,6 @@ objc_library(
|
||||
"device_info.h",
|
||||
"preferences_manager.h",
|
||||
"timer.h",
|
||||
"webrtc.h",
|
||||
"wifi.h",
|
||||
"wifi_hotspot.h",
|
||||
"wifi_lan.h",
|
||||
@@ -102,6 +112,7 @@ objc_library(
|
||||
"//internal/base:file_path",
|
||||
"//internal/base:files",
|
||||
"//internal/base:masker",
|
||||
"//internal/platform/implementation/apple/Flags",
|
||||
"//internal/platform/implementation/apple/Mediums/Hotspot",
|
||||
"//internal/account",
|
||||
"//internal/crypto_cros",
|
||||
@@ -109,10 +120,6 @@ objc_library(
|
||||
"//internal/platform:logging",
|
||||
"//internal/platform:types",
|
||||
"//internal/proto:tachyon_cc_proto",
|
||||
"//third_party/webrtc/files/stable/webrtc/api/task_queue:default_task_queue_factory",
|
||||
"//third_party/webrtc/files/stable/webrtc/api:create_modular_peer_connection_factory",
|
||||
"//third_party/webrtc/files/stable/webrtc/api:libjingle_peerconnection_api",
|
||||
"//third_party/webrtc/files/stable/webrtc/rtc_base:checks",
|
||||
"//internal/platform:base",
|
||||
"//internal/platform/implementation:comm",
|
||||
"//internal/platform/implementation:platform",
|
||||
@@ -345,11 +352,11 @@ cc_test(
|
||||
deps = [
|
||||
":Platform_cc",
|
||||
"//internal/platform/implementation/g3:crypto",
|
||||
"//third_party/gloop/thread/fiber",
|
||||
"@com_github_protobuf_matchers//protobuf-matchers",
|
||||
"@com_google_absl//absl/base:core_headers",
|
||||
"@com_google_absl//absl/synchronization",
|
||||
"@com_google_absl//absl/time",
|
||||
"@com_google_googletest//:gtest_main",
|
||||
"@com_google_nisaba//nisaba/port:thread_pool/fiber",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -29,4 +29,13 @@
|
||||
/** Checks whether BLE L2CAP refactor is enabled in the Nearby Connections SDK. */
|
||||
@property(nonatomic, class, readonly) BOOL refactorBleL2capEnabled;
|
||||
|
||||
/** Checks whether shared peripheral manager is enabled in the Nearby Connections SDK. */
|
||||
@property(nonatomic, class, readonly) BOOL sharedPeripheralManagerEnabled;
|
||||
|
||||
/** Checks whether single copy read/write is enabled in the Nearby Connections SDK. */
|
||||
@property(nonatomic, class, readonly) BOOL singleCopyEnabled;
|
||||
|
||||
/** Checks whether BLE server socket deadlock is fixed in the Nearby Connections SDK. */
|
||||
@property(nonatomic, class, readonly) BOOL fixBleServerSocketDeadlockEnabled;
|
||||
|
||||
@end
|
||||
|
||||
@@ -42,4 +42,21 @@
|
||||
kRefactorBleL2cap);
|
||||
}
|
||||
|
||||
+ (BOOL)sharedPeripheralManagerEnabled {
|
||||
return nearby::NearbyFlags::GetInstance().GetBoolFlag(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager);
|
||||
}
|
||||
|
||||
+ (BOOL)singleCopyEnabled {
|
||||
return nearby::NearbyFlags::GetInstance().GetBoolFlag(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::kEnableSingleCopy);
|
||||
}
|
||||
|
||||
+ (BOOL)fixBleServerSocketDeadlockEnabled {
|
||||
return nearby::NearbyFlags::GetInstance().GetBoolFlag(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kFixBleServerSocketDeadlock);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -34,6 +34,13 @@
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::kEnableBleL2cap,
|
||||
false);
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::kRefactorBleL2cap,
|
||||
false);
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
false);
|
||||
|
||||
[super tearDown];
|
||||
}
|
||||
@@ -78,4 +85,34 @@
|
||||
XCTAssertFalse([GNCFeatureFlags bleL2capEnabled]);
|
||||
}
|
||||
|
||||
- (void)testRefactorBleL2capEnabled_WhenFlagIsTrue {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::kRefactorBleL2cap,
|
||||
YES);
|
||||
XCTAssertTrue([GNCFeatureFlags refactorBleL2capEnabled]);
|
||||
}
|
||||
|
||||
- (void)testRefactorBleL2capEnabled_WhenFlagIsFalse {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::kRefactorBleL2cap,
|
||||
NO);
|
||||
XCTAssertFalse([GNCFeatureFlags refactorBleL2capEnabled]);
|
||||
}
|
||||
|
||||
- (void)testSharedPeripheralManagerEnabled_WhenFlagIsTrue {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
YES);
|
||||
XCTAssertTrue([GNCFeatureFlags sharedPeripheralManagerEnabled]);
|
||||
}
|
||||
|
||||
- (void)testSharedPeripheralManagerEnabled_WhenFlagIsFalse {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
NO);
|
||||
XCTAssertFalse([GNCFeatureFlags sharedPeripheralManagerEnabled]);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -39,6 +39,7 @@ objc_library(
|
||||
"GNCMConnection.m",
|
||||
"GNCPeripheral.m",
|
||||
"GNCPeripheralManager.m",
|
||||
"GNCPeripheralManagerMultiplexer.m",
|
||||
"NSData+GNCBase85.mm",
|
||||
"NSData+GNCWebSafeBase64.m",
|
||||
],
|
||||
@@ -59,6 +60,7 @@ objc_library(
|
||||
"GNCMConnection.h",
|
||||
"GNCPeripheral.h",
|
||||
"GNCPeripheralManager.h",
|
||||
"GNCPeripheralManagerMultiplexer.h",
|
||||
"NSData+GNCBase85.h",
|
||||
"NSData+GNCWebSafeBase64.h",
|
||||
],
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#import <CoreBluetooth/CoreBluetooth.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCPeripheralManager.h"
|
||||
|
||||
@class GNCBLEGATTCharacteristic;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
@@ -57,7 +59,18 @@ typedef void (^GNCStopAdvertisingCompletionHandler)(NSError *_Nullable error);
|
||||
*
|
||||
* @note The public APIs of this class are thread safe.
|
||||
*/
|
||||
@interface GNCBLEGATTServer : NSObject
|
||||
@interface GNCBLEGATTServer : NSObject <GNCPeripheralManagerDelegate>
|
||||
|
||||
/**
|
||||
* Initializes the GATT server.
|
||||
*
|
||||
* @param peripheralManager The peripheral manager to use.
|
||||
* @param queue The queue to use for delegate callbacks and internal operations.
|
||||
*/
|
||||
- (instancetype)initWithPeripheralManager:(nullable id<GNCPeripheralManager>)peripheralManager
|
||||
queue:(nullable dispatch_queue_t)queue;
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/**
|
||||
* Creates a characteristic and adds it to the GATT server.
|
||||
|
||||
@@ -59,34 +59,37 @@ static const int kMaxAdvertisementLengthOnIOS = 23;
|
||||
NSDictionary<NSString *, id> *_advertisementData;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
- (instancetype)initWithPeripheralManager:(nullable id<GNCPeripheralManager>)peripheralManager
|
||||
queue:(nullable dispatch_queue_t)queue {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_queue = dispatch_queue_create(kGNCBLEGATTServerQueueLabel, DISPATCH_QUEUE_SERIAL);
|
||||
_peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:nil queue:_queue];
|
||||
// Set for @c GNCPeripheralManager to be able to forward callbacks.
|
||||
_peripheralManager.peripheralDelegate = self;
|
||||
_queue = queue ?: dispatch_queue_create(kGNCBLEGATTServerQueueLabel, DISPATCH_QUEUE_SERIAL);
|
||||
_services = [[NSMutableDictionary alloc] init];
|
||||
_pendingCharacteristics = [[NSMutableDictionary alloc] init];
|
||||
_characteristicValues = [[NSMutableDictionary alloc] init];
|
||||
_advertisementData = nil;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
// This is private and should only be used for tests. The provided peripheral manager must call
|
||||
// delegate methods on the main queue.
|
||||
- (instancetype)initWithPeripheralManager:(nullable id<GNCPeripheralManager>)peripheralManager {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_queue = dispatch_get_main_queue();
|
||||
_peripheralManager = peripheralManager;
|
||||
// Set for @c GNCPeripheralManager to be able to forward callbacks.
|
||||
_peripheralManager.peripheralDelegate = self;
|
||||
_services = [[NSMutableDictionary alloc] init];
|
||||
_pendingCharacteristics = [[NSMutableDictionary alloc] init];
|
||||
_characteristicValues = [[NSMutableDictionary alloc] init];
|
||||
_advertisementData = nil;
|
||||
if (GNCFeatureFlags.sharedPeripheralManagerEnabled) {
|
||||
if (!peripheralManager) {
|
||||
// In shared mode, the peripheral manager must be injected.
|
||||
[NSException raise:NSInvalidArgumentException
|
||||
format:@"Peripheral manager cannot be nil when shared manager is enabled."];
|
||||
}
|
||||
_peripheralManager = peripheralManager;
|
||||
// In shared mode, do NOT set the delegate. The Multiplexer handles callbacks.
|
||||
} else {
|
||||
// Legacy mode: Create a new manager if one isn't provided.
|
||||
if (!peripheralManager) {
|
||||
peripheralManager = [[CBPeripheralManager alloc]
|
||||
initWithDelegate:nil
|
||||
queue:_queue
|
||||
options:@{CBPeripheralManagerOptionShowPowerAlertKey : @NO}];
|
||||
}
|
||||
_peripheralManager = peripheralManager;
|
||||
// In legacy mode, we own the manager (or use the injected one as if we own it) and set the
|
||||
// delegate.
|
||||
_peripheralManager.peripheralDelegate = self;
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -342,6 +345,12 @@ static const int kMaxAdvertisementLengthOnIOS = 23;
|
||||
- (void)gnc_peripheralManager:(id<GNCPeripheralManager>)peripheral
|
||||
didReceiveReadRequest:(CBATTRequest *)request {
|
||||
dispatch_assert_queue(_queue);
|
||||
if (!_services[request.characteristic.service.UUID]) {
|
||||
// This server does not own the requested service. Ignore the request to allow other listeners
|
||||
// (or future listeners) to handle it.
|
||||
return;
|
||||
}
|
||||
|
||||
NSData *value =
|
||||
_characteristicValues[request.characteristic.service.UUID][request.characteristic.UUID];
|
||||
if (!value) {
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#import <CoreBluetooth/CoreBluetooth.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCPeripheralManager.h"
|
||||
|
||||
@class GNCBLEL2CAPStream;
|
||||
@protocol GNCPeripheralManager;
|
||||
|
||||
@@ -42,7 +44,7 @@ typedef void (^GNCOpenL2CAPServerChannelOpendCompletionHandler)(GNCBLEL2CAPStrea
|
||||
*
|
||||
* @note The public APIs of this class are thread safe.
|
||||
*/
|
||||
@interface GNCBLEL2CAPServer : NSObject
|
||||
@interface GNCBLEL2CAPServer : NSObject <GNCPeripheralManagerDelegate>
|
||||
|
||||
// Represents a PSM (Protocol/Service Multiplexer) value for an L2CAP channel.
|
||||
@property(atomic, readonly) CBL2CAPPSM PSM;
|
||||
@@ -57,6 +59,8 @@ typedef void (^GNCOpenL2CAPServerChannelOpendCompletionHandler)(GNCBLEL2CAPStrea
|
||||
- (instancetype)initWithPeripheralManager:(nullable id<GNCPeripheralManager>)peripheralManager
|
||||
queue:(nullable dispatch_queue_t)queue;
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/**
|
||||
* Starts listening for an L2CAP channel.
|
||||
*
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#import <CoreBluetooth/CoreBluetooth.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "internal/platform/implementation/apple/Flags/GNCFeatureFlags.h"
|
||||
#import "internal/platform/implementation/apple/Log/GNCLogger.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEError.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPStream.h"
|
||||
@@ -43,21 +44,25 @@ static char *const kGNCBLEL2CAPServerQueueLabel = "com.google.nearby.GNCBLEL2CAP
|
||||
BOOL _alreadyStartedWhenPeripheralPoweredOff;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
return [self initWithPeripheralManager:nil queue:nil];
|
||||
}
|
||||
|
||||
// This is private and should only be used for tests. The provided peripheral manager must call
|
||||
// delegate methods on the main queue.
|
||||
- (instancetype)initWithPeripheralManager:(nullable id<GNCPeripheralManager>)peripheralManager
|
||||
queue:(nullable dispatch_queue_t)queue {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_queue = queue ?: dispatch_queue_create(kGNCBLEL2CAPServerQueueLabel, DISPATCH_QUEUE_SERIAL);
|
||||
if (peripheralManager) {
|
||||
if (GNCFeatureFlags.sharedPeripheralManagerEnabled) {
|
||||
if (!peripheralManager) {
|
||||
// In shared mode, the peripheral manager must be injected.
|
||||
[NSException raise:NSInvalidArgumentException
|
||||
format:@"Peripheral manager cannot be nil when shared manager is enabled."];
|
||||
}
|
||||
_peripheralManager = peripheralManager;
|
||||
// Set for @c GNCPeripheralManager to be able to forward callbacks.
|
||||
_peripheralManager.peripheralDelegate = self;
|
||||
// In shared mode, do NOT set the delegate. The Multiplexer handles callbacks.
|
||||
} else {
|
||||
if (peripheralManager) {
|
||||
_peripheralManager = peripheralManager;
|
||||
// Set for @c GNCPeripheralManager to be able to forward callbacks.
|
||||
_peripheralManager.peripheralDelegate = self;
|
||||
}
|
||||
}
|
||||
}
|
||||
return self;
|
||||
@@ -70,22 +75,42 @@ static char *const kGNCBLEL2CAPServerQueueLabel = "com.google.nearby.GNCBLEL2CAP
|
||||
channelOpenedCompletionHandler {
|
||||
_psmPublishedCompletionHandler = [psmPublishedCompletionHandler copy];
|
||||
_channelOpenedCompletionHandler = [channelOpenedCompletionHandler copy];
|
||||
if (!_queue) {
|
||||
_psmPublishedCompletionHandler(0, [NSError errorWithDomain:GNCBLEErrorDomain
|
||||
code:GNCBLEErrorL2CAPListeningOnQueueNil
|
||||
userInfo:nil]);
|
||||
return;
|
||||
}
|
||||
if (!_peripheralManager) {
|
||||
// Lazy initialization to avoid system dialog on app startup before pairing.
|
||||
_peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:nil
|
||||
queue:_queue
|
||||
options:nil];
|
||||
if (GNCFeatureFlags.sharedPeripheralManagerEnabled) {
|
||||
if (!_queue) {
|
||||
if (_psmPublishedCompletionHandler) {
|
||||
_psmPublishedCompletionHandler(0,
|
||||
[NSError errorWithDomain:GNCBLEErrorDomain
|
||||
code:GNCBLEErrorL2CAPListeningOnQueueNil
|
||||
userInfo:nil]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!_peripheralManager) {
|
||||
GNCLoggerError(@"[NEARBY] Peripheral manager must not be nil.");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!_queue) {
|
||||
_psmPublishedCompletionHandler(0, [NSError errorWithDomain:GNCBLEErrorDomain
|
||||
code:GNCBLEErrorL2CAPListeningOnQueueNil
|
||||
userInfo:nil]);
|
||||
return;
|
||||
}
|
||||
if (!_peripheralManager) {
|
||||
// Lazy initialization to avoid system dialog on app startup before pairing.
|
||||
_peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:nil
|
||||
queue:_queue
|
||||
options:nil];
|
||||
|
||||
// Set for @c GNCPeripheralManager to be able to forward callbacks.
|
||||
_peripheralManager.peripheralDelegate = self;
|
||||
// Set for @c GNCPeripheralManager to be able to forward callbacks.
|
||||
_peripheralManager.peripheralDelegate = self;
|
||||
}
|
||||
}
|
||||
|
||||
if (_PSM > 0) {
|
||||
GNCLoggerInfo((@"[NEARBY] Unpublish L2CAP channel with PSM: %@"), @(_PSM));
|
||||
[_peripheralManager unpublishL2CAPChannel:_PSM];
|
||||
}
|
||||
if (_peripheralManager.state == CBManagerStatePoweredOn) {
|
||||
// Bluetooth link is already encrypted, however encryption is not required here to avoid getting
|
||||
// insufficient authentication errors due to initialization order.
|
||||
|
||||
@@ -106,15 +106,6 @@ typedef void (^GNCGATTConnectionCompletionHandler)(GNCBLEGATTClient *_Nullable c
|
||||
*/
|
||||
- (instancetype)init;
|
||||
|
||||
/**
|
||||
* Initializes the BLE medium with a custom central manager.
|
||||
*
|
||||
* @param centralManager The central manager to use for BLE operations.
|
||||
* @param queue The queue to use for all internal operations.
|
||||
*/
|
||||
- (instancetype)initWithCentralManager:(id<GNCCentralManager>)centralManager
|
||||
queue:(nullable dispatch_queue_t)queue;
|
||||
|
||||
/** The hardware supports BOTH advertising extensions and extended scans. */
|
||||
@property(nonatomic, readonly) BOOL supportsExtendedAdvertisements;
|
||||
|
||||
|
||||
@@ -22,10 +22,10 @@
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEError.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTClient.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTServer.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPClient.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPServer.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCCentralManager.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCPeripheral.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCPeripheralManagerMultiplexer.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/NSData+GNCBase85.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/NSData+GNCWebSafeBase64.h"
|
||||
|
||||
@@ -40,22 +40,17 @@ static NSError *AlreadyScanningError() {
|
||||
return [NSError errorWithDomain:GNCBLEErrorDomain code:GNCBLEErrorAlreadyScanning userInfo:nil];
|
||||
}
|
||||
|
||||
static GNCBLEL2CAPServer *_Nonnull CreateL2CapServer(
|
||||
id<GNCPeripheralManager> _Nullable peripheralManager) {
|
||||
if (!peripheralManager) {
|
||||
return [[GNCBLEL2CAPServer alloc] init];
|
||||
} else {
|
||||
return [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:peripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
}
|
||||
}
|
||||
|
||||
@interface GNCBLEMedium () <GNCCentralManagerDelegate, CBCentralManagerDelegate>
|
||||
- (instancetype)initWithCentralManager:(id<GNCCentralManager>)centralManager
|
||||
peripheralManager:(nullable id<GNCPeripheralManager>)peripheralManager
|
||||
queue:(dispatch_queue_t)queue;
|
||||
@end
|
||||
|
||||
@implementation GNCBLEMedium {
|
||||
dispatch_queue_t _queue;
|
||||
id<GNCCentralManager> _centralManager;
|
||||
id<GNCPeripheralManager> _peripheralManager;
|
||||
GNCPeripheralManagerMultiplexer *_multiplexer;
|
||||
|
||||
// The active GATT server, or @nil if one hasn't been started yet.
|
||||
GNCBLEGATTServer *_server;
|
||||
@@ -99,21 +94,31 @@ static GNCBLEL2CAPServer *_Nonnull CreateL2CapServer(
|
||||
- (instancetype)init {
|
||||
dispatch_queue_t queue = dispatch_queue_create(kBLEMediumQueueLabel, DISPATCH_QUEUE_SERIAL);
|
||||
CBCentralManager *centralManager =
|
||||
[[CBCentralManager alloc] initWithDelegate:self
|
||||
[[CBCentralManager alloc] initWithDelegate:nil
|
||||
queue:queue
|
||||
options:@{CBCentralManagerOptionShowPowerAlertKey : @NO}];
|
||||
return [self initWithCentralManager:centralManager queue:queue];
|
||||
CBPeripheralManager *peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:nil
|
||||
queue:queue];
|
||||
return [self initWithCentralManager:centralManager
|
||||
peripheralManager:peripheralManager
|
||||
queue:queue];
|
||||
}
|
||||
|
||||
// This is private and should only be used for tests. The provided central manager must call
|
||||
// delegate methods on the main queue.
|
||||
- (instancetype)initWithCentralManager:(id<GNCCentralManager>)centralManager
|
||||
queue:(nullable dispatch_queue_t)queue {
|
||||
peripheralManager:(nullable id<GNCPeripheralManager>)peripheralManager
|
||||
queue:(dispatch_queue_t)queue {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_queue = queue ?: dispatch_get_main_queue();
|
||||
_queue = queue;
|
||||
_centralManager = centralManager;
|
||||
_centralManager.centralDelegate = self;
|
||||
_peripheralManager = peripheralManager;
|
||||
if (GNCFeatureFlags.sharedPeripheralManagerEnabled && _peripheralManager) {
|
||||
_multiplexer = [[GNCPeripheralManagerMultiplexer alloc] initWithCallbackQueue:_queue];
|
||||
_peripheralManager.peripheralDelegate = _multiplexer;
|
||||
}
|
||||
_gattConnectionCompletionHandlers = [NSMutableDictionary dictionary];
|
||||
_gattDisconnectionHandlers = [NSMutableDictionary dictionary];
|
||||
_scanningServiceUUIDs = [NSMutableArray array];
|
||||
@@ -160,11 +165,27 @@ static GNCBLEL2CAPServer *_Nonnull CreateL2CapServer(
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[_centralManager stopScan];
|
||||
_centralManager.centralDelegate = nil;
|
||||
|
||||
[_peripheralManager stopAdvertising];
|
||||
_peripheralManager.peripheralDelegate = nil;
|
||||
}
|
||||
|
||||
- (void)startAdvertisingData:(NSDictionary<CBUUID *, NSData *> *)serviceData
|
||||
completionHandler:(nullable GNCStartAdvertisingCompletionHandler)completionHandler {
|
||||
dispatch_async(_queue, ^{
|
||||
if (!_server) {
|
||||
_server = [[GNCBLEGATTServer alloc] init];
|
||||
if (GNCFeatureFlags.sharedPeripheralManagerEnabled) {
|
||||
_server = [[GNCBLEGATTServer alloc] initWithPeripheralManager:_peripheralManager
|
||||
queue:_queue];
|
||||
[_multiplexer addListener:_server];
|
||||
} else {
|
||||
// In legacy mode, we pass nil (or a separate manager) and do NOT add to multiplexer.
|
||||
// GNCBLEGATTServer will create its own internal manager.
|
||||
_server = [[GNCBLEGATTServer alloc] initWithPeripheralManager:nil queue:nil];
|
||||
}
|
||||
}
|
||||
[_server startAdvertisingData:serviceData completionHandler:completionHandler];
|
||||
});
|
||||
@@ -206,7 +227,7 @@ static GNCBLEL2CAPServer *_Nonnull CreateL2CapServer(
|
||||
[_scanningServiceUUIDs addObjectsFromArray:serviceUUIDs];
|
||||
_advertisementFoundHandler = advertisementFoundHandler;
|
||||
|
||||
[self internalStartScanningIfPoweredOn];
|
||||
[self updateScanningState];
|
||||
if (completionHandler) {
|
||||
completionHandler(nil);
|
||||
}
|
||||
@@ -227,7 +248,7 @@ static GNCBLEL2CAPServer *_Nonnull CreateL2CapServer(
|
||||
|
||||
- (void)resumeMediumScanning:(nullable GNCStartScanningCompletionHandler)completionHandler {
|
||||
dispatch_async(_queue, ^{
|
||||
[self internalStartScanningIfPoweredOn];
|
||||
[self updateScanningState];
|
||||
if (completionHandler) {
|
||||
completionHandler(nil);
|
||||
}
|
||||
@@ -238,7 +259,13 @@ static GNCBLEL2CAPServer *_Nonnull CreateL2CapServer(
|
||||
(nullable GNCGATTServerCompletionHandler)completionHandler {
|
||||
dispatch_async(_queue, ^{
|
||||
if (!_server) {
|
||||
_server = [[GNCBLEGATTServer alloc] init];
|
||||
if (GNCFeatureFlags.sharedPeripheralManagerEnabled) {
|
||||
_server = [[GNCBLEGATTServer alloc] initWithPeripheralManager:_peripheralManager
|
||||
queue:_queue];
|
||||
[_multiplexer addListener:_server];
|
||||
} else {
|
||||
_server = [[GNCBLEGATTServer alloc] initWithPeripheralManager:nil queue:nil];
|
||||
}
|
||||
}
|
||||
if (completionHandler) {
|
||||
completionHandler(_server, nil);
|
||||
@@ -284,7 +311,22 @@ static GNCBLEL2CAPServer *_Nonnull CreateL2CapServer(
|
||||
(nullable id<GNCPeripheralManager>)peripheralManager {
|
||||
dispatch_async(_queue, ^{
|
||||
if (!_l2capServer) {
|
||||
_l2capServer = CreateL2CapServer(peripheralManager);
|
||||
if (GNCFeatureFlags.sharedPeripheralManagerEnabled) {
|
||||
_l2capServer = [[GNCBLEL2CAPServer alloc]
|
||||
initWithPeripheralManager:peripheralManager ?: _peripheralManager
|
||||
queue:peripheralManager ? dispatch_get_main_queue() : _queue];
|
||||
// Only add to multiplexer if we are using the internal shared manager.
|
||||
// If a specific manager was passed in (e.g. for testing?), we might still need logic here.
|
||||
// But typically `peripheralManager` is nil in prod.
|
||||
if (peripheralManager == nil || peripheralManager == _peripheralManager) {
|
||||
[_multiplexer addListener:_l2capServer];
|
||||
}
|
||||
} else {
|
||||
// Legacy mode
|
||||
_l2capServer = [[GNCBLEL2CAPServer alloc]
|
||||
initWithPeripheralManager:peripheralManager // Likely nil, so Server creates new one
|
||||
queue:peripheralManager ? dispatch_get_main_queue() : nil];
|
||||
}
|
||||
}
|
||||
[_l2capServer
|
||||
startListeningChannelWithPSMPublishedCompletionHandler:psmPublishedCompletionHandler
|
||||
@@ -335,7 +377,7 @@ static GNCBLEL2CAPServer *_Nonnull CreateL2CapServer(
|
||||
|
||||
#pragma mark - Internal
|
||||
|
||||
- (void)internalStartScanningIfPoweredOn {
|
||||
- (void)updateScanningState {
|
||||
dispatch_assert_queue(_queue);
|
||||
// Scanning can only be done when powered on and must be restarted if bluetooth is turned off
|
||||
// then back on. This will be called anytime the central manager's state changes, so
|
||||
@@ -463,7 +505,7 @@ static GNCBLEL2CAPServer *_Nonnull CreateL2CapServer(
|
||||
return;
|
||||
}
|
||||
dispatch_assert_queue(_queue);
|
||||
[self internalStartScanningIfPoweredOn];
|
||||
[self updateScanningState];
|
||||
}
|
||||
|
||||
- (void)gnc_centralManager:(id<GNCCentralManager>)central
|
||||
@@ -480,6 +522,7 @@ static GNCBLEL2CAPServer *_Nonnull CreateL2CapServer(
|
||||
didConnectPeripheral:(id<GNCPeripheral>)peripheral {
|
||||
dispatch_assert_queue(_queue);
|
||||
[self cancelConnectionTimeout];
|
||||
|
||||
if (_l2capPSM > 0) {
|
||||
[self internalOpenL2CAPChannel:peripheral];
|
||||
return;
|
||||
@@ -525,6 +568,7 @@ static GNCBLEL2CAPServer *_Nonnull CreateL2CapServer(
|
||||
didDisconnectPeripheral:(id<GNCPeripheral>)peripheral
|
||||
error:(nullable NSError *)error {
|
||||
dispatch_assert_queue(_queue);
|
||||
|
||||
GNCGATTDisconnectionHandler handler = _gattDisconnectionHandlers[peripheral.identifier];
|
||||
_gattDisconnectionHandlers[peripheral.identifier] = nil;
|
||||
if (handler) {
|
||||
|
||||
@@ -22,8 +22,10 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@implementation CBCentralManager (GNCCentralManagerAdditions)
|
||||
|
||||
- (void)setCentralDelegate:(nullable id<GNCCentralManagerDelegate>)centralDelegate {
|
||||
NSAssert([centralDelegate conformsToProtocol:@protocol(CBCentralManagerDelegate)],
|
||||
@"centralDelegate must conform to protocol CBCentralManagerDelegate");
|
||||
if (centralDelegate) {
|
||||
NSAssert([centralDelegate conformsToProtocol:@protocol(CBCentralManagerDelegate)],
|
||||
@"centralDelegate must conform to protocol CBCentralManagerDelegate");
|
||||
}
|
||||
self.delegate = (id<CBCentralManagerDelegate>)centralDelegate;
|
||||
}
|
||||
|
||||
|
||||
@@ -90,9 +90,11 @@ NSData *_Nullable GNCMGenerateBLEL2CAPPacket(GNCMBLEL2CAPCommand command, NSData
|
||||
|
||||
/**
|
||||
* Calls the completion handler with (a) YES if the GNSSocket connected, or (b) NO if it failed to
|
||||
* connect for any reason. The completion handler is called on the main queue.
|
||||
* connect for any reason. The completion handler is called on the given queue. If the queue is nil,
|
||||
* the completion handler is called on the main queue.
|
||||
*/
|
||||
void GNCMWaitForConnection(GNSSocket *socket, GNCMBoolHandler completion);
|
||||
void GNCMWaitForConnection(GNSSocket *socket, dispatch_queue_t _Nullable queue,
|
||||
GNCMBoolHandler completion);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
||||
@@ -235,18 +235,20 @@ NSData *_Nullable GNCMGenerateBLEL2CAPPacket(GNCMBLEL2CAPCommand command, NSData
|
||||
|
||||
@end
|
||||
|
||||
void GNCMWaitForConnection(GNSSocket *socket, GNCMBoolHandler completion) {
|
||||
void GNCMWaitForConnection(GNSSocket *socket, dispatch_queue_t _Nullable queue,
|
||||
GNCMBoolHandler completion) {
|
||||
// This function passes YES to the completion when the socket has successfully connected, and
|
||||
// otherwise passes NO to the completion after a timeout of several seconds. We shouldn't retain
|
||||
// the completion after it's been called, so store it in a __block variable and nil it out once
|
||||
// the socket has connected.
|
||||
__block GNCMBoolHandler completionRef = completion;
|
||||
dispatch_queue_t targetQueue = queue ?: dispatch_get_main_queue();
|
||||
|
||||
// The delegate listens for the socket connection callbacks. It's retained by the block passed to
|
||||
// dispatch_after below, so it will live long enough to do its job.
|
||||
GNCMBleSocketDelegate *delegate =
|
||||
[GNCMBleSocketDelegate delegateWithConnectedHandler:^(BOOL didConnect) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
dispatch_async(targetQueue, ^{
|
||||
if (completionRef) completionRef(didConnect);
|
||||
completionRef = nil;
|
||||
});
|
||||
@@ -254,9 +256,10 @@ void GNCMWaitForConnection(GNSSocket *socket, GNCMBoolHandler completion) {
|
||||
socket.delegate = delegate;
|
||||
dispatch_after(
|
||||
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kBleSocketConnectionTimeout * NSEC_PER_SEC)),
|
||||
dispatch_get_main_queue(), ^{
|
||||
targetQueue, ^{
|
||||
(void)delegate; // make sure it's retained until the timeout
|
||||
if (completionRef) completionRef(NO);
|
||||
completionRef = nil;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright 2026 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCPeripheralManager.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* A multiplexer that forwards @c CBPeripheralManagerDelegate and @c GNCPeripheralManagerDelegate
|
||||
*/
|
||||
@interface GNCPeripheralManagerMultiplexer : NSObject <GNCPeripheralManagerDelegate>
|
||||
|
||||
/**
|
||||
* Initializes the multiplexer.
|
||||
*
|
||||
* @param callbackQueue The queue to use for forwarding delegate callbacks.
|
||||
*/
|
||||
- (instancetype)initWithCallbackQueue:(dispatch_queue_t)callbackQueue NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/**
|
||||
* Adds a listener to the multiplexer. Listeners are held weakly.
|
||||
*
|
||||
* @param listener The listener to add.
|
||||
*/
|
||||
- (void)addListener:(id<GNCPeripheralManagerDelegate>)listener;
|
||||
|
||||
/**
|
||||
* Removes a listener from the multiplexer.
|
||||
*
|
||||
* @param listener The listener to remove.
|
||||
*/
|
||||
- (void)removeListener:(id<GNCPeripheralManagerDelegate>)listener;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,194 @@
|
||||
// Copyright 2026 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCPeripheralManagerMultiplexer.h"
|
||||
|
||||
#import <CoreBluetooth/CoreBluetooth.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCPeripheralManager.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@implementation GNCPeripheralManagerMultiplexer {
|
||||
NSHashTable<id<GNCPeripheralManagerDelegate>> *_listeners;
|
||||
dispatch_queue_t _callbackQueue;
|
||||
dispatch_queue_t _syncQueue;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCallbackQueue:(dispatch_queue_t)callbackQueue {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_listeners = [NSHashTable weakObjectsHashTable];
|
||||
_callbackQueue = callbackQueue;
|
||||
_syncQueue = dispatch_queue_create("com.google.nearby.GNCPeripheralManagerMultiplexerSync",
|
||||
DISPATCH_QUEUE_SERIAL);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
return [self initWithCallbackQueue:dispatch_get_main_queue()];
|
||||
}
|
||||
|
||||
- (void)addListener:(id<GNCPeripheralManagerDelegate>)listener {
|
||||
dispatch_async(_syncQueue, ^{
|
||||
[self->_listeners addObject:listener];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)removeListener:(id<GNCPeripheralManagerDelegate>)listener {
|
||||
dispatch_async(_syncQueue, ^{
|
||||
[self->_listeners removeObject:listener];
|
||||
});
|
||||
}
|
||||
|
||||
- (NSArray<id<GNCPeripheralManagerDelegate>> *)allListeners {
|
||||
__block NSArray<id<GNCPeripheralManagerDelegate>> *listeners;
|
||||
dispatch_sync(_syncQueue, ^{
|
||||
listeners = [self->_listeners allObjects];
|
||||
});
|
||||
return listeners;
|
||||
}
|
||||
|
||||
#pragma mark - GNCPeripheralManagerDelegate
|
||||
|
||||
- (void)gnc_peripheralManagerDidUpdateState:(id<GNCPeripheralManager>)peripheral {
|
||||
NSArray<id<GNCPeripheralManagerDelegate>> *listeners = [self allListeners];
|
||||
dispatch_async(_callbackQueue, ^{
|
||||
for (id<GNCPeripheralManagerDelegate> listener in listeners) {
|
||||
[listener gnc_peripheralManagerDidUpdateState:peripheral];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)gnc_peripheralManagerDidStartAdvertising:(id<GNCPeripheralManager>)peripheral
|
||||
error:(nullable NSError *)error {
|
||||
NSArray<id<GNCPeripheralManagerDelegate>> *listeners = [self allListeners];
|
||||
dispatch_async(_callbackQueue, ^{
|
||||
for (id<GNCPeripheralManagerDelegate> listener in listeners) {
|
||||
if ([listener respondsToSelector:@selector(gnc_peripheralManagerDidStartAdvertising:error:)]) {
|
||||
[listener gnc_peripheralManagerDidStartAdvertising:peripheral error:error];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)gnc_peripheralManager:(id<GNCPeripheralManager>)peripheral
|
||||
didAddService:(CBService *)service
|
||||
error:(nullable NSError *)error {
|
||||
NSArray<id<GNCPeripheralManagerDelegate>> *listeners = [self allListeners];
|
||||
dispatch_async(_callbackQueue, ^{
|
||||
for (id<GNCPeripheralManagerDelegate> listener in listeners) {
|
||||
if ([listener respondsToSelector:@selector(gnc_peripheralManager:didAddService:error:)]) {
|
||||
[listener gnc_peripheralManager:peripheral didAddService:service error:error];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)gnc_peripheralManager:(id<GNCPeripheralManager>)peripheral
|
||||
didReceiveReadRequest:(CBATTRequest *)request {
|
||||
NSArray<id<GNCPeripheralManagerDelegate>> *listeners = [self allListeners];
|
||||
dispatch_async(_callbackQueue, ^{
|
||||
for (id<GNCPeripheralManagerDelegate> listener in listeners) {
|
||||
if ([listener respondsToSelector:@selector(gnc_peripheralManager:didReceiveReadRequest:)]) {
|
||||
[listener gnc_peripheralManager:peripheral didReceiveReadRequest:request];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)gnc_peripheralManager:(id<GNCPeripheralManager>)peripheral
|
||||
didPublishL2CAPChannel:(CBL2CAPPSM)PSM
|
||||
error:(nullable NSError *)error {
|
||||
NSArray<id<GNCPeripheralManagerDelegate>> *listeners = [self allListeners];
|
||||
dispatch_async(_callbackQueue, ^{
|
||||
for (id<GNCPeripheralManagerDelegate> listener in listeners) {
|
||||
if ([listener respondsToSelector:@selector(gnc_peripheralManager:didPublishL2CAPChannel:error:)]) {
|
||||
[listener gnc_peripheralManager:peripheral didPublishL2CAPChannel:PSM error:error];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)gnc_peripheralManager:(id<GNCPeripheralManager>)peripheral
|
||||
didUnpublishL2CAPChannel:(CBL2CAPPSM)PSM
|
||||
error:(NSError *)error {
|
||||
NSArray<id<GNCPeripheralManagerDelegate>> *listeners = [self allListeners];
|
||||
dispatch_async(_callbackQueue, ^{
|
||||
for (id<GNCPeripheralManagerDelegate> listener in listeners) {
|
||||
if ([listener respondsToSelector:@selector(gnc_peripheralManager:didUnpublishL2CAPChannel:error:)]) {
|
||||
[listener gnc_peripheralManager:peripheral didUnpublishL2CAPChannel:PSM error:error];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)gnc_peripheralManager:(id<GNCPeripheralManager>)peripheral
|
||||
didOpenL2CAPChannel:(nullable CBL2CAPChannel *)channel
|
||||
error:(nullable NSError *)error {
|
||||
NSArray<id<GNCPeripheralManagerDelegate>> *listeners = [self allListeners];
|
||||
dispatch_async(_callbackQueue, ^{
|
||||
for (id<GNCPeripheralManagerDelegate> listener in listeners) {
|
||||
if ([listener respondsToSelector:@selector(gnc_peripheralManager:didOpenL2CAPChannel:error:)]) {
|
||||
[listener gnc_peripheralManager:peripheral didOpenL2CAPChannel:channel error:error];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - CBPeripheralManagerDelegate
|
||||
|
||||
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
|
||||
[self gnc_peripheralManagerDidUpdateState:peripheral];
|
||||
}
|
||||
|
||||
- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral
|
||||
error:(nullable NSError *)error {
|
||||
[self gnc_peripheralManagerDidStartAdvertising:peripheral error:error];
|
||||
}
|
||||
|
||||
- (void)peripheralManager:(CBPeripheralManager *)peripheral
|
||||
didAddService:(CBService *)service
|
||||
error:(nullable NSError *)error {
|
||||
[self gnc_peripheralManager:peripheral didAddService:service error:error];
|
||||
}
|
||||
|
||||
- (void)peripheralManager:(CBPeripheralManager *)peripheral
|
||||
didReceiveReadRequest:(CBATTRequest *)request {
|
||||
[self gnc_peripheralManager:peripheral didReceiveReadRequest:request];
|
||||
}
|
||||
|
||||
- (void)peripheralManager:(CBPeripheralManager *)peripheral
|
||||
didPublishL2CAPChannel:(CBL2CAPPSM)PSM
|
||||
error:(nullable NSError *)error {
|
||||
[self gnc_peripheralManager:peripheral didPublishL2CAPChannel:PSM error:error];
|
||||
}
|
||||
|
||||
- (void)peripheralManager:(CBPeripheralManager *)peripheral
|
||||
didUnpublishL2CAPChannel:(CBL2CAPPSM)PSM
|
||||
error:(nullable NSError *)error {
|
||||
[self gnc_peripheralManager:peripheral didUnpublishL2CAPChannel:PSM error:error];
|
||||
}
|
||||
|
||||
- (void)peripheralManager:(CBPeripheralManager *)peripheral
|
||||
didOpenL2CAPChannel:(nullable CBL2CAPChannel *)channel
|
||||
error:(nullable NSError *)error {
|
||||
[self gnc_peripheralManager:peripheral didOpenL2CAPChannel:channel error:error];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -30,9 +30,6 @@ objc_library(
|
||||
]) + [
|
||||
"Source/GNSCentral.h",
|
||||
],
|
||||
copts = [
|
||||
"-Wno-enum-compare", # TODO(b/418286948): Remove this when the error is fixed.
|
||||
],
|
||||
deps = [
|
||||
":Shared",
|
||||
"//internal/platform/implementation/apple/Log:GNCLogger",
|
||||
@@ -51,9 +48,6 @@ objc_library(
|
||||
]) + [
|
||||
"Source/GNSPeripheral.h",
|
||||
],
|
||||
copts = [
|
||||
"-Wno-enum-compare", # TODO(b/418286948): Remove this when the error is fixed.
|
||||
],
|
||||
deps = [
|
||||
":Shared",
|
||||
"//internal/platform/implementation/apple/Log:GNCLogger",
|
||||
|
||||
+1
-1
@@ -371,7 +371,7 @@ static NSTimeInterval gKBTCrashLoopMaxTimeBetweenResetting = 15.f;
|
||||
}
|
||||
|
||||
- (void)updateBTCrashLoopHeuristic {
|
||||
NSAssert(_cbPeripheralManager.state == CBCentralManagerStateResetting, @"Unexpected CB state %@",
|
||||
NSAssert(_cbPeripheralManager.state == CBManagerStateResetting, @"Unexpected CB state %@",
|
||||
CBManagerStateString(_cbPeripheralManager.state));
|
||||
NSDate *now = [NSDate date];
|
||||
if ([now timeIntervalSinceDate:_btCrashLastResettingDate] >
|
||||
|
||||
@@ -26,13 +26,13 @@ objc_library(
|
||||
srcs = [
|
||||
"GNCBLEGATTCharacteristicTest.mm",
|
||||
"GNCBLEGATTClientTest.m",
|
||||
"GNCBLEGATTServerTest.m",
|
||||
"GNCBLEGATTServerTest.mm",
|
||||
"GNCBLEL2CAPClientTest.m",
|
||||
"GNCBLEL2CAPConnectionTest.m",
|
||||
"GNCBLEL2CAPFakeInputOutputStream.m",
|
||||
"GNCBLEL2CAPServerTest.m",
|
||||
"GNCBLEL2CAPServerTest.mm",
|
||||
"GNCBLEL2CAPStreamTest.m",
|
||||
"GNCBLEMediumTest.m",
|
||||
"GNCBLEMediumTest.mm",
|
||||
"GNCFakeBLEGATTServer.m",
|
||||
"GNCFakeBLEMedium.m",
|
||||
"GNCFakeCBL2CAPChannel.m",
|
||||
@@ -44,6 +44,7 @@ objc_library(
|
||||
"GNCMBleUtilsTest.m",
|
||||
"GNCMConnectionsTest.m",
|
||||
"GNCMFakeConnection.mm",
|
||||
"GNCPeripheralManagerMultiplexerTest.m",
|
||||
"GNCPeripheralManagerTest.m",
|
||||
"GNCPeripheralTest.m",
|
||||
"NSData+GNCBase85Test.m",
|
||||
@@ -66,6 +67,8 @@ objc_library(
|
||||
"GNCMFakeConnection.h",
|
||||
],
|
||||
deps = [
|
||||
"//connections/implementation/flags:connections_flags",
|
||||
"//internal/flags:nearby_flags",
|
||||
"//internal/platform/implementation/apple", # buildcleaner: keep
|
||||
"//internal/platform/implementation/apple/Mediums/BLE",
|
||||
"//internal/platform/implementation/apple/Mediums/BLE/Sockets:Shared",
|
||||
|
||||
+362
-104
@@ -22,6 +22,9 @@
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEGATTServer+Testing.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheralManager.h"
|
||||
|
||||
#include "connections/implementation/flags/nearby_connections_feature_flags.h"
|
||||
#include "internal/flags/nearby_flags.h"
|
||||
|
||||
static NSString *const kServiceUUID1 = @"0000FEF3-0000-1000-8000-00805F9B34FB";
|
||||
static NSString *const kServiceUUID2 = @"0000FEF4-0000-1000-8000-00805F9B34FB";
|
||||
static NSString *const kCharacteristicUUID1 = @"00000000-0000-3000-8000-000000000000";
|
||||
@@ -34,11 +37,60 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000
|
||||
|
||||
#pragma mark - Create Characteristic
|
||||
|
||||
- (void)testCreateCharacteristic {
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
- (void)tearDown {
|
||||
nearby::NearbyFlags::GetInstance().ResetOverridedValues();
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager];
|
||||
- (void)testInit_setsDelegateCorrectlyBasedOnFlag {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEGATTServer *server =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
|
||||
if (enabled.boolValue) {
|
||||
// In shared mode, functionality is delegated to the multiplexer, so the server should NOT
|
||||
// self-assign as delegate.
|
||||
XCTAssertNil(fakePeripheralManager.peripheralDelegate);
|
||||
} else {
|
||||
// In legacy mode, the server owns the manager and sets itself as delegate.
|
||||
XCTAssertEqual(fakePeripheralManager.peripheralDelegate, server);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testInit_throwsWithNilManagerWhenFlagEnabled {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
true);
|
||||
|
||||
XCTAssertThrowsSpecificNamed(
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:(id<GNCPeripheralManager>)nil
|
||||
queue:dispatch_get_main_queue()],
|
||||
NSException, NSInvalidArgumentException);
|
||||
}
|
||||
|
||||
- (void)testCreateCharacteristic {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
if (enabled.boolValue) {
|
||||
fakePeripheralManager.peripheralDelegate = gattServer;
|
||||
}
|
||||
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
|
||||
@@ -62,13 +114,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testCreateMultipleCharacteristicsForOneService {
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager];
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
if (enabled.boolValue) {
|
||||
fakePeripheralManager.peripheralDelegate = gattServer;
|
||||
}
|
||||
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
|
||||
@@ -105,13 +167,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000
|
||||
[self waitForExpectations:@[ expectation1, expectation2 ] timeout:3];
|
||||
XCTAssertEqual(fakePeripheralManager.services.count, 1);
|
||||
XCTAssertEqual(fakePeripheralManager.services[0].characteristics.count, 2);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testCreateDuplicateCharacteristics {
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager];
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
if (enabled.boolValue) {
|
||||
fakePeripheralManager.peripheralDelegate = gattServer;
|
||||
}
|
||||
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
|
||||
@@ -140,13 +212,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
XCTAssertEqual(fakePeripheralManager.services.count, 1);
|
||||
XCTAssertEqual(fakePeripheralManager.services[0].characteristics.count, 1);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testCreateDuplicatePendingCharacteristics {
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager];
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
if (enabled.boolValue) {
|
||||
fakePeripheralManager.peripheralDelegate = gattServer;
|
||||
}
|
||||
|
||||
XCTestExpectation *expectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"Create characteristic."];
|
||||
@@ -171,13 +253,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testCreateCharacteristicNotPoweredOn {
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager];
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
if (enabled.boolValue) {
|
||||
fakePeripheralManager.peripheralDelegate = gattServer;
|
||||
}
|
||||
|
||||
XCTestExpectation *expectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"Create characteristic."];
|
||||
@@ -198,13 +290,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testCreateCharacteristicServiceFailure {
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager];
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
if (enabled.boolValue) {
|
||||
fakePeripheralManager.peripheralDelegate = gattServer;
|
||||
}
|
||||
|
||||
fakePeripheralManager.didAddServiceError = [NSError errorWithDomain:@"fake" code:0 userInfo:nil];
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
@@ -228,15 +330,25 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Read Request
|
||||
|
||||
- (void)testReadRequest {
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager];
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
if (enabled.boolValue) {
|
||||
fakePeripheralManager.peripheralDelegate = gattServer;
|
||||
}
|
||||
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
|
||||
@@ -265,14 +377,24 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000
|
||||
characteristic:characteristicUUID];
|
||||
|
||||
[self waitForExpectations:@[ fakePeripheralManager.respondToRequestSuccessExpectation ]
|
||||
timeout:0];
|
||||
timeout:3];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testReadRequestInvalidService {
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
- (void)testReadRequestInvalidCharacteristic {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager];
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
if (enabled.boolValue) {
|
||||
fakePeripheralManager.peripheralDelegate = gattServer;
|
||||
}
|
||||
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
|
||||
@@ -296,73 +418,106 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000
|
||||
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
|
||||
CBUUID *invalidCharacteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID2];
|
||||
|
||||
[fakePeripheralManager
|
||||
simulatePeripheralManagerDidReceiveReadRequestForService:serviceUUID
|
||||
characteristic:invalidCharacteristicUUID];
|
||||
|
||||
[self waitForExpectations:@[ fakePeripheralManager.respondToRequestErrorExpectation ] timeout:3];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testReadRequestInvalidService {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
if (enabled.boolValue) {
|
||||
fakePeripheralManager.peripheralDelegate = gattServer;
|
||||
}
|
||||
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
|
||||
XCTestExpectation *expectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"Create characteristic."];
|
||||
|
||||
CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1];
|
||||
CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1];
|
||||
[gattServer createCharacteristicWithServiceID:serviceUUID
|
||||
characteristicUUID:characteristicUUID
|
||||
permissions:CBAttributePermissionsReadable
|
||||
properties:CBCharacteristicPropertyRead
|
||||
completionHandler:^(GNCBLEGATTCharacteristic *characteristic,
|
||||
NSError *error) {
|
||||
[expectation fulfill];
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
|
||||
CBUUID *invalidServiceUUID = [CBUUID UUIDWithString:kServiceUUID2];
|
||||
|
||||
// Expectation that we *should not* receive a response.
|
||||
fakePeripheralManager.respondToRequestSuccessExpectation.inverted = YES;
|
||||
fakePeripheralManager.respondToRequestErrorExpectation.inverted = YES;
|
||||
|
||||
[fakePeripheralManager
|
||||
simulatePeripheralManagerDidReceiveReadRequestForService:invalidServiceUUID
|
||||
characteristic:characteristicUUID];
|
||||
|
||||
[self waitForExpectations:@[ fakePeripheralManager.respondToRequestErrorExpectation ] timeout:0];
|
||||
}
|
||||
|
||||
- (void)testReadRequestInvalidCharacteristic {
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager];
|
||||
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
|
||||
XCTestExpectation *expectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"Create and update characteristic."];
|
||||
|
||||
CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1];
|
||||
CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1];
|
||||
[gattServer createCharacteristicWithServiceID:serviceUUID
|
||||
characteristicUUID:characteristicUUID
|
||||
permissions:CBAttributePermissionsReadable
|
||||
properties:CBCharacteristicPropertyRead
|
||||
completionHandler:^(GNCBLEGATTCharacteristic *characteristic,
|
||||
NSError *error) {
|
||||
[gattServer updateCharacteristic:characteristic
|
||||
value:[NSData data]
|
||||
completionHandler:^(NSError *error) {
|
||||
[expectation fulfill];
|
||||
}];
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
|
||||
CBUUID *invalidCharacteristicUUID =
|
||||
[CBUUID UUIDWithString:kCharacteristicUUID2];
|
||||
|
||||
[fakePeripheralManager
|
||||
simulatePeripheralManagerDidReceiveReadRequestForService:serviceUUID
|
||||
characteristic:invalidCharacteristicUUID];
|
||||
|
||||
[self waitForExpectations:@[ fakePeripheralManager.respondToRequestErrorExpectation ] timeout:0];
|
||||
[self waitForExpectations:@[
|
||||
fakePeripheralManager.respondToRequestSuccessExpectation,
|
||||
fakePeripheralManager.respondToRequestErrorExpectation
|
||||
]
|
||||
timeout:3];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Stop
|
||||
|
||||
- (void)testStop {
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager];
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
if (enabled.boolValue) {
|
||||
fakePeripheralManager.peripheralDelegate = gattServer;
|
||||
}
|
||||
|
||||
[gattServer stop];
|
||||
[gattServer stop];
|
||||
|
||||
XCTAssertEqual(fakePeripheralManager.services.count, 0);
|
||||
XCTAssertEqual(fakePeripheralManager.services.count, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Start Advertising
|
||||
|
||||
- (void)testStartAdvertisingNoServiceData {
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager];
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
if (enabled.boolValue) {
|
||||
fakePeripheralManager.peripheralDelegate = gattServer;
|
||||
}
|
||||
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
|
||||
@@ -377,13 +532,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testStartAdvertisingEmptyServiceData {
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager];
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
if (enabled.boolValue) {
|
||||
fakePeripheralManager.peripheralDelegate = gattServer;
|
||||
}
|
||||
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
|
||||
@@ -402,13 +567,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testStartAdvertisingShortServiceData {
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager];
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
if (enabled.boolValue) {
|
||||
fakePeripheralManager.peripheralDelegate = gattServer;
|
||||
}
|
||||
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
|
||||
@@ -429,13 +604,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testStartAdvertising20ByteServiceData {
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager];
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
if (enabled.boolValue) {
|
||||
fakePeripheralManager.peripheralDelegate = gattServer;
|
||||
}
|
||||
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
|
||||
@@ -457,13 +642,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testStartAdvertisingLongServiceData {
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager];
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
if (enabled.boolValue) {
|
||||
fakePeripheralManager.peripheralDelegate = gattServer;
|
||||
}
|
||||
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
|
||||
@@ -482,13 +677,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testStartAdvertisingWithEmojiServiceData {
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager];
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
if (enabled.boolValue) {
|
||||
fakePeripheralManager.peripheralDelegate = gattServer;
|
||||
}
|
||||
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
|
||||
@@ -510,13 +715,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testStartAdvertisingMultipleServices {
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager];
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
if (enabled.boolValue) {
|
||||
fakePeripheralManager.peripheralDelegate = gattServer;
|
||||
}
|
||||
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
|
||||
@@ -536,13 +751,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testStartAdvertisingNotPoweredOn {
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager];
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
if (enabled.boolValue) {
|
||||
fakePeripheralManager.peripheralDelegate = gattServer;
|
||||
}
|
||||
|
||||
XCTestExpectation *expectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"Start advertising."];
|
||||
@@ -556,13 +781,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testStartAdvertisingStartFailure {
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager];
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
if (enabled.boolValue) {
|
||||
fakePeripheralManager.peripheralDelegate = gattServer;
|
||||
}
|
||||
|
||||
fakePeripheralManager.didStartAdvertisingError = [NSError errorWithDomain:@"fake"
|
||||
code:0
|
||||
@@ -581,13 +816,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testStartAdvertisingAlreadyAdvertising {
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager];
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
if (enabled.boolValue) {
|
||||
fakePeripheralManager.peripheralDelegate = gattServer;
|
||||
}
|
||||
|
||||
fakePeripheralManager.didStartAdvertisingError = [NSError errorWithDomain:@"fake"
|
||||
code:0
|
||||
@@ -608,13 +853,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testStartStopStartAdvertising {
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager];
|
||||
GNCBLEGATTServer *gattServer =
|
||||
[[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
if (enabled.boolValue) {
|
||||
fakePeripheralManager.peripheralDelegate = gattServer;
|
||||
}
|
||||
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
|
||||
@@ -648,6 +903,9 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -1,329 +0,0 @@
|
||||
// Copyright 2025 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPServer.h"
|
||||
|
||||
#import <CoreBluetooth/CoreBluetooth.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEL2CAPServer+Testing.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheralManager.h"
|
||||
|
||||
static const NSTimeInterval kTestTimeout = 1.0;
|
||||
|
||||
@interface GNCBLEL2CAPServerTest : XCTestCase
|
||||
@end
|
||||
|
||||
@implementation GNCBLEL2CAPServerTest
|
||||
|
||||
#pragma mark Tests
|
||||
|
||||
- (void)testInit {
|
||||
GNCBLEL2CAPServer *l2capServer = [[GNCBLEL2CAPServer alloc] init];
|
||||
XCTAssertNotNil(l2capServer);
|
||||
XCTAssertNil([l2capServer valueForKey:@"_peripheralManager"]);
|
||||
}
|
||||
|
||||
- (void)testPublishL2CAPChannelAndOpenChannelWhenStartListeningChannel {
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEL2CAPServer *l2capServer =
|
||||
[[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
XCTestExpectation *psmPublishedExpectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"PSM published."];
|
||||
XCTestExpectation *channelOpenedexpectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"Channel opened."];
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
|
||||
[l2capServer
|
||||
startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM,
|
||||
NSError *_Nullable error) {
|
||||
XCTAssertEqual(error, nil);
|
||||
XCTAssertEqual(PSM, fakePeripheralManager.PSM);
|
||||
[psmPublishedExpectation fulfill];
|
||||
}
|
||||
channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream,
|
||||
NSError *_Nullable error) {
|
||||
XCTAssertNil(error);
|
||||
XCTAssertNotNil(stream);
|
||||
[channelOpenedexpectation fulfill];
|
||||
}];
|
||||
[self waitForExpectations:@[ psmPublishedExpectation, channelOpenedexpectation ]
|
||||
timeout:kTestTimeout];
|
||||
XCTAssertNotNil([l2capServer valueForKey:@"l2CAPChannel"]);
|
||||
XCTAssertNotNil([l2capServer valueForKey:@"l2CAPStream"]);
|
||||
}
|
||||
|
||||
- (void)testFailedToPublishL2CAPChannel {
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
fakePeripheralManager.didPublishL2CAPChannelError = [NSError errorWithDomain:@"fake"
|
||||
code:0
|
||||
userInfo:nil];
|
||||
GNCBLEL2CAPServer *l2capServer =
|
||||
[[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
XCTestExpectation *psmPublishedExpectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"PSM published with error."];
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
|
||||
[l2capServer
|
||||
startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM,
|
||||
NSError *_Nullable error) {
|
||||
XCTAssertEqual(error, fakePeripheralManager.didPublishL2CAPChannelError);
|
||||
XCTAssertEqual(PSM, 0);
|
||||
[psmPublishedExpectation fulfill];
|
||||
}
|
||||
channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream,
|
||||
NSError *_Nullable error){
|
||||
}];
|
||||
[self waitForExpectations:@[ psmPublishedExpectation ] timeout:kTestTimeout];
|
||||
}
|
||||
|
||||
- (void)testPoweredOffUnpublishesChannel {
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEL2CAPServer *l2capServer =
|
||||
[[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
|
||||
[l2capServer
|
||||
startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM,
|
||||
NSError *_Nullable error) {
|
||||
XCTAssertEqual(error, nil);
|
||||
XCTAssertEqual(PSM, fakePeripheralManager.PSM);
|
||||
}
|
||||
channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream,
|
||||
NSError *_Nullable error){
|
||||
}];
|
||||
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOff];
|
||||
|
||||
[self waitForExpectations:@[ fakePeripheralManager.unpublishExpectation ] timeout:kTestTimeout];
|
||||
}
|
||||
|
||||
- (void)testPeripheralManagerDidUpdateStatePoweredOffUnpublishesChannel {
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEL2CAPServer *l2capServer =
|
||||
[[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
fakePeripheralManager.state = CBManagerStatePoweredOn;
|
||||
[(id<CBPeripheralManagerDelegate>)l2capServer
|
||||
peripheralManagerDidUpdateState:(CBPeripheralManager *)fakePeripheralManager];
|
||||
|
||||
[l2capServer
|
||||
startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM,
|
||||
NSError *_Nullable error) {
|
||||
XCTAssertEqual(error, nil);
|
||||
XCTAssertEqual(PSM, fakePeripheralManager.PSM);
|
||||
}
|
||||
channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream,
|
||||
NSError *_Nullable error){
|
||||
}];
|
||||
|
||||
fakePeripheralManager.state = CBManagerStatePoweredOff;
|
||||
[(id<CBPeripheralManagerDelegate>)l2capServer
|
||||
peripheralManagerDidUpdateState:(CBPeripheralManager *)fakePeripheralManager];
|
||||
|
||||
[self waitForExpectations:@[ fakePeripheralManager.unpublishExpectation ] timeout:kTestTimeout];
|
||||
}
|
||||
|
||||
- (void)testStartPeripheralManagerInitiallyOff {
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEL2CAPServer *l2capServer =
|
||||
[[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
XCTestExpectation *psmPublishedExpectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"PSM published."];
|
||||
XCTestExpectation *channelOpenedexpectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"Channel opened."];
|
||||
|
||||
[l2capServer
|
||||
startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM,
|
||||
NSError *_Nullable error) {
|
||||
XCTAssertEqual(error, nil);
|
||||
XCTAssertEqual(PSM, fakePeripheralManager.PSM);
|
||||
[psmPublishedExpectation fulfill];
|
||||
}
|
||||
channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream,
|
||||
NSError *_Nullable error) {
|
||||
[channelOpenedexpectation fulfill];
|
||||
}];
|
||||
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
[self waitForExpectations:@[ psmPublishedExpectation, channelOpenedexpectation ]
|
||||
timeout:kTestTimeout];
|
||||
}
|
||||
|
||||
- (void)testClose {
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEL2CAPServer *l2capServer =
|
||||
[[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
|
||||
[l2capServer
|
||||
startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM,
|
||||
NSError *_Nullable error) {
|
||||
XCTAssertEqual(error, nil);
|
||||
XCTAssertEqual(PSM, fakePeripheralManager.PSM);
|
||||
}
|
||||
channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream,
|
||||
NSError *_Nullable error){
|
||||
}];
|
||||
|
||||
[l2capServer close];
|
||||
|
||||
XCTAssertEqual([l2capServer PSM], 0);
|
||||
}
|
||||
|
||||
- (void)testCloseDoesNotUnpublishesChannelIfNotConnected {
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
fakePeripheralManager.unpublishExpectation.inverted = YES;
|
||||
GNCBLEL2CAPServer *l2capServer =
|
||||
[[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
|
||||
[l2capServer close];
|
||||
|
||||
[self waitForExpectations:@[ fakePeripheralManager.unpublishExpectation ] timeout:kTestTimeout];
|
||||
}
|
||||
|
||||
- (void)testFailedToOpenL2CAPChannel {
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
fakePeripheralManager.didOpenL2CAPChannelError = [NSError errorWithDomain:@"fake"
|
||||
code:0
|
||||
userInfo:nil];
|
||||
GNCBLEL2CAPServer *l2capServer =
|
||||
[[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
XCTestExpectation *channelOpenedexpectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"Channel opened."];
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
|
||||
[l2capServer
|
||||
startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM,
|
||||
NSError *_Nullable error) {
|
||||
XCTAssertEqual(error, nil);
|
||||
XCTAssertEqual(PSM, fakePeripheralManager.PSM);
|
||||
}
|
||||
channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream,
|
||||
NSError *_Nullable error) {
|
||||
XCTAssertNil(stream);
|
||||
XCTAssertEqual(error, fakePeripheralManager.didOpenL2CAPChannelError);
|
||||
[channelOpenedexpectation fulfill];
|
||||
}];
|
||||
[self waitForExpectations:@[ channelOpenedexpectation ] timeout:kTestTimeout];
|
||||
XCTAssertNil([l2capServer valueForKey:@"l2CAPChannel"]);
|
||||
XCTAssertNil([l2capServer valueForKey:@"l2CAPStream"]);
|
||||
}
|
||||
|
||||
- (void)testPeripheralManagerDidUnpublishL2CAPChannel {
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEL2CAPServer *l2capServer =
|
||||
[[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
[l2capServer
|
||||
startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM,
|
||||
NSError *_Nullable error) {
|
||||
}
|
||||
channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream,
|
||||
NSError *_Nullable error){
|
||||
}];
|
||||
|
||||
[(id<CBPeripheralManagerDelegate>)l2capServer
|
||||
peripheralManager:(CBPeripheralManager *)fakePeripheralManager
|
||||
didUnpublishL2CAPChannel:l2capServer.PSM
|
||||
error:[NSError errorWithDomain:@"fake" code:0 userInfo:nil]];
|
||||
|
||||
XCTAssertEqual(l2capServer.PSM, 0);
|
||||
}
|
||||
|
||||
- (void)testCloseL2CAPChannel {
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEL2CAPServer *l2capServer =
|
||||
[[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
XCTestExpectation *channelOpenedexpectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"Channel opened."];
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
[l2capServer
|
||||
startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM,
|
||||
NSError *_Nullable error) {
|
||||
}
|
||||
channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream,
|
||||
NSError *_Nullable error) {
|
||||
[channelOpenedexpectation fulfill];
|
||||
}];
|
||||
[self waitForExpectations:@[ channelOpenedexpectation ] timeout:kTestTimeout];
|
||||
|
||||
[l2capServer closeL2CAPChannel];
|
||||
|
||||
XCTAssertNil([l2capServer valueForKey:@"l2CAPChannel"]);
|
||||
XCTAssertNil([l2capServer valueForKey:@"l2CAPStream"]);
|
||||
}
|
||||
|
||||
- (void)testPeripheralManagerDidPublishL2CAPChannel {
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEL2CAPServer *l2capServer =
|
||||
[[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
XCTestExpectation *expectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"completion called"];
|
||||
[l2capServer
|
||||
startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM,
|
||||
NSError *_Nullable error) {
|
||||
XCTAssertEqual(PSM, 1);
|
||||
XCTAssertNil(error);
|
||||
[expectation fulfill];
|
||||
}
|
||||
channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream,
|
||||
NSError *_Nullable error){
|
||||
}];
|
||||
[(id<CBPeripheralManagerDelegate>)l2capServer
|
||||
peripheralManager:(CBPeripheralManager *)fakePeripheralManager
|
||||
didPublishL2CAPChannel:1
|
||||
error:nil];
|
||||
[self waitForExpectations:@[ expectation ] timeout:kTestTimeout];
|
||||
XCTAssertEqual(l2capServer.PSM, 1);
|
||||
}
|
||||
|
||||
- (void)testPeripheralManagerDidPublishL2CAPChannelWithError {
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEL2CAPServer *l2capServer =
|
||||
[[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
XCTestExpectation *expectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"completion called"];
|
||||
NSError *publishError = [NSError errorWithDomain:@"test" code:0 userInfo:nil];
|
||||
[l2capServer
|
||||
startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM,
|
||||
NSError *_Nullable error) {
|
||||
XCTAssertEqual(PSM, 0);
|
||||
XCTAssertEqualObjects(error, publishError);
|
||||
[expectation fulfill];
|
||||
}
|
||||
channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream,
|
||||
NSError *_Nullable error){
|
||||
}];
|
||||
[(id<CBPeripheralManagerDelegate>)l2capServer
|
||||
peripheralManager:(CBPeripheralManager *)fakePeripheralManager
|
||||
didPublishL2CAPChannel:0
|
||||
error:publishError];
|
||||
[self waitForExpectations:@[ expectation ] timeout:kTestTimeout];
|
||||
XCTAssertEqual(l2capServer.PSM, 0);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,485 @@
|
||||
// Copyright 2025 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPServer.h"
|
||||
|
||||
#import <CoreBluetooth/CoreBluetooth.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEL2CAPServer+Testing.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheralManager.h"
|
||||
|
||||
#include "connections/implementation/flags/nearby_connections_feature_flags.h"
|
||||
#include "internal/flags/nearby_flags.h"
|
||||
|
||||
static const NSTimeInterval kTestTimeout = 1.0;
|
||||
|
||||
@interface GNCBLEL2CAPServerTest : XCTestCase
|
||||
@end
|
||||
|
||||
@implementation GNCBLEL2CAPServerTest
|
||||
|
||||
#pragma mark Tests
|
||||
|
||||
- (void)tearDown {
|
||||
nearby::NearbyFlags::GetInstance().ResetOverridedValues();
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
- (void)testInit_setsDelegateCorrectlyBasedOnFlag {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEL2CAPServer *server =
|
||||
[[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
|
||||
if (enabled.boolValue) {
|
||||
// In shared mode, functionality is delegated to the multiplexer, so the server should NOT
|
||||
// self-assign as delegate.
|
||||
XCTAssertNil(fakePeripheralManager.peripheralDelegate);
|
||||
} else {
|
||||
// In legacy mode, the server owns the manager and sets itself as delegate.
|
||||
XCTAssertEqual(fakePeripheralManager.peripheralDelegate, server);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testInit_throwsWithNilManagerWhenFlagEnabled {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
true);
|
||||
|
||||
XCTAssertThrowsSpecificNamed(
|
||||
[[GNCBLEL2CAPServer alloc] initWithPeripheralManager:(id<GNCPeripheralManager>)nil
|
||||
queue:dispatch_get_main_queue()],
|
||||
NSException, NSInvalidArgumentException);
|
||||
}
|
||||
|
||||
- (void)testPublishL2CAPChannelAndOpenChannelWhenStartListeningChannel {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEL2CAPServer *l2capServer =
|
||||
[[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
if (enabled.boolValue) {
|
||||
fakePeripheralManager.peripheralDelegate = l2capServer;
|
||||
}
|
||||
|
||||
XCTestExpectation *psmPublishedExpectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"PSM published."];
|
||||
XCTestExpectation *channelOpenedexpectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"Channel opened."];
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
|
||||
[l2capServer
|
||||
startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM,
|
||||
NSError *_Nullable error) {
|
||||
XCTAssertEqual(error, nil);
|
||||
XCTAssertEqual(PSM, fakePeripheralManager.PSM);
|
||||
[psmPublishedExpectation fulfill];
|
||||
}
|
||||
channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream,
|
||||
NSError *_Nullable error) {
|
||||
XCTAssertNil(error);
|
||||
XCTAssertNotNil(stream);
|
||||
[channelOpenedexpectation fulfill];
|
||||
}];
|
||||
[self waitForExpectations:@[ psmPublishedExpectation, channelOpenedexpectation ]
|
||||
timeout:kTestTimeout];
|
||||
XCTAssertNotNil([l2capServer valueForKey:@"l2CAPChannel"]);
|
||||
XCTAssertNotNil([l2capServer valueForKey:@"l2CAPStream"]);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testFailedToPublishL2CAPChannel {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
fakePeripheralManager.didPublishL2CAPChannelError = [NSError errorWithDomain:@"fake"
|
||||
code:0
|
||||
userInfo:nil];
|
||||
GNCBLEL2CAPServer *l2capServer =
|
||||
[[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
if (enabled.boolValue) {
|
||||
fakePeripheralManager.peripheralDelegate = l2capServer;
|
||||
}
|
||||
|
||||
XCTestExpectation *psmPublishedExpectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"PSM published with error."];
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
|
||||
[l2capServer
|
||||
startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM,
|
||||
NSError *_Nullable error) {
|
||||
XCTAssertEqual(error, fakePeripheralManager.didPublishL2CAPChannelError);
|
||||
XCTAssertEqual(PSM, 0);
|
||||
[psmPublishedExpectation fulfill];
|
||||
}
|
||||
channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream,
|
||||
NSError *_Nullable error){
|
||||
}];
|
||||
[self waitForExpectations:@[ psmPublishedExpectation ] timeout:kTestTimeout];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testPoweredOffUnpublishesChannel {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEL2CAPServer *l2capServer =
|
||||
[[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
if (enabled.boolValue) {
|
||||
fakePeripheralManager.peripheralDelegate = l2capServer;
|
||||
}
|
||||
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
|
||||
[l2capServer
|
||||
startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM,
|
||||
NSError *_Nullable error) {
|
||||
XCTAssertEqual(error, nil);
|
||||
XCTAssertEqual(PSM, fakePeripheralManager.PSM);
|
||||
}
|
||||
channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream,
|
||||
NSError *_Nullable error){
|
||||
}];
|
||||
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOff];
|
||||
|
||||
[self waitForExpectations:@[ fakePeripheralManager.unpublishExpectation ] timeout:kTestTimeout];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testPeripheralManagerDidUpdateStatePoweredOffUnpublishesChannel {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEL2CAPServer *l2capServer =
|
||||
[[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
if (enabled.boolValue) {
|
||||
fakePeripheralManager.peripheralDelegate = l2capServer;
|
||||
}
|
||||
fakePeripheralManager.state = CBManagerStatePoweredOn;
|
||||
[(id<CBPeripheralManagerDelegate>)l2capServer
|
||||
peripheralManagerDidUpdateState:(CBPeripheralManager *)fakePeripheralManager];
|
||||
|
||||
[l2capServer
|
||||
startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM,
|
||||
NSError *_Nullable error) {
|
||||
XCTAssertEqual(error, nil);
|
||||
XCTAssertEqual(PSM, fakePeripheralManager.PSM);
|
||||
}
|
||||
channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream,
|
||||
NSError *_Nullable error){
|
||||
}];
|
||||
|
||||
fakePeripheralManager.state = CBManagerStatePoweredOff;
|
||||
[(id<CBPeripheralManagerDelegate>)l2capServer
|
||||
peripheralManagerDidUpdateState:(CBPeripheralManager *)fakePeripheralManager];
|
||||
|
||||
[self waitForExpectations:@[ fakePeripheralManager.unpublishExpectation ] timeout:kTestTimeout];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testStartPeripheralManagerInitiallyOff {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEL2CAPServer *l2capServer =
|
||||
[[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
if (enabled.boolValue) {
|
||||
fakePeripheralManager.peripheralDelegate = l2capServer;
|
||||
}
|
||||
|
||||
XCTestExpectation *psmPublishedExpectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"PSM published."];
|
||||
XCTestExpectation *channelOpenedexpectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"Channel opened."];
|
||||
|
||||
[l2capServer
|
||||
startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM,
|
||||
NSError *_Nullable error) {
|
||||
XCTAssertEqual(error, nil);
|
||||
XCTAssertEqual(PSM, fakePeripheralManager.PSM);
|
||||
[psmPublishedExpectation fulfill];
|
||||
}
|
||||
channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream,
|
||||
NSError *_Nullable error) {
|
||||
[channelOpenedexpectation fulfill];
|
||||
}];
|
||||
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
[self waitForExpectations:@[ psmPublishedExpectation, channelOpenedexpectation ]
|
||||
timeout:kTestTimeout];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testClose {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEL2CAPServer *l2capServer =
|
||||
[[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
if (enabled.boolValue) {
|
||||
fakePeripheralManager.peripheralDelegate = l2capServer;
|
||||
}
|
||||
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
|
||||
[l2capServer
|
||||
startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM,
|
||||
NSError *_Nullable error) {
|
||||
XCTAssertEqual(error, nil);
|
||||
XCTAssertEqual(PSM, fakePeripheralManager.PSM);
|
||||
}
|
||||
channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream,
|
||||
NSError *_Nullable error){
|
||||
}];
|
||||
|
||||
[l2capServer close];
|
||||
|
||||
XCTAssertEqual([l2capServer PSM], 0);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testCloseDoesNotUnpublishesChannelIfNotConnected {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
fakePeripheralManager.unpublishExpectation.inverted = YES;
|
||||
GNCBLEL2CAPServer *l2capServer =
|
||||
[[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
if (enabled.boolValue) {
|
||||
fakePeripheralManager.peripheralDelegate = l2capServer;
|
||||
}
|
||||
|
||||
[l2capServer close];
|
||||
|
||||
[self waitForExpectations:@[ fakePeripheralManager.unpublishExpectation ] timeout:kTestTimeout];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testFailedToOpenL2CAPChannel {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
fakePeripheralManager.didOpenL2CAPChannelError = [NSError errorWithDomain:@"fake"
|
||||
code:0
|
||||
userInfo:nil];
|
||||
GNCBLEL2CAPServer *l2capServer =
|
||||
[[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
if (enabled.boolValue) {
|
||||
fakePeripheralManager.peripheralDelegate = l2capServer;
|
||||
}
|
||||
|
||||
XCTestExpectation *channelOpenedexpectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"Channel opened."];
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
|
||||
[l2capServer
|
||||
startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM,
|
||||
NSError *_Nullable error) {
|
||||
XCTAssertEqual(error, nil);
|
||||
XCTAssertEqual(PSM, fakePeripheralManager.PSM);
|
||||
}
|
||||
channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream,
|
||||
NSError *_Nullable error) {
|
||||
XCTAssertNil(stream);
|
||||
XCTAssertEqual(error, fakePeripheralManager.didOpenL2CAPChannelError);
|
||||
[channelOpenedexpectation fulfill];
|
||||
}];
|
||||
[self waitForExpectations:@[ channelOpenedexpectation ] timeout:kTestTimeout];
|
||||
XCTAssertNil([l2capServer valueForKey:@"l2CAPChannel"]);
|
||||
XCTAssertNil([l2capServer valueForKey:@"l2CAPStream"]);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testPeripheralManagerDidUnpublishL2CAPChannel {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEL2CAPServer *l2capServer =
|
||||
[[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
if (enabled.boolValue) {
|
||||
fakePeripheralManager.peripheralDelegate = l2capServer;
|
||||
}
|
||||
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
[l2capServer
|
||||
startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM,
|
||||
NSError *_Nullable error) {
|
||||
}
|
||||
channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream,
|
||||
NSError *_Nullable error){
|
||||
}];
|
||||
|
||||
[(id<CBPeripheralManagerDelegate>)l2capServer
|
||||
peripheralManager:(CBPeripheralManager *)fakePeripheralManager
|
||||
didUnpublishL2CAPChannel:l2capServer.PSM
|
||||
error:[NSError errorWithDomain:@"fake" code:0 userInfo:nil]];
|
||||
|
||||
XCTAssertEqual(l2capServer.PSM, 0);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testCloseL2CAPChannel {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEL2CAPServer *l2capServer =
|
||||
[[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
if (enabled.boolValue) {
|
||||
fakePeripheralManager.peripheralDelegate = l2capServer;
|
||||
}
|
||||
|
||||
XCTestExpectation *channelOpenedexpectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"Channel opened."];
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
[l2capServer
|
||||
startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM,
|
||||
NSError *_Nullable error) {
|
||||
}
|
||||
channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream,
|
||||
NSError *_Nullable error) {
|
||||
[channelOpenedexpectation fulfill];
|
||||
}];
|
||||
[self waitForExpectations:@[ channelOpenedexpectation ] timeout:kTestTimeout];
|
||||
|
||||
[l2capServer closeL2CAPChannel];
|
||||
|
||||
XCTAssertNil([l2capServer valueForKey:@"l2CAPChannel"]);
|
||||
XCTAssertNil([l2capServer valueForKey:@"l2CAPStream"]);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testPeripheralManagerDidPublishL2CAPChannel {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEL2CAPServer *l2capServer =
|
||||
[[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
if (enabled.boolValue) {
|
||||
fakePeripheralManager.peripheralDelegate = l2capServer;
|
||||
}
|
||||
|
||||
XCTestExpectation *expectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"completion called"];
|
||||
[l2capServer
|
||||
startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM,
|
||||
NSError *_Nullable error) {
|
||||
XCTAssertEqual(PSM, 1);
|
||||
XCTAssertNil(error);
|
||||
[expectation fulfill];
|
||||
}
|
||||
channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream,
|
||||
NSError *_Nullable error){
|
||||
}];
|
||||
[(id<CBPeripheralManagerDelegate>)l2capServer
|
||||
peripheralManager:(CBPeripheralManager *)fakePeripheralManager
|
||||
didPublishL2CAPChannel:1
|
||||
error:nil];
|
||||
[self waitForExpectations:@[ expectation ] timeout:kTestTimeout];
|
||||
XCTAssertEqual(l2capServer.PSM, 1);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testPeripheralManagerDidPublishL2CAPChannelWithError {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEL2CAPServer *l2capServer =
|
||||
[[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
if (enabled.boolValue) {
|
||||
fakePeripheralManager.peripheralDelegate = l2capServer;
|
||||
}
|
||||
|
||||
XCTestExpectation *expectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"completion called"];
|
||||
NSError *publishError = [NSError errorWithDomain:@"test" code:0 userInfo:nil];
|
||||
[l2capServer
|
||||
startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM,
|
||||
NSError *_Nullable error) {
|
||||
XCTAssertEqual(PSM, 0);
|
||||
XCTAssertEqualObjects(error, publishError);
|
||||
[expectation fulfill];
|
||||
}
|
||||
channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream,
|
||||
NSError *_Nullable error){
|
||||
}];
|
||||
[(id<CBPeripheralManagerDelegate>)l2capServer
|
||||
peripheralManager:(CBPeripheralManager *)fakePeripheralManager
|
||||
didPublishL2CAPChannel:0
|
||||
error:publishError];
|
||||
[self waitForExpectations:@[ expectation ] timeout:kTestTimeout];
|
||||
XCTAssertEqual(l2capServer.PSM, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -24,16 +24,19 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@interface GNCBLEMedium (Testing)
|
||||
|
||||
/**
|
||||
* Creates a BLE Medium with a provided central manager.
|
||||
* Creates a BLE Medium with a provided central manager and peripheral manager.
|
||||
*
|
||||
* This is only exposed for testing and can be used to inject a fake central manager.
|
||||
* This is only exposed for tests and can be used to inject a fake central manager and
|
||||
* peripheral manager.
|
||||
*
|
||||
* @param centralManager The central manager instance.
|
||||
* @param peripheralManager The peripheral manager instance.
|
||||
* @param queue The queue to run on, this must match the queue that the central manager's delegate
|
||||
* is running on. Defaults to the main queue when @c nil.
|
||||
* is running on.
|
||||
*/
|
||||
- (instancetype)initWithCentralManager:(id<GNCCentralManager>)centralManager
|
||||
queue:(nullable dispatch_queue_t)queue;
|
||||
peripheralManager:(nullable id<GNCPeripheralManager>)peripheralManager
|
||||
queue:(dispatch_queue_t)queue;
|
||||
|
||||
- (NSDictionary<CBUUID *, NSData *> *)decodeAdvertisementData:
|
||||
(NSDictionary<NSString *, id> *)advertisementData;
|
||||
|
||||
@@ -1,404 +0,0 @@
|
||||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEMedium.h"
|
||||
|
||||
#import <CoreBluetooth/CoreBluetooth.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTClient.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTServer.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCPeripheral.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEL2CAPClient+Testing.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEMedium+Testing.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakeCentralManager.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheral.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheralManager.h"
|
||||
|
||||
static NSString *const kServiceUUID = @"0000FEF3-0000-1000-8000-00805F9B34FB";
|
||||
|
||||
@interface GNCBLEMediumTest : XCTestCase
|
||||
@end
|
||||
|
||||
@implementation GNCBLEMediumTest
|
||||
|
||||
#pragma mark - Supports Extended Advertisements
|
||||
|
||||
- (void)testSupportsExtendedAdvertisements {
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil];
|
||||
|
||||
XCTAssertFalse([medium supportsExtendedAdvertisements]);
|
||||
}
|
||||
|
||||
#pragma mark - Start Scanning
|
||||
|
||||
- (void)testStartScanning {
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil];
|
||||
XCTestExpectation *startScanningExpectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"Start scanning."];
|
||||
XCTestExpectation *advertisementFoundExpectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"Advertisement found."];
|
||||
CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID];
|
||||
|
||||
[fakeCentralManager simulateCentralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
|
||||
[medium startScanningForService:serviceUUID
|
||||
advertisementFoundHandler:^(id<GNCPeripheral> peripheral,
|
||||
NSDictionary<CBUUID *, NSData *> *data) {
|
||||
NSDictionary<CBUUID *, NSData *> *expected = @{
|
||||
serviceUUID : [@"test" dataUsingEncoding:NSUTF8StringEncoding],
|
||||
};
|
||||
XCTAssertEqualObjects(expected, data);
|
||||
[advertisementFoundExpectation fulfill];
|
||||
}
|
||||
completionHandler:^(NSError *error) {
|
||||
XCTAssertNil(error);
|
||||
[startScanningExpectation fulfill];
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ startScanningExpectation ] timeout:3];
|
||||
|
||||
XCTAssertEqualObjects(@[ serviceUUID ], fakeCentralManager.serviceUUIDs);
|
||||
|
||||
[fakeCentralManager
|
||||
simulateCentralManagerDidDiscoverPeripheral:[[GNCFakePeripheral alloc] init]
|
||||
advertisementData:@{
|
||||
CBAdvertisementDataLocalNameKey : @"dGVzdA",
|
||||
CBAdvertisementDataServiceUUIDsKey : @[ serviceUUID ],
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ advertisementFoundExpectation ] timeout:3];
|
||||
}
|
||||
|
||||
- (void)testAlreadyScanning {
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil];
|
||||
XCTestExpectation *expectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"Start scanning."];
|
||||
|
||||
[medium startScanningForService:[CBUUID UUIDWithString:kServiceUUID]
|
||||
advertisementFoundHandler:^(id<GNCPeripheral> peripheral,
|
||||
NSDictionary<CBUUID *, NSData *> *data) {
|
||||
}
|
||||
completionHandler:nil];
|
||||
|
||||
[medium startScanningForService:[CBUUID UUIDWithString:kServiceUUID]
|
||||
advertisementFoundHandler:^(id<GNCPeripheral> peripheral,
|
||||
NSDictionary<CBUUID *, NSData *> *data) {
|
||||
}
|
||||
completionHandler:^(NSError *error) {
|
||||
XCTAssertNotNil(error);
|
||||
[expectation fulfill];
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
}
|
||||
|
||||
- (void)testStartStopStartScanning {
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil];
|
||||
XCTestExpectation *expectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"Start scanning."];
|
||||
|
||||
CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID];
|
||||
|
||||
[medium startScanningForService:serviceUUID
|
||||
advertisementFoundHandler:^(id<GNCPeripheral> peripheral,
|
||||
NSDictionary<CBUUID *, NSData *> *data) {
|
||||
}
|
||||
completionHandler:^(NSError *error) {
|
||||
XCTAssertNil(error);
|
||||
[medium stopScanningWithCompletionHandler:^(NSError *error) {
|
||||
XCTAssertNil(error);
|
||||
[medium startScanningForService:serviceUUID
|
||||
advertisementFoundHandler:^(id<GNCPeripheral> peripheral,
|
||||
NSDictionary<CBUUID *, NSData *> *data) {
|
||||
}
|
||||
completionHandler:^(NSError *error) {
|
||||
XCTAssertNil(error);
|
||||
[expectation fulfill];
|
||||
}];
|
||||
}];
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
}
|
||||
|
||||
#pragma mark - Decode Advertisement Data
|
||||
|
||||
- (void)testDecodeAndroidStyleAdvertisementData {
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil];
|
||||
CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID];
|
||||
|
||||
NSDictionary<CBUUID *, NSData *> *expected = @{
|
||||
serviceUUID : [@"test" dataUsingEncoding:NSUTF8StringEncoding],
|
||||
};
|
||||
|
||||
NSDictionary<NSString *, id> *data = @{
|
||||
CBAdvertisementDataServiceDataKey : @{
|
||||
serviceUUID : [@"test" dataUsingEncoding:NSUTF8StringEncoding],
|
||||
},
|
||||
};
|
||||
NSDictionary<CBUUID *, NSData *> *actual = [medium decodeAdvertisementData:data];
|
||||
|
||||
XCTAssertEqualObjects(expected, actual);
|
||||
}
|
||||
|
||||
- (void)testDecodeAndroidStyleAdvertisementDataWithLocalName {
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil];
|
||||
CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID];
|
||||
|
||||
NSDictionary<CBUUID *, NSData *> *expected = @{
|
||||
serviceUUID : [@"test" dataUsingEncoding:NSUTF8StringEncoding],
|
||||
};
|
||||
|
||||
NSDictionary<NSString *, id> *data = @{
|
||||
CBAdvertisementDataLocalNameKey : @"Nearby", // Just happens to be base64 decodable.
|
||||
CBAdvertisementDataServiceUUIDsKey : @[ serviceUUID ],
|
||||
CBAdvertisementDataServiceDataKey : @{
|
||||
serviceUUID : [@"test" dataUsingEncoding:NSUTF8StringEncoding],
|
||||
},
|
||||
};
|
||||
NSDictionary<CBUUID *, NSData *> *actual = [medium decodeAdvertisementData:data];
|
||||
|
||||
XCTAssertEqualObjects(expected, actual);
|
||||
}
|
||||
|
||||
- (void)testDecodeAppleStyleAdvertisementData {
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil];
|
||||
CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID];
|
||||
|
||||
NSDictionary<CBUUID *, NSData *> *expected = @{
|
||||
serviceUUID : [@"test" dataUsingEncoding:NSUTF8StringEncoding],
|
||||
};
|
||||
|
||||
NSDictionary<NSString *, id> *data = @{
|
||||
CBAdvertisementDataLocalNameKey : @"dGVzdA",
|
||||
CBAdvertisementDataServiceUUIDsKey : @[ serviceUUID ],
|
||||
};
|
||||
NSDictionary<CBUUID *, NSData *> *actual = [medium decodeAdvertisementData:data];
|
||||
|
||||
XCTAssertEqualObjects(expected, actual);
|
||||
}
|
||||
|
||||
- (void)testDecodeInvalidAdvertisementData {
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil];
|
||||
CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID];
|
||||
|
||||
NSDictionary<NSString *, id> *data = @{
|
||||
CBAdvertisementDataLocalNameKey : @"!@#$",
|
||||
CBAdvertisementDataServiceUUIDsKey : @[ serviceUUID ],
|
||||
};
|
||||
NSDictionary<CBUUID *, NSData *> *actual = [medium decodeAdvertisementData:data];
|
||||
|
||||
XCTAssertEqualObjects(@{}, actual);
|
||||
}
|
||||
|
||||
- (void)testDecodeEmptyAdvertisementData {
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil];
|
||||
|
||||
NSDictionary<CBUUID *, NSData *> *actual = [medium decodeAdvertisementData:@{}];
|
||||
|
||||
XCTAssertEqualObjects(@{}, actual);
|
||||
}
|
||||
|
||||
#pragma mark - Start GATT Server
|
||||
|
||||
- (void)testStartGATTServer {
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil];
|
||||
XCTestExpectation *expectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"Start GATT server."];
|
||||
|
||||
[medium startGATTServerWithCompletionHandler:^(GNCBLEGATTServer *server, NSError *error) {
|
||||
XCTAssertNotNil(server);
|
||||
XCTAssertNil(error);
|
||||
[expectation fulfill];
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
}
|
||||
|
||||
#pragma mark - Start Advertising
|
||||
|
||||
- (void)testStartAdvertising {
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil];
|
||||
XCTestExpectation *expectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"Start advertising."];
|
||||
|
||||
// Start advertising is fully covered with @c GNCBLEGATTServer tests. We are passing invalid
|
||||
// advertising data here so we can test code paths relevant to @c GNCBLEMedium, but bail early
|
||||
// enough to avoid making actual CoreBluetooth calls.
|
||||
[medium startAdvertisingData:@{}
|
||||
completionHandler:^(NSError *error) {
|
||||
XCTAssertNotNil(error);
|
||||
[expectation fulfill];
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
}
|
||||
|
||||
- (void)testStopAdvertising {
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil];
|
||||
XCTestExpectation *expectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"Stop advertising."];
|
||||
|
||||
// Stop advertising is fully covered with @c GNCBLEGATTServer tests. We are only testing stopping
|
||||
// without having started which tests the code paths relevant to @c GNCBLEMedium.
|
||||
[medium stopAdvertisingWithCompletionHandler:^(NSError *error) {
|
||||
XCTAssertNil(error);
|
||||
[expectation fulfill];
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
}
|
||||
|
||||
#pragma mark - Open L2CAP Channel
|
||||
|
||||
- (void)testOpenL2CAPServerSocket {
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil];
|
||||
XCTestExpectation *psmPublishedexpectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"PSM published."];
|
||||
XCTestExpectation *channelOpenedexpectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"Channel opened."];
|
||||
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
|
||||
// Open L2CAP server is fully covered with @c GNCBLEL2CAPServer tests.
|
||||
[medium
|
||||
openL2CAPServerWithPSMPublishedCompletionHandler:^(uint16_t PSM, NSError *error) {
|
||||
XCTAssertEqual(PSM, fakePeripheralManager.PSM);
|
||||
XCTAssertNil(error);
|
||||
[psmPublishedexpectation fulfill];
|
||||
}
|
||||
channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *stream, NSError *error) {
|
||||
XCTAssertNil(error);
|
||||
[channelOpenedexpectation fulfill];
|
||||
}
|
||||
peripheralManager:fakePeripheralManager];
|
||||
|
||||
[self waitForExpectations:@[ psmPublishedexpectation ] timeout:0.1];
|
||||
[self waitForExpectations:@[ channelOpenedexpectation ] timeout:0.5];
|
||||
}
|
||||
|
||||
- (void)testSuccessfulOpenL2CAPChannel {
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil];
|
||||
XCTestExpectation *expectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"Open L2CAP channel."];
|
||||
|
||||
GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init];
|
||||
GNCBLEL2CAPClient *l2capClient =
|
||||
[[GNCBLEL2CAPClient alloc] initWithQueue:nil
|
||||
requestDisconnectionHandler:^(id<GNCPeripheral> _Nonnull peripheral){
|
||||
}];
|
||||
[medium setL2CAPClient:l2capClient];
|
||||
[medium openL2CAPChannelWithPSM:123
|
||||
peripheral:fakePeripheral
|
||||
completionHandler:^(GNCBLEL2CAPStream *_Nullable stream, NSError *_Nullable error) {
|
||||
[expectation fulfill];
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
}
|
||||
|
||||
#pragma mark - Connect
|
||||
|
||||
- (void)testSuccessfulConnect {
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil];
|
||||
XCTestExpectation *expectation = [[XCTestExpectation alloc] initWithDescription:@"Connect."];
|
||||
|
||||
[medium connectToGATTServerForPeripheral:[[GNCFakePeripheral alloc] init]
|
||||
disconnectionHandler:nil
|
||||
completionHandler:^(GNCBLEGATTClient *client, NSError *error) {
|
||||
XCTAssertNotNil(client);
|
||||
XCTAssertNil(error);
|
||||
[expectation fulfill];
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
}
|
||||
|
||||
- (void)testFailedConnect {
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil];
|
||||
XCTestExpectation *expectation = [[XCTestExpectation alloc] initWithDescription:@"Connect."];
|
||||
|
||||
fakeCentralManager.didFailToConnectPeripheralError = [NSError errorWithDomain:@"fake"
|
||||
code:0
|
||||
userInfo:nil];
|
||||
|
||||
[medium connectToGATTServerForPeripheral:[[GNCFakePeripheral alloc] init]
|
||||
disconnectionHandler:nil
|
||||
completionHandler:^(GNCBLEGATTClient *client, NSError *error) {
|
||||
XCTAssertNil(client);
|
||||
XCTAssertNotNil(error);
|
||||
[expectation fulfill];
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
}
|
||||
|
||||
- (void)testDisconnect {
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil];
|
||||
XCTestExpectation *disconnectExpectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"Disconnect."];
|
||||
|
||||
GNCFakePeripheral *peripheral = [[GNCFakePeripheral alloc] init];
|
||||
|
||||
[medium connectToGATTServerForPeripheral:peripheral
|
||||
disconnectionHandler:^() {
|
||||
[disconnectExpectation fulfill];
|
||||
}
|
||||
completionHandler:^(GNCBLEGATTClient *client, NSError *error) {
|
||||
XCTAssertNotNil(client);
|
||||
XCTAssertNil(error);
|
||||
[client disconnect];
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ disconnectExpectation ] timeout:3];
|
||||
}
|
||||
|
||||
- (void)testRetrievePeripheralWithIdentifier_exists {
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil];
|
||||
XCTAssertNotNil(
|
||||
[medium retrievePeripheralWithIdentifier:
|
||||
[[NSUUID alloc] initWithUUIDString:@"11111111-1111-1111-1111-111111111111"]]);
|
||||
}
|
||||
|
||||
- (void)testRetrievePeripheralWithIdentifier_doesNotExist {
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil];
|
||||
XCTAssertNil(
|
||||
[medium retrievePeripheralWithIdentifier:
|
||||
[[NSUUID alloc] initWithUUIDString:@"11111111-1111-1111-1111-111111111112"]]);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,621 @@
|
||||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEMedium.h"
|
||||
|
||||
#import <CoreBluetooth/CoreBluetooth.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTClient.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTServer.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCPeripheral.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEL2CAPClient+Testing.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEMedium+Testing.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakeCentralManager.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheral.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheralManager.h"
|
||||
|
||||
#include "connections/implementation/flags/nearby_connections_feature_flags.h"
|
||||
#include "internal/flags/nearby_flags.h"
|
||||
|
||||
static NSString *const kServiceUUID = @"0000FEF3-0000-1000-8000-00805F9B34FB";
|
||||
|
||||
@interface GNCBLEMediumTest : XCTestCase
|
||||
@end
|
||||
|
||||
@implementation GNCBLEMediumTest
|
||||
|
||||
- (void)tearDown {
|
||||
nearby::NearbyFlags::GetInstance().ResetOverridedValues();
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
- (void)testInit_allocatesMultiplexerWhenFlagEnabled {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager
|
||||
peripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
|
||||
id multiplexer = [medium valueForKey:@"_multiplexer"];
|
||||
if (enabled.boolValue) {
|
||||
XCTAssertNotNil(multiplexer);
|
||||
XCTAssertEqual(fakePeripheralManager.peripheralDelegate, multiplexer);
|
||||
} else {
|
||||
XCTAssertNil(multiplexer);
|
||||
XCTAssertNil(fakePeripheralManager.peripheralDelegate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Supports Extended Advertisements
|
||||
|
||||
- (void)testSupportsExtendedAdvertisements {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager
|
||||
peripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
|
||||
XCTAssertFalse([medium supportsExtendedAdvertisements]);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Start Scanning
|
||||
|
||||
- (void)testStartScanning {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager
|
||||
peripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
XCTestExpectation *startScanningExpectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"Start scanning."];
|
||||
XCTestExpectation *advertisementFoundExpectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"Advertisement found."];
|
||||
CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID];
|
||||
|
||||
[fakeCentralManager simulateCentralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
|
||||
[medium startScanningForService:serviceUUID
|
||||
advertisementFoundHandler:^(id<GNCPeripheral> peripheral,
|
||||
NSDictionary<CBUUID *, NSData *> *data) {
|
||||
NSDictionary<CBUUID *, NSData *> *expected = @{
|
||||
serviceUUID : [@"test" dataUsingEncoding:NSUTF8StringEncoding],
|
||||
};
|
||||
XCTAssertEqualObjects(expected, data);
|
||||
[advertisementFoundExpectation fulfill];
|
||||
}
|
||||
completionHandler:^(NSError *error) {
|
||||
XCTAssertNil(error);
|
||||
[startScanningExpectation fulfill];
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ startScanningExpectation ] timeout:3];
|
||||
|
||||
XCTAssertEqualObjects(@[ serviceUUID ], fakeCentralManager.serviceUUIDs);
|
||||
|
||||
[fakeCentralManager
|
||||
simulateCentralManagerDidDiscoverPeripheral:[[GNCFakePeripheral alloc] init]
|
||||
advertisementData:@{
|
||||
CBAdvertisementDataLocalNameKey : @"dGVzdA",
|
||||
CBAdvertisementDataServiceUUIDsKey : @[ serviceUUID ],
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ advertisementFoundExpectation ] timeout:3];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testAlreadyScanning {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager
|
||||
peripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
XCTestExpectation *expectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"Start scanning."];
|
||||
|
||||
[medium startScanningForService:[CBUUID UUIDWithString:kServiceUUID]
|
||||
advertisementFoundHandler:^(id<GNCPeripheral> peripheral,
|
||||
NSDictionary<CBUUID *, NSData *> *data) {
|
||||
}
|
||||
completionHandler:nil];
|
||||
|
||||
[medium startScanningForService:[CBUUID UUIDWithString:kServiceUUID]
|
||||
advertisementFoundHandler:^(id<GNCPeripheral> peripheral,
|
||||
NSDictionary<CBUUID *, NSData *> *data) {
|
||||
}
|
||||
completionHandler:^(NSError *error) {
|
||||
XCTAssertNotNil(error);
|
||||
[expectation fulfill];
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testStartStopStartScanning {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager
|
||||
peripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
XCTestExpectation *expectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"Start scanning."];
|
||||
|
||||
CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID];
|
||||
|
||||
[medium startScanningForService:serviceUUID
|
||||
advertisementFoundHandler:^(id<GNCPeripheral> peripheral,
|
||||
NSDictionary<CBUUID *, NSData *> *data) {
|
||||
}
|
||||
completionHandler:^(NSError *error) {
|
||||
XCTAssertNil(error);
|
||||
[medium stopScanningWithCompletionHandler:^(NSError *error) {
|
||||
XCTAssertNil(error);
|
||||
[medium startScanningForService:serviceUUID
|
||||
advertisementFoundHandler:^(id<GNCPeripheral> peripheral,
|
||||
NSDictionary<CBUUID *, NSData *> *data) {
|
||||
}
|
||||
completionHandler:^(NSError *error) {
|
||||
XCTAssertNil(error);
|
||||
[expectation fulfill];
|
||||
}];
|
||||
}];
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Decode Advertisement Data
|
||||
|
||||
- (void)testDecodeAndroidStyleAdvertisementData {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager
|
||||
peripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID];
|
||||
|
||||
NSDictionary<CBUUID *, NSData *> *expected = @{
|
||||
serviceUUID : [@"test" dataUsingEncoding:NSUTF8StringEncoding],
|
||||
};
|
||||
|
||||
NSDictionary<NSString *, id> *data = @{
|
||||
CBAdvertisementDataServiceDataKey : @{
|
||||
serviceUUID : [@"test" dataUsingEncoding:NSUTF8StringEncoding],
|
||||
},
|
||||
};
|
||||
NSDictionary<CBUUID *, NSData *> *actual = [medium decodeAdvertisementData:data];
|
||||
|
||||
XCTAssertEqualObjects(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testDecodeAndroidStyleAdvertisementDataWithLocalName {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager
|
||||
peripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID];
|
||||
|
||||
NSDictionary<CBUUID *, NSData *> *expected = @{
|
||||
serviceUUID : [@"test" dataUsingEncoding:NSUTF8StringEncoding],
|
||||
};
|
||||
|
||||
NSDictionary<NSString *, id> *data = @{
|
||||
CBAdvertisementDataLocalNameKey : @"Nearby", // Just happens to be base64 decodable.
|
||||
CBAdvertisementDataServiceUUIDsKey : @[ serviceUUID ],
|
||||
CBAdvertisementDataServiceDataKey : @{
|
||||
serviceUUID : [@"test" dataUsingEncoding:NSUTF8StringEncoding],
|
||||
},
|
||||
};
|
||||
NSDictionary<CBUUID *, NSData *> *actual = [medium decodeAdvertisementData:data];
|
||||
|
||||
XCTAssertEqualObjects(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testDecodeAppleStyleAdvertisementData {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager
|
||||
peripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID];
|
||||
|
||||
NSDictionary<CBUUID *, NSData *> *expected = @{
|
||||
serviceUUID : [@"test" dataUsingEncoding:NSUTF8StringEncoding],
|
||||
};
|
||||
|
||||
NSDictionary<NSString *, id> *data = @{
|
||||
CBAdvertisementDataLocalNameKey : @"dGVzdA",
|
||||
CBAdvertisementDataServiceUUIDsKey : @[ serviceUUID ],
|
||||
};
|
||||
NSDictionary<CBUUID *, NSData *> *actual = [medium decodeAdvertisementData:data];
|
||||
|
||||
XCTAssertEqualObjects(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testDecodeInvalidAdvertisementData {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager
|
||||
peripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID];
|
||||
|
||||
NSDictionary<NSString *, id> *data = @{
|
||||
CBAdvertisementDataLocalNameKey : @"!@#$",
|
||||
CBAdvertisementDataServiceUUIDsKey : @[ serviceUUID ],
|
||||
};
|
||||
NSDictionary<CBUUID *, NSData *> *actual = [medium decodeAdvertisementData:data];
|
||||
|
||||
XCTAssertEqualObjects(@{}, actual);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testDecodeEmptyAdvertisementData {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager
|
||||
peripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
|
||||
NSDictionary<CBUUID *, NSData *> *actual = [medium decodeAdvertisementData:@{}];
|
||||
|
||||
XCTAssertEqualObjects(@{}, actual);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Start GATT Server
|
||||
|
||||
- (void)testStartGATTServer {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager
|
||||
peripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
XCTestExpectation *expectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"Start GATT server."];
|
||||
|
||||
[medium startGATTServerWithCompletionHandler:^(GNCBLEGATTServer *server, NSError *error) {
|
||||
XCTAssertNotNil(server);
|
||||
XCTAssertNil(error);
|
||||
|
||||
// Verify internal structure based on flag
|
||||
id mediumManager = [medium valueForKey:@"_peripheralManager"];
|
||||
id serverManager = [server valueForKey:@"_peripheralManager"];
|
||||
|
||||
if (enabled.boolValue) {
|
||||
XCTAssertEqual(mediumManager, serverManager);
|
||||
id multiplexer = [medium valueForKey:@"_multiplexer"];
|
||||
XCTAssertEqual([mediumManager peripheralDelegate], multiplexer);
|
||||
} else {
|
||||
XCTAssertNotEqual(mediumManager, serverManager);
|
||||
id manager = [server valueForKey:@"_peripheralManager"];
|
||||
XCTAssertEqual([manager peripheralDelegate], server);
|
||||
}
|
||||
|
||||
[expectation fulfill];
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Start Advertising
|
||||
|
||||
- (void)testStartAdvertising {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager
|
||||
peripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
XCTestExpectation *expectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"Start advertising."];
|
||||
|
||||
// Start advertising is fully covered with @c GNCBLEGATTServer tests. We are passing invalid
|
||||
// advertising data here so we can test code paths relevant to @c GNCBLEMedium, but bail early
|
||||
// enough to avoid making actual CoreBluetooth calls.
|
||||
[medium startAdvertisingData:@{}
|
||||
completionHandler:^(NSError *error) {
|
||||
XCTAssertNotNil(error);
|
||||
[expectation fulfill];
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testStopAdvertising {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager
|
||||
peripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
XCTestExpectation *expectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"Stop advertising."];
|
||||
|
||||
// Stop advertising is fully covered with @c GNCBLEGATTServer tests. We are only testing stopping
|
||||
// without having started which tests the code paths relevant to @c GNCBLEMedium.
|
||||
[medium stopAdvertisingWithCompletionHandler:^(NSError *error) {
|
||||
XCTAssertNil(error);
|
||||
[expectation fulfill];
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Open L2CAP Channel
|
||||
|
||||
- (void)testOpenL2CAPServerSocket {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager
|
||||
peripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
XCTestExpectation *psmPublishedexpectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"PSM published."];
|
||||
XCTestExpectation *channelOpenedexpectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"Channel opened."];
|
||||
|
||||
[fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn];
|
||||
|
||||
// Open L2CAP server is fully covered with @c GNCBLEL2CAPServer tests.
|
||||
[medium
|
||||
openL2CAPServerWithPSMPublishedCompletionHandler:^(uint16_t PSM, NSError *error) {
|
||||
XCTAssertEqual(PSM, fakePeripheralManager.PSM);
|
||||
XCTAssertNil(error);
|
||||
[psmPublishedexpectation fulfill];
|
||||
}
|
||||
channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *stream, NSError *error) {
|
||||
XCTAssertNil(error);
|
||||
[channelOpenedexpectation fulfill];
|
||||
}
|
||||
peripheralManager:fakePeripheralManager];
|
||||
|
||||
[self waitForExpectations:@[ psmPublishedexpectation ] timeout:0.1];
|
||||
[self waitForExpectations:@[ channelOpenedexpectation ] timeout:0.5];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testSuccessfulOpenL2CAPChannel {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager
|
||||
peripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
XCTestExpectation *expectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"Open L2CAP channel."];
|
||||
|
||||
GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init];
|
||||
GNCBLEL2CAPClient *l2capClient =
|
||||
[[GNCBLEL2CAPClient alloc] initWithQueue:nil
|
||||
requestDisconnectionHandler:^(id<GNCPeripheral> _Nonnull peripheral){
|
||||
}];
|
||||
[medium setL2CAPClient:l2capClient];
|
||||
[medium openL2CAPChannelWithPSM:123
|
||||
peripheral:fakePeripheral
|
||||
completionHandler:^(GNCBLEL2CAPStream *_Nullable stream, NSError *_Nullable error) {
|
||||
[expectation fulfill];
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Connect
|
||||
|
||||
- (void)testSuccessfulConnect {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager
|
||||
peripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
XCTestExpectation *expectation = [[XCTestExpectation alloc] initWithDescription:@"Connect."];
|
||||
|
||||
[medium connectToGATTServerForPeripheral:[[GNCFakePeripheral alloc] init]
|
||||
disconnectionHandler:nil
|
||||
completionHandler:^(GNCBLEGATTClient *client, NSError *error) {
|
||||
XCTAssertNotNil(client);
|
||||
XCTAssertNil(error);
|
||||
[expectation fulfill];
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testFailedConnect {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager
|
||||
peripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
XCTestExpectation *expectation = [[XCTestExpectation alloc] initWithDescription:@"Connect."];
|
||||
|
||||
fakeCentralManager.didFailToConnectPeripheralError = [NSError errorWithDomain:@"fake"
|
||||
code:0
|
||||
userInfo:nil];
|
||||
|
||||
[medium connectToGATTServerForPeripheral:[[GNCFakePeripheral alloc] init]
|
||||
disconnectionHandler:nil
|
||||
completionHandler:^(GNCBLEGATTClient *client, NSError *error) {
|
||||
XCTAssertNil(client);
|
||||
XCTAssertNotNil(error);
|
||||
[expectation fulfill];
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ expectation ] timeout:3];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testDisconnect {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager
|
||||
peripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
XCTestExpectation *disconnectExpectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"Disconnect."];
|
||||
|
||||
GNCFakePeripheral *peripheral = [[GNCFakePeripheral alloc] init];
|
||||
|
||||
[medium connectToGATTServerForPeripheral:peripheral
|
||||
disconnectionHandler:^() {
|
||||
[disconnectExpectation fulfill];
|
||||
}
|
||||
completionHandler:^(GNCBLEGATTClient *client, NSError *error) {
|
||||
XCTAssertNotNil(client);
|
||||
XCTAssertNil(error);
|
||||
[client disconnect];
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ disconnectExpectation ] timeout:3];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testRetrievePeripheralWithIdentifier_exists {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager
|
||||
peripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
XCTAssertNotNil(
|
||||
[medium retrievePeripheralWithIdentifier:
|
||||
[[NSUUID alloc] initWithUUIDString:@"11111111-1111-1111-1111-111111111111"]]);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testRetrievePeripheralWithIdentifier_doesNotExist {
|
||||
for (NSNumber *enabled in @[ @NO, @YES ]) {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableSharedPeripheralManager,
|
||||
enabled.boolValue);
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager
|
||||
peripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
XCTAssertNil(
|
||||
[medium retrievePeripheralWithIdentifier:
|
||||
[[NSUUID alloc] initWithUUIDString:@"11111111-1111-1111-1111-111111111112"]]);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -29,7 +29,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/** The socket file descriptor for the L2CAP channel. */
|
||||
@property(nonatomic, readonly) int socketFD;
|
||||
/** The PSM (Protocol/Service Multiplexer) of the L2CAP channel. */
|
||||
@property(nonatomic, readonly) CBL2CAPPSM PSM;
|
||||
@property(nonatomic, readwrite) CBL2CAPPSM PSM;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -140,6 +140,7 @@ static const uint16_t kPSM = 192;
|
||||
GNCFakeCBL2CAPChannel *fakeChannel = [[GNCFakeCBL2CAPChannel alloc] init];
|
||||
fakeChannel.inputStream = fakeStream.inputStream;
|
||||
fakeChannel.outputStream = fakeStream.outputStream;
|
||||
fakeChannel.PSM = _PSM;
|
||||
[_peripheralDelegate gnc_peripheralManager:self
|
||||
didOpenL2CAPChannel:(CBL2CAPChannel *)fakeChannel
|
||||
error:_didOpenL2CAPChannelError];
|
||||
|
||||
@@ -131,7 +131,7 @@ static const NSTimeInterval kWaitForConnectionTimeout = 6.0; // Allow for the 5
|
||||
GNCFakeSocket *fakeSocket = [[GNCFakeSocket alloc] init];
|
||||
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"Connection success"];
|
||||
GNCMWaitForConnection((GNSSocket *)fakeSocket, ^(BOOL flag) {
|
||||
GNCMWaitForConnection((GNSSocket *)fakeSocket, nil, ^(BOOL flag) {
|
||||
XCTAssertTrue(flag);
|
||||
[expectation fulfill];
|
||||
});
|
||||
@@ -142,12 +142,31 @@ static const NSTimeInterval kWaitForConnectionTimeout = 6.0; // Allow for the 5
|
||||
[self waitForExpectationsWithTimeout:kTimeout handler:nil];
|
||||
}
|
||||
|
||||
- (void)testWaitForConnection_CustomQueue {
|
||||
GNCFakeSocket *fakeSocket = [[GNCFakeSocket alloc] init];
|
||||
dispatch_queue_t customQueue = dispatch_queue_create("com.google.nearby.testQueue", DISPATCH_QUEUE_SERIAL);
|
||||
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"Connection success on custom queue"];
|
||||
GNCMWaitForConnection((GNSSocket *)fakeSocket, customQueue, ^(BOOL flag) {
|
||||
XCTAssertTrue(flag);
|
||||
// Verify that we are on the custom queue
|
||||
const char *label = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL);
|
||||
XCTAssertEqual(strcmp(label, "com.google.nearby.testQueue"), 0);
|
||||
[expectation fulfill];
|
||||
});
|
||||
|
||||
// Simulate the connection callback
|
||||
[fakeSocket simulateSocketDidConnect];
|
||||
|
||||
[self waitForExpectationsWithTimeout:kTimeout handler:nil];
|
||||
}
|
||||
|
||||
- (void)testWaitForConnection_Failure_Disconnect {
|
||||
GNCFakeSocket *fakeSocket = [[GNCFakeSocket alloc] init];
|
||||
|
||||
XCTestExpectation *expectation =
|
||||
[self expectationWithDescription:@"Connection failed on disconnect"];
|
||||
GNCMWaitForConnection((GNSSocket *)fakeSocket, ^(BOOL flag) {
|
||||
GNCMWaitForConnection((GNSSocket *)fakeSocket, nil, ^(BOOL flag) {
|
||||
XCTAssertFalse(flag);
|
||||
[expectation fulfill];
|
||||
});
|
||||
@@ -164,7 +183,7 @@ static const NSTimeInterval kWaitForConnectionTimeout = 6.0; // Allow for the 5
|
||||
|
||||
XCTestExpectation *expectation =
|
||||
[self expectationWithDescription:@"Connection failed on timeout"];
|
||||
GNCMWaitForConnection((GNSSocket *)fakeSocket, ^(BOOL flag) {
|
||||
GNCMWaitForConnection((GNSSocket *)fakeSocket, nil, ^(BOOL flag) {
|
||||
XCTAssertFalse(flag);
|
||||
[expectation fulfill];
|
||||
});
|
||||
|
||||
+495
@@ -0,0 +1,495 @@
|
||||
// Copyright 2026 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCPeripheralManagerMultiplexer.h"
|
||||
|
||||
#import <CoreBluetooth/CoreBluetooth.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCPeripheralManager.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheralManager.h"
|
||||
|
||||
@interface GNCPeripheralManagerMultiplexerTest : XCTestCase
|
||||
@end
|
||||
|
||||
@interface FakePeripheralManagerDelegate : NSObject <GNCPeripheralManagerDelegate>
|
||||
@property(nonatomic) XCTestExpectation *expectation;
|
||||
@property(nonatomic) BOOL didUpdateStateCalled;
|
||||
@property(nonatomic) CBManagerState state;
|
||||
|
||||
@property(nonatomic) BOOL didStartAdvertisingCalled;
|
||||
@property(nonatomic) NSError *startAdvertisingError;
|
||||
|
||||
@property(nonatomic) BOOL didAddServiceCalled;
|
||||
@property(nonatomic) CBService *addedService;
|
||||
@property(nonatomic) NSError *addServiceError;
|
||||
|
||||
@property(nonatomic) BOOL didReceiveReadRequestCalled;
|
||||
@property(nonatomic) CBATTRequest *readRequest;
|
||||
|
||||
@property(nonatomic) BOOL didPublishL2CAPChannelCalled;
|
||||
@property(nonatomic) CBL2CAPPSM publishedPSM;
|
||||
@property(nonatomic) NSError *publishL2CAPChannelError;
|
||||
|
||||
@property(nonatomic) BOOL didUnpublishL2CAPChannelCalled;
|
||||
@property(nonatomic) CBL2CAPPSM unpublishedPSM;
|
||||
@property(nonatomic) NSError *unpublishL2CAPChannelError;
|
||||
|
||||
@property(nonatomic) BOOL didOpenL2CAPChannelCalled;
|
||||
@property(nonatomic) CBL2CAPChannel *openedChannel;
|
||||
@property(nonatomic) NSError *openL2CAPChannelError;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FakePeripheralManagerDelegate
|
||||
|
||||
- (void)gnc_peripheralManagerDidUpdateState:(id<GNCPeripheralManager>)peripheral {
|
||||
_didUpdateStateCalled = YES;
|
||||
_state = peripheral.state;
|
||||
if (_expectation) {
|
||||
[_expectation fulfill];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)gnc_peripheralManagerDidStartAdvertising:(id<GNCPeripheralManager>)peripheral
|
||||
error:(nullable NSError *)error {
|
||||
_didStartAdvertisingCalled = YES;
|
||||
_startAdvertisingError = error;
|
||||
if (_expectation) {
|
||||
[_expectation fulfill];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)gnc_peripheralManager:(id<GNCPeripheralManager>)peripheral
|
||||
didAddService:(CBService *)service
|
||||
error:(nullable NSError *)error {
|
||||
_didAddServiceCalled = YES;
|
||||
_addedService = service;
|
||||
_addServiceError = error;
|
||||
if (_expectation) {
|
||||
[_expectation fulfill];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)gnc_peripheralManager:(id<GNCPeripheralManager>)peripheral
|
||||
didReceiveReadRequest:(CBATTRequest *)request {
|
||||
_didReceiveReadRequestCalled = YES;
|
||||
_readRequest = request;
|
||||
if (_expectation) {
|
||||
[_expectation fulfill];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)gnc_peripheralManager:(id<GNCPeripheralManager>)peripheral
|
||||
didPublishL2CAPChannel:(CBL2CAPPSM)PSM
|
||||
error:(nullable NSError *)error {
|
||||
_didPublishL2CAPChannelCalled = YES;
|
||||
_publishedPSM = PSM;
|
||||
_publishL2CAPChannelError = error;
|
||||
if (_expectation) {
|
||||
[_expectation fulfill];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)gnc_peripheralManager:(id<GNCPeripheralManager>)peripheral
|
||||
didUnpublishL2CAPChannel:(CBL2CAPPSM)PSM
|
||||
error:(NSError *)error {
|
||||
_didUnpublishL2CAPChannelCalled = YES;
|
||||
_unpublishedPSM = PSM;
|
||||
_unpublishL2CAPChannelError = error;
|
||||
if (_expectation) {
|
||||
[_expectation fulfill];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)gnc_peripheralManager:(id<GNCPeripheralManager>)peripheral
|
||||
didOpenL2CAPChannel:(nullable CBL2CAPChannel *)channel
|
||||
error:(nullable NSError *)error {
|
||||
_didOpenL2CAPChannelCalled = YES;
|
||||
_openedChannel = channel;
|
||||
_openL2CAPChannelError = error;
|
||||
if (_expectation) {
|
||||
[_expectation fulfill];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)peripheralManagerDidUpdateState:(nonnull CBPeripheralManager *)peripheral {
|
||||
_didUpdateStateCalled = YES;
|
||||
_state = peripheral.state;
|
||||
if (_expectation) {
|
||||
[_expectation fulfill];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral
|
||||
error:(nullable NSError *)error {
|
||||
_didStartAdvertisingCalled = YES;
|
||||
_startAdvertisingError = error;
|
||||
if (_expectation) {
|
||||
[_expectation fulfill];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)peripheralManager:(CBPeripheralManager *)peripheral
|
||||
didAddService:(CBService *)service
|
||||
error:(nullable NSError *)error {
|
||||
_didAddServiceCalled = YES;
|
||||
_addedService = service;
|
||||
_addServiceError = error;
|
||||
if (_expectation) {
|
||||
[_expectation fulfill];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)peripheralManager:(CBPeripheralManager *)peripheral
|
||||
didReceiveReadRequest:(CBATTRequest *)request {
|
||||
_didReceiveReadRequestCalled = YES;
|
||||
_readRequest = request;
|
||||
if (_expectation) {
|
||||
[_expectation fulfill];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)peripheralManager:(CBPeripheralManager *)peripheral
|
||||
didPublishL2CAPChannel:(CBL2CAPPSM)PSM
|
||||
error:(nullable NSError *)error {
|
||||
_didPublishL2CAPChannelCalled = YES;
|
||||
_publishedPSM = PSM;
|
||||
_publishL2CAPChannelError = error;
|
||||
if (_expectation) {
|
||||
[_expectation fulfill];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)peripheralManager:(CBPeripheralManager *)peripheral
|
||||
didUnpublishL2CAPChannel:(CBL2CAPPSM)PSM
|
||||
error:(nullable NSError *)error {
|
||||
_didUnpublishL2CAPChannelCalled = YES;
|
||||
_unpublishedPSM = PSM;
|
||||
_unpublishL2CAPChannelError = error;
|
||||
if (_expectation) {
|
||||
[_expectation fulfill];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)peripheralManager:(CBPeripheralManager *)peripheral
|
||||
didOpenL2CAPChannel:(nullable CBL2CAPChannel *)channel
|
||||
error:(nullable NSError *)error {
|
||||
_didOpenL2CAPChannelCalled = YES;
|
||||
_openedChannel = channel;
|
||||
_openL2CAPChannelError = error;
|
||||
if (_expectation) {
|
||||
[_expectation fulfill];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GNCPeripheralManagerMultiplexerTest
|
||||
|
||||
- (void)testMultiplexerForwardsCallbacks {
|
||||
GNCPeripheralManagerMultiplexer *multiplexer =
|
||||
[[GNCPeripheralManagerMultiplexer alloc] initWithCallbackQueue:dispatch_get_main_queue()];
|
||||
FakePeripheralManagerDelegate *delegate1 = [[FakePeripheralManagerDelegate alloc] init];
|
||||
FakePeripheralManagerDelegate *delegate2 = [[FakePeripheralManagerDelegate alloc] init];
|
||||
|
||||
delegate1.expectation = [self expectationWithDescription:@"Delegate 1 called"];
|
||||
delegate2.expectation = [self expectationWithDescription:@"Delegate 2 called"];
|
||||
|
||||
[multiplexer addListener:delegate1];
|
||||
[multiplexer addListener:delegate2];
|
||||
|
||||
GNCFakePeripheralManager *fakeManager = [[GNCFakePeripheralManager alloc] init];
|
||||
fakeManager.state = CBManagerStatePoweredOn;
|
||||
|
||||
[multiplexer gnc_peripheralManagerDidUpdateState:fakeManager];
|
||||
|
||||
[self waitForExpectationsWithTimeout:1 handler:nil];
|
||||
|
||||
XCTAssertTrue(delegate1.didUpdateStateCalled);
|
||||
XCTAssertTrue(delegate2.didUpdateStateCalled);
|
||||
XCTAssertEqual(delegate1.state, CBManagerStatePoweredOn);
|
||||
XCTAssertEqual(delegate2.state, CBManagerStatePoweredOn);
|
||||
}
|
||||
|
||||
- (void)testMultiplexerRemovesListener {
|
||||
GNCPeripheralManagerMultiplexer *multiplexer =
|
||||
[[GNCPeripheralManagerMultiplexer alloc] initWithCallbackQueue:dispatch_get_main_queue()];
|
||||
FakePeripheralManagerDelegate *delegate1 = [[FakePeripheralManagerDelegate alloc] init];
|
||||
|
||||
delegate1.expectation = [self expectationWithDescription:@"Delegate 1 called"];
|
||||
|
||||
[multiplexer addListener:delegate1];
|
||||
|
||||
GNCFakePeripheralManager *fakeManager = [[GNCFakePeripheralManager alloc] init];
|
||||
fakeManager.state = CBManagerStatePoweredOn;
|
||||
|
||||
[multiplexer removeListener:delegate1];
|
||||
[multiplexer gnc_peripheralManagerDidUpdateState:fakeManager];
|
||||
|
||||
// We expect delegate1 NOT to be called.
|
||||
// Since removals are async, we wait a bit to ensure it had a chance (or didn't).
|
||||
XCTWaiterResult result = [XCTWaiter waitForExpectations:@[ delegate1.expectation ] timeout:0.5];
|
||||
XCTAssertEqual(result, XCTWaiterResultTimedOut);
|
||||
XCTAssertFalse(delegate1.didUpdateStateCalled);
|
||||
}
|
||||
|
||||
- (void)testMultiplexerForwardsDidStartAdvertising {
|
||||
GNCPeripheralManagerMultiplexer *multiplexer =
|
||||
[[GNCPeripheralManagerMultiplexer alloc] initWithCallbackQueue:dispatch_get_main_queue()];
|
||||
FakePeripheralManagerDelegate *delegate = [[FakePeripheralManagerDelegate alloc] init];
|
||||
delegate.expectation = [self expectationWithDescription:@"Delegate called"];
|
||||
[multiplexer addListener:delegate];
|
||||
|
||||
GNCFakePeripheralManager *fakeManager = [[GNCFakePeripheralManager alloc] init];
|
||||
NSError *error = [NSError errorWithDomain:@"test" code:1 userInfo:nil];
|
||||
|
||||
[multiplexer gnc_peripheralManagerDidStartAdvertising:fakeManager error:error];
|
||||
|
||||
[self waitForExpectationsWithTimeout:1 handler:nil];
|
||||
XCTAssertTrue(delegate.didStartAdvertisingCalled);
|
||||
XCTAssertEqualObjects(delegate.startAdvertisingError, error);
|
||||
}
|
||||
|
||||
- (void)testMultiplexerForwardsDidAddService {
|
||||
GNCPeripheralManagerMultiplexer *multiplexer =
|
||||
[[GNCPeripheralManagerMultiplexer alloc] initWithCallbackQueue:dispatch_get_main_queue()];
|
||||
FakePeripheralManagerDelegate *delegate = [[FakePeripheralManagerDelegate alloc] init];
|
||||
delegate.expectation = [self expectationWithDescription:@"Delegate called"];
|
||||
[multiplexer addListener:delegate];
|
||||
|
||||
GNCFakePeripheralManager *fakeManager = [[GNCFakePeripheralManager alloc] init];
|
||||
CBMutableService *service =
|
||||
[[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:@"180D"] primary:YES];
|
||||
NSError *error = [NSError errorWithDomain:@"test" code:2 userInfo:nil];
|
||||
|
||||
[multiplexer gnc_peripheralManager:fakeManager didAddService:service error:error];
|
||||
|
||||
[self waitForExpectationsWithTimeout:1 handler:nil];
|
||||
XCTAssertTrue(delegate.didAddServiceCalled);
|
||||
XCTAssertEqualObjects(delegate.addedService, service);
|
||||
XCTAssertEqualObjects(delegate.addServiceError, error);
|
||||
}
|
||||
|
||||
- (void)testMultiplexerForwardsDidReceiveReadRequest {
|
||||
GNCPeripheralManagerMultiplexer *multiplexer =
|
||||
[[GNCPeripheralManagerMultiplexer alloc] initWithCallbackQueue:dispatch_get_main_queue()];
|
||||
FakePeripheralManagerDelegate *delegate = [[FakePeripheralManagerDelegate alloc] init];
|
||||
delegate.expectation = [self expectationWithDescription:@"Delegate called"];
|
||||
[multiplexer addListener:delegate];
|
||||
|
||||
GNCFakePeripheralManager *fakeManager = [[GNCFakePeripheralManager alloc] init];
|
||||
id request = [NSNull null]; // Use NSNull or any object as placeholder since we can't create
|
||||
// CBATTRequest
|
||||
|
||||
[multiplexer gnc_peripheralManager:fakeManager didReceiveReadRequest:request];
|
||||
|
||||
[self waitForExpectationsWithTimeout:1 handler:nil];
|
||||
XCTAssertTrue(delegate.didReceiveReadRequestCalled);
|
||||
XCTAssertEqual(delegate.readRequest, request);
|
||||
}
|
||||
|
||||
- (void)testMultiplexerForwardsDidPublishL2CAPChannel {
|
||||
GNCPeripheralManagerMultiplexer *multiplexer =
|
||||
[[GNCPeripheralManagerMultiplexer alloc] initWithCallbackQueue:dispatch_get_main_queue()];
|
||||
FakePeripheralManagerDelegate *delegate = [[FakePeripheralManagerDelegate alloc] init];
|
||||
delegate.expectation = [self expectationWithDescription:@"Delegate called"];
|
||||
[multiplexer addListener:delegate];
|
||||
|
||||
GNCFakePeripheralManager *fakeManager = [[GNCFakePeripheralManager alloc] init];
|
||||
CBL2CAPPSM psm = 42;
|
||||
NSError *error = [NSError errorWithDomain:@"test" code:3 userInfo:nil];
|
||||
|
||||
[multiplexer gnc_peripheralManager:fakeManager didPublishL2CAPChannel:psm error:error];
|
||||
|
||||
[self waitForExpectationsWithTimeout:1 handler:nil];
|
||||
XCTAssertTrue(delegate.didPublishL2CAPChannelCalled);
|
||||
XCTAssertEqual(delegate.publishedPSM, psm);
|
||||
XCTAssertEqualObjects(delegate.publishL2CAPChannelError, error);
|
||||
}
|
||||
|
||||
- (void)testMultiplexerForwardsDidUnpublishL2CAPChannel {
|
||||
GNCPeripheralManagerMultiplexer *multiplexer =
|
||||
[[GNCPeripheralManagerMultiplexer alloc] initWithCallbackQueue:dispatch_get_main_queue()];
|
||||
FakePeripheralManagerDelegate *delegate = [[FakePeripheralManagerDelegate alloc] init];
|
||||
delegate.expectation = [self expectationWithDescription:@"Delegate called"];
|
||||
[multiplexer addListener:delegate];
|
||||
|
||||
GNCFakePeripheralManager *fakeManager = [[GNCFakePeripheralManager alloc] init];
|
||||
CBL2CAPPSM psm = 42;
|
||||
NSError *error = [NSError errorWithDomain:@"test" code:4 userInfo:nil];
|
||||
|
||||
[multiplexer gnc_peripheralManager:fakeManager didUnpublishL2CAPChannel:psm error:error];
|
||||
|
||||
[self waitForExpectationsWithTimeout:1 handler:nil];
|
||||
XCTAssertTrue(delegate.didUnpublishL2CAPChannelCalled);
|
||||
XCTAssertEqual(delegate.unpublishedPSM, psm);
|
||||
XCTAssertEqualObjects(delegate.unpublishL2CAPChannelError, error);
|
||||
}
|
||||
|
||||
- (void)testMultiplexerForwardsDidOpenL2CAPChannel {
|
||||
GNCPeripheralManagerMultiplexer *multiplexer =
|
||||
[[GNCPeripheralManagerMultiplexer alloc] initWithCallbackQueue:dispatch_get_main_queue()];
|
||||
FakePeripheralManagerDelegate *delegate = [[FakePeripheralManagerDelegate alloc] init];
|
||||
delegate.expectation = [self expectationWithDescription:@"Delegate called"];
|
||||
[multiplexer addListener:delegate];
|
||||
|
||||
GNCFakePeripheralManager *fakeManager = [[GNCFakePeripheralManager alloc] init];
|
||||
id channel = [NSNull null]; // Placeholder
|
||||
NSError *error = [NSError errorWithDomain:@"test" code:5 userInfo:nil];
|
||||
|
||||
[multiplexer gnc_peripheralManager:fakeManager didOpenL2CAPChannel:channel error:error];
|
||||
|
||||
[self waitForExpectationsWithTimeout:1 handler:nil];
|
||||
XCTAssertTrue(delegate.didOpenL2CAPChannelCalled);
|
||||
XCTAssertEqual(delegate.openedChannel, channel);
|
||||
XCTAssertEqualObjects(delegate.openL2CAPChannelError, error);
|
||||
}
|
||||
|
||||
- (void)testCBPeripheralManagerDelegateDidUpdateState {
|
||||
GNCPeripheralManagerMultiplexer *multiplexer =
|
||||
[[GNCPeripheralManagerMultiplexer alloc] initWithCallbackQueue:dispatch_get_main_queue()];
|
||||
FakePeripheralManagerDelegate *delegate = [[FakePeripheralManagerDelegate alloc] init];
|
||||
delegate.expectation = [self expectationWithDescription:@"Delegate called"];
|
||||
[multiplexer addListener:delegate];
|
||||
|
||||
GNCFakePeripheralManager *fakeManager = [[GNCFakePeripheralManager alloc] init];
|
||||
fakeManager.state = CBManagerStatePoweredOn;
|
||||
|
||||
[multiplexer peripheralManagerDidUpdateState:(CBPeripheralManager *)fakeManager];
|
||||
|
||||
[self waitForExpectationsWithTimeout:1 handler:nil];
|
||||
XCTAssertTrue(delegate.didUpdateStateCalled);
|
||||
XCTAssertEqual(delegate.state, CBManagerStatePoweredOn);
|
||||
}
|
||||
|
||||
- (void)testCBPeripheralManagerDelegateDidStartAdvertising {
|
||||
GNCPeripheralManagerMultiplexer *multiplexer =
|
||||
[[GNCPeripheralManagerMultiplexer alloc] initWithCallbackQueue:dispatch_get_main_queue()];
|
||||
FakePeripheralManagerDelegate *delegate = [[FakePeripheralManagerDelegate alloc] init];
|
||||
delegate.expectation = [self expectationWithDescription:@"Delegate called"];
|
||||
[multiplexer addListener:delegate];
|
||||
|
||||
GNCFakePeripheralManager *fakeManager = [[GNCFakePeripheralManager alloc] init];
|
||||
NSError *error = [NSError errorWithDomain:@"test" code:10 userInfo:nil];
|
||||
|
||||
[multiplexer peripheralManagerDidStartAdvertising:(CBPeripheralManager *)fakeManager error:error];
|
||||
|
||||
[self waitForExpectationsWithTimeout:1 handler:nil];
|
||||
XCTAssertTrue(delegate.didStartAdvertisingCalled);
|
||||
XCTAssertEqualObjects(delegate.startAdvertisingError, error);
|
||||
}
|
||||
|
||||
- (void)testCBPeripheralManagerDelegateDidAddService {
|
||||
GNCPeripheralManagerMultiplexer *multiplexer =
|
||||
[[GNCPeripheralManagerMultiplexer alloc] initWithCallbackQueue:dispatch_get_main_queue()];
|
||||
FakePeripheralManagerDelegate *delegate = [[FakePeripheralManagerDelegate alloc] init];
|
||||
delegate.expectation = [self expectationWithDescription:@"Delegate called"];
|
||||
[multiplexer addListener:delegate];
|
||||
|
||||
GNCFakePeripheralManager *fakeManager = [[GNCFakePeripheralManager alloc] init];
|
||||
CBMutableService *service =
|
||||
[[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:@"180F"] primary:YES];
|
||||
NSError *error = [NSError errorWithDomain:@"test" code:11 userInfo:nil];
|
||||
|
||||
[multiplexer peripheralManager:(CBPeripheralManager *)fakeManager didAddService:service error:error];
|
||||
|
||||
[self waitForExpectationsWithTimeout:1 handler:nil];
|
||||
XCTAssertTrue(delegate.didAddServiceCalled);
|
||||
XCTAssertEqualObjects(delegate.addedService, service);
|
||||
XCTAssertEqualObjects(delegate.addServiceError, error);
|
||||
}
|
||||
|
||||
- (void)testCBPeripheralManagerDelegateDidReceiveReadRequest {
|
||||
GNCPeripheralManagerMultiplexer *multiplexer =
|
||||
[[GNCPeripheralManagerMultiplexer alloc] initWithCallbackQueue:dispatch_get_main_queue()];
|
||||
FakePeripheralManagerDelegate *delegate = [[FakePeripheralManagerDelegate alloc] init];
|
||||
delegate.expectation = [self expectationWithDescription:@"Delegate called"];
|
||||
[multiplexer addListener:delegate];
|
||||
|
||||
GNCFakePeripheralManager *fakeManager = [[GNCFakePeripheralManager alloc] init];
|
||||
id request = [NSNull null];
|
||||
|
||||
[multiplexer peripheralManager:(CBPeripheralManager *)fakeManager didReceiveReadRequest:request];
|
||||
|
||||
[self waitForExpectationsWithTimeout:1 handler:nil];
|
||||
XCTAssertTrue(delegate.didReceiveReadRequestCalled);
|
||||
XCTAssertEqual(delegate.readRequest, request);
|
||||
}
|
||||
|
||||
- (void)testCBPeripheralManagerDelegateDidPublishL2CAPChannel {
|
||||
GNCPeripheralManagerMultiplexer *multiplexer =
|
||||
[[GNCPeripheralManagerMultiplexer alloc] initWithCallbackQueue:dispatch_get_main_queue()];
|
||||
FakePeripheralManagerDelegate *delegate = [[FakePeripheralManagerDelegate alloc] init];
|
||||
delegate.expectation = [self expectationWithDescription:@"Delegate called"];
|
||||
[multiplexer addListener:delegate];
|
||||
|
||||
GNCFakePeripheralManager *fakeManager = [[GNCFakePeripheralManager alloc] init];
|
||||
CBL2CAPPSM psm = 100;
|
||||
NSError *error = [NSError errorWithDomain:@"test" code:13 userInfo:nil];
|
||||
|
||||
[multiplexer peripheralManager:(CBPeripheralManager *)fakeManager
|
||||
didPublishL2CAPChannel:psm
|
||||
error:error];
|
||||
|
||||
[self waitForExpectationsWithTimeout:1 handler:nil];
|
||||
XCTAssertTrue(delegate.didPublishL2CAPChannelCalled);
|
||||
XCTAssertEqual(delegate.publishedPSM, psm);
|
||||
XCTAssertEqualObjects(delegate.publishL2CAPChannelError, error);
|
||||
}
|
||||
|
||||
- (void)testCBPeripheralManagerDelegateDidUnpublishL2CAPChannel {
|
||||
GNCPeripheralManagerMultiplexer *multiplexer =
|
||||
[[GNCPeripheralManagerMultiplexer alloc] initWithCallbackQueue:dispatch_get_main_queue()];
|
||||
FakePeripheralManagerDelegate *delegate = [[FakePeripheralManagerDelegate alloc] init];
|
||||
delegate.expectation = [self expectationWithDescription:@"Delegate called"];
|
||||
[multiplexer addListener:delegate];
|
||||
|
||||
GNCFakePeripheralManager *fakeManager = [[GNCFakePeripheralManager alloc] init];
|
||||
CBL2CAPPSM psm = 101;
|
||||
NSError *error = [NSError errorWithDomain:@"test" code:14 userInfo:nil];
|
||||
|
||||
[multiplexer peripheralManager:(CBPeripheralManager *)fakeManager
|
||||
didUnpublishL2CAPChannel:psm
|
||||
error:error];
|
||||
|
||||
[self waitForExpectationsWithTimeout:1 handler:nil];
|
||||
XCTAssertTrue(delegate.didUnpublishL2CAPChannelCalled);
|
||||
XCTAssertEqual(delegate.unpublishedPSM, psm);
|
||||
XCTAssertEqualObjects(delegate.unpublishL2CAPChannelError, error);
|
||||
}
|
||||
|
||||
- (void)testCBPeripheralManagerDelegateDidOpenL2CAPChannel {
|
||||
GNCPeripheralManagerMultiplexer *multiplexer =
|
||||
[[GNCPeripheralManagerMultiplexer alloc] initWithCallbackQueue:dispatch_get_main_queue()];
|
||||
FakePeripheralManagerDelegate *delegate = [[FakePeripheralManagerDelegate alloc] init];
|
||||
delegate.expectation = [self expectationWithDescription:@"Delegate called"];
|
||||
[multiplexer addListener:delegate];
|
||||
|
||||
GNCFakePeripheralManager *fakeManager = [[GNCFakePeripheralManager alloc] init];
|
||||
id channel = [NSNull null];
|
||||
NSError *error = [NSError errorWithDomain:@"test" code:15 userInfo:nil];
|
||||
|
||||
[multiplexer peripheralManager:(CBPeripheralManager *)fakeManager
|
||||
didOpenL2CAPChannel:channel
|
||||
error:error];
|
||||
|
||||
[self waitForExpectationsWithTimeout:1 handler:nil];
|
||||
XCTAssertTrue(delegate.didOpenL2CAPChannelCalled);
|
||||
XCTAssertEqual(delegate.openedChannel, channel);
|
||||
XCTAssertEqualObjects(delegate.openL2CAPChannelError, error);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -32,7 +32,7 @@ objc_library(
|
||||
"GNCNWFramework.m",
|
||||
"GNCNWFrameworkError.m",
|
||||
"GNCNWFrameworkServerSocket.m",
|
||||
"GNCNWFrameworkSocket.m",
|
||||
"GNCNWFrameworkSocket.mm",
|
||||
"GNCNWListenerImpl.m",
|
||||
"GNCNWParameters.m",
|
||||
],
|
||||
|
||||
@@ -26,4 +26,5 @@ typedef NS_ERROR_ENUM(GNCNWFrameworkErrorDomain, GNCNWFrameworkError){
|
||||
GNCNWFrameworkErrorUnknown,
|
||||
GNCNWFrameworkErrorTimedOut,
|
||||
GNCNWFrameworkErrorDuplicateDiscovererForServiceType,
|
||||
GNCNWFrameworkErrorNotConnected,
|
||||
};
|
||||
|
||||
@@ -15,6 +15,12 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Network/Network.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#endif
|
||||
|
||||
|
||||
@protocol GNCNWConnection;
|
||||
|
||||
@interface GNCNWFrameworkSocket : NSObject
|
||||
@@ -44,6 +50,21 @@
|
||||
*/
|
||||
- (nullable NSData *)readMaxLength:(NSUInteger)length error:(NSError **_Nullable)error;
|
||||
|
||||
/**
|
||||
* Reads the requested amount of bytes from the connection and converts it to a string.
|
||||
*
|
||||
* Blocks execution until the bytes have been read or an error occurs.
|
||||
*
|
||||
* @param length The number of bytes to read.
|
||||
* @param[out] error Error that will be populated on failure. A read may return non-nil data along
|
||||
* with an error. This normally happens if the data read is shorter than the
|
||||
* requested length.
|
||||
*/
|
||||
#ifdef __cplusplus
|
||||
- (std::optional<std::string>)readStringWithMaxLength:(NSUInteger)length
|
||||
error:(NSError **_Nullable)error;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Writes the given data to the connection.
|
||||
*
|
||||
@@ -54,6 +75,17 @@
|
||||
*/
|
||||
- (BOOL)write:(NSData *)data error:(NSError **_Nullable)error;
|
||||
|
||||
/**
|
||||
* Writes raw bytes to the connection.
|
||||
*
|
||||
* @param bytes The buffer to write.
|
||||
* @param length The number of bytes to write.
|
||||
* @param error Error that will be populated on failure.
|
||||
*/
|
||||
- (BOOL)writeBytes:(const void *)bytes
|
||||
length:(NSUInteger)length
|
||||
error:(NSError **_Nullable)error;
|
||||
|
||||
/**
|
||||
* Gracefully closes the connection to remote endpoint.
|
||||
*
|
||||
|
||||
+93
@@ -17,6 +17,9 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Network/Network.h>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#import "internal/platform/implementation/apple/Log/GNCLogger.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWConnection.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkError.h"
|
||||
@@ -85,6 +88,52 @@ static const NSTimeInterval kConnectionWriteTimeout = 5.0; // 5 seconds timeout
|
||||
return blockResult;
|
||||
}
|
||||
|
||||
- (std::optional<std::string>)readStringWithMaxLength:(NSUInteger)length error:(NSError **)error {
|
||||
if (!self.connection) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:GNCNWFrameworkErrorDomain
|
||||
code:GNCNWFrameworkErrorNotConnected
|
||||
userInfo:nil];
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
||||
__block std::string resultString;
|
||||
__block NSError *blockError = nil;
|
||||
__block BOOL contentReceived = NO;
|
||||
|
||||
[self.connection
|
||||
receiveMessageWithMinLength:(uint32_t)length
|
||||
maxLength:(uint32_t)length
|
||||
completionHandler:^(dispatch_data_t _Nullable content,
|
||||
nw_content_context_t _Nullable context, bool isComplete,
|
||||
nw_error_t _Nullable receiveError) {
|
||||
if (receiveError) {
|
||||
blockError = (__bridge_transfer NSError *)nw_error_copy_cf_error(receiveError);
|
||||
}
|
||||
if (content) {
|
||||
contentReceived = YES;
|
||||
// OPTIMIZATION: Copy directly from dispatch_data_t into std::string
|
||||
resultString.reserve(dispatch_data_get_size(content));
|
||||
dispatch_data_apply(content, ^bool(dispatch_data_t region, size_t offset,
|
||||
const void *buffer, size_t size) {
|
||||
resultString.append((const char *)buffer, size);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
dispatch_semaphore_signal(semaphore);
|
||||
}];
|
||||
|
||||
// Block the current thread until the network callback completes.
|
||||
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
|
||||
|
||||
if (error != nil) {
|
||||
*error = blockError;
|
||||
}
|
||||
return (!contentReceived) ? std::nullopt : std::make_optional(std::move(resultString));
|
||||
}
|
||||
|
||||
- (BOOL)write:(NSData *)data error:(NSError **)error {
|
||||
if (!self.connection) {
|
||||
if (error) {
|
||||
@@ -136,6 +185,50 @@ static const NSTimeInterval kConnectionWriteTimeout = 5.0; // 5 seconds timeout
|
||||
return signaled && blockSuccess;
|
||||
}
|
||||
|
||||
- (BOOL)writeBytes:(const void *)bytes length:(NSUInteger)length error:(NSError **)error {
|
||||
if (!self.connection) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:GNCNWFrameworkErrorDomain
|
||||
code:GNCNWFrameworkErrorNotConnected
|
||||
userInfo:nil];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
||||
|
||||
__block NSError *blockError = nil;
|
||||
|
||||
// OPTIMIZATION: Use DISPATCH_DATA_DESTRUCTOR_DEFAULT to perform a
|
||||
// single copy into a GCD-managed buffer. No NSData required.
|
||||
// TODO: edwinwu - Investigate to see if it is worth to make it zero-copy by replacing
|
||||
// DISPATCH_DATA_DESTRUCTOR_DEFAULT with a custom empty destructor:
|
||||
// dispatch_data_t dispatchData = dispatch_data_create(bytes, length, nil, ^{
|
||||
// // Zero-copy: ownership remains with the caller.
|
||||
// });
|
||||
dispatch_data_t dispatchData =
|
||||
dispatch_data_create(bytes, length, nil, DISPATCH_DATA_DESTRUCTOR_DEFAULT);
|
||||
|
||||
[self.connection sendData:dispatchData
|
||||
context:NW_CONNECTION_DEFAULT_MESSAGE_CONTEXT
|
||||
isComplete:NO
|
||||
completionHandler:^(nw_error_t _Nullable sendError) {
|
||||
if (sendError) {
|
||||
blockError = (__bridge_transfer NSError *)nw_error_copy_cf_error(sendError);
|
||||
}
|
||||
dispatch_semaphore_signal(semaphore);
|
||||
}];
|
||||
|
||||
// Wait until signaled or the 5-second timeout passes
|
||||
intptr_t waitResult = dispatch_semaphore_wait(
|
||||
semaphore,
|
||||
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kConnectionWriteTimeout * NSEC_PER_SEC)));
|
||||
if (error != nil) {
|
||||
*error = blockError;
|
||||
}
|
||||
return (waitResult == 0) && (blockError == nil);
|
||||
}
|
||||
|
||||
- (void)close {
|
||||
[_connection cancel];
|
||||
_connection = nil;
|
||||
@@ -29,7 +29,7 @@ objc_library(
|
||||
"GNCFakeNWConnection.m",
|
||||
"GNCFakeNWFramework.m",
|
||||
"GNCFakeNWFrameworkServerSocket.m",
|
||||
"GNCFakeNWFrameworkSocket.m",
|
||||
"GNCFakeNWFrameworkSocket.mm",
|
||||
"GNCFakeNWListener.m",
|
||||
],
|
||||
hdrs = [
|
||||
@@ -57,7 +57,7 @@ objc_library(
|
||||
"GNCNWBrowserImplTest.m",
|
||||
"GNCNWConnectionImplTest.m",
|
||||
"GNCNWFrameworkServerSocketTest.m",
|
||||
"GNCNWFrameworkSocketTest.m",
|
||||
"GNCNWFrameworkSocketTest.mm",
|
||||
"GNCNWFrameworkTest.m",
|
||||
"GNCNWListenerImplTest.m",
|
||||
"GNCNWParametersTest.m",
|
||||
|
||||
+30
@@ -44,6 +44,25 @@
|
||||
return [NSData data];
|
||||
}
|
||||
|
||||
- (std::optional<std::string>)readStringWithMaxLength:(NSUInteger)length error:(NSError **)error {
|
||||
if (self.readError) {
|
||||
if (error) *error = self.readError;
|
||||
return std::nullopt;
|
||||
}
|
||||
if (self.dataToRead) {
|
||||
NSData *data = self.dataToRead;
|
||||
self.dataToRead = nil;
|
||||
NSUInteger actualLength = MIN(length, data.length);
|
||||
if (data.length > actualLength) {
|
||||
self.dataToRead =
|
||||
[data subdataWithRange:NSMakeRange(actualLength, data.length - actualLength)];
|
||||
}
|
||||
NSData *returnData = [data subdataWithRange:NSMakeRange(0, actualLength)];
|
||||
return std::string((const char *)returnData.bytes, returnData.length);
|
||||
}
|
||||
return std::string();
|
||||
}
|
||||
|
||||
- (BOOL)write:(NSData *)data error:(NSError **)error {
|
||||
if (self.writeError) {
|
||||
if (error) {
|
||||
@@ -55,6 +74,17 @@
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)writeBytes:(const void *)bytes length:(NSUInteger)length error:(NSError **)error {
|
||||
if (self.writeError) {
|
||||
if (error) {
|
||||
*error = self.writeError;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
[self.writtenData appendBytes:bytes length:length];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)close {
|
||||
self.isClosed = YES;
|
||||
}
|
||||
+58
@@ -17,6 +17,9 @@
|
||||
#import <Network/Network.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/Tests/GNCFakeNWConnection.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
@@ -75,6 +78,38 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
XCTAssertNil(error);
|
||||
}
|
||||
|
||||
- (void)testReadStringWithMaxLength_Success {
|
||||
NSError *error = nil;
|
||||
NSString *testString = @"testData";
|
||||
NSData *testData = [testString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
dispatch_data_t dispatchData = dispatch_data_create(testData.bytes, testData.length, dispatch_get_main_queue(), ^{});
|
||||
_fakeConnection.dataToReceive = dispatchData;
|
||||
|
||||
std::optional<std::string> receivedString = [_socket readStringWithMaxLength:testData.length error:&error];
|
||||
|
||||
XCTAssertTrue(receivedString.has_value());
|
||||
XCTAssertEqualObjects(@(receivedString.value().c_str()), testString);
|
||||
XCTAssertNil(error);
|
||||
}
|
||||
|
||||
- (void)testReadStringWithMaxLength_Error {
|
||||
NSError *error = nil;
|
||||
_fakeConnection.simulateReceiveFailure = YES;
|
||||
|
||||
std::optional<std::string> receivedString = [_socket readStringWithMaxLength:10 error:&error];
|
||||
|
||||
XCTAssertFalse(receivedString.has_value());
|
||||
XCTAssertNil(error); // Fake doesn't produce an NSError
|
||||
}
|
||||
|
||||
- (void)testReadStringWithMaxLength_Zero {
|
||||
NSError *error = nil;
|
||||
std::optional<std::string> receivedString = [_socket readStringWithMaxLength:0 error:&error];
|
||||
|
||||
XCTAssertFalse(receivedString.has_value());
|
||||
XCTAssertNil(error);
|
||||
}
|
||||
|
||||
- (void)testWrite_Success {
|
||||
NSError *error = nil;
|
||||
NSString *testString = @"testData";
|
||||
@@ -97,6 +132,28 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
XCTAssertFalse(result);
|
||||
}
|
||||
|
||||
- (void)testWriteBytes_Success {
|
||||
NSError *error = nil;
|
||||
NSString *testString = @"testData";
|
||||
NSData *testData = [testString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
BOOL result = [_socket writeBytes:testData.bytes length:testData.length error:&error];
|
||||
|
||||
XCTAssertTrue(result);
|
||||
XCTAssertNil(error);
|
||||
}
|
||||
|
||||
- (void)testWriteBytes_Error {
|
||||
NSError *error = nil;
|
||||
NSString *testString = @"testData";
|
||||
NSData *testData = [testString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
_fakeConnection.simulateSendFailure = YES;
|
||||
|
||||
BOOL result = [_socket writeBytes:testData.bytes length:testData.length error:&error];
|
||||
|
||||
XCTAssertFalse(result);
|
||||
}
|
||||
|
||||
- (void)testClose {
|
||||
XCTAssertFalse(_fakeConnection.cancelCalled);
|
||||
[_socket close];
|
||||
@@ -105,6 +162,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
NSError *error = nil;
|
||||
XCTAssertNil([_socket readMaxLength:10 error:&error]);
|
||||
XCTAssertFalse([_socket write:[NSData data] error:&error]);
|
||||
XCTAssertFalse([_socket writeBytes:"test" length:4 error:&error]);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -56,8 +56,10 @@ objc_library(
|
||||
"//internal/platform/implementation:comm",
|
||||
"//internal/platform/implementation:platform",
|
||||
"//internal/platform/implementation:types",
|
||||
"//internal/platform/implementation:webrtc_platform",
|
||||
"//internal/platform/implementation/apple", # buildcleaner: keep
|
||||
"//internal/platform/implementation/apple:Shared",
|
||||
"//internal/platform/implementation/apple:apple_webrtc", # buildcleaner: keep
|
||||
"//internal/platform/implementation/apple:ble_v2",
|
||||
"//internal/platform/implementation/apple:network_utils",
|
||||
"//internal/platform/implementation/apple/Flags",
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "connections/implementation/flags/nearby_connections_feature_flags.h"
|
||||
#include "internal/flags/nearby_flags.h"
|
||||
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCIPv4Address.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFramework.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/Tests/GNCFakeNWConnection.h"
|
||||
@@ -45,6 +48,7 @@ static const int kTestPort = 1234;
|
||||
|
||||
- (void)tearDown {
|
||||
_awdlMedium.reset();
|
||||
nearby::NearbyFlags::GetInstance().ResetOverridedValues();
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
@@ -128,6 +132,10 @@ static const int kTestPort = 1234;
|
||||
}
|
||||
|
||||
- (void)testSocketAndStream {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
::nearby::connections::config_package_nearby::nearby_connections_feature::kEnableSingleCopy,
|
||||
false);
|
||||
|
||||
// Create a server socket.
|
||||
std::unique_ptr<nearby::api::AwdlServerSocket> serverSocket =
|
||||
_awdlMedium->ListenForService(kTestPort);
|
||||
@@ -167,6 +175,49 @@ static const int kTestPort = 1234;
|
||||
XCTAssertTrue(fakeServerSocket.isClosed);
|
||||
}
|
||||
|
||||
- (void)testSocketAndStream_SingleCopyEnabled {
|
||||
// Enable the flag.
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
::nearby::connections::config_package_nearby::nearby_connections_feature::kEnableSingleCopy,
|
||||
true);
|
||||
|
||||
// Create a server socket.
|
||||
std::unique_ptr<nearby::api::AwdlServerSocket> serverSocket =
|
||||
_awdlMedium->ListenForService(kTestPort);
|
||||
XCTAssertTrue(serverSocket != nullptr);
|
||||
|
||||
GNCFakeNWFrameworkServerSocket* fakeServerSocket =
|
||||
(GNCFakeNWFrameworkServerSocket*)_fakeNWFramework.serverSockets[0];
|
||||
GNCFakeNWConnection* connection = [[GNCFakeNWConnection alloc] init];
|
||||
GNCFakeNWFrameworkSocket* fakeSocket =
|
||||
[[GNCFakeNWFrameworkSocket alloc] initWithConnection:connection];
|
||||
fakeServerSocket.socketToReturnOnAccept = fakeSocket;
|
||||
|
||||
// Accept a client socket.
|
||||
std::unique_ptr<nearby::api::AwdlSocket> clientSocket = serverSocket->Accept();
|
||||
XCTAssertTrue(clientSocket != nullptr);
|
||||
|
||||
// Test input stream with optimized single-copy read.
|
||||
nearby::InputStream& inputStream = clientSocket->GetInputStream();
|
||||
fakeSocket.dataToRead = [@"optimized awdl data" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
// "optimized awdl data" is 19 bytes.
|
||||
nearby::ExceptionOr<nearby::ByteArray> readData = inputStream.Read(19);
|
||||
|
||||
XCTAssertTrue(readData.ok());
|
||||
XCTAssertEqual(std::string(readData.result()), "optimized awdl data");
|
||||
|
||||
// Test output stream.
|
||||
nearby::OutputStream& outputStream = clientSocket->GetOutputStream();
|
||||
absl::string_view writeData("write data");
|
||||
XCTAssertTrue(outputStream.Write(writeData).Ok());
|
||||
XCTAssertEqualObjects(fakeSocket.writtenData,
|
||||
[@"write data" dataUsingEncoding:NSUTF8StringEncoding]);
|
||||
|
||||
// Clean up.
|
||||
XCTAssertTrue(clientSocket->Close().Ok());
|
||||
XCTAssertTrue(serverSocket->Close().Ok());
|
||||
}
|
||||
|
||||
- (void)testServerSocketGetIPAddress {
|
||||
// Create a server socket.
|
||||
std::unique_ptr<nearby::api::AwdlServerSocket> serverSocket =
|
||||
@@ -195,6 +246,39 @@ static const int kTestPort = 1234;
|
||||
XCTAssertTrue(serverSocket->Close().Ok());
|
||||
}
|
||||
|
||||
- (void)testOutputStreamWrite_SingleCopyEnabled {
|
||||
// Enable the flag.
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
::nearby::connections::config_package_nearby::nearby_connections_feature::kEnableSingleCopy,
|
||||
true);
|
||||
|
||||
// Create a server socket and accept a client.
|
||||
std::unique_ptr<nearby::api::AwdlServerSocket> serverSocket =
|
||||
_awdlMedium->ListenForService(kTestPort);
|
||||
GNCFakeNWFrameworkServerSocket* fakeServerSocket =
|
||||
(GNCFakeNWFrameworkServerSocket*)_fakeNWFramework.serverSockets[0];
|
||||
GNCFakeNWConnection* connection = [[GNCFakeNWConnection alloc] init];
|
||||
GNCFakeNWFrameworkSocket* fakeSocket =
|
||||
[[GNCFakeNWFrameworkSocket alloc] initWithConnection:connection];
|
||||
fakeServerSocket.socketToReturnOnAccept = fakeSocket;
|
||||
std::unique_ptr<nearby::api::AwdlSocket> clientSocket = serverSocket->Accept();
|
||||
XCTAssertTrue(clientSocket != nullptr);
|
||||
|
||||
// Test output stream.
|
||||
nearby::OutputStream& outputStream = clientSocket->GetOutputStream();
|
||||
absl::string_view writeData("optimized write data");
|
||||
XCTAssertTrue(outputStream.Write(writeData).Ok());
|
||||
XCTAssertEqualObjects(fakeSocket.writtenData,
|
||||
[@"optimized write data" dataUsingEncoding:NSUTF8StringEncoding]);
|
||||
|
||||
// Clean up.
|
||||
XCTAssertTrue(clientSocket->Close().Ok());
|
||||
XCTAssertTrue(serverSocket->Close().Ok());
|
||||
|
||||
// Reset the flag.
|
||||
nearby::NearbyFlags::GetInstance().ResetOverridedValues();
|
||||
}
|
||||
|
||||
- (void)testOutputStreamClose {
|
||||
// Create a server socket and accept a client.
|
||||
std::unique_ptr<nearby::api::AwdlServerSocket> serverSocket =
|
||||
|
||||
@@ -49,27 +49,20 @@
|
||||
}
|
||||
|
||||
- (void)testGetDownloadPath {
|
||||
XCTAssertNotNil(@(_deviceInfo->GetDownloadPath().value().GetPath().c_str()));
|
||||
XCTAssertNotNil(@(_deviceInfo->GetDownloadPath().GetPath().c_str()));
|
||||
}
|
||||
|
||||
- (void)testGetLocalAppDataPath {
|
||||
XCTAssertNotNil(@(_deviceInfo->GetLocalAppDataPath().value().GetPath().c_str()));
|
||||
}
|
||||
|
||||
- (void)testGetCommonAppDataPath {
|
||||
XCTAssertNotNil(@(_deviceInfo->GetCommonAppDataPath().value().GetPath().c_str()));
|
||||
XCTAssertNotNil(
|
||||
@(_deviceInfo->GetLocalAppDataPath(nearby::FilePath("sub_path")).GetPath().c_str()));
|
||||
}
|
||||
|
||||
- (void)testGetTemporaryPath {
|
||||
XCTAssertNotNil(@(_deviceInfo->GetTemporaryPath().value().GetPath().c_str()));
|
||||
XCTAssertNotNil(@(_deviceInfo->GetTemporaryPath().GetPath().c_str()));
|
||||
}
|
||||
|
||||
- (void)testGetLogPath {
|
||||
XCTAssertNotNil(@(_deviceInfo->GetLogPath().value().GetPath().c_str()));
|
||||
}
|
||||
|
||||
- (void)testGetCrashDumpPath {
|
||||
XCTAssertNotNil(@(_deviceInfo->GetCrashDumpPath().value().GetPath().c_str()));
|
||||
XCTAssertNotNil(@(_deviceInfo->GetLogPath().GetPath().c_str()));
|
||||
}
|
||||
|
||||
- (void)testIsScreenLocked {
|
||||
|
||||
@@ -93,7 +93,7 @@ using MultiThreadExecutor = ::nearby::api::SubmittableExecutor;
|
||||
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_TARGET_QUEUE_DEFAULT, 0);
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"finished"];
|
||||
|
||||
const int kRunnableCount = 1000;
|
||||
const int kRunnableCount = 100;
|
||||
for (int i = 0; i < kRunnableCount; i++) {
|
||||
executor->Execute([self]() { self.counter++; });
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
#include "internal/platform/implementation/platform.h"
|
||||
#include "internal/platform/implementation/webrtc_platform.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
@@ -320,7 +321,7 @@ void GNCEnsureFileAtPath(std::string path) {
|
||||
}
|
||||
|
||||
- (void)testCreateWebRtcMedium {
|
||||
auto webrtc_medium = nearby::api::ImplementationPlatform::CreateWebRtcMedium();
|
||||
auto webrtc_medium = nearby::api::WebRtcImplementationPlatform::CreateWebRtcMedium();
|
||||
XCTAssertNotEqual(webrtc_medium.get(), nullptr);
|
||||
}
|
||||
|
||||
|
||||
@@ -76,20 +76,14 @@ using SingleThreadExecutor = ::nearby::api::SubmittableExecutor;
|
||||
// Tests that shutting down an existing task allows to complete.
|
||||
- (void)testShutdownToAllowExistingTaskComplete {
|
||||
std::unique_ptr<SingleThreadExecutor> executor([self executor]);
|
||||
|
||||
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_TARGET_QUEUE_DEFAULT, 0);
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"finished"];
|
||||
|
||||
executor->Execute([self]() { self.counter++; });
|
||||
|
||||
executor->Shutdown();
|
||||
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), queue, ^{
|
||||
XCTAssertEqual(self.counter, 1);
|
||||
executor->Execute([self, expectation]() {
|
||||
self.counter++;
|
||||
[expectation fulfill];
|
||||
});
|
||||
|
||||
[self waitForExpectationsWithTimeout:0.5 handler:nil];
|
||||
executor->Shutdown();
|
||||
[self waitForExpectationsWithTimeout:1.0 handler:nil];
|
||||
XCTAssertEqual(self.counter, 1);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
auto timer = std::make_unique<nearby::apple::Timer>();
|
||||
|
||||
std::atomic<int> fireCount = 0;
|
||||
XCTAssertTrue(timer->Create(10, 10, [&]() {
|
||||
XCTAssertTrue(timer->Create(100, 100, [&]() {
|
||||
if (fireCount.fetch_add(1) == 1) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[expectation fulfill];
|
||||
@@ -73,9 +73,9 @@
|
||||
}
|
||||
}));
|
||||
|
||||
[self waitForExpectationsWithTimeout:1.0 handler:nil];
|
||||
[self waitForExpectationsWithTimeout:2.0 handler:nil];
|
||||
XCTAssertTrue(timer->Stop());
|
||||
XCTAssertEqual(fireCount.load(), 2);
|
||||
XCTAssertGreaterThanOrEqual(fireCount.load(), 2);
|
||||
}
|
||||
|
||||
- (void)testRestart {
|
||||
|
||||
@@ -19,8 +19,11 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "connections/implementation/flags/nearby_connections_feature_flags.h"
|
||||
#include "internal/flags/nearby_flags.h"
|
||||
#include "internal/platform/byte_array.h"
|
||||
#include "internal/platform/exception.h"
|
||||
|
||||
#import "internal/platform/implementation/apple/Mediums/CoreLocation/CLLocationManager/Fake/CLLocationManagerFake.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/Hotspot/GNCHotspotMedium.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCIPv4Address.h"
|
||||
@@ -59,8 +62,8 @@ const char kIPAddress[] = "192.168.1.2";
|
||||
_medium.locationManager = _fakeLocationManager;
|
||||
_hotspotMedium = std::make_unique<nearby::apple::WifiHotspotMedium>(_medium);
|
||||
_service_address = {
|
||||
.address = {static_cast<char>(192), static_cast<char>(168), 1, 2},
|
||||
.port = 1234,
|
||||
.address = {static_cast<char>(192), static_cast<char>(168), 1, 2},
|
||||
.port = 1234,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -98,6 +101,10 @@ const char kIPAddress[] = "192.168.1.2";
|
||||
}
|
||||
|
||||
- (void)testInputStreamRead {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
::nearby::connections::config_package_nearby::nearby_connections_feature::kEnableSingleCopy,
|
||||
false);
|
||||
|
||||
nearby::CancellationFlag cancellationFlag;
|
||||
std::unique_ptr<nearby::api::WifiHotspotSocket> socket =
|
||||
_hotspotMedium->ConnectToService(_service_address, &cancellationFlag);
|
||||
@@ -110,6 +117,28 @@ const char kIPAddress[] = "192.168.1.2";
|
||||
XCTAssertTrue(readData.ok());
|
||||
XCTAssertEqual(readData.result().size(), 4);
|
||||
XCTAssertEqual(strncmp(readData.result().data(), "Test", 4), 0);
|
||||
|
||||
nearby::NearbyFlags::GetInstance().ResetOverridedValues();
|
||||
}
|
||||
|
||||
- (void)testInputStreamRead_SingleCopyEnabled {
|
||||
// Enable the flag
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
::nearby::connections::config_package_nearby::nearby_connections_feature::kEnableSingleCopy,
|
||||
true);
|
||||
|
||||
nearby::CancellationFlag cancellationFlag;
|
||||
std::unique_ptr<nearby::api::WifiHotspotSocket> socket =
|
||||
_hotspotMedium->ConnectToService(_service_address, &cancellationFlag);
|
||||
GNCFakeNWFrameworkSocket *fakeSocket = _fakeNWFramework.sockets.firstObject;
|
||||
fakeSocket.dataToRead = [@"HotspotOpt" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
nearby::ExceptionOr<nearby::ByteArray> readData = socket->GetInputStream().Read(10);
|
||||
|
||||
XCTAssertTrue(readData.ok());
|
||||
XCTAssertEqual(std::string(readData.result()), "HotspotOpt");
|
||||
|
||||
nearby::NearbyFlags::GetInstance().ResetOverridedValues();
|
||||
}
|
||||
|
||||
- (void)testInputStreamClose {
|
||||
@@ -125,6 +154,10 @@ const char kIPAddress[] = "192.168.1.2";
|
||||
}
|
||||
|
||||
- (void)testOutputStreamWrite {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
::nearby::connections::config_package_nearby::nearby_connections_feature::kEnableSingleCopy,
|
||||
false);
|
||||
|
||||
nearby::CancellationFlag cancellationFlag;
|
||||
std::unique_ptr<nearby::api::WifiHotspotSocket> socket =
|
||||
_hotspotMedium->ConnectToService(_service_address, &cancellationFlag);
|
||||
@@ -136,6 +169,29 @@ const char kIPAddress[] = "192.168.1.2";
|
||||
|
||||
XCTAssertTrue(writeResult.Ok());
|
||||
XCTAssertEqualObjects(fakeSocket.writtenData, data);
|
||||
|
||||
nearby::NearbyFlags::GetInstance().ResetOverridedValues();
|
||||
}
|
||||
|
||||
- (void)testOutputStreamWrite_SingleCopyEnabled {
|
||||
// Enable the flag
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
::nearby::connections::config_package_nearby::nearby_connections_feature::kEnableSingleCopy,
|
||||
true);
|
||||
|
||||
nearby::CancellationFlag cancellationFlag;
|
||||
std::unique_ptr<nearby::api::WifiHotspotSocket> socket =
|
||||
_hotspotMedium->ConnectToService(_service_address, &cancellationFlag);
|
||||
GNCFakeNWFrameworkSocket *fakeSocket = _fakeNWFramework.sockets.firstObject;
|
||||
NSData *data = [@"TestDataOpt" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
absl::string_view data_str(reinterpret_cast<const char *>(data.bytes), data.length);
|
||||
|
||||
nearby::Exception writeResult = socket->GetOutputStream().Write(data_str);
|
||||
|
||||
XCTAssertTrue(writeResult.Ok());
|
||||
XCTAssertEqualObjects(fakeSocket.writtenData, data);
|
||||
|
||||
nearby::NearbyFlags::GetInstance().ResetOverridedValues();
|
||||
}
|
||||
|
||||
- (void)testSocketClose {
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "connections/implementation/flags/nearby_connections_feature_flags.h"
|
||||
#include "internal/flags/nearby_flags.h"
|
||||
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCIPv4Address.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFramework.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/Tests/GNCFakeNWConnection.h"
|
||||
@@ -118,6 +121,10 @@
|
||||
}
|
||||
|
||||
- (void)testSocketAndStream {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
::nearby::connections::config_package_nearby::nearby_connections_feature::kEnableSingleCopy,
|
||||
false);
|
||||
|
||||
// Create a server socket.
|
||||
std::unique_ptr<nearby::api::WifiLanServerSocket> serverSocket =
|
||||
_wifiLanMedium->ListenForService(1234);
|
||||
@@ -155,6 +162,38 @@
|
||||
// Test closing the server socket.
|
||||
XCTAssertTrue(serverSocket->Close().Ok());
|
||||
XCTAssertTrue(fakeServerSocket.isClosed);
|
||||
// Reset the flag.
|
||||
nearby::NearbyFlags::GetInstance().ResetOverridedValues();
|
||||
}
|
||||
|
||||
- (void)testSocketAndStream_SingleCopyEnabled {
|
||||
// Enable the flag
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
::nearby::connections::config_package_nearby::nearby_connections_feature::kEnableSingleCopy,
|
||||
true);
|
||||
|
||||
// Create a server socket and accept a client.
|
||||
std::unique_ptr<nearby::api::WifiLanServerSocket> serverSocket =
|
||||
_wifiLanMedium->ListenForService(1234);
|
||||
GNCFakeNWFrameworkServerSocket* fakeServerSocket =
|
||||
(GNCFakeNWFrameworkServerSocket*)_fakeNWFramework.serverSockets[0];
|
||||
GNCFakeNWConnection* connection = [[GNCFakeNWConnection alloc] init];
|
||||
GNCFakeNWFrameworkSocket* fakeSocket =
|
||||
[[GNCFakeNWFrameworkSocket alloc] initWithConnection:connection];
|
||||
fakeServerSocket.socketToReturnOnAccept = fakeSocket;
|
||||
|
||||
std::unique_ptr<nearby::api::WifiLanSocket> clientSocket = serverSocket->Accept();
|
||||
nearby::InputStream& inputStream = clientSocket->GetInputStream();
|
||||
|
||||
// Test optimized single-copy read.
|
||||
fakeSocket.dataToRead = [@"optimized data" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
nearby::ExceptionOr<nearby::ByteArray> readData = inputStream.Read(14);
|
||||
|
||||
XCTAssertTrue(readData.ok());
|
||||
XCTAssertEqual(std::string(readData.result()), "optimized data");
|
||||
|
||||
// Reset the flag.
|
||||
nearby::NearbyFlags::GetInstance().ResetOverridedValues();
|
||||
}
|
||||
|
||||
- (void)testServerSocketGetIPAddress {
|
||||
@@ -185,6 +224,39 @@
|
||||
XCTAssertTrue(serverSocket->Close().Ok());
|
||||
}
|
||||
|
||||
- (void)testOutputStreamWrite_SingleCopyEnabled {
|
||||
// Enable the flag
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
::nearby::connections::config_package_nearby::nearby_connections_feature::kEnableSingleCopy,
|
||||
true);
|
||||
|
||||
// Create a server socket and accept a client.
|
||||
std::unique_ptr<nearby::api::WifiLanServerSocket> serverSocket =
|
||||
_wifiLanMedium->ListenForService(1234);
|
||||
GNCFakeNWFrameworkServerSocket* fakeServerSocket =
|
||||
(GNCFakeNWFrameworkServerSocket*)_fakeNWFramework.serverSockets[0];
|
||||
GNCFakeNWConnection* connection = [[GNCFakeNWConnection alloc] init];
|
||||
GNCFakeNWFrameworkSocket* fakeSocket =
|
||||
[[GNCFakeNWFrameworkSocket alloc] initWithConnection:connection];
|
||||
fakeServerSocket.socketToReturnOnAccept = fakeSocket;
|
||||
|
||||
std::unique_ptr<nearby::api::WifiLanSocket> clientSocket = serverSocket->Accept();
|
||||
nearby::OutputStream& outputStream = clientSocket->GetOutputStream();
|
||||
|
||||
// Test optimized single-copy write.
|
||||
absl::string_view writeData("optimized data");
|
||||
XCTAssertTrue(outputStream.Write(writeData).Ok());
|
||||
XCTAssertEqualObjects(fakeSocket.writtenData,
|
||||
[@"optimized data" dataUsingEncoding:NSUTF8StringEncoding]);
|
||||
|
||||
// Clean up.
|
||||
XCTAssertTrue(clientSocket->Close().Ok());
|
||||
XCTAssertTrue(serverSocket->Close().Ok());
|
||||
|
||||
// Reset the flag.
|
||||
nearby::NearbyFlags::GetInstance().ResetOverridedValues();
|
||||
}
|
||||
|
||||
- (void)testOutputStreamClose {
|
||||
// Create a server socket and accept a client.
|
||||
std::unique_ptr<nearby::api::WifiLanServerSocket> serverSocket =
|
||||
|
||||
@@ -56,8 +56,9 @@ using ::nearby::ObjCStringFromCppString;
|
||||
- (void)testUUIDStringFromNSUUID {
|
||||
NSString *uuidString = @"E621E1F8-C36C-495A-93FC-0C247A3E6E5F";
|
||||
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:uuidString];
|
||||
std::string expectedCppString = [uuidString UTF8String];
|
||||
XCTAssertEqual(nearby::UUIDStringFromNSUUID(uuid), expectedCppString);
|
||||
XCTAssert(nearby::UUIDStringFromNSUUID(uuid) ==
|
||||
std::string([uuidString UTF8String],
|
||||
[uuidString lengthOfBytesUsingEncoding:NSUTF8StringEncoding]));
|
||||
}
|
||||
|
||||
- (void)testBluetoothUUIDConversions {
|
||||
|
||||
@@ -210,19 +210,6 @@
|
||||
XCTAssertFalse(result);
|
||||
}
|
||||
|
||||
- (void)testSetCharacteristicSubscriptionReturnsFalse {
|
||||
GNCBLEGATTCharacteristic *characteristic =
|
||||
[[GNCBLEGATTCharacteristic alloc] initWithUUID:[CBUUID UUIDWithString:@"B2B4"]
|
||||
serviceUUID:[CBUUID UUIDWithString:@"FEF3"]
|
||||
permissions:CBAttributePermissionsReadable
|
||||
properties:CBCharacteristicPropertyNotify];
|
||||
nearby::api::ble::GattCharacteristic cppCharacteristic =
|
||||
nearby::apple::CPPGATTCharacteristicFromObjC(characteristic);
|
||||
BOOL result = _gattClient->SetCharacteristicSubscription(cppCharacteristic, true,
|
||||
[](absl::string_view value) {});
|
||||
XCTAssertFalse(result);
|
||||
}
|
||||
|
||||
- (void)testDisconnectWhenFlagEnabled {
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
|
||||
@@ -50,8 +50,9 @@
|
||||
|
||||
- (void)testBleL2capServerSocketAccept {
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"accept"];
|
||||
nearby::apple::BleL2capServerSocket *serverSocket = _serverSocket.get();
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
std::unique_ptr<nearby::api::ble::BleL2capSocket> clientSocket = _serverSocket->Accept();
|
||||
std::unique_ptr<nearby::api::ble::BleL2capSocket> clientSocket = serverSocket->Accept();
|
||||
XCTAssertNotEqual(clientSocket.get(), nullptr);
|
||||
[expectation fulfill];
|
||||
});
|
||||
@@ -76,8 +77,9 @@
|
||||
|
||||
- (void)testBleL2capServerSocketClose {
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"close"];
|
||||
nearby::apple::BleL2capServerSocket *serverSocket = _serverSocket.get();
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
std::unique_ptr<nearby::api::ble::BleL2capSocket> clientSocket = _serverSocket->Accept();
|
||||
std::unique_ptr<nearby::api::ble::BleL2capSocket> clientSocket = serverSocket->Accept();
|
||||
XCTAssertEqual(clientSocket.get(), nullptr);
|
||||
[expectation fulfill];
|
||||
});
|
||||
|
||||
@@ -23,9 +23,7 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTClient.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTServer.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPClient.h"
|
||||
#import "internal/platform/implementation/apple/Flags/GNCFeatureFlags.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEMedium.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCPeripheral.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/Sockets/Source/Central/GNSCentralManager.h"
|
||||
@@ -33,9 +31,13 @@
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/Sockets/Source/Peripheral/GNSPeripheralManager.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/Sockets/Source/Peripheral/GNSPeripheralServiceManager.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/Sockets/Source/Shared/GNSSocket.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEMedium+Testing.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakeBLEGATTServer.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakeBLEMedium.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakeCentralManager.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheral.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheralManager.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakeSocket.h"
|
||||
#include "internal/platform/implementation/apple/ble_utils.h"
|
||||
#include "internal/platform/implementation/ble.h"
|
||||
#import "third_party/objective_c/ocmock/v3/Source/OCMock/OCMock.h"
|
||||
@@ -45,11 +47,16 @@ namespace apple {
|
||||
|
||||
class BleMediumPeer {
|
||||
public:
|
||||
static void SetSocketCentralManager(BleMedium *ble_medium, GNSCentralManager *manager) {
|
||||
ble_medium->socketCentralManager_ = manager;
|
||||
static void SetPeripheralManagerFactory(BleMedium *ble_medium,
|
||||
BleMedium::PeripheralManagerFactory factory) {
|
||||
ble_medium->peripheral_manager_factory_ = std::move(factory);
|
||||
}
|
||||
static void SetSocketPeripheralManager(BleMedium *ble_medium, GNSPeripheralManager *manager) {
|
||||
ble_medium->socketPeripheralManager_ = manager;
|
||||
static void SetCentralManagerFactory(BleMedium *ble_medium,
|
||||
BleMedium::CentralManagerFactory factory) {
|
||||
ble_medium->central_manager_factory_ = std::move(factory);
|
||||
}
|
||||
static GNSPeripheralServiceManager *GetSocketPeripheralServiceManager(BleMedium *ble_medium) {
|
||||
return ble_medium->socketPeripheralServiceManager_;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -71,7 +78,11 @@ static const char *const kTestServiceID = "TestServiceID";
|
||||
|
||||
- (void)setUp {
|
||||
[super setUp];
|
||||
_fakeGNCBLEMedium = [[GNCFakeBLEMedium alloc] init];
|
||||
GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init];
|
||||
GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init];
|
||||
_fakeGNCBLEMedium = [[GNCFakeBLEMedium alloc] initWithCentralManager:fakeCentralManager
|
||||
peripheralManager:fakePeripheralManager
|
||||
queue:dispatch_get_main_queue()];
|
||||
_medium = std::make_unique<nearby::apple::BleMedium>((GNCBLEMedium *)_fakeGNCBLEMedium);
|
||||
}
|
||||
|
||||
@@ -79,6 +90,28 @@ static const char *const kTestServiceID = "TestServiceID";
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
- (void)testOpenServerSocket_UsesFactoryForInitialization {
|
||||
__block BOOL factoryWasCalled = NO;
|
||||
id mockPeripheralManager = OCMClassMock([GNSPeripheralManager class]);
|
||||
OCMStub([mockPeripheralManager addPeripheralServiceManager:[OCMArg any]
|
||||
bleServiceAddedCompletion:[OCMArg any]])
|
||||
.andDo(^(GNSPeripheralManager *localSelf, GNSPeripheralServiceManager *manager,
|
||||
void (^completion)(NSError *error)) {
|
||||
completion(nil);
|
||||
});
|
||||
|
||||
nearby::apple::BleMediumPeer::SetPeripheralManagerFactory(_medium.get(), ^() {
|
||||
factoryWasCalled = YES;
|
||||
return mockPeripheralManager;
|
||||
});
|
||||
|
||||
// This call should trigger the factory inside BleMedium.
|
||||
auto server_socket = _medium->OpenServerSocket(kTestServiceID);
|
||||
|
||||
XCTAssertTrue(factoryWasCalled, @"BleMedium should have requested the manager from the factory.");
|
||||
XCTAssertNotEqual(server_socket.get(), nullptr);
|
||||
}
|
||||
|
||||
#pragma mark - Advertising Tests
|
||||
|
||||
- (void)testStartAdvertising_Success {
|
||||
@@ -225,8 +258,8 @@ static const char *const kTestServiceID = "TestServiceID";
|
||||
#pragma mark - GATT Server Tests
|
||||
|
||||
- (void)testStartGattServer_Success {
|
||||
_fakeGNCBLEMedium.fakeGATTServer = [[GNCFakeBLEGATTServer alloc] init];
|
||||
|
||||
_fakeGNCBLEMedium.fakeGATTServer = [[GNCFakeBLEGATTServer alloc] initWithPeripheralManager:nil
|
||||
queue:nil];
|
||||
auto gatt_server = _medium->StartGattServer({});
|
||||
|
||||
XCTAssertNotEqual(gatt_server.get(), nullptr);
|
||||
@@ -422,7 +455,9 @@ static const char *const kTestServiceID = "TestServiceID";
|
||||
id mockCentralManager = OCMClassMock([GNSCentralManager class]);
|
||||
OCMStub([mockCentralManager retrieveCentralPeerWithIdentifier:fakePeripheral.identifier])
|
||||
.andReturn(nil);
|
||||
nearby::apple::BleMediumPeer::SetSocketCentralManager(_medium.get(), mockCentralManager);
|
||||
nearby::apple::BleMediumPeer::SetCentralManagerFactory(_medium.get(), ^(CBUUID *uuid) {
|
||||
return mockCentralManager;
|
||||
});
|
||||
|
||||
auto socket = _medium->Connect(kTestServiceID, nearby::api::ble::TxPowerLevel::kUltraLow,
|
||||
fakePeripheral.identifier.hash, nullptr);
|
||||
@@ -460,7 +495,9 @@ static const char *const kTestServiceID = "TestServiceID";
|
||||
id mockCentralManager = OCMClassMock([GNSCentralManager class]);
|
||||
OCMStub([mockCentralManager retrieveCentralPeerWithIdentifier:fakePeripheral.identifier])
|
||||
.andReturn(mockCentralPeerManager);
|
||||
nearby::apple::BleMediumPeer::SetSocketCentralManager(_medium.get(), mockCentralManager);
|
||||
nearby::apple::BleMediumPeer::SetCentralManagerFactory(_medium.get(), ^(CBUUID *uuid) {
|
||||
return mockCentralManager;
|
||||
});
|
||||
|
||||
auto socket = _medium->Connect(kTestServiceID, nearby::api::ble::TxPowerLevel::kUltraLow,
|
||||
fakePeripheral.identifier.hash, nullptr);
|
||||
@@ -470,7 +507,10 @@ static const char *const kTestServiceID = "TestServiceID";
|
||||
|
||||
#pragma mark - Server Socket Tests
|
||||
|
||||
- (void)testOpenServerSocket_Success {
|
||||
- (void)testOpenServerSocket_Success_LegacyPath {
|
||||
id mockFeatureFlags = OCMClassMock([GNCFeatureFlags class]);
|
||||
OCMStub([mockFeatureFlags fixBleServerSocketDeadlockEnabled]).andReturn(NO);
|
||||
|
||||
id mockPeripheralManager = OCMClassMock([GNSPeripheralManager class]);
|
||||
OCMStub([mockPeripheralManager addPeripheralServiceManager:[OCMArg any]
|
||||
bleServiceAddedCompletion:[OCMArg any]])
|
||||
@@ -478,11 +518,97 @@ static const char *const kTestServiceID = "TestServiceID";
|
||||
void (^completion)(NSError *error)) {
|
||||
completion(nil);
|
||||
});
|
||||
nearby::apple::BleMediumPeer::SetSocketPeripheralManager(_medium.get(), mockPeripheralManager);
|
||||
nearby::apple::BleMediumPeer::SetPeripheralManagerFactory(_medium.get(), ^() {
|
||||
return mockPeripheralManager;
|
||||
});
|
||||
|
||||
auto server_socket = _medium->OpenServerSocket(kTestServiceID);
|
||||
|
||||
XCTAssertNotEqual(server_socket.get(), nullptr);
|
||||
|
||||
GNSPeripheralServiceManager *serviceManager =
|
||||
nearby::apple::BleMediumPeer::GetSocketPeripheralServiceManager(_medium.get());
|
||||
XCTAssertNotNil(serviceManager);
|
||||
}
|
||||
|
||||
- (void)testOpenServerSocket_Success_OptimizedPath {
|
||||
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);
|
||||
});
|
||||
nearby::apple::BleMediumPeer::SetPeripheralManagerFactory(_medium.get(), ^() {
|
||||
return mockPeripheralManager;
|
||||
});
|
||||
|
||||
auto server_socket = _medium->OpenServerSocket(kTestServiceID);
|
||||
|
||||
XCTAssertNotEqual(server_socket.get(), nullptr);
|
||||
|
||||
GNSPeripheralServiceManager *serviceManager =
|
||||
nearby::apple::BleMediumPeer::GetSocketPeripheralServiceManager(_medium.get());
|
||||
XCTAssertNotNil(serviceManager);
|
||||
}
|
||||
|
||||
- (void)testOpenServerSocket_OptimizedPath_AcceptSocketAfterClose {
|
||||
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);
|
||||
});
|
||||
nearby::apple::BleMediumPeer::SetPeripheralManagerFactory(_medium.get(), ^() {
|
||||
return mockPeripheralManager;
|
||||
});
|
||||
|
||||
auto server_socket = _medium->OpenServerSocket(kTestServiceID);
|
||||
XCTAssertNotEqual(server_socket.get(), nullptr);
|
||||
__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_for_handler_capture = _medium->OpenServerSocket(kTestServiceID);
|
||||
XCTAssertNotEqual(server_socket_for_handler_capture.get(), nullptr);
|
||||
XCTAssertNotNil(capturedHandler);
|
||||
|
||||
// Invoke the shouldAcceptSocketHandler with a fake socket.
|
||||
GNCFakeSocket *fakeSocket = [[GNCFakeSocket alloc] init];
|
||||
BOOL result = capturedHandler((GNSSocket *)fakeSocket);
|
||||
XCTAssertTrue(result);
|
||||
|
||||
// Close the server_socket. This triggers the close notifier, setting server_socket_ptr_ to null.
|
||||
server_socket_for_handler_capture->Close();
|
||||
|
||||
// Now simulate the connection completing. It should safely ignore the connection because
|
||||
// server_socket_ptr_ is null, preventing use-after-free or deadlocks.
|
||||
[fakeSocket simulateSocketDidConnect];
|
||||
|
||||
// Since we use dispatch_async internally for connection callback, give it a small amount of time
|
||||
// to process so we know it didn't crash.
|
||||
XCTestExpectation *expectation2 = [self expectationWithDescription:@"Wait for async execution"];
|
||||
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];
|
||||
}
|
||||
|
||||
- (void)testOpenServerSocket_Failure {
|
||||
@@ -493,7 +619,9 @@ static const char *const kTestServiceID = "TestServiceID";
|
||||
void (^completion)(NSError *error)) {
|
||||
completion([NSError errorWithDomain:@"test" code:0 userInfo:nil]);
|
||||
});
|
||||
nearby::apple::BleMediumPeer::SetSocketPeripheralManager(_medium.get(), mockPeripheralManager);
|
||||
nearby::apple::BleMediumPeer::SetPeripheralManagerFactory(_medium.get(), ^() {
|
||||
return mockPeripheralManager;
|
||||
});
|
||||
|
||||
auto server_socket = _medium->OpenServerSocket(kTestServiceID);
|
||||
|
||||
@@ -508,7 +636,9 @@ static const char *const kTestServiceID = "TestServiceID";
|
||||
void (^completion)(NSError *error)){
|
||||
// Do not call completion to simulate timeout.
|
||||
});
|
||||
nearby::apple::BleMediumPeer::SetSocketPeripheralManager(_medium.get(), mockPeripheralManager);
|
||||
nearby::apple::BleMediumPeer::SetPeripheralManagerFactory(_medium.get(), ^() {
|
||||
return mockPeripheralManager;
|
||||
});
|
||||
|
||||
auto server_socket = _medium->OpenServerSocket(kTestServiceID);
|
||||
|
||||
@@ -646,18 +776,20 @@ static const char *const kTestServiceID = "TestServiceID";
|
||||
NSDictionary<CBUUID *, NSData *> *serviceData =
|
||||
@{[CBUUID UUIDWithString:kTestServiceUUIDString] : [NSData dataWithBytes:"test" length:4]};
|
||||
|
||||
__block XCTestExpectation *expectation1 = [self expectationWithDescription:@"Callback 1"];
|
||||
XCTestExpectation *expectation1 = [self expectationWithDescription:@"Callback 1"];
|
||||
XCTestExpectation *expectation2 = [self expectationWithDescription:@"Callback 2"];
|
||||
expectation2.inverted = YES; // Should NOT be called.
|
||||
|
||||
auto callback1_fulfilled = std::make_shared<std::atomic<bool>>(false);
|
||||
|
||||
nearby::api::ble::BleMedium::ScanCallback callback = {
|
||||
.advertisement_found_cb = std::function<void(nearby::api::ble::BlePeripheral::UniqueId,
|
||||
nearby::api::ble::BleAdvertisementData)>(
|
||||
^(nearby::api::ble::BlePeripheral::UniqueId peripheral_id,
|
||||
const nearby::api::ble::BleAdvertisementData &advertisement) {
|
||||
if ([expectation1.description isEqualToString:@"Callback 1"]) {
|
||||
[callback1_fulfilled, expectation1, expectation2](
|
||||
nearby::api::ble::BlePeripheral::UniqueId peripheral_id,
|
||||
const nearby::api::ble::BleAdvertisementData &advertisement) {
|
||||
if (!callback1_fulfilled->exchange(true)) {
|
||||
[expectation1 fulfill];
|
||||
expectation1 = nil; // Prevent double fulfillment
|
||||
} else {
|
||||
[expectation2 fulfill];
|
||||
}
|
||||
|
||||
@@ -42,8 +42,9 @@
|
||||
|
||||
- (void)testBleServerSocketAccept {
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"accept"];
|
||||
nearby::apple::BleServerSocket *serverSocket = _serverSocket.get();
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
std::unique_ptr<nearby::api::ble::BleSocket> clientSocket = _serverSocket->Accept();
|
||||
std::unique_ptr<nearby::api::ble::BleSocket> clientSocket = serverSocket->Accept();
|
||||
XCTAssertNotEqual(clientSocket.get(), nullptr);
|
||||
[expectation fulfill];
|
||||
});
|
||||
@@ -57,8 +58,9 @@
|
||||
|
||||
- (void)testBleServerSocketClose {
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"close"];
|
||||
nearby::apple::BleServerSocket *serverSocket = _serverSocket.get();
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
std::unique_ptr<nearby::api::ble::BleSocket> clientSocket = _serverSocket->Accept();
|
||||
std::unique_ptr<nearby::api::ble::BleSocket> clientSocket = serverSocket->Accept();
|
||||
XCTAssertEqual(clientSocket.get(), nullptr);
|
||||
[expectation fulfill];
|
||||
});
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include "internal/platform/implementation/apple/atomic_boolean.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "thread/fiber/fiber.h"
|
||||
#include "third_party/gloop/thread/fiber/fiber.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace apple {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include "internal/platform/implementation/apple/atomic_uint32.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "thread/fiber/fiber.h"
|
||||
#include "third_party/gloop/thread/fiber/fiber.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace apple {
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#import "internal/platform/implementation/apple/Flags/GNCFeatureFlags.h"
|
||||
#import "internal/platform/implementation/apple/Log/GNCLogger.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCIPv4Address.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFramework.h"
|
||||
@@ -35,12 +36,22 @@ AwdlInputStream::AwdlInputStream(GNCNWFrameworkSocket* socket) : socket_(socket)
|
||||
|
||||
ExceptionOr<ByteArray> AwdlInputStream::Read(std::int64_t size) {
|
||||
NSError* error = nil;
|
||||
NSData* data = [socket_ readMaxLength:size error:&error];
|
||||
if (data == nil) {
|
||||
GNCLoggerError(@"Error reading socket: %@", error);
|
||||
return {Exception::kIo};
|
||||
if (GNCFeatureFlags.singleCopyEnabled) {
|
||||
auto result = [socket_ readStringWithMaxLength:size error:&error];
|
||||
if (!result.has_value()) {
|
||||
GNCLoggerError(@"Error reading socket: %@", error);
|
||||
return {Exception::kIo};
|
||||
}
|
||||
// OPTIMIZATION: Zero-copy transfer from std::string to ByteArray
|
||||
return ExceptionOr<ByteArray>{ByteArray(std::move(result.value()))};
|
||||
} else {
|
||||
NSData* data = [socket_ readMaxLength:size error:&error];
|
||||
if (data == nil) {
|
||||
GNCLoggerError(@"Error reading socket: %@", error);
|
||||
return {Exception::kIo};
|
||||
}
|
||||
return ExceptionOr<ByteArray>{ByteArray((const char*)data.bytes, data.length)};
|
||||
}
|
||||
return ExceptionOr<ByteArray>{ByteArray((const char*)data.bytes, data.length)};
|
||||
}
|
||||
|
||||
Exception AwdlInputStream::Close() {
|
||||
@@ -55,7 +66,15 @@ AwdlOutputStream::AwdlOutputStream(GNCNWFrameworkSocket* socket) : socket_(socke
|
||||
|
||||
Exception AwdlOutputStream::Write(absl::string_view data) {
|
||||
NSError* error = nil;
|
||||
BOOL result = [socket_ write:[NSData dataWithBytes:data.data() length:data.size()] error:&error];
|
||||
BOOL result = NO;
|
||||
|
||||
if (GNCFeatureFlags.singleCopyEnabled) {
|
||||
// OPTIMIZATION: Write raw bytes directly, avoiding NSData creation.
|
||||
result = [socket_ writeBytes:data.data() length:data.size() error:&error];
|
||||
} else {
|
||||
result = [socket_ write:[NSData dataWithBytes:data.data() length:data.size()] error:&error];
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
GNCLoggerError(@"Error writing socket: %@", error);
|
||||
return {Exception::kIo};
|
||||
|
||||
@@ -66,16 +66,6 @@ class GattClient : public api::ble::GattClient {
|
||||
bool WriteCharacteristic(const api::ble::GattCharacteristic &characteristic,
|
||||
absl::string_view value, api::ble::GattClient::WriteType type) override;
|
||||
|
||||
// Enable or disable notifications/indications for a given characteristic.
|
||||
//
|
||||
// Once notifications are enabled for a characteristic, on_characteristic_changed_cb will be
|
||||
// triggered if the remote device indicates that the given characteristic has changed.
|
||||
//
|
||||
// Returns whether or not the subscription was successful.
|
||||
bool SetCharacteristicSubscription(
|
||||
const api::ble::GattCharacteristic &characteristic, bool enable,
|
||||
absl::AnyInvocable<void(absl::string_view value)> on_characteristic_changed_cb) override;
|
||||
|
||||
// Disconnects an established connection, or cancels a connection attempt currently in progress.
|
||||
void Disconnect() override;
|
||||
|
||||
|
||||
@@ -121,13 +121,6 @@ bool GattClient::WriteCharacteristic(const api::ble::GattCharacteristic &charact
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO(b/290385712): Implement.
|
||||
bool GattClient::SetCharacteristicSubscription(
|
||||
const api::ble::GattCharacteristic &characteristic, bool enable,
|
||||
absl::AnyInvocable<void(absl::string_view value)> on_characteristic_changed_cb) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void GattClient::Disconnect() {
|
||||
// There seems to be an issue between some iOS<>Android device pairs where the Android device will
|
||||
// not connect to the iOS device if the iOS device disconnects and then attempts to reconnect.
|
||||
|
||||
@@ -14,9 +14,11 @@
|
||||
|
||||
#import "internal/platform/implementation/apple/ble_l2cap_socket.h"
|
||||
|
||||
#import "internal/platform/implementation/apple/Flags/GNCFeatureFlags.h"
|
||||
#import "internal/platform/implementation/apple/Log/GNCLogger.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPConnection.h"
|
||||
#import "internal/platform/implementation/apple/utils.h"
|
||||
|
||||
#include "internal/platform/implementation/ble.h"
|
||||
|
||||
namespace nearby {
|
||||
@@ -52,40 +54,81 @@ BleL2capInputStream::~BleL2capInputStream() {
|
||||
}
|
||||
|
||||
ExceptionOr<ByteArray> BleL2capInputStream::Read(std::int64_t size) {
|
||||
// Block until either (a) the connection has been closed, (b) we have enough data to return.
|
||||
NSData *dataToReturn;
|
||||
[condition_ lock];
|
||||
while (true) {
|
||||
// Check if the stream has been closed or severed.
|
||||
if (!newDataPackets_) break;
|
||||
if (GNCFeatureFlags.singleCopyEnabled) {
|
||||
std::string dataToReturn;
|
||||
bool success = false;
|
||||
|
||||
if (newDataPackets_.count > 0) {
|
||||
// Add the packet data to the accumulated data.
|
||||
for (NSData *data in newDataPackets_) {
|
||||
if (data.length > 0) {
|
||||
[accumulatedData_ appendData:data];
|
||||
[condition_ lock];
|
||||
while (true) {
|
||||
// Check if the stream has been closed or severed.
|
||||
if (!newDataPackets_) break;
|
||||
|
||||
if (newDataPackets_.count > 0) {
|
||||
// Add the packet data to the accumulated data.
|
||||
for (NSData *data in newDataPackets_) {
|
||||
if (data.length > 0) {
|
||||
[accumulatedData_ appendData:data];
|
||||
}
|
||||
}
|
||||
[newDataPackets_ removeAllObjects];
|
||||
}
|
||||
[newDataPackets_ removeAllObjects];
|
||||
|
||||
if (accumulatedData_.length > 0) {
|
||||
std::int64_t sizeToReturn =
|
||||
(accumulatedData_.length < size) ? accumulatedData_.length : size;
|
||||
NSRange range = NSMakeRange(0, (NSUInteger)sizeToReturn);
|
||||
|
||||
// Copy bytes directly into std::string, avoiding [NSData subdataWithRange:]
|
||||
dataToReturn.assign((const char *)accumulatedData_.bytes, sizeToReturn);
|
||||
[accumulatedData_ replaceBytesInRange:range withBytes:nil length:0];
|
||||
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
[condition_ wait];
|
||||
}
|
||||
[condition_ unlock];
|
||||
|
||||
if (accumulatedData_.length > 0) {
|
||||
// Return up to |size| bytes of the data.
|
||||
std::int64_t sizeToReturn = (accumulatedData_.length < size) ? accumulatedData_.length : size;
|
||||
NSRange range = NSMakeRange(0, (NSUInteger)sizeToReturn);
|
||||
dataToReturn = [accumulatedData_ subdataWithRange:range];
|
||||
[accumulatedData_ replaceBytesInRange:range withBytes:nil length:0];
|
||||
break;
|
||||
if (success) {
|
||||
// OPTIMIZATION: Zero-copy transfer from std::string to ByteArray
|
||||
return ExceptionOr<ByteArray>{ByteArray(std::move(dataToReturn))};
|
||||
} else {
|
||||
return ExceptionOr<ByteArray>{Exception::kIo};
|
||||
}
|
||||
|
||||
[condition_ wait];
|
||||
}
|
||||
[condition_ unlock];
|
||||
|
||||
if (dataToReturn) {
|
||||
return ExceptionOr<ByteArray>{ByteArray((const char *)dataToReturn.bytes, dataToReturn.length)};
|
||||
} else {
|
||||
return ExceptionOr<ByteArray>{Exception::kIo};
|
||||
// Legacy Path
|
||||
NSData *dataToReturn;
|
||||
[condition_ lock];
|
||||
while (true) {
|
||||
if (!newDataPackets_) break;
|
||||
|
||||
if (newDataPackets_.count > 0) {
|
||||
for (NSData *data in newDataPackets_) {
|
||||
if (data.length > 0) {
|
||||
[accumulatedData_ appendData:data];
|
||||
}
|
||||
}
|
||||
[newDataPackets_ removeAllObjects];
|
||||
}
|
||||
|
||||
if (accumulatedData_.length > 0) {
|
||||
std::int64_t sizeToReturn =
|
||||
(accumulatedData_.length < size) ? accumulatedData_.length : size;
|
||||
NSRange range = NSMakeRange(0, (NSUInteger)sizeToReturn);
|
||||
dataToReturn = [accumulatedData_ subdataWithRange:range];
|
||||
[accumulatedData_ replaceBytesInRange:range withBytes:nil length:0];
|
||||
break;
|
||||
}
|
||||
[condition_ wait];
|
||||
}
|
||||
[condition_ unlock];
|
||||
|
||||
if (dataToReturn) {
|
||||
return ExceptionOr<ByteArray>{
|
||||
ByteArray((const char *)dataToReturn.bytes, dataToReturn.length)};
|
||||
} else {
|
||||
return ExceptionOr<ByteArray>{Exception::kIo};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +154,17 @@ Exception BleL2capOutputStream::Write(absl::string_view data) {
|
||||
return {Exception::kIo};
|
||||
}
|
||||
|
||||
NSMutableData *packet = [NSMutableData dataWithBytes:data.data() length:data.size()];
|
||||
NSData *packet;
|
||||
if (GNCFeatureFlags.singleCopyEnabled) {
|
||||
// OPTIMIZATION: Use DISPATCH_DATA_DESTRUCTOR_DEFAULT to perform a
|
||||
// single copy into a GCD-managed buffer. No NSData required.
|
||||
dispatch_data_t dispatchData =
|
||||
dispatch_data_create(data.data(), data.size(), nil, DISPATCH_DATA_DESTRUCTOR_DEFAULT);
|
||||
// dispatch_data_t is toll-free bridged to NSData
|
||||
packet = (NSData *)dispatchData;
|
||||
} else {
|
||||
packet = [NSMutableData dataWithBytes:data.data() length:data.size()];
|
||||
}
|
||||
|
||||
// Send the data, blocking until the completion handler is called.
|
||||
__block BOOL isComplete = NO;
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -50,6 +51,10 @@ class BleMedium : public api::ble::BleMedium {
|
||||
friend class BleMediumPeer;
|
||||
|
||||
public:
|
||||
// Define factory types for managers.
|
||||
using PeripheralManagerFactory = std::function<GNSPeripheralManager *()>;
|
||||
using CentralManagerFactory = std::function<GNSCentralManager *(CBUUID *)>;
|
||||
|
||||
BleMedium();
|
||||
// For testing only.
|
||||
explicit BleMedium(GNCBLEMedium *medium);
|
||||
@@ -207,9 +212,20 @@ class BleMedium : public api::ble::BleMedium {
|
||||
NSDictionary<CBUUID *, NSData *> *service_data);
|
||||
NSDate *GetLastTimestampToCleanExpiredAdvertisementPackets();
|
||||
|
||||
// Opens a BLE server socket based on service ID with deadlock safety.
|
||||
std::unique_ptr<api::ble::BleServerSocket> OpenServerSocketWithDeadlockSafety(
|
||||
const std::string &service_id);
|
||||
|
||||
// Opens a BLE server socket based on service ID using the legacy implementation.
|
||||
std::unique_ptr<api::ble::BleServerSocket> OpenServerSocketLegacy(const std::string &service_id);
|
||||
|
||||
// The executor for handling callbacks.
|
||||
apple::SingleThreadExecutor callback_executor_;
|
||||
|
||||
// Factories for lazy initialization
|
||||
PeripheralManagerFactory peripheral_manager_factory_ = nullptr;
|
||||
CentralManagerFactory central_manager_factory_ = nullptr;
|
||||
|
||||
GNCBLEMedium *medium_;
|
||||
|
||||
PeripheralsMap peripherals_;
|
||||
@@ -229,14 +245,22 @@ class BleMedium : public api::ble::BleMedium {
|
||||
|
||||
GNSPeripheralServiceManager *socketPeripheralServiceManager_;
|
||||
GNSPeripheralManager *socketPeripheralManager_;
|
||||
GNSCentralManager *socketCentralManager_;
|
||||
|
||||
absl::Mutex scanning_mutex_;
|
||||
GNSCentralManager *socketCentralManager_ ABSL_GUARDED_BY(scanning_mutex_);
|
||||
|
||||
// Used for the blocking version of StartAdvertising and only has an advertisement found callback.
|
||||
api::ble::BleMedium::ScanCallback scan_cb_;
|
||||
std::shared_ptr<api::ble::BleMedium::ScanCallback> scan_cb_ ABSL_GUARDED_BY(scanning_mutex_);
|
||||
// Used for the async version of StartAdvertising and has both an advertisement found and result
|
||||
// callback.
|
||||
api::ble::BleMedium::ScanningCallback scanning_cb_;
|
||||
std::shared_ptr<api::ble::BleMedium::ScanningCallback> scanning_cb_
|
||||
ABSL_GUARDED_BY(scanning_mutex_);
|
||||
|
||||
// Used for the BleServerSocket.
|
||||
absl::Mutex server_socket_mutex_;
|
||||
BleServerSocket *server_socket_ptr_ ABSL_GUARDED_BY(server_socket_mutex_) = nullptr;
|
||||
|
||||
// Used for the L2CAP server socket.
|
||||
absl::Mutex l2cap_server_socket_mutex_;
|
||||
BleL2capServerSocket *l2cap_server_socket_ptr_ = nullptr;
|
||||
|
||||
|
||||
@@ -173,11 +173,19 @@ void BleMedium::HandleAdvertisementFound(id<GNCPeripheral> peripheral,
|
||||
}
|
||||
#endif
|
||||
|
||||
if (scanning_cb_.advertisement_found_cb) {
|
||||
scanning_cb_.advertisement_found_cb(unique_id, data);
|
||||
std::shared_ptr<api::ble::BleMedium::ScanningCallback> scanning_cb;
|
||||
std::shared_ptr<api::ble::BleMedium::ScanCallback> scan_cb;
|
||||
{
|
||||
absl::MutexLock lock(&scanning_mutex_);
|
||||
scanning_cb = scanning_cb_;
|
||||
scan_cb = scan_cb_;
|
||||
}
|
||||
if (scan_cb_.advertisement_found_cb) {
|
||||
scan_cb_.advertisement_found_cb(unique_id, data);
|
||||
|
||||
if (scanning_cb && scanning_cb->advertisement_found_cb) {
|
||||
scanning_cb->advertisement_found_cb(unique_id, data);
|
||||
}
|
||||
if (scan_cb && scan_cb->advertisement_found_cb) {
|
||||
scan_cb->advertisement_found_cb(unique_id, data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,7 +193,17 @@ std::unique_ptr<api::ble::BleMedium::ScanningSession> BleMedium::StartScanning(
|
||||
const Uuid &service_uuid, api::ble::TxPowerLevel tx_power_level,
|
||||
api::ble::BleMedium::ScanningCallback callback) {
|
||||
CBUUID *serviceUUID = CBUUID128FromCPP(service_uuid);
|
||||
scanning_cb_ = std::move(callback);
|
||||
|
||||
{
|
||||
absl::MutexLock lock(&scanning_mutex_);
|
||||
scanning_cb_ = std::make_shared<api::ble::BleMedium::ScanningCallback>(std::move(callback));
|
||||
|
||||
if (central_manager_factory_) {
|
||||
socketCentralManager_ = central_manager_factory_(serviceUUID);
|
||||
} else {
|
||||
socketCentralManager_ = [[GNSCentralManager alloc] initWithSocketServiceUUID:serviceUUID];
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the map of discovered peripherals only when we are starting a new scan. If we cleared the
|
||||
// map every time we stopped a scan, we would not be able to connect to peripherals that we
|
||||
@@ -193,8 +211,10 @@ std::unique_ptr<api::ble::BleMedium::ScanningSession> BleMedium::StartScanning(
|
||||
peripherals_.Clear();
|
||||
ClearAdvertisementPacketsMap();
|
||||
|
||||
socketCentralManager_ = [[GNSCentralManager alloc] initWithSocketServiceUUID:serviceUUID];
|
||||
[socketCentralManager_ startNoScanModeWithAdvertisedServiceUUIDs:@[ serviceUUID ]];
|
||||
{
|
||||
absl::MutexLock lock(&scanning_mutex_);
|
||||
[socketCentralManager_ startNoScanModeWithAdvertisedServiceUUIDs:@[ serviceUUID ]];
|
||||
}
|
||||
|
||||
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
||||
__block NSError *blockError = nil;
|
||||
@@ -207,8 +227,13 @@ std::unique_ptr<api::ble::BleMedium::ScanningSession> BleMedium::StartScanning(
|
||||
}
|
||||
completionHandler:^(NSError *error) {
|
||||
blockError = error;
|
||||
if (scanning_cb_.start_scanning_result) {
|
||||
scanning_cb_.start_scanning_result(
|
||||
std::shared_ptr<api::ble::BleMedium::ScanningCallback> scanning_cb;
|
||||
{
|
||||
absl::MutexLock lock(&scanning_mutex_);
|
||||
scanning_cb = scanning_cb_;
|
||||
}
|
||||
if (scanning_cb && scanning_cb->start_scanning_result) {
|
||||
scanning_cb->start_scanning_result(
|
||||
error == nil ? absl::OkStatus()
|
||||
: absl::InternalError(error.localizedDescription.UTF8String));
|
||||
}
|
||||
@@ -218,8 +243,13 @@ std::unique_ptr<api::ble::BleMedium::ScanningSession> BleMedium::StartScanning(
|
||||
dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, kApiTimeoutInSeconds * NSEC_PER_SEC);
|
||||
if (dispatch_semaphore_wait(semaphore, timeout) != 0) {
|
||||
GNCLoggerError(@"Start scanning operation timed out.");
|
||||
if (scanning_cb_.start_scanning_result) {
|
||||
scanning_cb_.start_scanning_result(absl::DeadlineExceededError("Start scanning timed out"));
|
||||
std::shared_ptr<api::ble::BleMedium::ScanningCallback> scanning_cb;
|
||||
{
|
||||
absl::MutexLock lock(&scanning_mutex_);
|
||||
scanning_cb = scanning_cb_;
|
||||
}
|
||||
if (scanning_cb && scanning_cb->start_scanning_result) {
|
||||
scanning_cb->start_scanning_result(absl::DeadlineExceededError("Start scanning timed out"));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@@ -239,7 +269,17 @@ std::unique_ptr<api::ble::BleMedium::ScanningSession> BleMedium::StartScanning(
|
||||
bool BleMedium::StartScanning(const Uuid &service_uuid, api::ble::TxPowerLevel tx_power_level,
|
||||
api::ble::BleMedium::ScanCallback callback) {
|
||||
CBUUID *serviceUUID = CBUUID128FromCPP(service_uuid);
|
||||
scan_cb_ = std::move(callback);
|
||||
|
||||
{
|
||||
absl::MutexLock lock(&scanning_mutex_);
|
||||
scan_cb_ = std::make_shared<api::ble::BleMedium::ScanCallback>(std::move(callback));
|
||||
|
||||
if (central_manager_factory_) {
|
||||
socketCentralManager_ = central_manager_factory_(serviceUUID);
|
||||
} else {
|
||||
socketCentralManager_ = [[GNSCentralManager alloc] initWithSocketServiceUUID:serviceUUID];
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the map of discovered peripherals only when we are starting a new scan. If we cleared the
|
||||
// map every time we stopped a scan, we would not be able to connect to peripherals that we
|
||||
@@ -247,8 +287,10 @@ bool BleMedium::StartScanning(const Uuid &service_uuid, api::ble::TxPowerLevel t
|
||||
peripherals_.Clear();
|
||||
ClearAdvertisementPacketsMap();
|
||||
|
||||
socketCentralManager_ = [[GNSCentralManager alloc] initWithSocketServiceUUID:serviceUUID];
|
||||
[socketCentralManager_ startNoScanModeWithAdvertisedServiceUUIDs:@[ serviceUUID ]];
|
||||
{
|
||||
absl::MutexLock lock(&scanning_mutex_);
|
||||
[socketCentralManager_ startNoScanModeWithAdvertisedServiceUUIDs:@[ serviceUUID ]];
|
||||
}
|
||||
|
||||
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
||||
__block NSError *blockError = nil;
|
||||
@@ -286,7 +328,16 @@ bool BleMedium::StartMultipleServicesScanning(const std::vector<Uuid> &service_u
|
||||
[serviceUUIDs addObject:CBUUID128FromCPP(service_uuid)];
|
||||
}
|
||||
|
||||
scan_cb_ = std::move(callback);
|
||||
{
|
||||
absl::MutexLock lock(&scanning_mutex_);
|
||||
scan_cb_ = std::make_shared<api::ble::BleMedium::ScanCallback>(std::move(callback));
|
||||
|
||||
if (central_manager_factory_) {
|
||||
socketCentralManager_ = central_manager_factory_(serviceUUIDs[0]);
|
||||
} else {
|
||||
socketCentralManager_ = [[GNSCentralManager alloc] initWithSocketServiceUUID:serviceUUIDs[0]];
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the map of discovered peripherals only when we are starting a new scan. If we cleared the
|
||||
// map every time we stopped a scan, we would not be able to connect to peripherals that we
|
||||
@@ -294,8 +345,10 @@ bool BleMedium::StartMultipleServicesScanning(const std::vector<Uuid> &service_u
|
||||
peripherals_.Clear();
|
||||
ClearAdvertisementPacketsMap();
|
||||
|
||||
socketCentralManager_ = [[GNSCentralManager alloc] initWithSocketServiceUUID:serviceUUIDs[0]];
|
||||
[socketCentralManager_ startNoScanModeWithAdvertisedServiceUUIDs:@[ serviceUUIDs[0] ]];
|
||||
{
|
||||
absl::MutexLock lock(&scanning_mutex_);
|
||||
[socketCentralManager_ startNoScanModeWithAdvertisedServiceUUIDs:@[ serviceUUIDs[0] ]];
|
||||
}
|
||||
|
||||
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
||||
__block NSError *blockError = nil;
|
||||
@@ -321,7 +374,12 @@ bool BleMedium::StartMultipleServicesScanning(const std::vector<Uuid> &service_u
|
||||
}
|
||||
|
||||
bool BleMedium::StopScanning() {
|
||||
[socketCentralManager_ stopNoScanMode];
|
||||
{
|
||||
absl::MutexLock lock(&scanning_mutex_);
|
||||
[socketCentralManager_ stopNoScanMode];
|
||||
scan_cb_ = nullptr;
|
||||
scanning_cb_ = nullptr;
|
||||
}
|
||||
|
||||
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
||||
__block NSError *blockError = nil;
|
||||
@@ -448,24 +506,118 @@ std::unique_ptr<api::ble::GattClient> BleMedium::ConnectToGattServer(
|
||||
// TODO(b/293336684): Old Weave code that need to be deleted once shared Weave is complete.
|
||||
std::unique_ptr<api::ble::BleServerSocket> BleMedium::OpenServerSocket(
|
||||
const std::string &service_id) {
|
||||
if (GNCFeatureFlags.fixBleServerSocketDeadlockEnabled) {
|
||||
return OpenServerSocketWithDeadlockSafety(service_id);
|
||||
} else {
|
||||
return OpenServerSocketLegacy(service_id);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<api::ble::BleServerSocket> BleMedium::OpenServerSocketWithDeadlockSafety(
|
||||
const std::string &service_id) {
|
||||
auto server_socket = std::make_unique<BleServerSocket>();
|
||||
__block auto server_socket_ptr = server_socket.get();
|
||||
|
||||
if (socketPeripheralManager_ == nil) {
|
||||
socketPeripheralManager_ = [[GNSPeripheralManager alloc] initWithAdvertisedName:nil
|
||||
restoreIdentifier:nil];
|
||||
if (peripheral_manager_factory_) {
|
||||
socketPeripheralManager_ = peripheral_manager_factory_();
|
||||
} else {
|
||||
socketPeripheralManager_ = [[GNSPeripheralManager alloc] initWithAdvertisedName:nil
|
||||
restoreIdentifier:nil];
|
||||
}
|
||||
}
|
||||
|
||||
if (socketPeripheralManager_ == nil) {
|
||||
GNCLoggerError(@"Failed to create peripheral manager.");
|
||||
return nullptr;
|
||||
// Fix for b/494335036 (Registry + Background Queue)
|
||||
{
|
||||
absl::MutexLock lock(server_socket_mutex_);
|
||||
server_socket_ptr_ = server_socket.get();
|
||||
}
|
||||
server_socket->SetCloseNotifier([this]() {
|
||||
absl::MutexLock lock(server_socket_mutex_);
|
||||
server_socket_ptr_ = nullptr;
|
||||
});
|
||||
|
||||
socketPeripheralServiceManager_ = [[GNSPeripheralServiceManager alloc]
|
||||
initWithBleServiceUUID:[CBUUID UUIDWithString:kWeaveServiceUUID]
|
||||
addPairingCharacteristic:NO
|
||||
shouldAcceptSocketHandler:^BOOL(GNSSocket *socket) {
|
||||
GNCMWaitForConnection(socket, ^(BOOL didConnect) {
|
||||
// Optimized Path: Use background queue and registry validation.
|
||||
GNCMWaitForConnection(socket, connection_callback_queue_, ^(BOOL didConnect) {
|
||||
GNCMBleConnection *connection =
|
||||
[GNCMBleConnection connectionWithSocket:socket
|
||||
serviceID:nil
|
||||
expectedIntroPacket:YES
|
||||
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();
|
||||
|
||||
// Fix: Verify the BleServerSocket still exists before calling Connect().
|
||||
// This prevents the use-after-free/deadlock reported in b/494335036.
|
||||
absl::MutexLock lock(server_socket_mutex_);
|
||||
if (server_socket_ptr_) {
|
||||
server_socket_ptr_->Connect(std::move(socket_wrapper));
|
||||
GNCLoggerInfo(@"BleServerSocket is created with connection");
|
||||
} else {
|
||||
GNCLoggerWarning(@"BleServerSocket was destroyed; ignoring connection.");
|
||||
}
|
||||
});
|
||||
return YES;
|
||||
}];
|
||||
|
||||
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
||||
__block NSError *blockError = nil;
|
||||
[socketPeripheralManager_ addPeripheralServiceManager:socketPeripheralServiceManager_
|
||||
bleServiceAddedCompletion:^(NSError *error) {
|
||||
if (error != nil) {
|
||||
GNCLoggerError(@"Failed to add Weave service: %@", error);
|
||||
blockError = error;
|
||||
}
|
||||
dispatch_semaphore_signal(semaphore);
|
||||
}];
|
||||
[socketPeripheralManager_ start];
|
||||
dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, kApiTimeoutInSeconds * NSEC_PER_SEC);
|
||||
if (dispatch_semaphore_wait(semaphore, timeout) != 0) {
|
||||
GNCLoggerError(@"OpenServerSocket operation timed out.");
|
||||
return nullptr;
|
||||
}
|
||||
if (blockError != nil) {
|
||||
return nullptr;
|
||||
}
|
||||
return std::move(server_socket);
|
||||
}
|
||||
|
||||
std::unique_ptr<api::ble::BleServerSocket> BleMedium::OpenServerSocketLegacy(
|
||||
const std::string &service_id) {
|
||||
auto server_socket = std::make_unique<BleServerSocket>();
|
||||
|
||||
if (socketPeripheralManager_ == nil) {
|
||||
if (peripheral_manager_factory_) {
|
||||
socketPeripheralManager_ = peripheral_manager_factory_();
|
||||
} else {
|
||||
socketPeripheralManager_ = [[GNSPeripheralManager alloc] initWithAdvertisedName:nil
|
||||
restoreIdentifier:nil];
|
||||
}
|
||||
}
|
||||
|
||||
// Raw pointer for closure capture in the legacy path (risks use-after-free).
|
||||
BleServerSocket *server_socket_ptr = server_socket.get();
|
||||
|
||||
socketPeripheralServiceManager_ = [[GNSPeripheralServiceManager alloc]
|
||||
initWithBleServiceUUID:[CBUUID UUIDWithString:kWeaveServiceUUID]
|
||||
addPairingCharacteristic:NO
|
||||
shouldAcceptSocketHandler:^BOOL(GNSSocket *socket) {
|
||||
// Legacy Path: Verbatim copy of original code (blocks Main Thread).
|
||||
GNCMWaitForConnection(socket, nil, ^(BOOL didConnect) {
|
||||
GNCMBleConnection *connection =
|
||||
[GNCMBleConnection connectionWithSocket:socket
|
||||
// This must be nil as the advertiser even though we
|
||||
@@ -588,8 +740,12 @@ std::unique_ptr<api::ble::BleSocket> BleMedium::Connect(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GNSCentralPeerManager *updatedCentralPeerManager =
|
||||
[socketCentralManager_ retrieveCentralPeerWithIdentifier:peripheral.identifier];
|
||||
GNSCentralPeerManager *updatedCentralPeerManager;
|
||||
{
|
||||
absl::MutexLock lock(&scanning_mutex_);
|
||||
updatedCentralPeerManager =
|
||||
[socketCentralManager_ retrieveCentralPeerWithIdentifier:peripheral.identifier];
|
||||
}
|
||||
if (!updatedCentralPeerManager) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -603,7 +759,14 @@ std::unique_ptr<api::ble::BleSocket> BleMedium::Connect(
|
||||
dispatch_semaphore_signal(semaphore);
|
||||
return;
|
||||
}
|
||||
GNCMWaitForConnection(nssocket, ^(BOOL didConnect) {
|
||||
|
||||
// Suggestion: Use the connection callback queue instead of nil
|
||||
dispatch_queue_t targetQueue =
|
||||
GNCFeatureFlags.fixBleServerSocketDeadlockEnabled
|
||||
? connection_callback_queue_
|
||||
: nil;
|
||||
|
||||
GNCMWaitForConnection(nssocket, targetQueue, ^(BOOL didConnect) {
|
||||
if (!didConnect) {
|
||||
dispatch_semaphore_signal(semaphore);
|
||||
return;
|
||||
|
||||
@@ -14,13 +14,13 @@
|
||||
|
||||
#import "internal/platform/implementation/apple/ble_socket.h"
|
||||
|
||||
#include "internal/platform/implementation/ble.h"
|
||||
|
||||
#import "internal/platform/implementation/apple/Flags/GNCFeatureFlags.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCMBleConnection.h"
|
||||
#import "internal/platform/implementation/apple/ble_peripheral.h"
|
||||
#import "internal/platform/implementation/apple/ble_utils.h"
|
||||
#import "internal/platform/implementation/apple/utils.h"
|
||||
|
||||
#include "internal/platform/implementation/ble.h"
|
||||
// TODO(b/293336684): Remove this file when shared Weave is complete.
|
||||
|
||||
namespace nearby {
|
||||
@@ -55,45 +55,94 @@ BleInputStream::~BleInputStream() {
|
||||
}
|
||||
|
||||
ExceptionOr<ByteArray> BleInputStream::Read(std::int64_t size) {
|
||||
// Block until either (a) the connection has been closed, (b) we have enough data to return.
|
||||
NSData *dataToReturn;
|
||||
[condition_ lock];
|
||||
while (true) {
|
||||
// Check if the stream has been closed or severed.
|
||||
if (!newDataPackets_) break;
|
||||
if (GNCFeatureFlags.singleCopyEnabled) {
|
||||
std::string dataToReturn;
|
||||
bool success = false;
|
||||
|
||||
if (newDataPackets_.count > 0) {
|
||||
// Add the packet data to the accumulated data.
|
||||
for (NSData *data in newDataPackets_) {
|
||||
if (data.length > 0) {
|
||||
[accumulatedData_ appendData:data];
|
||||
[condition_ lock];
|
||||
while (true) {
|
||||
// Check if the stream has been closed or severed.
|
||||
if (!newDataPackets_) break;
|
||||
|
||||
if (newDataPackets_.count > 0) {
|
||||
// Add the packet data to the accumulated data.
|
||||
for (NSData *data in newDataPackets_) {
|
||||
if (data.length > 0) {
|
||||
[accumulatedData_ appendData:data];
|
||||
}
|
||||
}
|
||||
[newDataPackets_ removeAllObjects];
|
||||
}
|
||||
[newDataPackets_ removeAllObjects];
|
||||
|
||||
if ((size == -1) && (accumulatedData_.length > 0)) {
|
||||
// Return all of the data.
|
||||
dataToReturn.assign((const char *)accumulatedData_.bytes, accumulatedData_.length);
|
||||
accumulatedData_ = [NSMutableData data];
|
||||
success = true;
|
||||
break;
|
||||
} else if (accumulatedData_.length > 0) {
|
||||
// Return up to |size| bytes of the data.
|
||||
std::int64_t sizeToReturn =
|
||||
(accumulatedData_.length < size) ? accumulatedData_.length : size;
|
||||
NSRange range = NSMakeRange(0, (NSUInteger)sizeToReturn);
|
||||
// Copy bytes directly into std::string, avoiding [NSData subdataWithRange:]
|
||||
dataToReturn.assign((const char *)accumulatedData_.bytes, sizeToReturn);
|
||||
[accumulatedData_ replaceBytesInRange:range withBytes:nil length:0];
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
[condition_ wait];
|
||||
}
|
||||
[condition_ unlock];
|
||||
|
||||
if ((size == -1) && (accumulatedData_.length > 0)) {
|
||||
// Return all of the data.
|
||||
dataToReturn = accumulatedData_;
|
||||
accumulatedData_ = [NSMutableData data];
|
||||
break;
|
||||
} else if (accumulatedData_.length > 0) {
|
||||
// Return up to |size| bytes of the data.
|
||||
std::int64_t sizeToReturn = (accumulatedData_.length < size) ? accumulatedData_.length : size;
|
||||
NSRange range = NSMakeRange(0, (NSUInteger)sizeToReturn);
|
||||
dataToReturn = [accumulatedData_ subdataWithRange:range];
|
||||
[accumulatedData_ replaceBytesInRange:range withBytes:nil length:0];
|
||||
break;
|
||||
if (success) {
|
||||
// OPTIMIZATION: Zero-copy transfer from std::string to ByteArray
|
||||
return ExceptionOr<ByteArray>{ByteArray(std::move(dataToReturn))};
|
||||
} else {
|
||||
return ExceptionOr<ByteArray>{Exception::kIo};
|
||||
}
|
||||
|
||||
[condition_ wait];
|
||||
}
|
||||
[condition_ unlock];
|
||||
|
||||
if (dataToReturn) {
|
||||
return ExceptionOr<ByteArray>(ByteArrayFromNSData(dataToReturn));
|
||||
} else {
|
||||
return ExceptionOr<ByteArray>{Exception::kIo};
|
||||
// Legacy path
|
||||
NSData *dataToReturn;
|
||||
[condition_ lock];
|
||||
while (true) {
|
||||
// Check if the stream has been closed or severed.
|
||||
if (!newDataPackets_) break;
|
||||
|
||||
if (newDataPackets_.count > 0) {
|
||||
for (NSData *data in newDataPackets_) {
|
||||
if (data.length > 0) {
|
||||
[accumulatedData_ appendData:data];
|
||||
}
|
||||
}
|
||||
[newDataPackets_ removeAllObjects];
|
||||
}
|
||||
|
||||
if ((size == -1) && (accumulatedData_.length > 0)) {
|
||||
// Return all of the data.
|
||||
dataToReturn = accumulatedData_;
|
||||
accumulatedData_ = [NSMutableData data];
|
||||
break;
|
||||
} else if (accumulatedData_.length > 0) {
|
||||
// Return up to |size| bytes of the data.
|
||||
std::int64_t sizeToReturn =
|
||||
(accumulatedData_.length < size) ? accumulatedData_.length : size;
|
||||
NSRange range = NSMakeRange(0, (NSUInteger)sizeToReturn);
|
||||
dataToReturn = [accumulatedData_ subdataWithRange:range];
|
||||
[accumulatedData_ replaceBytesInRange:range withBytes:nil length:0];
|
||||
break;
|
||||
}
|
||||
|
||||
[condition_ wait];
|
||||
}
|
||||
[condition_ unlock];
|
||||
|
||||
if (dataToReturn) {
|
||||
return ExceptionOr<ByteArray>(ByteArrayFromNSData(dataToReturn));
|
||||
} else {
|
||||
return ExceptionOr<ByteArray>{Exception::kIo};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +168,17 @@ Exception BleOutputStream::Write(absl::string_view data) {
|
||||
return {Exception::kIo};
|
||||
}
|
||||
|
||||
NSMutableData *packet = [NSMutableData dataWithBytes:data.data() length:data.size()];
|
||||
NSData *packet;
|
||||
if (GNCFeatureFlags.singleCopyEnabled) {
|
||||
// OPTIMIZATION: Use DISPATCH_DATA_DESTRUCTOR_DEFAULT to perform a
|
||||
// single copy into a GCD-managed buffer. No NSData required.
|
||||
dispatch_data_t dispatchData =
|
||||
dispatch_data_create(data.data(), data.size(), nil, DISPATCH_DATA_DESTRUCTOR_DEFAULT);
|
||||
// dispatch_data_t is toll-free bridged to NSData
|
||||
packet = (NSData *)dispatchData;
|
||||
} else {
|
||||
packet = [NSMutableData dataWithBytes:data.data() length:data.size()];
|
||||
}
|
||||
|
||||
// Send the data, blocking until the completion handler is called.
|
||||
__block bool isComplete = NO;
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "absl/time/clock.h"
|
||||
#include "third_party/gloop/thread/fiber/fiber.h"
|
||||
#include "internal/platform/implementation/apple/mutex.h"
|
||||
#include "thread/fiber/fiber.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace apple {
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "absl/time/time.h"
|
||||
#include "thread/fiber/fiber.h"
|
||||
#include "third_party/gloop/thread/fiber/fiber.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace apple {
|
||||
|
||||
@@ -34,17 +34,13 @@ class DeviceInfo : public api::DeviceInfo {
|
||||
|
||||
api::DeviceInfo::OsType GetOsType() const override;
|
||||
|
||||
std::optional<FilePath> GetDownloadPath() const override;
|
||||
FilePath GetDownloadPath() const override;
|
||||
|
||||
std::optional<FilePath> GetLocalAppDataPath() const override;
|
||||
FilePath GetLocalAppDataPath(FilePath sub_path) const override;
|
||||
|
||||
std::optional<FilePath> GetCommonAppDataPath() const override;
|
||||
FilePath GetTemporaryPath() const override;
|
||||
|
||||
std::optional<FilePath> GetTemporaryPath() const override;
|
||||
|
||||
std::optional<FilePath> GetLogPath() const override;
|
||||
|
||||
std::optional<FilePath> GetCrashDumpPath() const override;
|
||||
FilePath GetLogPath() const override;
|
||||
|
||||
bool IsScreenLocked() const override;
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ api::DeviceInfo::OsType DeviceInfo::GetOsType() const {
|
||||
#endif
|
||||
}
|
||||
|
||||
std::optional<FilePath> DeviceInfo::GetDownloadPath() const {
|
||||
FilePath DeviceInfo::GetDownloadPath() const {
|
||||
NSFileManager *manager = [NSFileManager defaultManager];
|
||||
|
||||
NSError *error = nil;
|
||||
@@ -90,30 +90,24 @@ std::optional<FilePath> DeviceInfo::GetDownloadPath() const {
|
||||
error:&error];
|
||||
if (!downloadsURL) {
|
||||
GNCLoggerError(@"Failed to get download path: %@", error);
|
||||
return std::nullopt;
|
||||
return GetTemporaryPath();
|
||||
}
|
||||
|
||||
return FilePath(absl::string_view([downloadsURL.path cString]));
|
||||
}
|
||||
|
||||
std::optional<FilePath> DeviceInfo::GetLocalAppDataPath() const {
|
||||
return FilePath(absl::string_view([GNCLocalAppDataPath().path cString]));
|
||||
FilePath DeviceInfo::GetLocalAppDataPath(FilePath sub_path) const {
|
||||
return FilePath(absl::string_view([GNCLocalAppDataPath().path cString])).append(sub_path);
|
||||
}
|
||||
|
||||
std::optional<FilePath> DeviceInfo::GetCommonAppDataPath() const { return GetLocalAppDataPath(); }
|
||||
|
||||
std::optional<FilePath> DeviceInfo::GetTemporaryPath() const {
|
||||
FilePath DeviceInfo::GetTemporaryPath() const {
|
||||
return FilePath(absl::string_view([NSTemporaryDirectory() cString]));
|
||||
}
|
||||
|
||||
std::optional<FilePath> DeviceInfo::GetLogPath() const {
|
||||
FilePath DeviceInfo::GetLogPath() const {
|
||||
return FilePath(absl::string_view([GNCLogPath().path cString]));
|
||||
}
|
||||
|
||||
std::optional<FilePath> DeviceInfo::GetCrashDumpPath() const {
|
||||
return FilePath(absl::string_view([GNCCrashDumpPath().path cString]));
|
||||
}
|
||||
|
||||
bool DeviceInfo::IsScreenLocked() const { return false; }
|
||||
|
||||
void DeviceInfo::RegisterScreenLockedListener(
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#include "absl/base/thread_annotations.h"
|
||||
#include "absl/synchronization/notification.h"
|
||||
#include "absl/time/time.h"
|
||||
#include "thread/fiber/fiber.h"
|
||||
#include "third_party/gloop/thread/fiber/fiber.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace apple {
|
||||
|
||||
@@ -45,10 +45,6 @@
|
||||
#include "internal/platform/implementation/shared/file.h"
|
||||
#include "internal/platform/payload_id.h"
|
||||
|
||||
#ifndef NO_WEBRTC
|
||||
#import "internal/platform/implementation/apple/webrtc.h"
|
||||
#endif
|
||||
|
||||
namespace nearby {
|
||||
namespace api {
|
||||
|
||||
@@ -205,12 +201,6 @@ std::unique_ptr<WifiDirectMedium> ImplementationPlatform::CreateWifiDirectMedium
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifndef NO_WEBRTC
|
||||
std::unique_ptr<WebRtcMedium> ImplementationPlatform::CreateWebRtcMedium() {
|
||||
return std::make_unique<apple::WebRtcMedium>();
|
||||
}
|
||||
#endif
|
||||
|
||||
std::unique_ptr<AppLifecycleMonitor> ImplementationPlatform::CreateAppLifecycleMonitor(
|
||||
std::function<void(AppLifecycleMonitor::AppLifecycleState)> state_updated_callback) {
|
||||
#if TARGET_OS_IPHONE
|
||||
|
||||
@@ -27,11 +27,15 @@ bool CppBoolFromObjCBool(BOOL b) { return b ? true : false; }
|
||||
char CharFromNSNumber(NSNumber* n) { return n.charValue; }
|
||||
|
||||
NSString* ObjCStringFromCppString(absl::string_view s) {
|
||||
return [NSString stringWithUTF8String:s.data()];
|
||||
return [[NSString alloc] initWithBytes:s.data() length:s.size() encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
std::string CppStringFromObjCString(NSString* s) {
|
||||
return std::string([s UTF8String], [s lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
|
||||
if (!s) return std::string();
|
||||
const char* cstr = [s UTF8String];
|
||||
if (!cstr) return std::string();
|
||||
NSUInteger len = [s lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
|
||||
return std::string(cstr, len);
|
||||
}
|
||||
|
||||
NSData* NSDataFromByteArray(ByteArray byteArray) {
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
// Copyright 2025 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef PLATFORM_IMPL_APPLE_WEBRTC_H_
|
||||
#define PLATFORM_IMPL_APPLE_WEBRTC_H_
|
||||
|
||||
#ifndef NO_WEBRTC
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "internal/platform/implementation/webrtc.h"
|
||||
#include "webrtc/api/peer_connection_interface.h"
|
||||
|
||||
namespace nearby::apple {
|
||||
|
||||
class WebRtcMedium : public api::WebRtcMedium {
|
||||
public:
|
||||
~WebRtcMedium() override = default;
|
||||
|
||||
// Gets the default two-letter country code associated with current locale.
|
||||
// For example, en_US locale resolves to "US".
|
||||
// This follows the ISO 3166-1 Alpha-2 standard.
|
||||
std::string GetDefaultCountryCode() override;
|
||||
|
||||
// Creates and returns a new webrtc::PeerConnectionInterface object via
|
||||
// |callback|.
|
||||
void CreatePeerConnection(webrtc::PeerConnectionObserver* observer,
|
||||
PeerConnectionCallback callback) override;
|
||||
|
||||
// Creates and returns a new webrtc::PeerConnectionInterface object via
|
||||
// |callback| with |PeerConnectionFactoryInterface::Options|.
|
||||
void CreatePeerConnection(
|
||||
std::optional<webrtc::PeerConnectionFactoryInterface::Options> options,
|
||||
webrtc::PeerConnectionObserver* observer,
|
||||
PeerConnectionCallback callback) override;
|
||||
|
||||
// Returns a signaling messenger for sending WebRTC signaling messages.
|
||||
std::unique_ptr<api::WebRtcSignalingMessenger> GetSignalingMessenger(
|
||||
absl::string_view self_id,
|
||||
const location::nearby::connections::LocationHint& location_hint)
|
||||
override;
|
||||
};
|
||||
|
||||
} // namespace nearby::apple
|
||||
|
||||
#endif // #ifndef NO_WEBRTC
|
||||
|
||||
#endif // PLATFORM_IMPL_APPLE_WEBRTC_H_
|
||||
@@ -1,99 +0,0 @@
|
||||
// Copyright 2025 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef NO_WEBRTC
|
||||
|
||||
#include "internal/platform/implementation/apple/webrtc.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "internal/platform/count_down_latch.h"
|
||||
#include "internal/platform/crypto.h"
|
||||
#include "internal/platform/logging.h"
|
||||
#include "internal/platform/tachyon_express_signaling_messenger.h"
|
||||
#include "internal/proto/tachyon.pb.h"
|
||||
#include "internal/proto/tachyon_enums.proto.h"
|
||||
#include "webrtc/api/create_modular_peer_connection_factory.h"
|
||||
#include "webrtc/api/task_queue/default_task_queue_factory.h"
|
||||
|
||||
namespace nearby::apple {
|
||||
|
||||
std::string WebRtcMedium::GetDefaultCountryCode() {
|
||||
NSString* countryCode = [NSLocale.currentLocale objectForKey:NSLocaleCountryCode];
|
||||
if (countryCode) {
|
||||
return std::string([countryCode UTF8String]);
|
||||
}
|
||||
return "US";
|
||||
}
|
||||
|
||||
void WebRtcMedium::CreatePeerConnection(webrtc::PeerConnectionObserver* observer,
|
||||
PeerConnectionCallback callback) {
|
||||
CreatePeerConnection(std::nullopt, observer, std::move(callback));
|
||||
}
|
||||
|
||||
void WebRtcMedium::CreatePeerConnection(
|
||||
std::optional<webrtc::PeerConnectionFactoryInterface::Options> options,
|
||||
webrtc::PeerConnectionObserver* observer, PeerConnectionCallback callback) {
|
||||
webrtc::PeerConnectionInterface::RTCConfiguration rtc_config;
|
||||
rtc_config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan;
|
||||
// TODO: b/261663238 - Add the TURN servers and go beyond the default servers.
|
||||
webrtc::PeerConnectionInterface::IceServer ice_server;
|
||||
ice_server.urls.emplace_back("stun:stun.l.google.com:19302");
|
||||
ice_server.urls.emplace_back("stun:stun1.l.google.com:19302");
|
||||
ice_server.urls.emplace_back("stun:stun2.l.google.com:19302");
|
||||
ice_server.urls.emplace_back("stun:stun3.l.google.com:19302");
|
||||
ice_server.urls.emplace_back("stun:stun4.l.google.com:19302");
|
||||
rtc_config.servers.push_back(ice_server);
|
||||
|
||||
std::unique_ptr<webrtc::Thread> signaling_thread = webrtc::Thread::Create();
|
||||
signaling_thread->SetName("signaling_thread", nullptr);
|
||||
if (!signaling_thread->Start()) {
|
||||
callback(/*peer_connection=*/nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
webrtc::PeerConnectionDependencies dependencies(observer);
|
||||
webrtc::PeerConnectionFactoryDependencies factory_dependencies;
|
||||
factory_dependencies.signaling_thread = signaling_thread.release();
|
||||
|
||||
webrtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> peer_connection_factory =
|
||||
webrtc::CreateModularPeerConnectionFactory(std::move(factory_dependencies));
|
||||
if (options.has_value()) {
|
||||
peer_connection_factory->SetOptions(options.value());
|
||||
}
|
||||
webrtc::RTCErrorOr<webrtc::scoped_refptr<webrtc::PeerConnectionInterface>>
|
||||
peer_connection_or_error =
|
||||
peer_connection_factory->CreatePeerConnectionOrError(rtc_config, std::move(dependencies));
|
||||
if (peer_connection_or_error.ok()) {
|
||||
callback(peer_connection_or_error.MoveValue());
|
||||
} else {
|
||||
callback(/*peer_connection=*/nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<api::WebRtcSignalingMessenger> WebRtcMedium::GetSignalingMessenger(
|
||||
absl::string_view self_id, const location::nearby::connections::LocationHint& location_hint) {
|
||||
return std::make_unique<TachyonExpressSignalingMessenger>(self_id, location_hint);
|
||||
}
|
||||
|
||||
} // namespace nearby::apple
|
||||
|
||||
#endif // #ifndef NO_WEBRTC
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright 2026 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/implementation/webrtc_platform.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "connections/implementation/mediums/webrtc/webrtc_medium_impl.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace api {
|
||||
|
||||
std::unique_ptr<WebRtcMedium> WebRtcImplementationPlatform::CreateWebRtcMedium() {
|
||||
return std::make_unique<nearby::connections::mediums::WebRtcMediumImpl>();
|
||||
}
|
||||
|
||||
std::string WebRtcImplementationPlatform::GetDefaultCountryCode() {
|
||||
NSString* countryCode = [NSLocale.currentLocale objectForKey:NSLocaleCountryCode];
|
||||
if (countryCode) {
|
||||
return std::string([countryCode UTF8String]);
|
||||
}
|
||||
return "US";
|
||||
}
|
||||
|
||||
} // namespace api
|
||||
} // namespace nearby
|
||||
@@ -23,6 +23,8 @@
|
||||
#include <arpa/inet.h>
|
||||
#include "internal/base/masker.h"
|
||||
#include "internal/platform/cancellation_flag_listener.h"
|
||||
|
||||
#import "internal/platform/implementation/apple/Flags/GNCFeatureFlags.h"
|
||||
#import "internal/platform/implementation/apple/Log/GNCLogger.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/Hotspot/GNCHotspotMedium.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCIPv4Address.h"
|
||||
@@ -43,12 +45,22 @@ WifiHotspotInputStream::WifiHotspotInputStream(GNCNWFrameworkSocket* socket) : s
|
||||
|
||||
ExceptionOr<ByteArray> WifiHotspotInputStream::Read(std::int64_t size) {
|
||||
NSError* error = nil;
|
||||
NSData* data = [socket_ readMaxLength:size error:&error];
|
||||
if (data == nil) {
|
||||
GNCLoggerError(@"Error reading socket: %@", error);
|
||||
return {Exception::kIo};
|
||||
if (GNCFeatureFlags.singleCopyEnabled) {
|
||||
auto result = [socket_ readStringWithMaxLength:size error:&error];
|
||||
if (!result.has_value()) {
|
||||
GNCLoggerError(@"Error reading socket: %@", error);
|
||||
return {Exception::kIo};
|
||||
}
|
||||
// OPTIMIZATION: Zero-copy transfer from std::string to ByteArray
|
||||
return ExceptionOr<ByteArray>{ByteArray(std::move(result.value()))};
|
||||
} else {
|
||||
NSData* data = [socket_ readMaxLength:size error:&error];
|
||||
if (data == nil) {
|
||||
GNCLoggerError(@"Error reading socket: %@", error);
|
||||
return {Exception::kIo};
|
||||
}
|
||||
return ExceptionOr<ByteArray>{ByteArray((const char*)data.bytes, data.length)};
|
||||
}
|
||||
return ExceptionOr<ByteArray>{ByteArray((const char*)data.bytes, data.length)};
|
||||
}
|
||||
|
||||
Exception WifiHotspotInputStream::Close() {
|
||||
@@ -63,7 +75,15 @@ WifiHotspotOutputStream::WifiHotspotOutputStream(GNCNWFrameworkSocket* socket) :
|
||||
|
||||
Exception WifiHotspotOutputStream::Write(absl::string_view data) {
|
||||
NSError* error = nil;
|
||||
BOOL result = [socket_ write:[NSData dataWithBytes:data.data() length:data.size()] error:&error];
|
||||
BOOL result = NO;
|
||||
|
||||
if (GNCFeatureFlags.singleCopyEnabled) {
|
||||
// OPTIMIZATION: Write raw bytes directly, avoiding NSData creation.
|
||||
result = [socket_ writeBytes:data.data() length:data.size() error:&error];
|
||||
} else {
|
||||
result = [socket_ write:[NSData dataWithBytes:data.data() length:data.size()] error:&error];
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
GNCLoggerError(@"Error writing socket: %@", error);
|
||||
return {Exception::kIo};
|
||||
@@ -150,7 +170,7 @@ std::unique_ptr<api::WifiHotspotSocket> WifiHotspotMedium::ConnectToService(
|
||||
}
|
||||
// 4 bytes IP address format.
|
||||
NSData* host_ip_address = [NSData dataWithBytes:service_address.address.data()
|
||||
length:service_address.address.size()];
|
||||
length:service_address.address.size()];
|
||||
host = [GNCIPv4Address addressFromData:host_ip_address];
|
||||
GNCLoggerInfo(@"Connect to Hotspot host server: %@", [host dottedRepresentation]);
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#import "internal/platform/implementation/apple/Flags/GNCFeatureFlags.h"
|
||||
#import "internal/platform/implementation/apple/Log/GNCLogger.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCIPv4Address.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFramework.h"
|
||||
@@ -35,12 +36,22 @@ WifiLanInputStream::WifiLanInputStream(GNCNWFrameworkSocket* socket) : socket_(s
|
||||
|
||||
ExceptionOr<ByteArray> WifiLanInputStream::Read(std::int64_t size) {
|
||||
NSError* error = nil;
|
||||
NSData* data = [socket_ readMaxLength:size error:&error];
|
||||
if (data == nil) {
|
||||
GNCLoggerError(@"Error reading socket: %@", error);
|
||||
return {Exception::kIo};
|
||||
if (GNCFeatureFlags.singleCopyEnabled) {
|
||||
auto result = [socket_ readStringWithMaxLength:size error:&error];
|
||||
if (!result.has_value()) {
|
||||
GNCLoggerError(@"Error reading socket: %@", error);
|
||||
return {Exception::kIo};
|
||||
}
|
||||
// OPTIMIZATION: Zero-copy transfer from std::string to ByteArray
|
||||
return ExceptionOr<ByteArray>{ByteArray(std::move(result.value()))};
|
||||
} else {
|
||||
NSData* data = [socket_ readMaxLength:size error:&error];
|
||||
if (data == nil) {
|
||||
GNCLoggerError(@"Error reading socket: %@", error);
|
||||
return {Exception::kIo};
|
||||
}
|
||||
return ExceptionOr<ByteArray>{ByteArray((const char*)data.bytes, data.length)};
|
||||
}
|
||||
return ExceptionOr<ByteArray>{ByteArray((const char*)data.bytes, data.length)};
|
||||
}
|
||||
|
||||
Exception WifiLanInputStream::Close() {
|
||||
@@ -55,7 +66,15 @@ WifiLanOutputStream::WifiLanOutputStream(GNCNWFrameworkSocket* socket) : socket_
|
||||
|
||||
Exception WifiLanOutputStream::Write(absl::string_view data) {
|
||||
NSError* error = nil;
|
||||
BOOL result = [socket_ write:[NSData dataWithBytes:data.data() length:data.size()] error:&error];
|
||||
BOOL result = NO;
|
||||
|
||||
if (GNCFeatureFlags.singleCopyEnabled) {
|
||||
// OPTIMIZATION: Write raw bytes directly, avoiding NSData creation.
|
||||
result = [socket_ writeBytes:data.data() length:data.size() error:&error];
|
||||
} else {
|
||||
result = [socket_ write:[NSData dataWithBytes:data.data() length:data.size()] error:&error];
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
GNCLoggerError(@"Error writing socket: %@", error);
|
||||
return {Exception::kIo};
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
// Copyright 2024 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_AUTH_STATUS_H_
|
||||
#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_AUTH_STATUS_H_
|
||||
|
||||
namespace nearby {
|
||||
|
||||
enum AuthStatus {
|
||||
AUTH_STATUS_UNSPECIFIED = 0,
|
||||
// Request completed successfully, the results should be in the correct order
|
||||
// up to the given count.
|
||||
SUCCESS = 1,
|
||||
|
||||
// Request encountered a generic error.
|
||||
GENERIC_ERROR = 2,
|
||||
|
||||
// Request as specified is not supported.
|
||||
UNSUPPORTED = 3,
|
||||
|
||||
// Request failed and should be retried soon.
|
||||
TEMPORARILY_UNAVAILABLE = 4,
|
||||
|
||||
// Request failed due to an unavailable resource.
|
||||
UNAVAILABLE_RESOURCE = 5,
|
||||
|
||||
// The request failed due to an invalid argument.
|
||||
INVALID_ARGUMENT = 6,
|
||||
|
||||
// In case the status could not be retrieved.
|
||||
UNKNOWN_STATUS = 7,
|
||||
|
||||
// Currently used as a way to signal an ETag mismatch.
|
||||
PRECONDITION_FAILED = 8,
|
||||
|
||||
// Exclusively used to report when user did not consent to required scopes.
|
||||
// Do NOT use this for another other scenarios.
|
||||
PERMISSION_DENIED = 9,
|
||||
|
||||
// The resource exists, but the requested attribute of it does not.
|
||||
MISSING_ATTRIBUTE = 10,
|
||||
|
||||
// The method was interrupted and the caller should exit the current unit of
|
||||
// work immediately.
|
||||
INTERRUPTED = 11,
|
||||
|
||||
// User signed in with an unexpected account.
|
||||
SIGNED_IN_WITH_WRONG_ACCOUNT = 12,
|
||||
|
||||
// Used when data cannot be parsed properly.
|
||||
PARSE_ERROR = 13,
|
||||
|
||||
// Used to report that the local HTTP server for receiving the authorization
|
||||
// code cannot be created.
|
||||
CANT_CREATE_AUTH_SERVER = 14,
|
||||
|
||||
// Used to report that the system browser for authenticating the user cannot
|
||||
// be open.
|
||||
CANT_OPEN_BROWSER_FOR_AUTH = 15,
|
||||
|
||||
// Used to report that the authorization code cannot be received.
|
||||
CANT_RECEIVE_AUTH_CODE = 16,
|
||||
|
||||
// Used to report that the account is blocked (e.g. CAA).
|
||||
ACCOUNT_BLOCKED = 17,
|
||||
|
||||
// Receiving the authorization code failed because it took longer than the
|
||||
// timeout.
|
||||
AUTH_CODE_TIMEOUT_EXCEEDED = 18,
|
||||
|
||||
// Used to report when user presses the cancel button during login process.
|
||||
USER_CANCELED = 19,
|
||||
};
|
||||
|
||||
} // namespace nearby
|
||||
|
||||
#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_AUTH_STATUS_H_
|
||||
@@ -29,7 +29,6 @@
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "internal/platform/byte_array.h"
|
||||
#include "internal/platform/cancellation_flag.h"
|
||||
#include "internal/platform/exception.h"
|
||||
@@ -155,26 +154,26 @@ struct GattCharacteristic {
|
||||
Property property;
|
||||
|
||||
// overloading operator for enum class Permission and Property
|
||||
friend inline Permission operator|(Permission a, Permission b) {
|
||||
friend Permission operator|(Permission a, Permission b) {
|
||||
return static_cast<Permission>(static_cast<int>(a) | static_cast<int>(b));
|
||||
}
|
||||
|
||||
friend inline Permission operator&(Permission a, Permission b) {
|
||||
friend Permission operator&(Permission a, Permission b) {
|
||||
return static_cast<Permission>(static_cast<int>(a) & static_cast<int>(b));
|
||||
}
|
||||
friend inline Permission& operator|=(Permission& a, Permission b) {
|
||||
friend Permission& operator|=(Permission& a, Permission b) {
|
||||
a = a | b;
|
||||
return a;
|
||||
}
|
||||
|
||||
friend inline Property operator|(Property a, Property b) {
|
||||
friend Property operator|(Property a, Property b) {
|
||||
return static_cast<Property>(static_cast<int>(a) | static_cast<int>(b));
|
||||
}
|
||||
|
||||
friend inline Property operator&(Property a, Property b) {
|
||||
friend Property operator&(Property a, Property b) {
|
||||
return static_cast<Property>(static_cast<int>(a) & static_cast<int>(b));
|
||||
}
|
||||
friend inline Property& operator|=(Property& a, Property b) {
|
||||
friend Property& operator|=(Property& a, Property b) {
|
||||
a = a | b;
|
||||
return a;
|
||||
}
|
||||
@@ -231,13 +230,13 @@ class GattClient {
|
||||
// It is okay for duplicate services to exist, as long as the specified
|
||||
// characteristic UUID is unique among all services of the same UUID.
|
||||
// NOLINTNEXTLINE(google3-legacy-absl-backports)
|
||||
virtual absl::optional<GattCharacteristic> GetCharacteristic(
|
||||
virtual std::optional<GattCharacteristic> GetCharacteristic(
|
||||
const Uuid& service_uuid, const Uuid& characteristic_uuid) = 0;
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#readCharacteristic(android.bluetooth.BluetoothGattCharacteristic)
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothGattCharacteristic.html#getValue()
|
||||
// NOLINTNEXTLINE(google3-legacy-absl-backports)
|
||||
virtual absl::optional<std::string> ReadCharacteristic(
|
||||
virtual std::optional<std::string> ReadCharacteristic(
|
||||
const GattCharacteristic& characteristic) = 0;
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothGattCharacteristic.html#setValue(byte[])
|
||||
@@ -248,14 +247,6 @@ class GattClient {
|
||||
virtual bool WriteCharacteristic(const GattCharacteristic& characteristic,
|
||||
absl::string_view value, WriteType type) = 0;
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#setCharacteristicNotification(android.bluetooth.BluetoothGattCharacteristic,%20boolean)
|
||||
//
|
||||
// Enable or disable notifications/indications for a given characteristic.
|
||||
virtual bool SetCharacteristicSubscription(
|
||||
const GattCharacteristic& characteristic, bool enable,
|
||||
absl::AnyInvocable<void(absl::string_view value)>
|
||||
on_characteristic_changed_cb) = 0;
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#disconnect()
|
||||
virtual void Disconnect() = 0;
|
||||
};
|
||||
@@ -281,7 +272,7 @@ class GattServer {
|
||||
// more information about this descriptor, please go to:
|
||||
// https://www.bluetooth.com/specifications/Gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.Gatt.client_characteristic_configuration.xml
|
||||
// NOLINTNEXTLINE(google3-legacy-absl-backports)
|
||||
virtual absl::optional<GattCharacteristic> CreateCharacteristic(
|
||||
virtual std::optional<GattCharacteristic> CreateCharacteristic(
|
||||
const Uuid& service_uuid, const Uuid& characteristic_uuid,
|
||||
GattCharacteristic::Permission permission,
|
||||
GattCharacteristic::Property property) = 0;
|
||||
|
||||
@@ -15,12 +15,14 @@
|
||||
#ifndef PLATFORM_API_DEVICE_INFO_H_
|
||||
#define PLATFORM_API_DEVICE_INFO_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "internal/base/file_path.h"
|
||||
#include "internal/base/files.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace api {
|
||||
@@ -46,12 +48,15 @@ class DeviceInfo {
|
||||
virtual OsType GetOsType() const = 0;
|
||||
|
||||
// Gets known paths of current user.
|
||||
virtual std::optional<FilePath> GetDownloadPath() const = 0;
|
||||
virtual std::optional<FilePath> GetLocalAppDataPath() const = 0;
|
||||
virtual std::optional<FilePath> GetCommonAppDataPath() const = 0;
|
||||
virtual std::optional<FilePath> GetTemporaryPath() const = 0;
|
||||
virtual std::optional<FilePath> GetLogPath() const = 0;
|
||||
virtual std::optional<FilePath> GetCrashDumpPath() const = 0;
|
||||
virtual FilePath GetDownloadPath() const = 0;
|
||||
virtual FilePath GetLocalAppDataPath(FilePath sub_path) const = 0;
|
||||
virtual FilePath GetTemporaryPath() const = 0;
|
||||
virtual FilePath GetLogPath() const = 0;
|
||||
|
||||
virtual std::optional<size_t> GetAvailableDiskSpaceInBytes(
|
||||
const FilePath& path) const {
|
||||
return Files::GetAvailableDiskSpaceInBytes(path);
|
||||
};
|
||||
|
||||
// Monitor screen status
|
||||
virtual bool IsScreenLocked() const = 0;
|
||||
@@ -66,6 +71,24 @@ class DeviceInfo {
|
||||
virtual bool AllowSleep() = 0;
|
||||
};
|
||||
|
||||
template <typename Sink>
|
||||
void AbslStringify(Sink& sink, DeviceInfo::DeviceType device_type) {
|
||||
switch (device_type) {
|
||||
case DeviceInfo::DeviceType::kUnknown:
|
||||
sink.Append("Unknown");
|
||||
return;
|
||||
case DeviceInfo::DeviceType::kPhone:
|
||||
sink.Append("Phone");
|
||||
return;
|
||||
case DeviceInfo::DeviceType::kTablet:
|
||||
sink.Append("Tablet");
|
||||
return;
|
||||
case DeviceInfo::DeviceType::kLaptop:
|
||||
sink.Append("PC");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace api
|
||||
} // namespace nearby
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2026 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/implementation/device_info.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
|
||||
namespace nearby::api {
|
||||
namespace {
|
||||
|
||||
TEST(DeviceInfoTest, DeviceTypeToStringTest) {
|
||||
DeviceInfo::DeviceType type = DeviceInfo::DeviceType::kPhone;
|
||||
EXPECT_EQ(absl::StrCat(type), "Phone");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace nearby::api
|
||||
@@ -85,6 +85,7 @@ cc_library(
|
||||
"bluetooth_adapter.cc",
|
||||
"bluetooth_classic.cc",
|
||||
"credential_storage_impl.cc",
|
||||
"webrtc.cc",
|
||||
"wifi_direct.cc",
|
||||
"wifi_hotspot.cc",
|
||||
"wifi_lan.cc",
|
||||
@@ -96,6 +97,7 @@ cc_library(
|
||||
"bluetooth_classic.h",
|
||||
"credential_storage_impl.h",
|
||||
"socket_base.h",
|
||||
"webrtc.h",
|
||||
"wifi.h",
|
||||
"wifi_direct.h",
|
||||
"wifi_hotspot.h",
|
||||
@@ -112,11 +114,14 @@ cc_library(
|
||||
"//internal/platform:types",
|
||||
"//internal/platform:uuid",
|
||||
"//internal/platform/implementation:comm",
|
||||
"//internal/platform/implementation:webrtc_platform",
|
||||
"//internal/platform/implementation:wifi_utils",
|
||||
"//internal/proto:credential_cc_proto",
|
||||
"//third_party/webrtc/files/stable/webrtc/api:create_modular_peer_connection_factory",
|
||||
"//third_party/webrtc/files/stable/webrtc/api:libjingle_peerconnection_api",
|
||||
"//third_party/webrtc/files/stable/webrtc/api:peer_connection_interface",
|
||||
"//third_party/webrtc/files/stable/webrtc/api:scoped_refptr",
|
||||
"//third_party/webrtc/files/stable/webrtc/rtc_base:checks",
|
||||
"//third_party/webrtc/files/stable/webrtc/rtc_base:threading",
|
||||
"@com_google_absl//absl/base:core_headers",
|
||||
"@com_google_absl//absl/container:flat_hash_map",
|
||||
"@com_google_absl//absl/container:flat_hash_set",
|
||||
@@ -177,8 +182,8 @@ cc_library(
|
||||
testonly = True,
|
||||
srcs = [
|
||||
"platform.cc",
|
||||
"webrtc_platform.cc",
|
||||
],
|
||||
defines = ["NO_WEBRTC"],
|
||||
visibility = [
|
||||
"//connections:__subpackages__",
|
||||
"//connections:partners",
|
||||
@@ -191,10 +196,9 @@ cc_library(
|
||||
"//internal/preferences:__subpackages__",
|
||||
"//internal/proto/analytics:__subpackages__",
|
||||
"//internal/weave:__subpackages__",
|
||||
"//location/nearby/cpp:__subpackages__",
|
||||
"//location/nearby/sharing/sdk:__subpackages__",
|
||||
"//presence:__subpackages__",
|
||||
"//sharing:__subpackages__",
|
||||
"//third_party/nearby/presence:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
":comm",
|
||||
@@ -208,14 +212,16 @@ cc_library(
|
||||
"//internal/platform/implementation:comm",
|
||||
"//internal/platform/implementation:platform",
|
||||
"//internal/platform/implementation:types",
|
||||
"//internal/platform/implementation:webrtc_platform",
|
||||
"//internal/platform/implementation/shared:count_down_latch",
|
||||
"//internal/platform/implementation/shared:file",
|
||||
"//third_party/gloop/thread",
|
||||
"@com_google_absl//absl/base:core_headers",
|
||||
"@com_google_absl//absl/status",
|
||||
"@com_google_absl//absl/status:statusor",
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_nisaba//nisaba/port:thread_pool",
|
||||
],
|
||||
alwayslink = 1,
|
||||
)
|
||||
|
||||
cc_library(
|
||||
|
||||
@@ -679,33 +679,6 @@ bool BleMedium::GattClient::WriteCharacteristic(
|
||||
return status.ok();
|
||||
}
|
||||
|
||||
bool BleMedium::GattClient::SetCharacteristicSubscription(
|
||||
const api::ble::GattCharacteristic& characteristic, bool enable,
|
||||
absl::AnyInvocable<void(absl::string_view value)>
|
||||
on_characteristic_changed_cb) {
|
||||
absl::MutexLock lock(mutex_);
|
||||
if (!is_connection_alive_) {
|
||||
return false;
|
||||
}
|
||||
Borrowed<api::ble::GattServer*> borrowed = gatt_server_.Borrow();
|
||||
if (!borrowed) {
|
||||
return false;
|
||||
}
|
||||
BleMedium::GattServer* gatt_server =
|
||||
static_cast<BleMedium::GattServer*>(*borrowed);
|
||||
LOG(INFO) << "G3 Ble SetCharacteristicSubscription, characteristic=("
|
||||
<< characteristic.service_uuid.Get16BitAsString() << ","
|
||||
<< std::string(characteristic.uuid) << "), enable = " << enable;
|
||||
if (enable) {
|
||||
return gatt_server->AddCharacteristicSubscription(
|
||||
peripheral_id_, characteristic,
|
||||
std::move(on_characteristic_changed_cb));
|
||||
} else {
|
||||
return gatt_server->RemoveCharacteristicSubscription(peripheral_id_,
|
||||
characteristic);
|
||||
}
|
||||
}
|
||||
|
||||
void BleMedium::GattClient::Disconnect() {
|
||||
bool was_alive = is_connection_alive_.exchange(false);
|
||||
if (!was_alive) return;
|
||||
|
||||
@@ -282,11 +282,6 @@ class BleMedium : public api::ble::BleMedium {
|
||||
absl::string_view value,
|
||||
api::ble::GattClient::WriteType write_type) override;
|
||||
|
||||
bool SetCharacteristicSubscription(
|
||||
const api::ble::GattCharacteristic& characteristic, bool enable,
|
||||
absl::AnyInvocable<void(absl::string_view value)>
|
||||
on_characteristic_changed_cb) override;
|
||||
|
||||
void Disconnect() override;
|
||||
|
||||
void OnServerDisconnected();
|
||||
|
||||
@@ -45,33 +45,24 @@ class DeviceInfo : public api::DeviceInfo {
|
||||
return api::DeviceInfo::OsType::kChromeOs;
|
||||
}
|
||||
|
||||
std::optional<FilePath> GetDownloadPath() const override {
|
||||
FilePath GetDownloadPath() const override {
|
||||
return Files::GetTemporaryDirectory();
|
||||
}
|
||||
|
||||
std::optional<FilePath> GetLocalAppDataPath() const override {
|
||||
FilePath GetLocalAppDataPath(FilePath sub_path) const override {
|
||||
if (MediumEnvironment::Instance()
|
||||
.GetEnvironmentConfig()
|
||||
.use_temporary_directory_for_app_path) {
|
||||
return Files::GetTemporaryDirectory();
|
||||
return Files::GetTemporaryDirectory().append(sub_path);
|
||||
}
|
||||
|
||||
return GetAppDataPath();
|
||||
return GetAppDataPath().append(sub_path);
|
||||
}
|
||||
|
||||
std::optional<FilePath> GetCommonAppDataPath() const override {
|
||||
FilePath GetTemporaryPath() const override {
|
||||
return Files::GetTemporaryDirectory();
|
||||
}
|
||||
|
||||
std::optional<FilePath> GetTemporaryPath() const override {
|
||||
return Files::GetTemporaryDirectory();
|
||||
}
|
||||
|
||||
std::optional<FilePath> GetLogPath() const override {
|
||||
return Files::GetTemporaryDirectory();
|
||||
}
|
||||
|
||||
std::optional<FilePath> GetCrashDumpPath() const override {
|
||||
FilePath GetLogPath() const override {
|
||||
return Files::GetTemporaryDirectory();
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
#include "internal/platform/implementation/platform.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
@@ -25,6 +24,7 @@
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "third_party/gloop/thread/thread.h"
|
||||
#include "internal/base/file_path.h"
|
||||
#include "internal/base/files.h"
|
||||
#include "internal/platform/implementation/app_lifecycle_monitor.h"
|
||||
@@ -55,11 +55,6 @@
|
||||
#include "internal/platform/logging.h"
|
||||
#include "internal/platform/os_name.h"
|
||||
#include "internal/platform/payload_id.h"
|
||||
#include "thread/thread.h"
|
||||
#ifndef NO_WEBRTC
|
||||
#include "internal/platform/implementation/g3/webrtc.h"
|
||||
#include "internal/platform/implementation/webrtc.h"
|
||||
#endif
|
||||
#include "internal/platform/implementation/g3/atomic_boolean.h"
|
||||
#include "internal/platform/implementation/g3/atomic_reference.h"
|
||||
#include "internal/platform/implementation/g3/ble.h"
|
||||
@@ -80,7 +75,6 @@
|
||||
#include "internal/platform/implementation/g3/wifi_lan.h"
|
||||
#include "internal/platform/implementation/shared/file.h"
|
||||
#include "internal/platform/implementation/wifi.h"
|
||||
#include "internal/platform/medium_environment.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace api {
|
||||
@@ -219,16 +213,6 @@ ImplementationPlatform::CreateWifiDirectMedium() {
|
||||
return std::make_unique<g3::WifiDirectMedium>();
|
||||
}
|
||||
|
||||
#ifndef NO_WEBRTC
|
||||
std::unique_ptr<WebRtcMedium> ImplementationPlatform::CreateWebRtcMedium() {
|
||||
if (MediumEnvironment::Instance().GetEnvironmentConfig().webrtc_enabled) {
|
||||
return std::make_unique<g3::WebRtcMedium>();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
std::unique_ptr<AppLifecycleMonitor>
|
||||
ImplementationPlatform::CreateAppLifecycleMonitor(
|
||||
std::function<void(AppLifecycleMonitor::AppLifecycleState)>
|
||||
|
||||
@@ -16,16 +16,15 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "absl/time/time.h"
|
||||
#include "internal/platform/implementation/cancelable.h"
|
||||
#include "internal/platform/logging.h"
|
||||
#include "internal/platform/medium_environment.h"
|
||||
#include "internal/platform/runnable.h"
|
||||
#include "internal/test/fake_clock.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace g3 {
|
||||
@@ -66,20 +65,13 @@ class ScheduledCancelable : public api::Cancelable {
|
||||
} // namespace
|
||||
|
||||
ScheduledExecutor::ScheduledExecutor() {
|
||||
std::optional<FakeClock*> fake_clock =
|
||||
MediumEnvironment::Instance().GetSimulatedClock();
|
||||
if (fake_clock.has_value()) {
|
||||
name_ = absl::StrFormat("G3 scheduled executor %p", this);
|
||||
(*fake_clock)->AddObserver(name_, [this]() { RunReadyTasks(); });
|
||||
}
|
||||
name_ = absl::StrFormat("G3 scheduled executor %p", this);
|
||||
MediumEnvironment::Instance().AddSimulatedClockObserver(
|
||||
name_, [this]() { RunReadyTasks(); });
|
||||
}
|
||||
|
||||
ScheduledExecutor::~ScheduledExecutor() {
|
||||
std::optional<FakeClock*> fake_clock =
|
||||
MediumEnvironment::Instance().GetSimulatedClock();
|
||||
if (fake_clock.has_value()) {
|
||||
(*fake_clock)->RemoveObserver(name_);
|
||||
}
|
||||
MediumEnvironment::Instance().RemoveSimulatedClockObserver(name_);
|
||||
executor_.Shutdown();
|
||||
}
|
||||
|
||||
@@ -96,10 +88,10 @@ std::shared_ptr<api::Cancelable> ScheduledExecutor::Schedule(
|
||||
runnable();
|
||||
}
|
||||
};
|
||||
std::optional<FakeClock*> fake_clock =
|
||||
MediumEnvironment::Instance().GetSimulatedClock();
|
||||
if (fake_clock.has_value()) {
|
||||
absl::Time trigger_time = (*fake_clock)->Now() + delay;
|
||||
if (MediumEnvironment::Instance()
|
||||
.GetEnvironmentConfig()
|
||||
.use_simulated_clock) {
|
||||
absl::Time trigger_time = MediumEnvironment::Instance().Now() + delay;
|
||||
absl::MutexLock lock(mutex_);
|
||||
tasks_.insert(std::pair<absl::Time, std::unique_ptr<Runnable>>(
|
||||
trigger_time, std::make_unique<Runnable>(std::move(task))));
|
||||
@@ -110,15 +102,12 @@ std::shared_ptr<api::Cancelable> ScheduledExecutor::Schedule(
|
||||
}
|
||||
|
||||
void ScheduledExecutor::RunReadyTasks() {
|
||||
std::optional<FakeClock*> fake_clock =
|
||||
MediumEnvironment::Instance().GetSimulatedClock();
|
||||
if (executor_.InShutdown()) {
|
||||
return;
|
||||
}
|
||||
if (!fake_clock.has_value()) {
|
||||
return;
|
||||
}
|
||||
absl::Time current_time = (*fake_clock)->Now();
|
||||
CHECK(
|
||||
MediumEnvironment::Instance().GetEnvironmentConfig().use_simulated_clock);
|
||||
absl::Time current_time = MediumEnvironment::Instance().Now();
|
||||
absl::MutexLock lock(mutex_);
|
||||
for (auto it = tasks_.begin(); it != tasks_.end();) {
|
||||
if (it->first <= current_time) {
|
||||
|
||||
@@ -15,26 +15,21 @@
|
||||
#include "internal/platform/implementation/system_clock.h"
|
||||
|
||||
#include "absl/time/clock.h"
|
||||
#include "absl/time/time.h"
|
||||
#include "internal/platform/exception.h"
|
||||
#include "internal/platform/medium_environment.h"
|
||||
#include "internal/test/fake_clock.h"
|
||||
|
||||
namespace nearby {
|
||||
|
||||
absl::Time SystemClock::ElapsedRealtime() {
|
||||
absl::optional<FakeClock*> fake_clock =
|
||||
MediumEnvironment::Instance().GetSimulatedClock();
|
||||
if (fake_clock.has_value()) {
|
||||
return (*fake_clock)->Now();
|
||||
}
|
||||
return absl::Now();
|
||||
return MediumEnvironment::Instance().Now();
|
||||
}
|
||||
|
||||
Exception SystemClock::Sleep(absl::Duration duration) {
|
||||
absl::optional<FakeClock*> fake_clock =
|
||||
MediumEnvironment::Instance().GetSimulatedClock();
|
||||
if (fake_clock.has_value()) {
|
||||
(*fake_clock)->FastForward(duration);
|
||||
if (MediumEnvironment::Instance()
|
||||
.GetEnvironmentConfig()
|
||||
.use_simulated_clock) {
|
||||
MediumEnvironment::Instance().FastForward(duration);
|
||||
} else {
|
||||
absl::SleepFor(duration);
|
||||
}
|
||||
|
||||
@@ -24,10 +24,11 @@
|
||||
#include "internal/platform/byte_array.h"
|
||||
#include "internal/platform/implementation/webrtc.h"
|
||||
#include "internal/platform/medium_environment.h"
|
||||
#include "webrtc/api/create_modular_peer_connection_factory.h"
|
||||
#include "webrtc/api/peer_connection_interface.h"
|
||||
#include "webrtc/api/scoped_refptr.h"
|
||||
#include "webrtc/rtc_base/checks.h"
|
||||
#include "third_party/webrtc/files/stable/webrtc/api/create_modular_peer_connection_factory.h"
|
||||
#include "third_party/webrtc/files/stable/webrtc/api/peer_connection_interface.h"
|
||||
#include "third_party/webrtc/files/stable/webrtc/api/scoped_refptr.h"
|
||||
#include "third_party/webrtc/files/stable/webrtc/rtc_base/checks.h"
|
||||
#include "third_party/webrtc/files/stable/webrtc/rtc_base/thread.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace g3 {
|
||||
@@ -60,8 +61,6 @@ void WebRtcSignalingMessenger::StopReceivingMessages() {
|
||||
|
||||
WebRtcMedium::~WebRtcMedium() { single_thread_executor_.Shutdown(); }
|
||||
|
||||
std::string WebRtcMedium::GetDefaultCountryCode() { return "US"; }
|
||||
|
||||
void WebRtcMedium::CreatePeerConnection(
|
||||
webrtc::PeerConnectionObserver* observer, PeerConnectionCallback callback) {
|
||||
CreatePeerConnection(std::nullopt, observer, std::move(callback));
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "internal/platform/byte_array.h"
|
||||
#include "internal/platform/implementation/webrtc.h"
|
||||
#include "internal/platform/implementation/g3/single_thread_executor.h"
|
||||
#include "webrtc/api/peer_connection_interface.h"
|
||||
#include "third_party/webrtc/files/stable/webrtc/api/peer_connection_interface.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace g3 {
|
||||
@@ -59,8 +59,6 @@ class WebRtcMedium : public api::WebRtcMedium {
|
||||
WebRtcMedium() = default;
|
||||
~WebRtcMedium() override;
|
||||
|
||||
std::string GetDefaultCountryCode() override;
|
||||
|
||||
// Creates and returns a new webrtc::PeerConnectionInterface object via
|
||||
// |callback|.
|
||||
void CreatePeerConnection(webrtc::PeerConnectionObserver* observer,
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright 2026 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/implementation/webrtc_platform.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "internal/platform/implementation/g3/webrtc.h"
|
||||
#include "internal/platform/implementation/webrtc.h"
|
||||
#include "internal/platform/medium_environment.h"
|
||||
|
||||
namespace nearby::api {
|
||||
|
||||
std::unique_ptr<WebRtcMedium>
|
||||
WebRtcImplementationPlatform::CreateWebRtcMedium() {
|
||||
if (MediumEnvironment::Instance().GetEnvironmentConfig().webrtc_enabled) {
|
||||
return std::make_unique<g3::WebRtcMedium>();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::string WebRtcImplementationPlatform::GetDefaultCountryCode() {
|
||||
return "US";
|
||||
}
|
||||
|
||||
} // namespace nearby::api
|
||||
@@ -17,6 +17,9 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "absl/base/thread_annotations.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "internal/platform/implementation/wifi.h"
|
||||
#include "internal/platform/medium_environment.h"
|
||||
|
||||
@@ -39,15 +39,10 @@
|
||||
#include "internal/platform/implementation/log_message.h"
|
||||
#include "internal/platform/implementation/mutex.h"
|
||||
#include "internal/platform/implementation/output_file.h"
|
||||
#include "internal/platform/implementation/preferences_manager.h"
|
||||
#include "internal/platform/implementation/scheduled_executor.h"
|
||||
#include "internal/platform/implementation/submittable_executor.h"
|
||||
#include "internal/platform/implementation/timer.h"
|
||||
#ifndef NO_WEBRTC
|
||||
#include "internal/platform/implementation/webrtc.h"
|
||||
#endif
|
||||
#ifndef NEARBY_CHROMIUM
|
||||
#include "internal/platform/implementation/preferences_manager.h"
|
||||
#endif
|
||||
#include "internal/platform/implementation/wifi.h"
|
||||
#include "internal/platform/implementation/wifi_direct.h"
|
||||
#include "internal/platform/implementation/wifi_hotspot.h"
|
||||
@@ -136,10 +131,6 @@ class ImplementationPlatform {
|
||||
static std::unique_ptr<WifiHotspotMedium> CreateWifiHotspotMedium();
|
||||
static std::unique_ptr<WifiDirectMedium> CreateWifiDirectMedium();
|
||||
static std::unique_ptr<Timer> CreateTimer();
|
||||
static std::unique_ptr<DeviceInfo> CreateDeviceInfo();
|
||||
#ifndef NO_WEBRTC
|
||||
static std::unique_ptr<WebRtcMedium> CreateWebRtcMedium();
|
||||
#endif
|
||||
|
||||
#if defined(NEARBY_CHROMIUM)
|
||||
static std::unique_ptr<AppLifecycleMonitor> CreateAppLifecycleMonitor(
|
||||
@@ -147,11 +138,17 @@ class ImplementationPlatform {
|
||||
state_updated_callback) {
|
||||
return nullptr;
|
||||
}
|
||||
static std::unique_ptr<nearby::api::PreferencesManager>
|
||||
CreatePreferencesManager(absl::string_view path) {
|
||||
return nullptr;
|
||||
}
|
||||
#else
|
||||
static std::unique_ptr<AppLifecycleMonitor> CreateAppLifecycleMonitor(
|
||||
std::function<void(AppLifecycleMonitor::AppLifecycleState)>
|
||||
state_updated_callback);
|
||||
#endif
|
||||
static std::unique_ptr<nearby::api::PreferencesManager>
|
||||
CreatePreferencesManager(absl::string_view path);
|
||||
static std::unique_ptr<DeviceInfo> CreateDeviceInfo();
|
||||
|
||||
// Gets HTTP response from remote server.
|
||||
//
|
||||
@@ -161,15 +158,6 @@ class ImplementationPlatform {
|
||||
// return WebResponse if HTTP status code between 200 and 300.
|
||||
// other cases will return absl Status in error.
|
||||
static absl::StatusOr<WebResponse> SendRequest(const WebRequest& request);
|
||||
|
||||
#if defined(NEARBY_CHROMIUM)
|
||||
static std::unique_ptr<nearby::api::PreferencesManager>
|
||||
CreatePreferencesManager(absl::string_view path) {
|
||||
return nullptr;
|
||||
}
|
||||
#else
|
||||
static std::unique_ptr<nearby::api::PreferencesManager>
|
||||
CreatePreferencesManager(absl::string_view path);
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,236 @@
|
||||
// Copyright 2026 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 <signal.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/synchronization/notification.h"
|
||||
#include "internal/platform/system_clock.h"
|
||||
#include "internal/platform/single_thread_executor.h"
|
||||
#include "presence/broadcast_request.h"
|
||||
#include "presence/data_element.h"
|
||||
#include "presence/implementation/broadcast_manager.h"
|
||||
#include "presence/implementation/credential_manager.h"
|
||||
#include "presence/implementation/mediums/mediums.h"
|
||||
#include "presence/power_mode.h"
|
||||
|
||||
namespace {
|
||||
|
||||
absl::Notification g_shutdown;
|
||||
|
||||
void QuitHandler(int, siginfo_t*, void*) {
|
||||
if (!g_shutdown.HasBeenNotified()) g_shutdown.Notify();
|
||||
}
|
||||
|
||||
void PrintUsage(const char* program) {
|
||||
std::cerr
|
||||
<< "Usage: " << program << " [--name <device_name>] [--tx_power <int>]\n"
|
||||
<< "Example:\n"
|
||||
<< " " << program << " --name \"Linux Presence Demo\" --tx_power 20\n";
|
||||
}
|
||||
|
||||
// For a public-identity broadcast demo, credentials are never requested.
|
||||
class DemoCredentialManager : public nearby::presence::CredentialManager {
|
||||
public:
|
||||
void GenerateCredentials(
|
||||
const nearby::internal::DeviceIdentityMetaData&,
|
||||
absl::string_view,
|
||||
const std::vector<nearby::internal::IdentityType>&,
|
||||
int,
|
||||
int,
|
||||
nearby::presence::GenerateCredentialsResultCallback callback) override {
|
||||
callback.credentials_generated_cb(
|
||||
absl::UnimplementedError("Not used in public broadcast demo"));
|
||||
}
|
||||
|
||||
void UpdateRemotePublicCredentials(
|
||||
absl::string_view,
|
||||
absl::string_view,
|
||||
const std::vector<nearby::internal::SharedCredential>&,
|
||||
nearby::presence::UpdateRemotePublicCredentialsCallback callback)
|
||||
override {
|
||||
callback.credentials_updated_cb(
|
||||
absl::UnimplementedError("Not used in public broadcast demo"));
|
||||
}
|
||||
|
||||
void UpdateLocalCredential(
|
||||
const nearby::presence::CredentialSelector&,
|
||||
nearby::internal::LocalCredential,
|
||||
nearby::presence::SaveCredentialsResultCallback callback) override {
|
||||
callback.credentials_saved_cb(
|
||||
absl::UnimplementedError("Not used in public broadcast demo"));
|
||||
}
|
||||
|
||||
void GetLocalCredentials(
|
||||
const nearby::presence::CredentialSelector&,
|
||||
nearby::presence::GetLocalCredentialsResultCallback callback) override {
|
||||
callback.credentials_fetched_cb(
|
||||
absl::NotFoundError("No local credentials in demo manager"));
|
||||
}
|
||||
|
||||
void GetPublicCredentials(
|
||||
const nearby::presence::CredentialSelector&,
|
||||
nearby::presence::PublicCredentialType,
|
||||
nearby::presence::GetPublicCredentialsResultCallback callback) override {
|
||||
callback.credentials_fetched_cb(
|
||||
absl::NotFoundError("No public credentials in demo manager"));
|
||||
}
|
||||
|
||||
nearby::presence::SubscriberId SubscribeForPublicCredentials(
|
||||
const nearby::presence::CredentialSelector&,
|
||||
nearby::presence::PublicCredentialType,
|
||||
nearby::presence::GetPublicCredentialsResultCallback callback) override {
|
||||
callback.credentials_fetched_cb(
|
||||
absl::NotFoundError("No subscriptions in demo manager"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void UnsubscribeFromPublicCredentials(nearby::presence::SubscriberId) override {
|
||||
}
|
||||
|
||||
std::string DecryptDeviceIdentityMetaData(
|
||||
absl::string_view,
|
||||
absl::string_view,
|
||||
absl::string_view) override {
|
||||
return "";
|
||||
}
|
||||
|
||||
void SetDeviceIdentityMetaData(
|
||||
const nearby::internal::DeviceIdentityMetaData& device_identity_metadata,
|
||||
bool,
|
||||
absl::string_view,
|
||||
const std::vector<nearby::internal::IdentityType>&,
|
||||
int,
|
||||
int,
|
||||
nearby::presence::GenerateCredentialsResultCallback) override {
|
||||
metadata_ = device_identity_metadata;
|
||||
}
|
||||
|
||||
nearby::internal::DeviceIdentityMetaData GetDeviceIdentityMetaData()
|
||||
override {
|
||||
return metadata_;
|
||||
}
|
||||
|
||||
private:
|
||||
nearby::internal::DeviceIdentityMetaData metadata_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
// Force-link the platform SystemClock implementation from static archives.
|
||||
(void)nearby::SystemClock::ElapsedRealtime();
|
||||
|
||||
std::string device_name = "Nearby Presence Demo";
|
||||
int tx_power = 20;
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
std::string arg = argv[i];
|
||||
if ((arg == "-h") || (arg == "--help")) {
|
||||
PrintUsage(argv[0]);
|
||||
return 0;
|
||||
}
|
||||
if (arg == "--name") {
|
||||
if (i + 1 >= argc) {
|
||||
std::cerr << "Missing value for --name\n";
|
||||
return 2;
|
||||
}
|
||||
device_name = argv[++i];
|
||||
continue;
|
||||
}
|
||||
if (arg == "--tx_power") {
|
||||
if (i + 1 >= argc) {
|
||||
std::cerr << "Missing value for --tx_power\n";
|
||||
return 2;
|
||||
}
|
||||
tx_power = std::atoi(argv[++i]);
|
||||
continue;
|
||||
}
|
||||
std::cerr << "Unknown argument: " << arg << "\n";
|
||||
PrintUsage(argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
struct sigaction action {};
|
||||
action.sa_sigaction = QuitHandler;
|
||||
action.sa_flags = SA_SIGINFO;
|
||||
sigaction(SIGINT, &action, nullptr);
|
||||
sigaction(SIGTERM, &action, nullptr);
|
||||
|
||||
nearby::SingleThreadExecutor executor;
|
||||
nearby::presence::Mediums mediums;
|
||||
DemoCredentialManager credential_manager;
|
||||
nearby::presence::BroadcastManager manager(mediums, credential_manager,
|
||||
executor);
|
||||
|
||||
nearby::presence::PresenceBroadcast::BroadcastSection section = {
|
||||
.identity = nearby::internal::IdentityType::IDENTITY_TYPE_PUBLIC,
|
||||
.extended_properties = {
|
||||
nearby::presence::DataElement(
|
||||
nearby::presence::ActionBit::kNearbyShareAction),
|
||||
nearby::presence::DataElement(
|
||||
nearby::presence::ActionBit::kPresenceManagerAction),
|
||||
},
|
||||
.account_name = "",
|
||||
.manager_app_id = "",
|
||||
};
|
||||
nearby::presence::PresenceBroadcast presence_broadcast = {
|
||||
.sections = {section},
|
||||
};
|
||||
nearby::presence::BroadcastRequest request = {
|
||||
.tx_power = tx_power,
|
||||
.power_mode = nearby::presence::PowerMode::kLowLatency,
|
||||
.variant = presence_broadcast,
|
||||
};
|
||||
|
||||
absl::Status broadcast_status;
|
||||
absl::Notification started;
|
||||
auto session = manager.StartBroadcast(
|
||||
request,
|
||||
nearby::presence::BroadcastCallback{
|
||||
.start_broadcast_cb =
|
||||
[&broadcast_status, &started](absl::Status status) {
|
||||
broadcast_status = status;
|
||||
started.Notify();
|
||||
},
|
||||
});
|
||||
if (!session.ok()) {
|
||||
std::cerr << "Failed to initiate broadcast: " << session.status() << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
started.WaitForNotification();
|
||||
if (!broadcast_status.ok()) {
|
||||
std::cerr << "Broadcast start failed: " << broadcast_status << "\n";
|
||||
manager.StopBroadcast(*session);
|
||||
executor.Shutdown();
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "Nearby Presence advertising started.\n"
|
||||
<< " device_name: " << device_name << "\n"
|
||||
<< " session_id: " << *session << "\n"
|
||||
<< "Press Ctrl+C to stop.\n";
|
||||
|
||||
g_shutdown.WaitForNotification();
|
||||
manager.StopBroadcast(*session);
|
||||
executor.Shutdown();
|
||||
std::cout << "Stopped Presence advertising.\n";
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,444 @@
|
||||
// Copyright 2026 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 <signal.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/synchronization/notification.h"
|
||||
#include "internal/platform/bluetooth_adapter.h"
|
||||
#include "internal/platform/system_clock.h"
|
||||
#include "internal/proto/credential.pb.h"
|
||||
#include "presence/power_mode.h"
|
||||
#include "presence/presence_device.h"
|
||||
#include "presence/presence_service_impl.h"
|
||||
#include "presence/scan_request.h"
|
||||
|
||||
#if defined(__linux__)
|
||||
#include "internal/platform/implementation/linux/bluetooth_adapter.h"
|
||||
#include "internal/platform/implementation/linux/bluetooth_devices.h"
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
absl::Notification g_shutdown;
|
||||
|
||||
void QuitHandler(int, siginfo_t*, void*) {
|
||||
if (!g_shutdown.HasBeenNotified()) g_shutdown.Notify();
|
||||
}
|
||||
|
||||
std::string RangeTypeToString(
|
||||
const std::string& range) {
|
||||
return range;
|
||||
}
|
||||
|
||||
std::string EstimateZone(double distance_meters) {
|
||||
constexpr double kTapDistanceThresholdMeters = 0.08;
|
||||
constexpr double kReachDistanceThresholdMeters = 0.56;
|
||||
constexpr double kShortRangeDistanceThresholdMeters = 1.26;
|
||||
constexpr double kLongRangeDistanceThresholdMeters = 3.06;
|
||||
|
||||
if (distance_meters <= kTapDistanceThresholdMeters) return "tap";
|
||||
if (distance_meters <= kReachDistanceThresholdMeters) return "reach";
|
||||
if (distance_meters <= kShortRangeDistanceThresholdMeters) return "short";
|
||||
if (distance_meters <= kLongRangeDistanceThresholdMeters) return "long";
|
||||
return "far";
|
||||
}
|
||||
|
||||
double EstimateDistanceMeters(int adjusted_rssi) {
|
||||
// Mirrors presence/fpp/fpp/src/fspl_converter.rs.
|
||||
constexpr int kTxPowerAtZeroMetersDb = -20;
|
||||
constexpr int kFsplAtOneMeterDb = 40;
|
||||
const int fspl = kTxPowerAtZeroMetersDb - adjusted_rssi;
|
||||
return std::pow(10.0, static_cast<double>(fspl - kFsplAtOneMeterDb) / 20.0);
|
||||
}
|
||||
|
||||
std::string FormatProximity(int rssi, std::optional<int8_t> tx_power) {
|
||||
const int adjusted_rssi = rssi + tx_power.value_or(0);
|
||||
const double distance_meters = EstimateDistanceMeters(adjusted_rssi);
|
||||
const std::string zone = EstimateZone(distance_meters);
|
||||
std::ostringstream stream;
|
||||
stream << std::fixed << std::setprecision(2) << "distance_m="
|
||||
<< distance_meters << " zone=" << RangeTypeToString(zone)
|
||||
<< " rssi=" << rssi;
|
||||
if (tx_power.has_value()) {
|
||||
stream << " tx_power=" << static_cast<int>(*tx_power);
|
||||
}
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::string FormatProximityUnavailable() { return "proximity=unavailable"; }
|
||||
|
||||
std::string IdentityTypeToString(nearby::internal::IdentityType identity_type) {
|
||||
switch (identity_type) {
|
||||
case nearby::internal::IDENTITY_TYPE_PUBLIC:
|
||||
return "public";
|
||||
case nearby::internal::IDENTITY_TYPE_PRIVATE_GROUP:
|
||||
return "private_group";
|
||||
case nearby::internal::IDENTITY_TYPE_CONTACTS_GROUP:
|
||||
return "contacts_group";
|
||||
default:
|
||||
return "unspecified";
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<uint64_t> ParseHexId(const std::string& hex_id) {
|
||||
if (hex_id.empty()) return std::nullopt;
|
||||
try {
|
||||
size_t parsed_size = 0;
|
||||
uint64_t id = std::stoull(hex_id, &parsed_size, 16);
|
||||
if (parsed_size != hex_id.size()) return std::nullopt;
|
||||
return id;
|
||||
} catch (...) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
class SignalProvider {
|
||||
public:
|
||||
SignalProvider() {
|
||||
#if defined(__linux__)
|
||||
nearby::BluetoothAdapter adapter;
|
||||
auto* linux_adapter =
|
||||
dynamic_cast<nearby::linux::BluetoothAdapter*>(&adapter.GetImpl());
|
||||
if (linux_adapter == nullptr) {
|
||||
return;
|
||||
}
|
||||
shared_devices_ = nearby::linux::GetSharedBluetoothDevices(
|
||||
linux_adapter->GetConnection(), linux_adapter->GetObjectPath());
|
||||
#endif
|
||||
}
|
||||
|
||||
std::optional<int> GetRssi(uint64_t device_id) const {
|
||||
#if defined(__linux__)
|
||||
if (!shared_devices_) return std::nullopt;
|
||||
auto device = shared_devices_->devices->get_device_by_unique_id(device_id);
|
||||
if (!device) return std::nullopt;
|
||||
auto rssi = device->GetRssi();
|
||||
if (!rssi.has_value()) return std::nullopt;
|
||||
return static_cast<int>(*rssi);
|
||||
#else
|
||||
(void)device_id;
|
||||
return std::nullopt;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::optional<int8_t> GetTxPower(uint64_t device_id) const {
|
||||
#if defined(__linux__)
|
||||
if (!shared_devices_) return std::nullopt;
|
||||
auto device = shared_devices_->devices->get_device_by_unique_id(device_id);
|
||||
if (!device) return std::nullopt;
|
||||
auto tx_power = device->GetTxPower();
|
||||
if (!tx_power.has_value()) return std::nullopt;
|
||||
if (*tx_power < std::numeric_limits<int8_t>::min() ||
|
||||
*tx_power > std::numeric_limits<int8_t>::max()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return static_cast<int8_t>(*tx_power);
|
||||
#else
|
||||
(void)device_id;
|
||||
return std::nullopt;
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
#if defined(__linux__)
|
||||
std::shared_ptr<nearby::linux::SharedBluetoothDevices> shared_devices_;
|
||||
#endif
|
||||
};
|
||||
|
||||
void PrintUsage(const char* program) {
|
||||
std::cerr << "Usage: " << program
|
||||
<< " [--power low|balanced|low-latency] [--screen-on-only]"
|
||||
<< " [--identities all|public|private|contacts|csv]"
|
||||
<< " [--manager-app-id <id>] [--account-name <name>]\n";
|
||||
}
|
||||
|
||||
nearby::presence::PowerMode ParsePowerMode(const std::string& arg) {
|
||||
if (arg == "low") return nearby::presence::PowerMode::kLowPower;
|
||||
if (arg == "low-latency") return nearby::presence::PowerMode::kLowLatency;
|
||||
return nearby::presence::PowerMode::kBalanced;
|
||||
}
|
||||
|
||||
bool AddIdentityTypeFromToken(
|
||||
const std::string& token,
|
||||
std::vector<nearby::internal::IdentityType>& identity_types) {
|
||||
using IdentityType = nearby::internal::IdentityType;
|
||||
IdentityType identity = IdentityType::IDENTITY_TYPE_UNSPECIFIED;
|
||||
if (token == "public") {
|
||||
identity = IdentityType::IDENTITY_TYPE_PUBLIC;
|
||||
} else if (token == "private") {
|
||||
identity = IdentityType::IDENTITY_TYPE_PRIVATE_GROUP;
|
||||
} else if (token == "contacts") {
|
||||
identity = IdentityType::IDENTITY_TYPE_CONTACTS_GROUP;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto existing : identity_types) {
|
||||
if (existing == identity) return true;
|
||||
}
|
||||
identity_types.push_back(identity);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseIdentityTypes(
|
||||
const std::string& arg,
|
||||
std::vector<nearby::internal::IdentityType>& identity_types) {
|
||||
identity_types.clear();
|
||||
if (arg == "all") {
|
||||
return true;
|
||||
}
|
||||
size_t start = 0;
|
||||
while (start < arg.size()) {
|
||||
size_t comma = arg.find(',', start);
|
||||
std::string token = arg.substr(
|
||||
start, comma == std::string::npos ? std::string::npos : comma - start);
|
||||
if (!AddIdentityTypeFromToken(token, identity_types)) return false;
|
||||
if (comma == std::string::npos) break;
|
||||
start = comma + 1;
|
||||
}
|
||||
return !identity_types.empty();
|
||||
}
|
||||
|
||||
bool ContainsIdentityType(const std::vector<nearby::internal::IdentityType>& ids,
|
||||
nearby::internal::IdentityType identity_type) {
|
||||
for (auto id : ids) {
|
||||
if (id == identity_type) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string IdentitySelectionToString(
|
||||
const std::vector<nearby::internal::IdentityType>& identity_types) {
|
||||
if (identity_types.empty()) {
|
||||
return "all";
|
||||
}
|
||||
std::string result;
|
||||
for (size_t i = 0; i < identity_types.size(); ++i) {
|
||||
if (i > 0) result += ",";
|
||||
result += IdentityTypeToString(identity_types[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
(void)nearby::SystemClock::ElapsedRealtime();
|
||||
|
||||
nearby::presence::PowerMode power_mode = nearby::presence::PowerMode::kBalanced;
|
||||
bool screen_on_only = false;
|
||||
std::string identities_arg = "all";
|
||||
std::string manager_app_id;
|
||||
std::string account_name;
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
std::string arg = argv[i];
|
||||
if ((arg == "-h") || (arg == "--help")) {
|
||||
PrintUsage(argv[0]);
|
||||
return 0;
|
||||
}
|
||||
if (arg == "--power") {
|
||||
if (i + 1 >= argc) {
|
||||
std::cerr << "Missing value for --power\n";
|
||||
return 2;
|
||||
}
|
||||
power_mode = ParsePowerMode(argv[++i]);
|
||||
continue;
|
||||
}
|
||||
if (arg == "--screen-on-only") {
|
||||
screen_on_only = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "--identities") {
|
||||
if (i + 1 >= argc) {
|
||||
std::cerr << "Missing value for --identities\n";
|
||||
return 2;
|
||||
}
|
||||
identities_arg = argv[++i];
|
||||
continue;
|
||||
}
|
||||
if (arg == "--manager-app-id") {
|
||||
if (i + 1 >= argc) {
|
||||
std::cerr << "Missing value for --manager-app-id\n";
|
||||
return 2;
|
||||
}
|
||||
manager_app_id = argv[++i];
|
||||
continue;
|
||||
}
|
||||
if (arg == "--account-name") {
|
||||
if (i + 1 >= argc) {
|
||||
std::cerr << "Missing value for --account-name\n";
|
||||
return 2;
|
||||
}
|
||||
account_name = argv[++i];
|
||||
continue;
|
||||
}
|
||||
std::cerr << "Unknown argument: " << arg << "\n";
|
||||
PrintUsage(argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
std::vector<nearby::internal::IdentityType> identity_types;
|
||||
if (!ParseIdentityTypes(identities_arg, identity_types)) {
|
||||
std::cerr << "Invalid --identities value: " << identities_arg << "\n";
|
||||
PrintUsage(argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
struct sigaction action {};
|
||||
action.sa_sigaction = QuitHandler;
|
||||
action.sa_flags = SA_SIGINFO;
|
||||
sigaction(SIGINT, &action, nullptr);
|
||||
sigaction(SIGTERM, &action, nullptr);
|
||||
|
||||
SignalProvider signal_provider;
|
||||
nearby::presence::PresenceServiceImpl service;
|
||||
auto client = service.CreatePresenceClient();
|
||||
|
||||
nearby::presence::ScanRequest request;
|
||||
request.use_ble = true;
|
||||
request.scan_type = nearby::presence::ScanType::kPresenceScan;
|
||||
request.power_mode = power_mode;
|
||||
request.scan_only_when_screen_on = screen_on_only;
|
||||
request.manager_app_id = manager_app_id;
|
||||
request.account_name = account_name;
|
||||
request.identity_types = identity_types;
|
||||
|
||||
const bool scans_non_public =
|
||||
request.identity_types.empty() ||
|
||||
ContainsIdentityType(request.identity_types,
|
||||
nearby::internal::IdentityType::
|
||||
IDENTITY_TYPE_PRIVATE_GROUP) ||
|
||||
ContainsIdentityType(request.identity_types,
|
||||
nearby::internal::IdentityType::
|
||||
IDENTITY_TYPE_CONTACTS_GROUP);
|
||||
if (scans_non_public && request.manager_app_id.empty()) {
|
||||
std::cerr << "Warning: scanning private/contact identities without "
|
||||
"--manager-app-id usually results in empty remote credentials.\n";
|
||||
}
|
||||
|
||||
absl::Status start_status;
|
||||
absl::Notification started;
|
||||
auto scan_session = client->StartScan(
|
||||
request,
|
||||
nearby::presence::ScanCallback{
|
||||
.start_scan_cb =
|
||||
[&start_status, &started](absl::Status status) {
|
||||
start_status = status;
|
||||
started.Notify();
|
||||
},
|
||||
.on_discovered_cb =
|
||||
[&signal_provider](nearby::presence::PresenceDevice device) {
|
||||
std::string remote_hex =
|
||||
device.GetDeviceIdentityMetadata().bluetooth_mac_address();
|
||||
std::optional<uint64_t> device_id = ParseHexId(remote_hex);
|
||||
if (!device_id.has_value()) {
|
||||
std::cout << "[DISCOVERED] endpoint=" << device.GetEndpointId()
|
||||
<< " identity="
|
||||
<< IdentityTypeToString(device.GetIdentityType())
|
||||
<< " remote_hex=" << remote_hex
|
||||
<< " " << FormatProximityUnavailable() << "\n";
|
||||
return;
|
||||
}
|
||||
std::optional<int> rssi = signal_provider.GetRssi(*device_id);
|
||||
std::optional<int8_t> tx_power =
|
||||
signal_provider.GetTxPower(*device_id);
|
||||
std::string proximity_desc = FormatProximityUnavailable();
|
||||
if (rssi.has_value()) {
|
||||
proximity_desc = FormatProximity(*rssi, tx_power);
|
||||
}
|
||||
std::cout << "[DISCOVERED] endpoint=" << device.GetEndpointId()
|
||||
<< " identity="
|
||||
<< IdentityTypeToString(device.GetIdentityType())
|
||||
<< " remote_hex=" << remote_hex << " "
|
||||
<< proximity_desc << "\n";
|
||||
},
|
||||
.on_updated_cb =
|
||||
[&signal_provider](nearby::presence::PresenceDevice device) {
|
||||
std::string remote_hex =
|
||||
device.GetDeviceIdentityMetadata().bluetooth_mac_address();
|
||||
std::optional<uint64_t> device_id = ParseHexId(remote_hex);
|
||||
if (!device_id.has_value()) {
|
||||
std::cout << "[UPDATED] endpoint=" << device.GetEndpointId()
|
||||
<< " identity="
|
||||
<< IdentityTypeToString(device.GetIdentityType())
|
||||
<< " remote_hex=" << remote_hex
|
||||
<< " " << FormatProximityUnavailable() << "\n";
|
||||
return;
|
||||
}
|
||||
std::optional<int> rssi = signal_provider.GetRssi(*device_id);
|
||||
std::optional<int8_t> tx_power =
|
||||
signal_provider.GetTxPower(*device_id);
|
||||
std::string proximity_desc = FormatProximityUnavailable();
|
||||
if (rssi.has_value()) {
|
||||
proximity_desc = FormatProximity(*rssi, tx_power);
|
||||
}
|
||||
std::cout << "[UPDATED] endpoint=" << device.GetEndpointId()
|
||||
<< " identity="
|
||||
<< IdentityTypeToString(device.GetIdentityType())
|
||||
<< " remote_hex=" << remote_hex << " "
|
||||
<< proximity_desc << "\n";
|
||||
},
|
||||
.on_lost_cb =
|
||||
[](nearby::presence::PresenceDevice device) {
|
||||
std::cout << "[LOST] endpoint=" << device.GetEndpointId()
|
||||
<< " remote_hex="
|
||||
<< device.GetDeviceIdentityMetadata()
|
||||
.bluetooth_mac_address()
|
||||
<< "\n";
|
||||
}});
|
||||
|
||||
if (!scan_session.ok()) {
|
||||
std::cerr << "Failed to initiate scan: " << scan_session.status() << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
started.WaitForNotification();
|
||||
if (!start_status.ok()) {
|
||||
std::cerr << "Scan start failed: " << start_status << "\n";
|
||||
client->StopScan(*scan_session);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "Presence scan started.\n"
|
||||
<< " session_id: " << *scan_session << "\n"
|
||||
<< " identities: "
|
||||
<< IdentitySelectionToString(request.identity_types) << "\n"
|
||||
<< " manager_app_id: "
|
||||
<< (request.manager_app_id.empty() ? "<empty>"
|
||||
: request.manager_app_id)
|
||||
<< "\n"
|
||||
<< " account_name: "
|
||||
<< (request.account_name.empty() ? "<empty>" : request.account_name)
|
||||
<< "\n"
|
||||
<< " mode: printing discovered/updated/lost events with proximity "
|
||||
"estimates when RSSI is available\n"
|
||||
<< "Press Ctrl+C to stop.\n";
|
||||
|
||||
g_shutdown.WaitForNotification();
|
||||
client->StopScan(*scan_session);
|
||||
std::cout << "Stopped Presence scan.\n";
|
||||
return 0;
|
||||
}
|
||||
@@ -15,7 +15,6 @@
|
||||
#ifndef PLATFORM_IMPL_SHARED_FILE_H_
|
||||
#define PLATFORM_IMPL_SHARED_FILE_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
@@ -23,6 +22,7 @@
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/time/time.h"
|
||||
#include "internal/platform/byte_array.h"
|
||||
#include "internal/platform/exception.h"
|
||||
#include "internal/platform/implementation/input_file.h"
|
||||
#include "internal/platform/implementation/output_file.h"
|
||||
@@ -49,7 +49,7 @@ class IOFile final : public api::InputFile, public api::OutputFile {
|
||||
void SetLastModifiedTime(absl::Time last_modified_time) override;
|
||||
|
||||
private:
|
||||
explicit IOFile(absl::string_view file_path) : path_(file_path) {};
|
||||
explicit IOFile(absl::string_view file_path) : path_(file_path) {}
|
||||
void OpenForRead();
|
||||
void OpenForWrite();
|
||||
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
// Copyright 2024 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_SIGNIN_ATTEMPT_H_
|
||||
#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_SIGNIN_ATTEMPT_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "absl/functional/any_invocable.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "internal/platform/implementation/account_info.h"
|
||||
#include "internal/platform/implementation/auth_status.h"
|
||||
|
||||
namespace nearby {
|
||||
|
||||
class SigninAttempt {
|
||||
public:
|
||||
SigninAttempt() = default;
|
||||
virtual ~SigninAttempt() = default;
|
||||
|
||||
// Starts a new sign-in attempt.
|
||||
// `callback` is called with the status of the request, client_id,
|
||||
// client_secret, and account_info if the request is successful. Returns the
|
||||
// auth url if the request is successful.
|
||||
virtual std::string Start(
|
||||
absl::AnyInvocable<void(AuthStatus, absl::string_view, absl::string_view,
|
||||
const AccountInfo&)>
|
||||
callback) = 0;
|
||||
|
||||
// Tears down the machinery set up to request auth tokens, including the HTTP
|
||||
// server.
|
||||
virtual void Close() = 0;
|
||||
};
|
||||
|
||||
} // namespace nearby
|
||||
|
||||
#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_SIGNIN_ATTEMPT_H_
|
||||
@@ -15,17 +15,15 @@
|
||||
#ifndef PLATFORM_API_WEBRTC_H_
|
||||
#define PLATFORM_API_WEBRTC_H_
|
||||
|
||||
#ifndef NO_WEBRTC
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "absl/functional/any_invocable.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "connections/implementation/proto/offline_wire_formats.pb.h"
|
||||
#include "internal/platform/byte_array.h"
|
||||
#include "webrtc/api/peer_connection_interface.h"
|
||||
#include "third_party/webrtc/files/stable/webrtc/api/peer_connection_interface.h"
|
||||
#include "third_party/webrtc/files/stable/webrtc/api/scoped_refptr.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace api {
|
||||
@@ -53,10 +51,6 @@ class WebRtcMedium {
|
||||
|
||||
virtual ~WebRtcMedium() = default;
|
||||
|
||||
// Gets the default two-letter country code associated with current locale.
|
||||
// For example, en_US locale resolves to "US".
|
||||
virtual std::string GetDefaultCountryCode() = 0;
|
||||
|
||||
// Creates and returns a new webrtc::PeerConnectionInterface object via
|
||||
// |callback|.
|
||||
virtual void CreatePeerConnection(webrtc::PeerConnectionObserver* observer,
|
||||
@@ -78,6 +72,4 @@ class WebRtcMedium {
|
||||
} // namespace api
|
||||
} // namespace nearby
|
||||
|
||||
#endif
|
||||
|
||||
#endif // PLATFORM_API_WEBRTC_H_
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright 2026 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef PLATFORM_API_WEBRTC_PLATFORM_H_
|
||||
#define PLATFORM_API_WEBRTC_PLATFORM_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "internal/platform/implementation/webrtc.h"
|
||||
|
||||
namespace nearby::api {
|
||||
|
||||
class WebRtcImplementationPlatform {
|
||||
public:
|
||||
static std::unique_ptr<WebRtcMedium> CreateWebRtcMedium();
|
||||
|
||||
// Gets the default two-letter country code associated with current locale.
|
||||
// For example, en_US locale resolves to "US".
|
||||
// This follows the ISO 3166-1 Alpha-2 standard.
|
||||
static std::string GetDefaultCountryCode();
|
||||
};
|
||||
|
||||
} // namespace nearby::api
|
||||
|
||||
#endif // PLATFORM_API_WEBRTC_PLATFORM_H_
|
||||
@@ -17,12 +17,12 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "internal/platform/cancellation_flag.h"
|
||||
#include "internal/platform/exception.h"
|
||||
#include "internal/platform/input_stream.h"
|
||||
@@ -121,7 +121,7 @@ class WifiDirectMedium {
|
||||
virtual bool DisconnectWifiDirect() = 0;
|
||||
|
||||
// Returns the port range as a pair of min and max port.
|
||||
virtual absl::optional<std::pair<std::int32_t, std::int32_t>>
|
||||
virtual std::optional<std::pair<std::int32_t, std::int32_t>>
|
||||
GetDynamicPortRange() = 0;
|
||||
|
||||
// Returns the supported WifiDirect auth types.
|
||||
|
||||
@@ -74,9 +74,7 @@ cc_library(
|
||||
],
|
||||
defines = ["_SILENCE_CLANG_COROUTINE_MESSAGE"],
|
||||
tags = ["windows"],
|
||||
visibility = [
|
||||
"//sharing/internal/impl/windows:__pkg__",
|
||||
],
|
||||
visibility = ["//visibility:private"],
|
||||
deps = [
|
||||
":device_paths",
|
||||
":string_utils",
|
||||
@@ -151,10 +149,7 @@ cc_library(
|
||||
"WINVER=_WIN32_WINNT_WIN10",
|
||||
],
|
||||
tags = ["windows"],
|
||||
visibility = [
|
||||
"//:__subpackages__",
|
||||
"//location/nearby:__subpackages__",
|
||||
],
|
||||
visibility = ["//:__subpackages__"],
|
||||
deps = [
|
||||
":socket_address",
|
||||
":string_utils",
|
||||
@@ -175,10 +170,7 @@ cc_library(
|
||||
],
|
||||
compatible_with = ["//buildenv/target:non_prod"],
|
||||
tags = ["windows"],
|
||||
visibility = [
|
||||
"//:__subpackages__",
|
||||
"//location/nearby:__subpackages__",
|
||||
],
|
||||
visibility = ["//location/nearby:__subpackages__"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
@@ -191,10 +183,7 @@ cc_library(
|
||||
],
|
||||
compatible_with = ["//buildenv/target:non_prod"],
|
||||
tags = ["windows"],
|
||||
visibility = [
|
||||
"//:__subpackages__",
|
||||
"//location/nearby:__subpackages__",
|
||||
],
|
||||
visibility = ["//location/nearby:__subpackages__"],
|
||||
deps = [
|
||||
":scoped_wlan_memory",
|
||||
"//internal/platform:logging",
|
||||
@@ -243,6 +232,18 @@ cc_library(
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "webrtc_platform",
|
||||
srcs = ["webrtc_platform.cc"],
|
||||
tags = ["windows"],
|
||||
deps = [
|
||||
"//connections/implementation/mediums/webrtc:webrtc_medium_impl",
|
||||
"//internal/platform:logging",
|
||||
"//internal/platform/implementation:webrtc_platform",
|
||||
],
|
||||
alwayslink = True,
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "windows",
|
||||
srcs = [
|
||||
@@ -268,7 +269,6 @@ cc_library(
|
||||
"preferences_manager.cc",
|
||||
"preferences_repository.cc",
|
||||
"system_clock.cc",
|
||||
"webrtc.cc",
|
||||
"wifi_direct_medium.cc",
|
||||
"wifi_direct_server_socket.cc",
|
||||
"wifi_direct_socket.cc",
|
||||
@@ -301,7 +301,6 @@ cc_library(
|
||||
"nearby_server_socket.h",
|
||||
"preferences_manager.h",
|
||||
"preferences_repository.h",
|
||||
"webrtc.h",
|
||||
"wifi.h",
|
||||
"wifi_direct.h",
|
||||
"wifi_hotspot.h",
|
||||
@@ -316,6 +315,9 @@ cc_library(
|
||||
"-DNO_INTEL_PIE",
|
||||
"-D_WIN32_WINNT=_WIN32_WINNT_WIN10 -DWINVER=_WIN32_WINNT_WIN10",
|
||||
],
|
||||
linkopts = [
|
||||
"iphlpapi.lib",
|
||||
],
|
||||
tags = ["windows"],
|
||||
visibility = [
|
||||
"//chrome/chromeos/assistant/data_migration/lib:__pkg__",
|
||||
@@ -323,8 +325,8 @@ cc_library(
|
||||
"//connections:partners",
|
||||
"//internal/platform:__subpackages__",
|
||||
"//location/nearby:__subpackages__",
|
||||
"//presence:__subpackages__",
|
||||
"//sharing:__subpackages__",
|
||||
"//third_party/nearby/presence:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
":crypto", # build_cleaner: keep
|
||||
@@ -358,11 +360,6 @@ cc_library(
|
||||
"//internal/platform/implementation/shared:count_down_latch",
|
||||
"//internal/platform/implementation/windows/generated:types",
|
||||
"//third_party/intel/pie",
|
||||
"//third_party/webrtc/files/stable/webrtc/api:create_modular_peer_connection_factory",
|
||||
"//third_party/webrtc/files/stable/webrtc/api:libjingle_peerconnection_api",
|
||||
"//third_party/webrtc/files/stable/webrtc/api:rtc_error",
|
||||
"//third_party/webrtc/files/stable/webrtc/api:scoped_refptr",
|
||||
"//third_party/webrtc/files/stable/webrtc/rtc_base:threading",
|
||||
"@com_google_absl//absl/base:core_headers",
|
||||
"@com_google_absl//absl/base:nullability",
|
||||
"@com_google_absl//absl/container:flat_hash_map",
|
||||
@@ -374,7 +371,6 @@ cc_library(
|
||||
"@com_google_absl//absl/strings:str_format",
|
||||
"@com_google_absl//absl/synchronization",
|
||||
"@com_google_absl//absl/time",
|
||||
"@com_google_absl//absl/types:optional",
|
||||
"@com_google_absl//absl/types:span",
|
||||
"@com_google_protobuf//:protobuf",
|
||||
"@com_google_protobuf//json",
|
||||
@@ -396,9 +392,7 @@ cc_library(
|
||||
"WINVER=_WIN32_WINNT_WIN10",
|
||||
],
|
||||
tags = ["windows"],
|
||||
visibility = [
|
||||
"//:__subpackages__",
|
||||
],
|
||||
visibility = ["//visibility:private"],
|
||||
deps = [
|
||||
"//internal/platform:base",
|
||||
"//internal/platform:logging",
|
||||
@@ -460,7 +454,6 @@ cc_test(
|
||||
"thread_pool_test.cc",
|
||||
"timer_test.cc",
|
||||
"utils_test.cc",
|
||||
"webrtc_test.cc",
|
||||
"wifi_direct_test.cc",
|
||||
"wifi_hotspot_test.cc",
|
||||
"wifi_medium_test.cc",
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
@@ -30,7 +29,6 @@
|
||||
#include "absl/strings/str_join.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "internal/platform/implementation/ble.h"
|
||||
#include "internal/platform/implementation/windows/bluetooth_adapter.h"
|
||||
#include "internal/platform/implementation/windows/utils.h"
|
||||
@@ -261,7 +259,7 @@ bool BleGattClient::DiscoverServiceAndCharacteristics(
|
||||
return false;
|
||||
}
|
||||
|
||||
absl::optional<api::ble::GattCharacteristic> BleGattClient::GetCharacteristic(
|
||||
std::optional<api::ble::GattCharacteristic> BleGattClient::GetCharacteristic(
|
||||
const Uuid& service_uuid, const Uuid& characteristic_uuid) {
|
||||
absl::MutexLock lock(mutex_);
|
||||
VLOG(1) << __func__ << ": Stared to get characteristic UUID="
|
||||
@@ -273,7 +271,7 @@ absl::optional<api::ble::GattCharacteristic> BleGattClient::GetCharacteristic(
|
||||
|
||||
if (!gatt_characteristic.has_value()) {
|
||||
LOG(ERROR) << __func__ << ": Failed to get native GATT characteristic.";
|
||||
return absl::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
api::ble::GattCharacteristic result;
|
||||
@@ -323,10 +321,10 @@ absl::optional<api::ble::GattCharacteristic> BleGattClient::GetCharacteristic(
|
||||
<< error.code() << ": " << winrt::to_string(error.message());
|
||||
}
|
||||
|
||||
return absl::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
absl::optional<std::string> BleGattClient::ReadCharacteristic(
|
||||
std::optional<std::string> BleGattClient::ReadCharacteristic(
|
||||
const api::ble::GattCharacteristic& characteristic) {
|
||||
absl::MutexLock lock(mutex_);
|
||||
VLOG(1) << __func__
|
||||
@@ -338,7 +336,7 @@ absl::optional<std::string> BleGattClient::ReadCharacteristic(
|
||||
|
||||
if (!gatt_characteristic.has_value()) {
|
||||
LOG(ERROR) << __func__ << ": Failed to get native GATT characteristic.";
|
||||
return absl::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
GattReadResult result =
|
||||
@@ -347,7 +345,7 @@ absl::optional<std::string> BleGattClient::ReadCharacteristic(
|
||||
LOG(ERROR) << __func__
|
||||
<< ": Failed to read GATT characteristic with error: "
|
||||
<< GattCommunicationStatusToString(result.Status());
|
||||
return absl::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
IBuffer buffer = result.Value();
|
||||
@@ -377,7 +375,7 @@ absl::optional<std::string> BleGattClient::ReadCharacteristic(
|
||||
<< error.code() << ": " << winrt::to_string(error.message());
|
||||
}
|
||||
|
||||
return absl::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool BleGattClient::WriteCharacteristic(
|
||||
@@ -431,80 +429,6 @@ bool BleGattClient::WriteCharacteristic(
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BleGattClient::SetCharacteristicSubscription(
|
||||
const api::ble::GattCharacteristic& characteristic, bool enable,
|
||||
absl::AnyInvocable<void(absl::string_view value)>
|
||||
on_characteristic_changed_cb) {
|
||||
absl::MutexLock lock(mutex_);
|
||||
VLOG(1) << __func__ << ": Started to set Characteristic Subscription.";
|
||||
GattClientCharacteristicConfigurationDescriptorValue gcccd_value =
|
||||
GattClientCharacteristicConfigurationDescriptorValue::None;
|
||||
if ((characteristic.property & Property::kNotify) != Property::kNone) {
|
||||
gcccd_value = GattClientCharacteristicConfigurationDescriptorValue::Notify;
|
||||
} else if ((characteristic.property & Property::kIndicate) !=
|
||||
Property::kNone) {
|
||||
gcccd_value =
|
||||
GattClientCharacteristicConfigurationDescriptorValue::Indicate;
|
||||
} else {
|
||||
LOG(WARNING) << "Characeristic: " << std::string(characteristic.uuid)
|
||||
<< " supports neither notifications nor indications.";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<GattCharacteristic> gatt_characteristic;
|
||||
|
||||
gatt_characteristic =
|
||||
native_characteristic_map_[characteristic].native_characteristic;
|
||||
|
||||
if (!gatt_characteristic.has_value()) {
|
||||
LOG(ERROR) << __func__ << ": Failed to get native GATT characteristic.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write characteristic configuration descriptor
|
||||
if (!WriteCharacteristicConfigurationDescriptor(
|
||||
gatt_characteristic.value(),
|
||||
enable
|
||||
? gcccd_value
|
||||
: GattClientCharacteristicConfigurationDescriptorValue::None)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set value changed handler
|
||||
try {
|
||||
if (enable) {
|
||||
native_characteristic_map_[characteristic].on_characteristic_changed_cb =
|
||||
std::move(on_characteristic_changed_cb);
|
||||
native_characteristic_map_[characteristic].notification_token =
|
||||
gatt_characteristic->ValueChanged(
|
||||
[&](GattCharacteristic const& native_characteristic,
|
||||
GattValueChangedEventArgs args) {
|
||||
BleGattClient::OnCharacteristicValueChanged(characteristic,
|
||||
args);
|
||||
});
|
||||
|
||||
if (!native_characteristic_map_[characteristic].notification_token) {
|
||||
LOG(ERROR) << __func__ << ": Failed to add value change handler.";
|
||||
return false;
|
||||
}
|
||||
} else if (native_characteristic_map_[characteristic].notification_token) {
|
||||
gatt_characteristic->ValueChanged(std::exchange(
|
||||
native_characteristic_map_[characteristic].notification_token, {}));
|
||||
}
|
||||
LOG(ERROR) << __func__ << ": Successfully set Characteristic Subscription.";
|
||||
return true;
|
||||
} catch (std::exception exception) {
|
||||
LOG(ERROR) << __func__ << ": Failed to set Characteristic Subscription."
|
||||
<< exception.what();
|
||||
} catch (const winrt::hresult_error& error) {
|
||||
LOG(ERROR) << __func__
|
||||
<< ": Failed to set Characteristic Subscription."
|
||||
" WinRT exception: "
|
||||
<< error.code() << ": " << winrt::to_string(error.message());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void BleGattClient::Disconnect() {
|
||||
absl::MutexLock lock(mutex_);
|
||||
try {
|
||||
@@ -533,12 +457,12 @@ std::optional<GattCharacteristic> BleGattClient::GetNativeCharacteristic(
|
||||
try {
|
||||
if (ble_device_ == nullptr) {
|
||||
LOG(ERROR) << __func__ << ": BLE device is disconnected.";
|
||||
return absl::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (gatt_devices_services_result_ == nullptr) {
|
||||
LOG(ERROR) << __func__ << ": No available GATT services.";
|
||||
return absl::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
for (const auto& service : gatt_devices_services_result_.Services()) {
|
||||
@@ -579,7 +503,7 @@ std::optional<GattCharacteristic> BleGattClient::GetNativeCharacteristic(
|
||||
<< error.code() << ": " << winrt::to_string(error.message());
|
||||
}
|
||||
|
||||
return absl::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool BleGattClient::WriteCharacteristicConfigurationDescriptor(
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -28,8 +26,6 @@
|
||||
#include "absl/functional/any_invocable.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "internal/platform/byte_array.h"
|
||||
#include "internal/platform/implementation/ble.h"
|
||||
#include "internal/platform/uuid.h"
|
||||
#include "winrt/Windows.Devices.Bluetooth.GenericAttributeProfile.h"
|
||||
@@ -50,11 +46,11 @@ class BleGattClient : public api::ble::GattClient {
|
||||
const std::vector<Uuid>& characteristic_uuids) override
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
absl::optional<api::ble::GattCharacteristic> GetCharacteristic(
|
||||
std::optional<api::ble::GattCharacteristic> GetCharacteristic(
|
||||
const Uuid& service_uuid, const Uuid& characteristic_uuid) override
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
absl::optional<std::string> ReadCharacteristic(
|
||||
std::optional<std::string> ReadCharacteristic(
|
||||
const api::ble::GattCharacteristic& characteristic) override
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
@@ -63,11 +59,6 @@ class BleGattClient : public api::ble::GattClient {
|
||||
api::ble::GattClient::WriteType write_type) override
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
bool SetCharacteristicSubscription(
|
||||
const api::ble::GattCharacteristic& characteristic, bool enable,
|
||||
absl::AnyInvocable<void(absl::string_view value)>
|
||||
on_characteristic_changed_cb) override ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
void Disconnect() override ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
private:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user