diff --git a/Package.swift b/Package.swift index 45725c95..4dcdd9a5 100644 --- a/Package.swift +++ b/Package.swift @@ -245,7 +245,7 @@ let package = Package( .target( name: "json", path: "third_party/json", - exclude:[ + exclude: [ "json/LICENSES", "json/cmake", "json/docs", @@ -430,10 +430,11 @@ let package = Package( "connections/implementation/mediums/BUILD", "connections/implementation/BUILD", "connections/implementation/fuzzers", + "connections/v3/BUILD", "connections/BUILD", - "internal/BUILD", "internal/crypto/BUILD", "internal/crypto/BUILD.gn", + "internal/interop/BUILD", "internal/weave/BUILD", "internal/platform/flags/BUILD", "internal/platform/implementation/shared/BUILD", diff --git a/connections/BUILD b/connections/BUILD index e2351f8f..1912dc89 100644 --- a/connections/BUILD +++ b/connections/BUILD @@ -30,8 +30,9 @@ cc_library( deps = [ ":core_types", "//connections/implementation:internal", - "//internal:device", + "//connections/v3:v3_types", "//internal/analytics:event_logger", + "//internal/interop:device", "//internal/platform:base", "//internal/platform:logging", "//internal/platform:types", @@ -54,7 +55,6 @@ cc_library( hdrs = [ "advertising_options.h", "connection_options.h", - "device_provider.h", "discovery_options.h", "listeners.h", "medium_selector.h", @@ -74,7 +74,6 @@ cc_library( "//location/nearby/testing:__subpackages__", ], deps = [ - "//internal:device", "//internal/platform:base", "//internal/platform:types", "//internal/platform:util", @@ -99,7 +98,7 @@ cc_test( ":core", ":core_types", "//connections/implementation:internal_test", - "//internal:device", + "//internal/interop:device", "//internal/platform:base", "//internal/platform:logging", "//internal/platform:types", diff --git a/connections/clients/ios/BUILD b/connections/clients/ios/BUILD index 14476ab4..b30e5c98 100644 --- a/connections/clients/ios/BUILD +++ b/connections/clients/ios/BUILD @@ -1,4 +1,4 @@ -# Copyright 2020 Google LLC +# Copyright 2020-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. @@ -16,6 +16,7 @@ load("//tools/build_defs/apple:ios.bzl", "ios_unit_test") load("//tools/build_defs/swift:swift_library.bzl", "swift_library") +load("//third_party/nearby:minimum_os.bzl", "IOS_MINIMUM_OS", "IOS_MINIMUM_TEST_RUNNER") licenses(["notice"]) @@ -71,8 +72,8 @@ swift_library( ios_unit_test( name = "BuildTests", - minimum_os_version = "13.0", - runner = "//testing/utp/ios:IOS_13", + minimum_os_version = IOS_MINIMUM_OS, + runner = IOS_MINIMUM_TEST_RUNNER, deps = [ ":BuildTestsLib", ], diff --git a/connections/core.h b/connections/core.h index a444f653..7142b6e4 100644 --- a/connections/core.h +++ b/connections/core.h @@ -17,17 +17,22 @@ #include #include +#include #include "absl/strings/string_view.h" #include "absl/types/span.h" -#include "connections/device_provider.h" +#include "connections/connection_options.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/service_controller.h" #include "connections/implementation/service_controller_router.h" #include "connections/listeners.h" #include "connections/params.h" +#include "connections/payload.h" +#include "connections/v3/connection_listening_options.h" +#include "connections/v3/listeners.h" #include "internal/analytics/event_logger.h" -#include "internal/device.h" +#include "internal/interop/device.h" +#include "internal/interop/device_provider.h" namespace nearby { namespace connections { @@ -246,33 +251,266 @@ class Core { std::string Dump(); - //******************************* V2 ******************************* - void RequestConnectionV2(const NearbyDevice& device, - const ConnectionRequestInfo& info, - ConnectionOptions& connection_options, - ResultCallback callback); + //******************************* V3 ******************************* + // NOTE: Do NOT mix with the V1 APIs above, this might result in undefined + // behavior! - void AcceptConnectionV2(const NearbyDevice& device, - const PayloadListener& listener, + // Starts advertising an endpoint for a local app. + // + // service_id - An identifier to advertise your app to other endpoints. + // This can be an arbitrary string, so long as it uniquely + // identifies your service. A good default is to use your + // app's package name. + // advertising_options - The options for advertising. + // local_device - The local device for use when advertising when a + // `DeviceProvider` has not been registered. + // callback - to access the status of the operation when available. + // Possible status codes include: + // Status::STATUS_OK if advertising started successfully. + // Status::STATUS_ALREADY_ADVERTISING if the app is already advertising. + // Status::STATUS_OUT_OF_ORDER_API_CALL if the app is currently + // connected to remote endpoints; call StopAllEndpoints first. + void StartAdvertisingV3(absl::string_view service_id, + const AdvertisingOptions& advertising_options, + const NearbyDevice& local_device, ResultCallback callback); - void RejectConnectionV2(const NearbyDevice& device, ResultCallback callback); + // Starts advertising an endpoint for a local app. + // Should only be used when DeviceProvider has been registered with this + // Nearby Connections instance. Otherwise, use the + // StartAdvertisingV3(service_id, advertising_options, local_device, callback) + // function above. + // + // service_id - An identifier to advertise your app to other endpoints. + // This can be an arbitrary string, so long as it uniquely + // identifies your service. A good default is to use your + // app's package name. + // advertising_options - The options for advertising. + // callback - to access the status of the operation when available. + // Possible status codes include: + // Status::STATUS_OK if advertising started successfully. + // Status::STATUS_ALREADY_ADVERTISING if the app is already advertising. + // Status::STATUS_OUT_OF_ORDER_API_CALL if the app is currently + // connected to remote endpoints; call StopAllEndpoints first. + void StartAdvertisingV3(absl::string_view service_id, + const AdvertisingOptions& advertising_options, + ResultCallback callback); - // Span is being used here as we will not be modifying this block of memory. - // We also do not need to own this block of memory, so we can use Span. - // We are using NearbyDevice* so as to not lose attributes when using a - // vector-like structure of NearbyDevice, as object information is stripped if - // using NearbyDevice or NearbyDevice&. - void SendPayloadV2(absl::Span devices, - const Payload& payload, ResultCallback callback); + // Stops advertising a local endpoint. Should be called after calling + // StartAdvertising, as soon as the application no longer needs to advertise + // itself or goes inactive. Payloads can still be sent to connected + // endpoints after advertising ends. + // + // result_cb - to access the status of the operation when available. + void StopAdvertisingV3(ResultCallback result_cb); - void DisconnectFromDeviceV2(const NearbyDevice& device, - ResultCallback callback); + // Starts discovery for remote endpoints with the specified service ID. + // + // service_id - The ID for the service to be discovered, as specified in + // the corresponding call to StartAdvertising. + // discovery_options - The options for discovery. + // listener_cb - A callback notified when a remote endpoint is discovered. + // result_cb - to access the status of the operation when available. + // Possible status codes include: + // Status::STATUS_OK if discovery started successfully. + // Status::STATUS_ALREADY_DISCOVERING if the app is already + // discovering the specified service. + // Status::STATUS_OUT_OF_ORDER_API_CALL if the app is currently + // connected to remote endpoints; call StopAllEndpoints first. + void StartDiscoveryV3(absl::string_view service_id, + const DiscoveryOptions& discovery_options, + v3::DiscoveryListener listener_cb, + ResultCallback callback); - void InitiateBandwidthUpgradeV2(const NearbyDevice& device, - ResultCallback callback); + // Stops discovery for remote endpoints, after a previous call to + // StartDiscovery, when the client no longer needs to discover endpoints or + // goes inactive. Payloads can still be sent to connected endpoints after + // discovery ends. + // + // result_cb - to access the status of the operation when available. + void StopDiscoveryV3(ResultCallback result_cb); - void RegisterDeviceProvider(const NearbyDeviceProvider& provider); + // Starts listening for incoming connections. + // + // listener_cb - The connection listener to broadcast any updates. + // options - The options for listening for a connection. + // result_cb - to access the status of the operation when available. + // Possible status codes include: + // Status::STATUS_OK if listening started successfully. + // Status::STATUS_ALREADY_LISTENING if the app is already listening. + // Status::STATUS_ALREADY_ADVERTISING if the app is already advertising. + // Status::STATUS_OUT_OF_ORDER_API_CALL if the app is currently connected + // to remote endpoints; call StopAllEndpoints first. + void StartListeningForIncomingConnections( + const v3::ConnectionListeningOptions& options, + v3::ConnectionListener listener_cb, ResultCallback result_cb); + + // Stops listening for incoming connections. Should be called after + // calling StartListeningForIncomingConnections. + void StopListeningForIncomingConnections(); + + // Sends a request to connect to a remote endpoint. + // + // local_device - The local device information which will be shown on the + // remote endpoint. Used only when a DeviceProvider is not + // registered. + // remote_device - The remote device to which a connection request will be + // sent. Should match the value provided in a call to + // DiscoveryListener::endpoint_found_cb() + // connection_options - Connection options for the new connection if both + // sides accept. + // connection_cb - The callback to be notified on connection events. + // result_cb - to access the status of the operation when available. + // Possible status codes include: + // Status::STATUS_OK if the connection request was sent. + // Status::STATUS_ALREADY_CONNECTED_TO_ENDPOINT if the app already + // has a connection to the specified endpoint. + // Status::STATUS_RADIO_ERROR if we failed to connect because of an + // issue with Bluetooth/WiFi. + // Status::STATUS_ERROR if we failed to connect for any other reason. + void RequestConnectionV3(const NearbyDevice& local_device, + const NearbyDevice& remote_device, + const ConnectionOptions& connection_options, + v3::ConnectionListener connection_cb, + ResultCallback result_cb); + + // Sends a request to connect to a remote endpoint. + // + // remote_device - The remote device to which a connection request will be + // sent. Should match the value provided in a call to + // DiscoveryListener::endpoint_found_cb() + // connection_options - Connection options for the new connection if both + // sides accept. + // connection_cb - The callback to be notified on connection events. + // result_cb - to access the status of the operation when available. + // Possible status codes include: + // Status::STATUS_OK if the connection request was sent. + // Status::STATUS_ALREADY_CONNECTED_TO_ENDPOINT if the app already + // has a connection to the specified endpoint. + // Status::STATUS_RADIO_ERROR if we failed to connect because of an + // issue with Bluetooth/WiFi. + // Status::STATUS_ERROR if we failed to connect for any other reason. + void RequestConnectionV3(const NearbyDevice& remote_device, + const ConnectionOptions& connection_options, + v3::ConnectionListener connection_cb, + ResultCallback result_cb); + + // Accepts a connection to a remote endpoint. This method must be called + // before Payloads can be exchanged with the remote endpoint. + // + // remote_device - The remote device. Should match the value provided in a + // call to ConnectionListener::OnConnectionInitiated. + // + // listener_cb - A callback for payloads exchanged with the remote endpoint. + // + // result_cb - to access the status of the operation when available. + // Possible status codes include: + // Status::STATUS_OK if the connection request was accepted. + // Status::STATUS_ALREADY_CONNECTED_TO_ENDPOINT if the app already. + // has a connection to the specified endpoint. + // Status::STATUS_ENDPOINT_UNKNOWN if the app doesn't currently have a + // pending connection to the remote device. + void AcceptConnectionV3(const NearbyDevice& remote_device, + v3::PayloadListener listener_cb, + ResultCallback result_cb); + + // Rejects a connection to a remote endpoint. + // + // remote_device - The device for the remote endpoint. Should match the + // value provided in a call to + // v3::ConnectionListener::OnConnectionInitiated(). + // result_cb - to access the status of the operation when available. + // Possible status codes include: + // Status::STATUS_OK} if the connection request was rejected. + // Status::STATUS_ALREADY_CONNECTED_TO_ENDPOINT} if the app already + // has a connection to the specified endpoint. + void RejectConnectionV3(const NearbyDevice& remote_device, + ResultCallback result_cb); + + // Sends a Payload to a remote device. Payloads can only be sent to remote + // devices once a notice of connection acceptance has been delivered via + // v3::ConnectionListener::OnConnectionResult(). + // + // remote_device - The remote device to which the payload should be sent. + // payload - The Payload to be sent. + // result_cb - to access the status of the operation when available. + // Possible status codes include: + // Status::STATUS_OUT_OF_ORDER_API_CALL if the device has not first + // performed advertisement or discovery (to set the Strategy. + // Status::STATUS_ENDPOINT_UNKNOWN if there's no active (or pending) + // connection to the remote endpoint. + // Status::STATUS_OK if none of the above errors occurred. Note that this + // indicates that Nearby Connections will attempt to send the Payload, + // but not that the send has successfully completed yet. Errors might + // still occur during transmission (and at different times for + // different endpoints), and will be delivered via + // PayloadCallback#onPayloadTransferUpdate. + void SendPayloadV3(const NearbyDevice& remote_device, const Payload& payload, + ResultCallback result_cb); + + // Cancels a Payload currently in-flight to or from remote endpoint(s). + // + // remote_device - The remote device with which the payload is being + // exchanged. + // payload_id - The identifier for the Payload to be canceled. + // result_cb - to access the status of the operation when available. + // Possible status codes include: + // Status::STATUS_OK if none of the above errors occurred. + void CancelPayloadV3(const NearbyDevice& remote_device, int64_t payload_id, + ResultCallback result_cb); + + // Disconnects from a remote endpoint. {@link Payload}s can no longer be sent + // to or received from the endpoint after this method is called. + // + // remote_device - The remote device to disconnect from. + // result_cb - to access the status of the operation when available. + // Possible status codes include: + // Status::STATUS_OK - finished successfully. + void DisconnectFromDeviceV3(const NearbyDevice& remote_device, + ResultCallback result_cb); + + // Disconnects from, and removes all traces, of all connected and/or + // discovered endpoints. This call is expected to be preceded by a call to + // StopAdvertising() or StopDiscovery() as needed. After calling + // StopAllDevices(), no further operations with remote endpoints will be + // possible until a new call to one of StartAdvertising() or StartDiscovery(). + // + // result_cb - To access the status of the operation when available. + void StopAllDevicesV3(ResultCallback result_cb); + + // Sends a request to initiate connection bandwidth upgrade. + // + // remote_device - The identifier for the remote device which will be + // switching to a higher connection data rate and possibly + // different wireless protocol. On success, calls + // ConnectionListener::bandwidth_changed_cb(). + // result_cb - to access the status of the operation when available. + // Possible status codes include: + // Status::STATUS_OK - finished successfully. + void InitiateBandwidthUpgradeV3(const NearbyDevice& remote_device, + ResultCallback result_cb); + + // Updates AdvertisingOptions. It compares the old AdvertisingOptions and the + // new AdvertisingOptions to start/stop each advertising medium. + // + // advertising_options - The new advertising options a client wishes to use. + // result_cb - to access the status of the operation when available. + void UpdateAdvertisingOptionsV3(const AdvertisingOptions& advertising_options, + ResultCallback result_cb); + + // Updates DiscoveryOptions. It compares the old DiscoveryOptions and the new + // DiscoveryOptions to start/stop each discovery medium. + // + // discovery_options - The new discovery options a client wishes to use. + // result_cb - to access the status of the operation when available. + void UpdateDiscoveryOptionsV3(const DiscoveryOptions& discovery_options, + ResultCallback result_cb); + + // Registers a DeviceProvider to provide functionality for Nearby Connections + // to interact with the DeviceProvider for retrieving the local device. + template ::value>::type* = nullptr> + void RegisterDeviceProvider(NearbyDeviceProvider* provider); private: ClientProxy client_; diff --git a/connections/implementation/BUILD b/connections/implementation/BUILD index 8b4fe91c..9cb96f4d 100644 --- a/connections/implementation/BUILD +++ b/connections/implementation/BUILD @@ -137,9 +137,9 @@ cc_library( "//connections/implementation/mediums:utils", "//connections/implementation/mediums/webrtc", "//connections/implementation/proto:offline_wire_formats_cc_proto", - "//internal:device", "//internal/analytics:event_logger", "//internal/flags:nearby_flags", + "//internal/interop:device", "//internal/platform:base", "//internal/platform:cancellation_flag", "//internal/platform:comm", diff --git a/connections/implementation/client_proxy.h b/connections/implementation/client_proxy.h index d11e395e..f3bc81bf 100644 --- a/connections/implementation/client_proxy.h +++ b/connections/implementation/client_proxy.h @@ -30,7 +30,7 @@ #include "connections/status.h" #include "connections/strategy.h" #include "internal/analytics/event_logger.h" -#include "internal/device.h" +#include "internal/interop/device.h" #include "internal/platform/byte_array.h" #include "internal/platform/cancelable_alarm.h" #include "internal/platform/cancellation_flag.h" @@ -199,53 +199,6 @@ class ClientProxy final { std::string Dump(); - //********************************** V2 ********************************** - void OnDeviceFound(const absl::string_view service_id, - const NearbyDevice& device, - location::nearby::proto::connections::Medium medium); - // Proxies to the client's DiscoveryListener::OnEndpointLost() callback. - void OnEndpointLost(absl::string_view service_id, const NearbyDevice& device); - - // Proxies to the client's ConnectionListener::OnInitiated() callback. - void OnConnectionInitiated(const NearbyDevice& device, - const ConnectionResponseInfo& info, - const ConnectionOptions& connection_options, - const ConnectionListener& listener, - absl::string_view connection_token); - - void OnConnectionAccepted(const NearbyDevice& device); - void OnConnectionRejected(const NearbyDevice& device, const Status& status); - - void OnBandwidthChanged(const NearbyDevice& device, Medium new_medium); - - // If notify is true, also calls the client's - // ConnectionListener.disconnected_cb() callback. - void OnDisconnected(const NearbyDevice& device, bool notify); - - BooleanMediumSelector GetUpgradableMediums(const NearbyDevice& device) const; - bool IsConnectedToEndpoint(const NearbyDevice& device) const; - // No payloads should be sent until isConnectedToEndpoint() - // returns true. - bool HasPendingConnectionToEndpoint(const NearbyDevice& device) const; - bool HasLocalEndpointResponded(const NearbyDevice& device) const; - bool HasRemoteEndpointResponded(const NearbyDevice& device) const; - void LocalEndpointAcceptedConnection(const NearbyDevice& device, - const PayloadListener& listener); - void LocalEndpointRejectedConnection(const NearbyDevice& device); - void RemoteEndpointAcceptedConnection(const NearbyDevice& device); - void RemoteEndpointRejectedConnection(const NearbyDevice& device); - bool IsConnectionAccepted(const NearbyDevice& device) const; - bool IsConnectionRejected(const NearbyDevice& device) const; - - void OnPayload(const NearbyDevice& device, Payload payload); - void OnPayloadProgress(const NearbyDevice& device, - const PayloadProgressInfo& info); - bool LocalConnectionIsAccepted(const NearbyDevice& device) const; - bool RemoteConnectionIsAccepted(const NearbyDevice& device) const; - void AddCancellationFlag(const NearbyDevice& device); - CancellationFlag* GetCancellationFlag(const NearbyDevice& device); - void CancelEndpoint(const NearbyDevice& device); - const location::nearby::connections::OsInfo& GetLocalOsInfo() const; std::optional GetRemoteOsInfo( absl::string_view endpoint_id) const; diff --git a/connections/implementation/flags/nearby_connections_feature_flags.h b/connections/implementation/flags/nearby_connections_feature_flags.h index 2ef5fc8e..61409685 100644 --- a/connections/implementation/flags/nearby_connections_feature_flags.h +++ b/connections/implementation/flags/nearby_connections_feature_flags.h @@ -27,10 +27,18 @@ constexpr absl::string_view kConfigPackage = "nearby"; // The Nearby Connections features. namespace nearby_connections_feature { +// LINT.IfChanged // Disable/Enable BLE v2 in Nearby Connections SDK. constexpr auto kEnableBleV2 = flags::Flag(kConfigPackage, "45401515", false); +// LINT.ThenChange( +// //depot/google3/location/nearby/cpp/sharing/clients/windows/nearby_sharing_service_adapter_dart.h, +// //depot/google3/location/nearby/cpp/sharing/clients/windows/nearby_sharing_service_adapter_dart.cc, +// //depot/google3/location/nearby/cpp/sharing/clients/dart/platform/lib/ffi_types.dart, +// //depot/google3/location/nearby/cpp/sharing/clients/dart/platform/lib/types/models.dart +// ) + } // namespace nearby_connections_feature } // namespace config_package_nearby } // namespace connections diff --git a/connections/implementation/p2p_cluster_pcp_handler_test.cc b/connections/implementation/p2p_cluster_pcp_handler_test.cc index 1b93c9cb..3de5438f 100644 --- a/connections/implementation/p2p_cluster_pcp_handler_test.cc +++ b/connections/implementation/p2p_cluster_pcp_handler_test.cc @@ -268,11 +268,12 @@ TEST_P(P2pClusterPcpHandlerTest, CanConnect) { const std::string kBssid = "34:36:3B:C7:8C:71"; const std::int32_t kFreq = 5200; - constexpr char kIp4Bytes[] = {(char)192, (char)168, (char)1, (char)37}; + constexpr char kIp4Bytes[] = {(char)192, (char)168, (char)1, (char)37, 0}; connection_options_.connection_info.supports_5_ghz = true; connection_options_.connection_info.bssid = kBssid; connection_options_.connection_info.ap_frequency = kFreq; + connection_options_.connection_info.ip_address.resize(4); connection_options_.connection_info.ip_address = std::string(kIp4Bytes); client_b_.AddCancellationFlag(discovered.endpoint_id); diff --git a/connections/implementation/p2p_point_to_point_pcp_handler_test.cc b/connections/implementation/p2p_point_to_point_pcp_handler_test.cc index 5eab2d1c..65d16f09 100644 --- a/connections/implementation/p2p_point_to_point_pcp_handler_test.cc +++ b/connections/implementation/p2p_point_to_point_pcp_handler_test.cc @@ -207,11 +207,12 @@ TEST_P(P2pPointToPointPcpHandlerTest, CanConnect) { const std::string kBssid = "34:36:3B:C7:8C:71"; const std::int32_t kFreq = 5200; - constexpr char kIp4Bytes[] = {(char)192, (char)168, (char)1, (char)37}; + constexpr char kIp4Bytes[] = {(char)192, (char)168, (char)1, (char)37, 0}; connection_options_.connection_info.supports_5_ghz = true; connection_options_.connection_info.bssid = kBssid; connection_options_.connection_info.ap_frequency = kFreq; + connection_options_.connection_info.ip_address.resize(4); connection_options_.connection_info.ip_address = std::string(kIp4Bytes); client_b_.AddCancellationFlag(discovered.endpoint_id); diff --git a/connections/implementation/proto/offline_wire_formats.proto b/connections/implementation/proto/offline_wire_formats.proto index 084c3e58..4fe839d5 100644 --- a/connections/implementation/proto/offline_wire_formats.proto +++ b/connections/implementation/proto/offline_wire_formats.proto @@ -43,6 +43,8 @@ message V1Frame { KEEP_ALIVE = 5; DISCONNECTION = 6; PAIRED_KEY_ENCRYPTION = 7; + AUTHENTICATION_MESSAGE = 8; + AUTHENTICATION_RESULT = 9; } optional FrameType type = 1; @@ -54,6 +56,8 @@ message V1Frame { optional KeepAliveFrame keep_alive = 6; optional DisconnectionFrame disconnection = 7; optional PairedKeyEncryptionFrame paired_key_encryption = 8; + optional AuthenticationMessageFrame authentication_message = 9; + optional AuthenticationResultFrame authentication_result = 10; } message ConnectionRequestFrame { @@ -251,6 +255,10 @@ message BandwidthUpgradeNegotiationFrame { optional int32 port = 3; optional int32 frequency = 4; optional string gateway = 5 [default = "0.0.0.0"]; + // IPv6 link-local address, network order (128bits). + // The GO should listen on both IPv4 and IPv6 addresses. + // https://en.wikipedia.org/wiki/Link-local_address#IPv6 + optional bytes ip_v6_address = 6; } // Accompanies Medium.WEB_RTC @@ -318,6 +326,21 @@ message PairedKeyEncryptionFrame { optional bytes signed_data = 1; } +// Nearby Connections authentication frame, contains the bytes format of a +// DeviceProvider's authentication message. +message AuthenticationMessageFrame { + // An auth message generated by DeviceProvider. + // To be sent to the remote device for verification during connection setups. + optional bytes auth_message = 1; +} + +// Nearby Connections authentication result frame. +message AuthenticationResultFrame { + // The authentication result. Non null if this frame is used to exchange + // authentication result. + optional int32 result = 1; +} + message MediumMetadata { // True if local device supports 5GHz. optional bool supports_5_ghz = 1; diff --git a/connections/swift/NearbyConnections/BUILD b/connections/swift/NearbyConnections/BUILD index d696a6fd..d828eebf 100644 --- a/connections/swift/NearbyConnections/BUILD +++ b/connections/swift/NearbyConnections/BUILD @@ -1,4 +1,4 @@ -# Copyright 2022 Google LLC +# Copyright 2022-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. @@ -14,6 +14,7 @@ load("//tools/build_defs/apple:ios.bzl", "ios_unit_test") load("//tools/build_defs/swift:swift_library.bzl", "swift_library") +load("//third_party/nearby:minimum_os.bzl", "IOS_MINIMUM_OS", "IOS_MINIMUM_TEST_RUNNER") licenses(["notice"]) @@ -41,8 +42,8 @@ swift_library( ios_unit_test( name = "Tests", - minimum_os_version = "13.0", - runner = "//testing/utp/ios:IOS_13", + minimum_os_version = IOS_MINIMUM_OS, + runner = IOS_MINIMUM_TEST_RUNNER, deps = [ ":TestsLib", ], diff --git a/connections/swift/NearbyConnections/Example/iOS Example.xcodeproj/project.pbxproj b/connections/swift/NearbyConnections/Example/iOS Example.xcodeproj/project.pbxproj index aaab107c..88e1bf7c 100644 --- a/connections/swift/NearbyConnections/Example/iOS Example.xcodeproj/project.pbxproj +++ b/connections/swift/NearbyConnections/Example/iOS Example.xcodeproj/project.pbxproj @@ -31,6 +31,7 @@ 23252B5729D739E400BB0370 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 23252B5929D739E500BB0370 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 23252B5C29D739E500BB0370 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 236C727829F83A1F00E54333 /* iOS-Example-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "iOS-Example-Info.plist"; sourceTree = SOURCE_ROOT; }; 23D1FD2329D8A73200620802 /* Config.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Config.swift; sourceTree = ""; }; 23D1FD2529D8A9FF00620802 /* DiscoveredEndpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscoveredEndpoint.swift; sourceTree = ""; }; 23D1FD2729D8AA2200620802 /* ConnectionRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionRequest.swift; sourceTree = ""; }; @@ -78,6 +79,7 @@ 23252B5429D739E400BB0370 /* iOS Example */ = { isa = PBXGroup; children = ( + 236C727829F83A1F00E54333 /* iOS-Example-Info.plist */, 23F6A71429D7B0AB00558F56 /* Model */, 23252B5529D739E400BB0370 /* HelloConnectionsApp.swift */, 23252B5729D739E400BB0370 /* ContentView.swift */, @@ -345,6 +347,9 @@ DEVELOPMENT_TEAM = ""; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "iOS-Example-Info.plist"; + INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "This app demonstrates Nearby Connections. Supply a value for this string to explain how the user benefits when they allow the app to use Bluetooth."; + INFOPLIST_KEY_NSLocalNetworkUsageDescription = "This app demonstrates Nearby Connections. Supply a value for this string to explain how the user benefits when they allow the app to use Local Network."; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; @@ -374,6 +379,9 @@ DEVELOPMENT_TEAM = ""; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "iOS-Example-Info.plist"; + INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "This app demonstrates Nearby Connections. Supply a value for this string to explain how the user benefits when they allow the app to use Bluetooth."; + INFOPLIST_KEY_NSLocalNetworkUsageDescription = "This app demonstrates Nearby Connections. Supply a value for this string to explain how the user benefits when they allow the app to use Local Network."; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; diff --git a/connections/swift/NearbyConnections/Example/iOS-Example-Info.plist b/connections/swift/NearbyConnections/Example/iOS-Example-Info.plist new file mode 100644 index 00000000..7954f507 --- /dev/null +++ b/connections/swift/NearbyConnections/Example/iOS-Example-Info.plist @@ -0,0 +1,10 @@ + + + + + NSBonjourServices + + _307BEAB11028._tcp + + + diff --git a/connections/swift/NearbyCoreAdapter/BUILD b/connections/swift/NearbyCoreAdapter/BUILD index 212c6fd0..5aebee41 100644 --- a/connections/swift/NearbyCoreAdapter/BUILD +++ b/connections/swift/NearbyCoreAdapter/BUILD @@ -1,4 +1,4 @@ -# Copyright 2022 Google LLC +# Copyright 2022-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. @@ -14,6 +14,7 @@ load("//tools/build_defs/apple:ios.bzl", "ios_unit_test") load("//tools/build_defs/swift:swift_library.bzl", "swift_library") +load("//third_party/nearby:minimum_os.bzl", "IOS_MINIMUM_OS", "IOS_MINIMUM_TEST_RUNNER") licenses(["notice"]) @@ -52,8 +53,8 @@ swift_library( ios_unit_test( name = "Tests", - minimum_os_version = "13.0", - runner = "//testing/utp/ios:IOS_13", + minimum_os_version = IOS_MINIMUM_OS, + runner = IOS_MINIMUM_TEST_RUNNER, deps = [ ":TestsLib", ], diff --git a/connections/v3/BUILD b/connections/v3/BUILD new file mode 100644 index 00000000..0c768dda --- /dev/null +++ b/connections/v3/BUILD @@ -0,0 +1,18 @@ +cc_library( + name = "v3_types", + hdrs = [ + "bandwidth_info.h", + "connection_listening_options.h", + "connection_resolution.h", + "listeners.h", + ], + visibility = [ + "//connections:__subpackages__", + ], + deps = [ + "//connections:core_types", + "//internal/interop:device", + "//proto:connections_enums_cc_proto", + "@com_google_absl//absl/functional:any_invocable", + ], +) diff --git a/connections/v3/bandwidth_info.h b/connections/v3/bandwidth_info.h new file mode 100644 index 00000000..1ed9fb18 --- /dev/null +++ b/connections/v3/bandwidth_info.h @@ -0,0 +1,54 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_NEARBY_CONNECTIONS_V3_BANDWIDTH_INFO_H_ +#define THIRD_PARTY_NEARBY_CONNECTIONS_V3_BANDWIDTH_INFO_H_ + +#include "proto/connections_enums.pb.h" + +namespace nearby { +namespace connections { +namespace v3 { + +// Represents the connection quality. +enum class Quality { + // Unknown connection quality. Recommended to wait for one of kMedium or + // kHigh. + kUnknown = 0, + // The connection quality is poor (5KBps) and is not suitable for + // sending files. It's recommended you wait until the connection + // quality improves. + kLow = 1, + // The connection quality is ok (60~200KBps) and is suitable for + // sending small files. For large files, it's recommended you wait + // until the connection quality improves. + kMedium = 2, + // The connection quality is good or great (1MBps~60MBps) and files + // can readily be sent. The connection quality cannot improve further + // but may still be impacted by environment or hardware limitations. + kHigh = 3, +}; + +// Used to indicate the new connection quality when upgrading to a new medium +// e.g. BT -> WiFi direct. +struct BandwidthInfo { + Quality quality; + ::location::nearby::proto::connections::Medium medium; +}; + +} // namespace v3 +} // namespace connections +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_CONNECTIONS_V3_BANDWIDTH_INFO_H_ diff --git a/connections/v3/connection_listening_options.h b/connections/v3/connection_listening_options.h new file mode 100644 index 00000000..5f3961ba --- /dev/null +++ b/connections/v3/connection_listening_options.h @@ -0,0 +1,47 @@ +// 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. + +#ifndef THIRD_PARTY_NEARBY_CONNECTIONS_V3_CONNECTION_LISTENING_OPTIONS_H_ +#define THIRD_PARTY_NEARBY_CONNECTIONS_V3_CONNECTION_LISTENING_OPTIONS_H_ + +#include + +#include "connections/strategy.h" +#include "internal/interop/device.h" +#include "proto/connections_enums.pb.h" + +namespace nearby { +namespace connections { +namespace v3 { + +struct ConnectionListeningOptions { + Strategy strategy; + bool allow_bluetooth_radio_toggling = true; + bool allow_wifi_radio_toggling = true; + bool enable_ble_listening = false; + bool enable_bluetooth_listening = true; + bool enable_wlan_listening = true; + bool auto_upgrade_bandwidth = true; + bool enforce_topology_constraints = true; + std::vector upgrade_mediums; + std::vector<::location::nearby::proto::connections::Medium> listening_mediums; + nearby::NearbyDevice::Type listening_endpoint_type = + NearbyDevice::Type::kConnectionsDevice; +}; + +} // namespace v3 +} // namespace connections +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_CONNECTIONS_V3_CONNECTION_LISTENING_OPTIONS_H_ diff --git a/connections/v3/connection_resolution.h b/connections/v3/connection_resolution.h new file mode 100644 index 00000000..18b361c5 --- /dev/null +++ b/connections/v3/connection_resolution.h @@ -0,0 +1,32 @@ +// 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. + +#ifndef THIRD_PARTY_NEARBY_CONNECTIONS_V3_CONNECTION_RESOLUTION_H_ +#define THIRD_PARTY_NEARBY_CONNECTIONS_V3_CONNECTION_RESOLUTION_H_ + +#include "connections/status.h" + +namespace nearby { +namespace connections { +namespace v3 { + +struct ConnectionResult { + nearby::connections::Status status; +}; + +} // namespace v3 +} // namespace connections +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_CONNECTIONS_V3_CONNECTION_RESOLUTION_H_ diff --git a/connections/v3/listeners.h b/connections/v3/listeners.h new file mode 100644 index 00000000..10d44509 --- /dev/null +++ b/connections/v3/listeners.h @@ -0,0 +1,133 @@ +// 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. + +#ifndef THIRD_PARTY_NEARBY_CONNECTIONS_V3_LISTENERS_H_ +#define THIRD_PARTY_NEARBY_CONNECTIONS_V3_LISTENERS_H_ + +#include "absl/functional/any_invocable.h" +#include "connections/listeners.h" +#include "connections/v3/bandwidth_info.h" +#include "connections/v3/connection_resolution.h" +#include "internal/interop/device.h" + +namespace nearby { +namespace connections { +namespace v3 { + +struct ConnectionListener { + // A basic encrypted channel has been created between you and the endpoint. + // Both sides are now asked if they wish to accept or reject the connection + // before any data can be sent over this channel. + // + // This is your chance, before you accept the connection, to confirm that you + // connected to the correct device. Both devices are given an identical token; + // it's up to you to decide how to verify it before proceeding. Typically this + // involves showing the token on both devices and having the users manually + // compare and confirm; however, this is only required if you desire a secure + // connection between the devices. + // + // Whichever route you decide to take (including not authenticating the other + // device), call Core::AcceptConnection() when you're ready to talk, or + // Core::RejectConnection() to close the connection. + // + // remote_device - The identifier for the remote endpoint. + // info - Other relevant information about the connection. + absl::AnyInvocable + initiated_cb = [](const NearbyDevice&, const ConnectionResponseInfo&) {}; + + // Called when both sides have accepted or either side has rejected the + // connection. If the {@link ConnectionResolution}'s status is {@link + // ConnectionsStatusCodes#SUCCESS}, both sides have accepted the + // connection and may now send {@link Payload}s to each other. + // Otherwise, the connection was rejected. + + // remote_device - The identifier for the remote endpoint. + // resolution - The resolution of the connection (accepted or rejected). + absl::AnyInvocable + result_cb = [](const NearbyDevice&, ConnectionResult) {}; + + // Called when a remote endpoint is disconnected or has become unreachable. + // At this point service (re-)discovery may start again. + // + // remote_device - The identifier for the remote endpoint. + absl::AnyInvocable disconnected_cb = + [](const NearbyDevice&) {}; + + // Called when the connection's available bandwidth has changed. + // + // remote_device - The identifier for the remote endpoint. + // bandwidth_info - Bandwidth info about the new medium that was upgraded to. + absl::AnyInvocable + bandwidth_changed_cb = [](const NearbyDevice&, BandwidthInfo) {}; +}; + +struct DiscoveryListener { + // Called when a remote endpoint is discovered. + // + // remote_device - The remote device that was discovered. + // service_id - The ID of the service advertised by the remote endpoint. + absl::AnyInvocable + endpoint_found_cb = [](const NearbyDevice&, const absl::string_view) {}; + + // Called when a remote endpoint is no longer discoverable; only called for + // endpoints that previously had been passed to {@link + // #onEndpointFound(String, DiscoveredEndpointInfo)}. + // + // remote_device - The ID of the remote endpoint that was lost. + absl::AnyInvocable endpoint_lost_cb = + [](const NearbyDevice&) {}; + + // Called when a remote endpoint is found with an updated distance. + // + // arguments: + // remote_device - The ID of the remote endpoint that was lost. + // info - The distance info, encoded as enum value. + absl::AnyInvocable + endpoint_distance_changed_cb = [](const NearbyDevice&, DistanceInfo) {}; +}; + +struct PayloadListener { + // Called when a Payload is received from a remote endpoint. Depending + // on the type of the Payload, all of the data may or may not have been + // received at the time of this call. Use OnPayloadProgress() to + // get updates on the status of the data received. + // + // remote_device - The identifier for the remote endpoint that sent the + // payload. + // payload - The Payload object received. + absl::AnyInvocable + payload_received_cb = [](const NearbyDevice&, Payload) {}; + + // Called with progress information about an active Payload transfer, either + // incoming or outgoing. + // + // remote_device - The identifier for the remote endpoint that is sending or + // receiving this payload. + // info - The PayloadProgressInfo structure describing the status of + // the transfer. + absl::AnyInvocable + payload_progress_cb = + [](const NearbyDevice&, const PayloadProgressInfo&) {}; +}; + +} // namespace v3 +} // namespace connections +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_CONNECTIONS_V3_LISTENERS_H_ diff --git a/fastpair/common/BUILD b/fastpair/common/BUILD index b7258241..96e95efc 100644 --- a/fastpair/common/BUILD +++ b/fastpair/common/BUILD @@ -39,3 +39,17 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "pair_failure_test", + size = "small", + srcs = [ + "pair_failure_test.cc", + ], + shard_count = 16, + deps = [ + ":common", + "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/fastpair/common/pair_failure.cc b/fastpair/common/pair_failure.cc index bd32d6a6..53373f21 100644 --- a/fastpair/common/pair_failure.cc +++ b/fastpair/common/pair_failure.cc @@ -90,6 +90,15 @@ std::ostream& operator<<(std::ostream& stream, PairFailure failure) { stream << "[Potential pairing device lost between GATT connection attempts]"; break; + case PairFailure::kDeviceLostMidPairing: + stream << "[Potential pairing device lost during pairing.]"; + break; + case PairFailure::kPairingAndConnect: + stream << "[Failed to pair with discovered device.]"; + break; + case PairFailure::kPairingTimeout: + stream << "[Potential pairing failed with timeout.]"; + break; } return stream; diff --git a/fastpair/common/pair_failure.h b/fastpair/common/pair_failure.h index c69b7bb6..c2b360fa 100644 --- a/fastpair/common/pair_failure.h +++ b/fastpair/common/pair_failure.h @@ -68,7 +68,13 @@ enum class PairFailure { kPasskeyMismatch = 19, // Potential pairing device lost between GATT connection attempts. kPairingDeviceLostBetweenGattConnectionAttempts = 20, - kMaxValue = kPairingDeviceLostBetweenGattConnectionAttempts, + // Potential pairing device lost during pairing. + kDeviceLostMidPairing = 21, + // Failed to pair and connect with discovered device. + kPairingAndConnect = 22, + // Potential pairing timeout. + kPairingTimeout = 23, + kMaxValue = kPairingTimeout, }; std::ostream& operator<<(std::ostream& stream, PairFailure failure); diff --git a/fastpair/common/pair_failure_test.cc b/fastpair/common/pair_failure_test.cc new file mode 100644 index 00000000..eea3da05 --- /dev/null +++ b/fastpair/common/pair_failure_test.cc @@ -0,0 +1,68 @@ +// 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. + +#include "fastpair/common/pair_failure.h" + +#include "gtest/gtest.h" + +namespace nearby { +namespace fastpair { +namespace { + +TEST(PairFailureTest, PairFailureValue) { + EXPECT_EQ(static_cast(PairFailure::kCreateGattConnection), 0); + EXPECT_EQ(static_cast(PairFailure::kGattServiceDiscovery), 1); + EXPECT_EQ(static_cast(PairFailure::kGattServiceDiscoveryTimeout), 2); + EXPECT_EQ(static_cast(PairFailure::kDataEncryptorRetrieval), 3); + EXPECT_EQ( + static_cast(PairFailure::kKeyBasedPairingCharacteristicDiscovery), + 4); + EXPECT_EQ(static_cast(PairFailure::kPasskeyCharacteristicDiscovery), 5); + EXPECT_EQ(static_cast(PairFailure::kAccountKeyCharacteristicDiscovery), + 6); + EXPECT_EQ( + static_cast(PairFailure::kKeyBasedPairingCharacteristicSubscription), + 7); + EXPECT_EQ(static_cast(PairFailure::kPasskeyCharacteristicSubscription), + 8); + EXPECT_EQ(static_cast( + PairFailure::kKeyBasedPairingCharacteristicSubscriptionTimeout), + 9); + EXPECT_EQ( + static_cast(PairFailure::kPasskeyCharacteristicSubscriptionTimeout), + 10); + EXPECT_EQ(static_cast(PairFailure::kKeyBasedPairingCharacteristicWrite), + 11); + EXPECT_EQ(static_cast(PairFailure::kPasskeyPairingCharacteristicWrite), + 12); + EXPECT_EQ(static_cast(PairFailure::kKeyBasedPairingResponseTimeout), 13); + EXPECT_EQ(static_cast(PairFailure::kPasskeyResponseTimeout), 14); + EXPECT_EQ( + static_cast(PairFailure::kKeybasedPairingResponseDecryptFailure), + 15); + EXPECT_EQ( + static_cast(PairFailure::kIncorrectKeyBasedPairingResponseType), 16); + EXPECT_EQ(static_cast(PairFailure::kPasskeyDecryptFailure), 17); + EXPECT_EQ(static_cast(PairFailure::kIncorrectPasskeyResponseType), 18); + EXPECT_EQ(static_cast(PairFailure::kPasskeyMismatch), 19); + EXPECT_EQ(static_cast( + PairFailure::kPairingDeviceLostBetweenGattConnectionAttempts), + 20); + EXPECT_EQ(static_cast(PairFailure::kDeviceLostMidPairing), 21); + EXPECT_EQ(static_cast(PairFailure::kPairingAndConnect), 22); + EXPECT_EQ(static_cast(PairFailure::kPairingTimeout), 23); +} +} // namespace +} // namespace fastpair +} // namespace nearby diff --git a/fastpair/dart/fast_pair_wrapper_impl.cc b/fastpair/dart/fast_pair_wrapper_impl.cc index de235a75..c29935e9 100644 --- a/fastpair/dart/fast_pair_wrapper_impl.cc +++ b/fastpair/dart/fast_pair_wrapper_impl.cc @@ -30,7 +30,8 @@ FastPairWrapperImpl::FastPairWrapperImpl() { FastPairWrapperImpl::~FastPairWrapperImpl() = default; void FastPairWrapperImpl::StartScan() { - scanner_broker_ = std::make_unique(); + Mediums mediums; + scanner_broker_ = std::make_unique(mediums); if (is_scanning_) { NEARBY_LOGS(VERBOSE) << __func__ << ": We're currently scanning. "; return; diff --git a/fastpair/dart/fast_pair_wrapper_impl_test.cc b/fastpair/dart/fast_pair_wrapper_impl_test.cc index afd1f768..b614ae37 100644 --- a/fastpair/dart/fast_pair_wrapper_impl_test.cc +++ b/fastpair/dart/fast_pair_wrapper_impl_test.cc @@ -16,8 +16,6 @@ #include -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" #include "internal/platform/medium_environment.h" @@ -42,7 +40,7 @@ TEST_F(FastPairWrapperImplTest, StartScanningSuccess) { EXPECT_FALSE(wrapper_->IsPairing()); EXPECT_FALSE(wrapper_->IsServerAccessing()); wrapper_->StartScan(); - SystemClock::Sleep(absl::Milliseconds(200)); + SystemClock::Sleep(absl::Milliseconds(2000)); EXPECT_TRUE(wrapper_->IsScanning()); env_.Stop(); } diff --git a/fastpair/dart/windows/BUILD b/fastpair/dart/windows/BUILD index 53d9d1b9..b289626c 100644 --- a/fastpair/dart/windows/BUILD +++ b/fastpair/dart/windows/BUILD @@ -27,6 +27,7 @@ lexan.cc_windows_dll( "-Ithird_party", ], defines = [ + "LOG_SEVERITY_VERBOSE", "_WIN32_WINNT=_WIN32_WINNT_WIN10", ], tags = ["windows-dll"], @@ -57,6 +58,7 @@ lexan.cc_windows_dll( "-Ithird_party", ], defines = [ + "LOG_SEVERITY_VERBOSE", "_WIN32_WINNT=_WIN32_WINNT_WIN10", ], tags = ["windows-dll"], diff --git a/fastpair/dart/windows/fast_pair_wrapper_adapter.cc b/fastpair/dart/windows/fast_pair_wrapper_adapter.cc index c6c3197d..41788d45 100644 --- a/fastpair/dart/windows/fast_pair_wrapper_adapter.cc +++ b/fastpair/dart/windows/fast_pair_wrapper_adapter.cc @@ -24,6 +24,11 @@ namespace windows { static FastPairWrapper *pWrapper_ = nullptr; void *InitFastPairWrapper() { +#if defined(NEARBY_LOG_SEVERITY) + // Direct override of logging level. + NEARBY_LOG_SET_SEVERITY(NEARBY_LOG_SEVERITY); +#endif // LOG_SEVERITY_VERBOSE; + FastPairWrapperImpl *pWrapper = new FastPairWrapperImpl(); pWrapper_ = pWrapper; return pWrapper; @@ -36,7 +41,7 @@ void CloseFastPairWrapper(FastPairWrapper *pWrapper) { } void __stdcall StartScan(FastPairWrapper *pWrapper) { - NEARBY_LOGS(INFO) << "StartScan is called"; + NEARBY_LOGS(VERBOSE) << "StartScan is called"; if (pWrapper_ == nullptr) { NEARBY_LOGS(INFO) << "The pWrapper is a null pointer."; return; diff --git a/fastpair/handshake/BUILD b/fastpair/handshake/BUILD index f099e941..40de6d37 100644 --- a/fastpair/handshake/BUILD +++ b/fastpair/handshake/BUILD @@ -20,6 +20,7 @@ cc_library( "fast_pair_data_encryptor_impl.cc", "fast_pair_gatt_service_client_impl.cc", "fast_pair_handshake_impl.cc", + "fast_pair_handshake_lookup.cc", ], hdrs = [ "fast_pair_data_encryptor.h", @@ -28,6 +29,7 @@ cc_library( "fast_pair_gatt_service_client_impl.h", "fast_pair_handshake.h", "fast_pair_handshake_impl.h", + "fast_pair_handshake_lookup.h", ], visibility = [ "//:__subpackages__", @@ -37,19 +39,22 @@ cc_library( "//fastpair/common", "//fastpair/crypto", "//fastpair/dataparser", - "//fastpair/internal/ble", + "//fastpair/internal/mediums", "//fastpair/repository", "//fastpair/server_access", "//internal/base:bluetooth_address", - "//internal/platform:base", "//internal/platform:comm", "//internal/platform:logging", "//internal/platform:types", "//internal/platform:uuid", "@boringssl//:crypto", + "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/functional:any_invocable", "@com_google_absl//absl/functional:bind_front", "@com_google_absl//absl/strings", + "@com_google_absl//absl/synchronization", + "@com_google_absl//absl/time", + "@com_google_absl//absl/types:span", ], ) @@ -82,7 +87,7 @@ cc_test( "//fastpair/common", "//fastpair/crypto", "//fastpair/dataparser", - "//fastpair/internal/ble", + "//fastpair/internal/mediums", "//fastpair/server_access:test_support", "//fastpair/testing", "//internal/platform:logging", @@ -107,6 +112,7 @@ cc_test( ":handshake", ":test_support", "//fastpair/common", + "//fastpair/internal/mediums", "//fastpair/testing", "//internal/platform:base", "//internal/platform:comm", @@ -116,6 +122,7 @@ cc_test( "//internal/platform/implementation/g3", # build_cleaner: keep "//internal/test", "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_absl//absl/status", "@com_google_absl//absl/strings", "@com_google_absl//absl/time", "@com_google_googletest//:gtest_main", @@ -142,3 +149,23 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "fast_pair_handshake_lookup_test", + size = "small", + srcs = [ + "fast_pair_handshake_lookup_test.cc", + ], + shard_count = 16, + deps = [ + ":handshake", + "//fastpair/common", + "//fastpair/internal/mediums", + "//internal/platform:test_util", + "//internal/platform:types", + "//internal/platform/implementation/g3", # build_cleaner: keep + "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/fastpair/handshake/fake_fast_pair_gatt_service_client.h b/fastpair/handshake/fake_fast_pair_gatt_service_client.h index 2274034e..019e2e0d 100644 --- a/fastpair/handshake/fake_fast_pair_gatt_service_client.h +++ b/fastpair/handshake/fake_fast_pair_gatt_service_client.h @@ -43,26 +43,28 @@ class FakeFastPairGattServiceClient : public FastPairGattServiceClient { WriteResponseCallback write_response_callback) override { key_based_write_response_callback_ = std::move(write_response_callback); } + void WritePasskeyAsync( uint8_t message_type, uint32_t passkey, const FastPairDataEncryptor& fast_pair_data_encryptor, WriteResponseCallback write_response_callback) override { passkey_write_response_callback_ = std::move(write_response_callback); } + void RunOnGattClientInitializedCallback( - std::optional failure = absl::nullopt) { + std::optional failure = std::nullopt) { std::move(on_initialized_callback_)(failure); } void RunWriteResponseCallback( absl::string_view value, - std::optional failure = absl::nullopt) { + std::optional failure = std::nullopt) { std::move(key_based_write_response_callback_)(value, failure); } void RunWritePasskeyCallback( absl::string_view value, - std::optional failure = absl::nullopt) { + std::optional failure = std::nullopt) { std::move(passkey_write_response_callback_)(value, failure); } diff --git a/fastpair/handshake/fast_pair_data_encryptor_impl.cc b/fastpair/handshake/fast_pair_data_encryptor_impl.cc index 6f9eba7a..17c5d745 100644 --- a/fastpair/handshake/fast_pair_data_encryptor_impl.cc +++ b/fastpair/handshake/fast_pair_data_encryptor_impl.cc @@ -88,7 +88,8 @@ void FastPairDataEncryptorImpl::Factory::CreateAsyncWithKeyExchange( NEARBY_LOGS(INFO) << __func__ << ": Attempting to get device metadata."; FastPairRepository::Get()->GetDeviceMetadata( device.GetModelId(), - [&on_get_instance_callback](DeviceMetadata& metadata) { + [on_get_instance_callback = std::move(on_get_instance_callback)]( + DeviceMetadata& metadata) mutable { FastPairDataEncryptorImpl::Factory::DeviceMetadataRetrieved( std::move(on_get_instance_callback), metadata); }); diff --git a/fastpair/handshake/fast_pair_gatt_service_client_impl.cc b/fastpair/handshake/fast_pair_gatt_service_client_impl.cc index bb0685f5..c347db66 100644 --- a/fastpair/handshake/fast_pair_gatt_service_client_impl.cc +++ b/fastpair/handshake/fast_pair_gatt_service_client_impl.cc @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -27,11 +28,15 @@ #include "absl/functional/any_invocable.h" #include "absl/functional/bind_front.h" #include "absl/strings/string_view.h" +#include "absl/time/time.h" +#include "absl/types/span.h" #include "fastpair/common/constant.h" +#include "fastpair/common/fast_pair_device.h" +#include "fastpair/common/pair_failure.h" #include "fastpair/handshake/fast_pair_data_encryptor.h" #include "fastpair/handshake/fast_pair_gatt_service_client.h" +#include "fastpair/internal/mediums/mediums.h" #include "internal/base/bluetooth_address.h" -#include "internal/platform/ble_v2.h" #include "internal/platform/logging.h" #include "internal/platform/uuid.h" #include @@ -63,12 +68,13 @@ FastPairGattServiceClientImpl::Factory* // static std::unique_ptr -FastPairGattServiceClientImpl::Factory::Create(const FastPairDevice& device) { +FastPairGattServiceClientImpl::Factory::Create(const FastPairDevice& device, + Mediums& mediums) { if (g_test_factory_) { return g_test_factory_->CreateInstance(); } - return std::make_unique(device); + return std::make_unique(device, mediums); } // static @@ -80,8 +86,8 @@ void FastPairGattServiceClientImpl::Factory::SetFactoryForTesting( FastPairGattServiceClientImpl::Factory::~Factory() = default; FastPairGattServiceClientImpl::FastPairGattServiceClientImpl( - const FastPairDevice& device) - : device_address_(device.GetBleAddress()) {} + const FastPairDevice& device, Mediums& mediums) + : device_address_(device.GetBleAddress()), mediums_(mediums) {} void FastPairGattServiceClientImpl::InitializeGattConnection( absl::AnyInvocable)> @@ -113,7 +119,10 @@ void FastPairGattServiceClientImpl::AttemptGattConnection() { void FastPairGattServiceClientImpl::CreateGattConnection() { NEARBY_LOGS(INFO) << __func__ << " : Create Gatt Connection to the device."; - gatt_client_ = ble_.ConnectToGattServer(device_address_); + if (mediums_.GetBluetoothRadio().Enable() && + mediums_.GetBleV2().IsAvailable()) { + gatt_client_ = mediums_.GetBleV2().ConnectToGattServer(device_address_); + } if (!gatt_client_) { // The device must have been lost between connection attempts. NotifyInitializedError( @@ -168,7 +177,7 @@ void FastPairGattServiceClientImpl::GetFastPairGattCharacteristics() { } is_initialized_ = true; - std::move(on_gatt_initialized_callback_)(absl::nullopt); + std::move(on_gatt_initialized_callback_)(std::nullopt); } std::optional @@ -255,7 +264,7 @@ void FastPairGattServiceClientImpl::WriteRequestAsync( std::vector data_to_write_vec(data_to_write.begin(), data_to_write.end()); - // Append the public version of the private key to the message so thedevice + // Append the public version of the private key to the message so the device // can generate the shared secret to decrypt the message. const std::optional> public_key = fast_pair_data_encryptor.GetPublicKey(); @@ -340,7 +349,7 @@ void FastPairGattServiceClientImpl::WritePasskeyAsync( // Subscribe the notification once the passkey characteristic's value changed if (SubscribePasskeyCharacteristic()) { is_passkey_notification_subscribed_ = true; - // Write passkey confonirmation request to the passkey characteristic + // Write passkey confirmation request to the passkey characteristic WritePasskeyCharacteristic( std::string(data_to_write_vec.begin(), data_to_write_vec.end())); } diff --git a/fastpair/handshake/fast_pair_gatt_service_client_impl.h b/fastpair/handshake/fast_pair_gatt_service_client_impl.h index d904e14a..bd8251b9 100644 --- a/fastpair/handshake/fast_pair_gatt_service_client_impl.h +++ b/fastpair/handshake/fast_pair_gatt_service_client_impl.h @@ -25,7 +25,7 @@ #include "fastpair/common/fast_pair_device.h" #include "fastpair/common/pair_failure.h" #include "fastpair/handshake/fast_pair_gatt_service_client.h" -#include "fastpair/internal/ble/ble.h" +#include "fastpair/internal/mediums/mediums.h" #include "internal/platform/ble_v2.h" #include "internal/platform/timer_impl.h" @@ -41,7 +41,7 @@ class FastPairGattServiceClientImpl : public FastPairGattServiceClient { class Factory { public: static std::unique_ptr Create( - const FastPairDevice& device); + const FastPairDevice& device, Mediums& mediums); static void SetFactoryForTesting(Factory* test_factory); protected: @@ -52,7 +52,8 @@ class FastPairGattServiceClientImpl : public FastPairGattServiceClient { static Factory* g_test_factory_; }; - explicit FastPairGattServiceClientImpl(const FastPairDevice& device); + explicit FastPairGattServiceClientImpl(const FastPairDevice& device, + Mediums& mediums); FastPairGattServiceClientImpl(const FastPairGattServiceClientImpl&) = delete; FastPairGattServiceClientImpl& operator=( const FastPairGattServiceClientImpl&) = delete; @@ -144,7 +145,7 @@ class FastPairGattServiceClientImpl : public FastPairGattServiceClient { bool is_initialized_ = false; std::string device_address_; std::unique_ptr gatt_client_; - Ble ble_; + Mediums& mediums_; }; } // namespace fastpair } // namespace nearby diff --git a/fastpair/handshake/fast_pair_gatt_service_client_impl_test.cc b/fastpair/handshake/fast_pair_gatt_service_client_impl_test.cc index 07f36962..8bd35736 100644 --- a/fastpair/handshake/fast_pair_gatt_service_client_impl_test.cc +++ b/fastpair/handshake/fast_pair_gatt_service_client_impl_test.cc @@ -15,17 +15,22 @@ #include "fastpair/handshake/fast_pair_gatt_service_client_impl.h" #include +#include #include #include #include #include "gtest/gtest.h" +#include "absl/status/status.h" #include "absl/strings/string_view.h" #include "absl/time/time.h" #include "fastpair/common/constant.h" #include "fastpair/common/fast_pair_device.h" #include "fastpair/common/pair_failure.h" +#include "fastpair/common/protocol.h" #include "fastpair/handshake/fake_fast_pair_data_encryptor.h" +#include "fastpair/handshake/fast_pair_gatt_service_client.h" +#include "fastpair/internal/mediums/mediums.h" #include "internal/platform/ble_v2.h" #include "internal/platform/bluetooth_adapter.h" #include "internal/platform/byte_array.h" @@ -161,7 +166,9 @@ class FastPairGattServiceClientTest : public testing::Test { void InitializeFastPairGattServiceClient() { FastPairDevice device(kMetadataId, kProviderAddress, Protocol::kFastPairInitialPairing); - gatt_client_ = FastPairGattServiceClientImpl::Factory::Create(device); + Mediums mediums; + gatt_client_ = + FastPairGattServiceClientImpl::Factory::Create(device, mediums); gatt_client_->InitializeGattConnection( [this](std::optional failure) { initalized_failure_ = failure; @@ -188,18 +195,16 @@ class FastPairGattServiceClientTest : public testing::Test { passkey_characteristic_.value(), false, {}); } - absl::optional GetInitializedCallbackResult() { + std::optional GetInitializedCallbackResult() { return initalized_failure_; } void WriteTestCallback(absl::string_view response, - absl::optional failure) { + std::optional failure) { write_failure_ = failure; } - absl::optional GetWriteCallbackResult() { - return write_failure_; - } + std::optional GetWriteCallbackResult() { return write_failure_; } void WriteRequestToKeyBased() { gatt_client_->WriteRequestAsync( @@ -240,8 +245,8 @@ class FastPairGattServiceClientTest : public testing::Test { private: std::optional key_based_characteristic_; std::optional passkey_characteristic_; - absl::optional initalized_failure_; - absl::optional write_failure_; + std::optional initalized_failure_; + std::optional write_failure_; Property properties_ = Property::kWrite | Property::kNotify; Permission permissions_ = Permission::kWrite; }; @@ -277,7 +282,7 @@ TEST_F(FastPairGattServiceClientTest, SuccessfulWriteKeyBaseCharacteristics) { InitializeFastPairGattServiceClient(); WriteRequestToKeyBased(); EXPECT_EQ(TriggerKeyBasedGattChanged(), absl::OkStatus()); - EXPECT_EQ(GetWriteCallbackResult(), absl::nullopt); + EXPECT_EQ(GetWriteCallbackResult(), std::nullopt); } TEST_F(FastPairGattServiceClientTest, SuccessfulWritePasskeyCharacteristics) { @@ -285,7 +290,7 @@ TEST_F(FastPairGattServiceClientTest, SuccessfulWritePasskeyCharacteristics) { InitializeFastPairGattServiceClient(); WriteRequestToPasskey(); EXPECT_EQ(TriggerPasskeyGattChanged(), absl::OkStatus()); - EXPECT_EQ(GetWriteCallbackResult(), absl::nullopt); + EXPECT_EQ(GetWriteCallbackResult(), std::nullopt); } TEST_F(FastPairGattServiceClientTest, FailedSubscribeKeybaseCharacteristic) { @@ -297,7 +302,7 @@ TEST_F(FastPairGattServiceClientTest, FailedSubscribeKeybaseCharacteristic) { PairFailure::kKeyBasedPairingCharacteristicSubscription); WriteRequestToPasskey(); EXPECT_EQ(TriggerPasskeyGattChanged(), absl::OkStatus()); - EXPECT_EQ(GetWriteCallbackResult(), absl::nullopt); + EXPECT_EQ(GetWriteCallbackResult(), std::nullopt); } TEST_F(FastPairGattServiceClientTest, FailedSubscribePasskeyCharacteristic) { @@ -306,7 +311,7 @@ TEST_F(FastPairGattServiceClientTest, FailedSubscribePasskeyCharacteristic) { RemoveDiscoveredPasskeyCharacteristic(); WriteRequestToKeyBased(); EXPECT_EQ(TriggerKeyBasedGattChanged(), absl::OkStatus()); - EXPECT_EQ(GetWriteCallbackResult(), absl::nullopt); + EXPECT_EQ(GetWriteCallbackResult(), std::nullopt); WriteRequestToPasskey(); EXPECT_EQ(GetWriteCallbackResult(), PairFailure::kPasskeyCharacteristicSubscription); diff --git a/fastpair/handshake/fast_pair_handshake.h b/fastpair/handshake/fast_pair_handshake.h index c42da161..73b1e730 100644 --- a/fastpair/handshake/fast_pair_handshake.h +++ b/fastpair/handshake/fast_pair_handshake.h @@ -49,11 +49,10 @@ class FastPairHandshake { FastPairDevice& device, std::optional failure)>; FastPairHandshake( - FastPairDevice& device, OnCompleteCallback on_complete_cb, + OnCompleteCallback on_complete_cb, std::unique_ptr data_encryptor, std::unique_ptr gatt_service_client) - : device_(&device), - on_complete_callback_(std::move(on_complete_cb)), + : on_complete_callback_(std::move(on_complete_cb)), fast_pair_data_encryptor_(std::move(data_encryptor)), fast_pair_gatt_service_client_(std::move(gatt_service_client)) {} @@ -73,7 +72,6 @@ class FastPairHandshake { protected: bool completed_successfully_ = false; - FastPairDevice* device_; OnCompleteCallback on_complete_callback_; std::unique_ptr fast_pair_data_encryptor_; std::unique_ptr fast_pair_gatt_service_client_; diff --git a/fastpair/handshake/fast_pair_handshake_impl.cc b/fastpair/handshake/fast_pair_handshake_impl.cc index a2d6a2dc..734c7112 100644 --- a/fastpair/handshake/fast_pair_handshake_impl.cc +++ b/fastpair/handshake/fast_pair_handshake_impl.cc @@ -31,23 +31,24 @@ namespace nearby { namespace fastpair { FastPairHandshakeImpl::FastPairHandshakeImpl(FastPairDevice& device, + Mediums& mediums, OnCompleteCallback on_complete) - : FastPairHandshake(device, std::move(on_complete), nullptr, nullptr) { + : FastPairHandshake(std::move(on_complete), nullptr, nullptr) { fast_pair_gatt_service_client_ = - FastPairGattServiceClientImpl::Factory::Create(device); + FastPairGattServiceClientImpl::Factory::Create(device, mediums); fast_pair_gatt_service_client_->InitializeGattConnection( - [this](std::optional failure) { - OnGattClientInitializedCallback(failure); + [&](std::optional failure) { + OnGattClientInitializedCallback(device, failure); }); } void FastPairHandshakeImpl::OnGattClientInitializedCallback( - std::optional failure) { + FastPairDevice& device, std::optional failure) { if (failure.has_value()) { NEARBY_LOGS(WARNING) << __func__ << ": Failed to init gatt client with failure = " << failure.value(); - std::move(on_complete_callback_)(*device_, failure.value()); + std::move(on_complete_callback_)(device, failure.value()); return; } @@ -55,16 +56,19 @@ void FastPairHandshakeImpl::OnGattClientInitializedCallback( << __func__ << ": Fast Pair GATT service client initialization successful."; FastPairDataEncryptorImpl::Factory::CreateAsync( - *device_, absl::bind_front( - &FastPairHandshakeImpl::OnDataEncryptorCreateAsync, this)); + device, + [&](std::unique_ptr fast_pair_data_encryptor) { + OnDataEncryptorCreateAsync(device, std::move(fast_pair_data_encryptor)); + }); } void FastPairHandshakeImpl::OnDataEncryptorCreateAsync( + FastPairDevice& device, std::unique_ptr fast_pair_data_encryptor) { if (!fast_pair_data_encryptor) { NEARBY_LOGS(WARNING) << __func__ << ": Failed to create Fast Pair Data Encryptor."; - std::move(on_complete_callback_)(*device_, + std::move(on_complete_callback_)(device, PairFailure::kDataEncryptorRetrieval); return; } @@ -74,21 +78,22 @@ void FastPairHandshakeImpl::OnDataEncryptorCreateAsync( fast_pair_gatt_service_client_->WriteRequestAsync( /*message_type=*/kKeyBasedPairingType, /*flags=*/kInitialOrSubsequentFlags, - /*provider_address=*/device_->GetBleAddress(), + /*provider_address=*/device.GetBleAddress(), /*seekers_address=*/"", *fast_pair_data_encryptor_, - [this](absl::string_view response, std::optional failure) { - OnWriteResponse(response, failure); + [&](absl::string_view response, std::optional failure) { + OnWriteResponse(device, response, failure); }); } void FastPairHandshakeImpl::OnWriteResponse( - absl::string_view response, std::optional failure) { + FastPairDevice& device, absl::string_view response, + std::optional failure) { if (failure.has_value()) { NEARBY_LOGS(WARNING) << __func__ << ": Failed during key-based pairing protocol with failure = " << failure.value(); - std::move(on_complete_callback_)(*device_, failure.value()); + std::move(on_complete_callback_)(device, failure.value()); return; } @@ -98,33 +103,33 @@ void FastPairHandshakeImpl::OnWriteResponse( NEARBY_LOGS(WARNING) << __func__ << ": Handshake failed because of incorrect response size."; std::move(on_complete_callback_)( - *device_, PairFailure::kKeybasedPairingResponseDecryptFailure); + device, PairFailure::kKeybasedPairingResponseDecryptFailure); return; } std::vector response_bytes(response.begin(), response.end()); fast_pair_data_encryptor_->ParseDecryptResponse( - response_bytes, [this](std::optional response) { - OnParseDecryptedResponse(response); + response_bytes, [&](std::optional response) { + OnParseDecryptedResponse(device, response); }); } void FastPairHandshakeImpl::OnParseDecryptedResponse( - std::optional& response) { + FastPairDevice& device, std::optional& response) { if (!response.has_value()) { NEARBY_LOGS(WARNING) << __func__ << ": Missing decrypted response from parse."; std::move(on_complete_callback_)( - *device_, PairFailure::kKeybasedPairingResponseDecryptFailure); + device, PairFailure::kKeybasedPairingResponseDecryptFailure); return; } NEARBY_LOGS(INFO) << __func__ << ": Successfully decrypted and parsed response."; - device_->set_public_address( + device.set_public_address( device::CanonicalizeBluetoothAddress(response->address_bytes)); completed_successfully_ = true; - std::move(on_complete_callback_)(*device_, absl::nullopt); + std::move(on_complete_callback_)(device, absl::nullopt); } } // namespace fastpair diff --git a/fastpair/handshake/fast_pair_handshake_impl.h b/fastpair/handshake/fast_pair_handshake_impl.h index c41bbc30..fa8a46a3 100644 --- a/fastpair/handshake/fast_pair_handshake_impl.h +++ b/fastpair/handshake/fast_pair_handshake_impl.h @@ -22,23 +22,28 @@ #include "fastpair/common/pair_failure.h" #include "fastpair/crypto/decrypted_response.h" #include "fastpair/handshake/fast_pair_handshake.h" +#include "fastpair/internal/mediums/mediums.h" namespace nearby { namespace fastpair { class FastPairHandshakeImpl : public FastPairHandshake { public: - FastPairHandshakeImpl(FastPairDevice& device, OnCompleteCallback on_complete); + explicit FastPairHandshakeImpl(FastPairDevice& device, Mediums& mediums, + OnCompleteCallback on_complete); FastPairHandshakeImpl(const FastPairHandshakeImpl&) = delete; FastPairHandshakeImpl& operator=(const FastPairHandshakeImpl&) = delete; private: - void OnGattClientInitializedCallback(std::optional failure); + void OnGattClientInitializedCallback(FastPairDevice& device, + std::optional failure); void OnDataEncryptorCreateAsync( + FastPairDevice& device, std::unique_ptr fast_pair_data_encryptor); - void OnWriteResponse(absl::string_view response, + void OnWriteResponse(FastPairDevice& device, absl::string_view response, std::optional failure); - void OnParseDecryptedResponse(std::optional& response); + void OnParseDecryptedResponse(FastPairDevice& device, + std::optional& response); }; } // namespace fastpair diff --git a/fastpair/handshake/fast_pair_handshake_impl_test.cc b/fastpair/handshake/fast_pair_handshake_impl_test.cc index 47dac67c..d3511542 100644 --- a/fastpair/handshake/fast_pair_handshake_impl_test.cc +++ b/fastpair/handshake/fast_pair_handshake_impl_test.cc @@ -162,8 +162,9 @@ TEST_F(FastPairHandshakeImplTest, Success) { FastPairDevice device(kMetadataId, kProviderAddress, Protocol::kFastPairInitialPairing); CountDownLatch latch(1); + Mediums mediums; handshake_ = std::make_unique( - device, + device, mediums, [&](FastPairDevice& callback_device, std::optional failure) { EXPECT_EQ(&device, &callback_device); EXPECT_EQ(device.public_address(), kPublicAddress); @@ -180,8 +181,9 @@ TEST_F(FastPairHandshakeImplTest, GattError) { FastPairDevice device(kMetadataId, kProviderAddress, Protocol::kFastPairInitialPairing); CountDownLatch latch(1); + Mediums mediums; handshake_ = std::make_unique( - device, + device, mediums, [&](FastPairDevice& callback_device, std::optional failure) { EXPECT_EQ(&device, &callback_device); EXPECT_EQ(failure.value(), PairFailure::kCreateGattConnection); @@ -197,8 +199,9 @@ TEST_F(FastPairHandshakeImplTest, DataEncryptorCreateError) { FastPairDevice device(kMetadataId, kProviderAddress, Protocol::kFastPairInitialPairing); CountDownLatch latch(1); + Mediums mediums; handshake_ = std::make_unique( - device, + device, mediums, [&](FastPairDevice& callback_device, std::optional failure) { EXPECT_EQ(&device, &callback_device); EXPECT_EQ(failure.value(), PairFailure::kDataEncryptorRetrieval); @@ -214,8 +217,9 @@ TEST_F(FastPairHandshakeImplTest, WriteResponseError) { FastPairDevice device(kMetadataId, kProviderAddress, Protocol::kFastPairInitialPairing); CountDownLatch latch(1); + Mediums mediums; handshake_ = std::make_unique( - device, + device, mediums, [&](FastPairDevice& callback_device, std::optional failure) { EXPECT_EQ(&device, &callback_device); EXPECT_EQ(failure.value(), @@ -233,8 +237,9 @@ TEST_F(FastPairHandshakeImplTest, WriteResponseWrongSize) { FastPairDevice device(kMetadataId, kProviderAddress, Protocol::kFastPairInitialPairing); CountDownLatch latch(1); + Mediums mediums; handshake_ = std::make_unique( - device, + device, mediums, [&](FastPairDevice& callback_device, std::optional failure) { EXPECT_EQ(&device, &callback_device); EXPECT_EQ(failure.value(), @@ -252,8 +257,9 @@ TEST_F(FastPairHandshakeImplTest, ParseResponseError) { FastPairDevice device(kMetadataId, kProviderAddress, Protocol::kFastPairInitialPairing); CountDownLatch latch(1); + Mediums mediums; handshake_ = std::make_unique( - device, + device, mediums, [&](FastPairDevice& callback_device, std::optional failure) { EXPECT_EQ(&device, &callback_device); EXPECT_EQ(failure.value(), diff --git a/fastpair/handshake/fast_pair_handshake_lookup.cc b/fastpair/handshake/fast_pair_handshake_lookup.cc new file mode 100644 index 00000000..93dc075e --- /dev/null +++ b/fastpair/handshake/fast_pair_handshake_lookup.cc @@ -0,0 +1,90 @@ +// 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. + +#include "fastpair/handshake/fast_pair_handshake_lookup.h" + +#include +#include + +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" +#include "fastpair/handshake/fast_pair_handshake_impl.h" +#include "internal/platform/logging.h" + +namespace nearby { +namespace fastpair { + +FastPairHandshakeLookup* FastPairHandshakeLookup::instance_ = nullptr; +absl::Mutex FastPairHandshakeLookup::mutex_(absl::kConstInit); + +// static +FastPairHandshakeLookup* FastPairHandshakeLookup::GetInstance() { + absl::MutexLock lock(&mutex_); + if (!instance_) { + instance_ = new FastPairHandshakeLookup(); + } + return instance_; +} + +FastPairHandshake* FastPairHandshakeLookup::Get(FastPairDevice* device) { + absl::MutexLock lock(&mutex_); + auto it = fast_pair_handshakes_.find(device); + return it != fast_pair_handshakes_.end() ? it->second.get() : nullptr; +} + +FastPairHandshake* FastPairHandshakeLookup::Get(absl::string_view address) { + absl::MutexLock lock(&mutex_); + for (const auto& pair : fast_pair_handshakes_) { + if (pair.first->public_address() == address || + pair.first->GetBleAddress() == address) { + return pair.second.get(); + } + } + return nullptr; +} + +bool FastPairHandshakeLookup::Erase(FastPairDevice* device) { + absl::MutexLock lock(&mutex_); + return fast_pair_handshakes_.erase(device) == 1; +} + +bool FastPairHandshakeLookup::Erase(absl::string_view address) { + absl::MutexLock lock(&mutex_); + for (const auto& pair : fast_pair_handshakes_) { + if (pair.first->public_address() == address || + pair.first->GetBleAddress() == address) { + fast_pair_handshakes_.erase(pair.first); + return true; + } + } + return false; +} + +void FastPairHandshakeLookup::Clear() { + absl::MutexLock lock(&mutex_); + fast_pair_handshakes_.clear(); +} + +FastPairHandshake* FastPairHandshakeLookup::Create( + FastPairDevice& device, Mediums& mediums, OnCompleteCallback on_complete) { + absl::MutexLock lock(&mutex_); + auto it = fast_pair_handshakes_.emplace( + &device, std::make_unique(device, mediums, + std::move(on_complete))); + DCHECK(it.second); + return it.first->second.get(); +} + +} // namespace fastpair +} // namespace nearby diff --git a/fastpair/handshake/fast_pair_handshake_lookup.h b/fastpair/handshake/fast_pair_handshake_lookup.h new file mode 100644 index 00000000..2b459077 --- /dev/null +++ b/fastpair/handshake/fast_pair_handshake_lookup.h @@ -0,0 +1,87 @@ +// 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. + +#ifndef THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_FAST_PAIR_HANDSHAKE_LOOKUP_H_ +#define THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_FAST_PAIR_HANDSHAKE_LOOKUP_H_ + +#include +#include + +#include "absl/container/flat_hash_map.h" +#include "absl/functional/any_invocable.h" +#include "absl/synchronization/mutex.h" +#include "fastpair/common/fast_pair_device.h" +#include "fastpair/common/pair_failure.h" +#include "fastpair/handshake/fast_pair_handshake.h" +#include "fastpair/internal/mediums/mediums.h" + +namespace nearby { +namespace fastpair { + +// This Singletonclass creates, deletes and exposes FastPairHandshake instances. +class FastPairHandshakeLookup { + public: + using OnCompleteCallback = absl::AnyInvocable failure)>; + + // This is the static method that controls the access to the singleton + // instance. On the first run, it creates a singleton object and places it + // into the static field. On subsequent runs, it returns the existing object + // stored in the static field. + static FastPairHandshakeLookup* GetInstance(); + + // Singletons should not be cloneable. + FastPairHandshakeLookup(const FastPairHandshakeLookup&) = delete; + // Singletons should not be assignable. + FastPairHandshakeLookup& operator=(const FastPairHandshakeLookup&) = delete; + + // Get an existing instance for |FastPairdevice|. + FastPairHandshake* Get(FastPairDevice* device); + + // Get an existing instance for |address|. + FastPairHandshake* Get(absl::string_view address); + + // Erases the FastPairHandshake instance for |FastPairdevice| if it exists. + bool Erase(FastPairDevice* device); + + // Erases the FastPairHandshake instance for |FastPairdevice| if it exists. + bool Erase(absl::string_view address); + + // Deletes all existing FastPairHandshake instances. + void Clear(); + + // Creates and returns a new instance for |FastPairdevice| if no instance + // already exists. + // Returns the existing instance if there is one. + FastPairHandshake* Create(FastPairDevice& device, Mediums& mediums, + OnCompleteCallback on_complete); + + protected: + // Constructor/destructor of singleton object should not be public + // for which the destructor will never be called. + // and constructor will be invoked once from GetInstance() static method. + FastPairHandshakeLookup() = default; + ~FastPairHandshakeLookup() = default; + + private: + static absl::Mutex mutex_; + static FastPairHandshakeLookup* instance_ ABSL_GUARDED_BY(mutex_); + + absl::flat_hash_map> + fast_pair_handshakes_ ABSL_GUARDED_BY(mutex_); +}; +} // namespace fastpair +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_FAST_PAIR_HANDSHAKE_LOOKUP_H_ diff --git a/fastpair/handshake/fast_pair_handshake_lookup_test.cc b/fastpair/handshake/fast_pair_handshake_lookup_test.cc new file mode 100644 index 00000000..f95ab526 --- /dev/null +++ b/fastpair/handshake/fast_pair_handshake_lookup_test.cc @@ -0,0 +1,127 @@ +// 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. + +#include "fastpair/handshake/fast_pair_handshake_lookup.h" + +#include +#include + +#include "gtest/gtest.h" +#include "absl/strings/string_view.h" +#include "fastpair/common/fast_pair_device.h" +#include "fastpair/common/pair_failure.h" +#include "fastpair/common/protocol.h" +#include "fastpair/internal/mediums/mediums.h" +#include "internal/platform/count_down_latch.h" + +namespace nearby { +namespace fastpair { +namespace { +constexpr absl::string_view kValidModelId("718c17"); +constexpr absl::string_view kBLEAddress("ble_address"); +constexpr absl::string_view kPubliceAddress("public_address"); +class FastPairHandshakeLookupTest : public ::testing::Test { + public: + FastPairHandshakeLookupTest() { + device_ = new FastPairDevice(kValidModelId, kBLEAddress, + Protocol::kFastPairInitialPairing); + device_->set_public_address(kPubliceAddress); + } + + void CreateFastPairHandshkeInstanceForDevice(FastPairDevice& device) { + CountDownLatch latch(1); + Mediums mediums; + EXPECT_TRUE(FastPairHandshakeLookup::GetInstance()->Create( + device, mediums, + [&](FastPairDevice& cb_device, std::optional failure) { + EXPECT_EQ(&device, &cb_device); + EXPECT_EQ(failure, PairFailure::kCreateGattConnection); + latch.CountDown(); + })); + latch.Await(); + } + + FastPairDevice* device_ = nullptr; +}; + +TEST_F(FastPairHandshakeLookupTest, CreateFastPairHandshkeInstanceForDevice) { + EXPECT_FALSE(FastPairHandshakeLookup::GetInstance()->Get(device_)); + EXPECT_FALSE(FastPairHandshakeLookup::GetInstance()->Get(kBLEAddress)); + EXPECT_FALSE(FastPairHandshakeLookup::GetInstance()->Get(kPubliceAddress)); + + CreateFastPairHandshkeInstanceForDevice(*device_); + + // GetFastPairHandshakeWithDevicePtr + EXPECT_TRUE(FastPairHandshakeLookup::GetInstance()->Get(device_)); + // GetFastPairHandshakeWithBLEAddress + EXPECT_TRUE(FastPairHandshakeLookup::GetInstance()->Get(kBLEAddress)); + // GetFastPairHandshakeWithPublicAddress + EXPECT_TRUE(FastPairHandshakeLookup::GetInstance()->Get(kPubliceAddress)); + // GetFastPairHandshakeWithDevicePtr + EXPECT_TRUE(FastPairHandshakeLookup::GetInstance()->Get(device_)); + // GetFastPairHandshakeWithWrongAddress + EXPECT_FALSE(FastPairHandshakeLookup::GetInstance()->Get("")); +} + +TEST_F(FastPairHandshakeLookupTest, EraseFastPairHandshakeWithDevicePtr) { + CreateFastPairHandshkeInstanceForDevice(*device_); + + EXPECT_TRUE(FastPairHandshakeLookup::GetInstance()->Get(device_)); + + EXPECT_TRUE(FastPairHandshakeLookup::GetInstance()->Erase(device_)); + // Already Erased + EXPECT_FALSE(FastPairHandshakeLookup::GetInstance()->Get(device_)); + EXPECT_FALSE(FastPairHandshakeLookup::GetInstance()->Erase(kBLEAddress)); + EXPECT_FALSE(FastPairHandshakeLookup::GetInstance()->Erase(kPubliceAddress)); +} + +TEST_F(FastPairHandshakeLookupTest, EraseFastPairHandshakeWithBLEAddress) { + CreateFastPairHandshkeInstanceForDevice(*device_); + + EXPECT_TRUE(FastPairHandshakeLookup::GetInstance()->Get(device_)); + // Erase Wrong Address + EXPECT_FALSE(FastPairHandshakeLookup::GetInstance()->Erase("")); + + EXPECT_TRUE(FastPairHandshakeLookup::GetInstance()->Erase(kBLEAddress)); + // Already Erased + EXPECT_FALSE(FastPairHandshakeLookup::GetInstance()->Get(device_)); + EXPECT_FALSE(FastPairHandshakeLookup::GetInstance()->Erase(device_)); + EXPECT_FALSE(FastPairHandshakeLookup::GetInstance()->Erase(kPubliceAddress)); +} + +TEST_F(FastPairHandshakeLookupTest, EraseFastPairHandshakeWithPublicAddress) { + CreateFastPairHandshkeInstanceForDevice(*device_); + + EXPECT_TRUE(FastPairHandshakeLookup::GetInstance()->Get(device_)); + + EXPECT_TRUE(FastPairHandshakeLookup::GetInstance()->Erase(kPubliceAddress)); + // Already Erased + EXPECT_FALSE(FastPairHandshakeLookup::GetInstance()->Get(device_)); + EXPECT_FALSE(FastPairHandshakeLookup::GetInstance()->Erase(device_)); + EXPECT_FALSE(FastPairHandshakeLookup::GetInstance()->Erase(kBLEAddress)); +} + +TEST_F(FastPairHandshakeLookupTest, ClearAllFastPairHandshakeInstances) { + CreateFastPairHandshkeInstanceForDevice(*device_); + + EXPECT_TRUE(FastPairHandshakeLookup::GetInstance()->Get(device_)); + + FastPairHandshakeLookup::GetInstance()->Clear(); + + EXPECT_FALSE(FastPairHandshakeLookup::GetInstance()->Get(device_)); +} + +} // namespace +} // namespace fastpair +} // namespace nearby diff --git a/fastpair/internal/ble/BUILD b/fastpair/internal/mediums/BUILD similarity index 55% rename from fastpair/internal/ble/BUILD rename to fastpair/internal/mediums/BUILD index 2fe38e54..22bcdd14 100644 --- a/fastpair/internal/ble/BUILD +++ b/fastpair/internal/mediums/BUILD @@ -15,12 +15,17 @@ licenses(["notice"]) cc_library( - name = "ble", + name = "mediums", srcs = [ "ble.cc", + "ble_v2.cc", + "bluetooth_radio.cc", ], hdrs = [ "ble.h", + "ble_v2.h", + "bluetooth_radio.h", + "mediums.h", ], visibility = [ "//fastpair:__subpackages__", @@ -30,13 +35,41 @@ cc_library( "//internal/platform:comm", "//internal/platform:logging", "//internal/platform:types", - "@com_google_absl//absl/container:flat_hash_map", - "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/strings", "@com_google_absl//absl/time", ], ) +cc_test( + name = "bluetooth_radio_test", + size = "small", + srcs = [ + "bluetooth_radio_test.cc", + ], + shard_count = 16, + deps = [ + ":mediums", + "//internal/platform/implementation/g3", # build_cleaner: keep + "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "mediums_test", + size = "small", + srcs = [ + "mediums_test.cc", + ], + shard_count = 16, + deps = [ + ":mediums", + "//internal/platform/implementation/g3", # build_cleaner: keep + "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_googletest//:gtest_main", + ], +) + cc_test( name = "ble_test", size = "small", @@ -45,7 +78,7 @@ cc_test( ], shard_count = 16, deps = [ - ":ble", + ":mediums", "//internal/platform:base", "//internal/platform:comm", "//internal/platform:test_util", @@ -57,3 +90,18 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "ble_v2_test", + size = "small", + srcs = [ + "ble_v2_test.cc", + ], + shard_count = 16, + deps = [ + ":mediums", + "//internal/platform/implementation/g3", # build_cleaner: keep + "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/fastpair/internal/ble/ble.cc b/fastpair/internal/mediums/ble.cc similarity index 64% rename from fastpair/internal/ble/ble.cc rename to fastpair/internal/mediums/ble.cc index 7edfd25e..14c790da 100644 --- a/fastpair/internal/ble/ble.cc +++ b/fastpair/internal/mediums/ble.cc @@ -12,87 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "fastpair/internal/ble/ble.h" +#include "fastpair/internal/mediums/ble.h" -#include #include #include -#include "internal/platform/ble_v2.h" +#include "fastpair/internal/mediums/bluetooth_radio.h" #include "internal/platform/bluetooth_adapter.h" #include "internal/platform/logging.h" +#include "internal/platform/mutex_lock.h" namespace nearby { namespace fastpair { -namespace { -// A stub BlePeripheral implementation. -class BlePeripheralStub : public api::ble_v2::BlePeripheral { - public: - explicit BlePeripheralStub(absl::string_view ble_address) { - ble_address_ = std::string(ble_address); - } - - std::string GetAddress() const override { return ble_address_; } - - private: - std::string ble_address_; -}; -} // namespace - -Ble::~Ble() { - // We never enabled Bluetooth, nothing to do. - if (!ever_saved_state_.Get()) { - NEARBY_LOG(INFO, "BT adapter was not used. Not touching HW."); - return; - } - - NEARBY_LOG(INFO, "Bring BT adapter to original state"); - if (!SetBluetoothState(originally_enabled_.Get())) { - NEARBY_LOG(INFO, "Failed to restore BT adapter original state."); - } -} - -bool Ble::Enable() { - if (!SaveOriginalState()) { - return false; - } - - return SetBluetoothState(true); -} - -bool Ble::Disable() { - if (!SaveOriginalState()) { - return false; - } - - return SetBluetoothState(false); -} - -bool Ble::IsEnabled() const { - return IsAdapterValid() && IsInDesiredState(true); -} - -bool Ble::SetBluetoothState(bool enable) { - return adapter_.SetStatus(enable ? BluetoothAdapter::Status::kEnabled - : BluetoothAdapter::Status::kDisabled); -} - -bool Ble::IsInDesiredState(bool should_be_enabled) const { - return adapter_.IsEnabled() == should_be_enabled; -} - -bool Ble::SaveOriginalState() { - if (!IsAdapterValid()) { - return false; - } - - // If we haven't saved the original state of the radio, save it. - if (!ever_saved_state_.Set(true)) { - originally_enabled_.Set(adapter_.IsEnabled()); - } - - return true; -} +Ble::Ble(BluetoothRadio& radio) : radio_(radio) {} bool Ble::IsAvailable() const { MutexLock lock(&mutex_); @@ -122,7 +54,7 @@ bool Ble::StartScanning(const std::string& service_id, return false; } - if (!IsEnabled()) { + if (!radio_.IsEnabled()) { NEARBY_LOGS(INFO) << "Can't start BLE scanning because Bluetooth was NOT enabled"; return false; @@ -183,18 +115,9 @@ bool Ble::StopScanning(const std::string& service_id) { return ret; } -std::unique_ptr Ble::ConnectToGattServer( - absl::string_view ble_address) { - MutexLock lock(&mutex_); - auto v2_peripheral = std::make_unique(ble_address); - return v2_medium_.ConnectToGattServer(BleV2Peripheral(v2_peripheral.get()), - api::ble_v2::TxPowerLevel::kUnknown, - {}); -} - bool Ble::IsScanning() { + NEARBY_LOGS(INFO) << __func__; MutexLock lock(&mutex_); - return IsScanningLocked(); } diff --git a/fastpair/internal/ble/ble.h b/fastpair/internal/mediums/ble.h similarity index 50% rename from fastpair/internal/ble/ble.h rename to fastpair/internal/mediums/ble.h index b1710bdf..c6923384 100644 --- a/fastpair/internal/ble/ble.h +++ b/fastpair/internal/mediums/ble.h @@ -15,59 +15,30 @@ #ifndef THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_BLE_BLE_H_ #define THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_BLE_BLE_H_ -#include -#include #include -#include "absl/container/flat_hash_map.h" -#include "absl/container/flat_hash_set.h" -#include "internal/platform/atomic_boolean.h" +#include "fastpair/internal/mediums/bluetooth_radio.h" #include "internal/platform/ble.h" -#include "internal/platform/ble_v2.h" #include "internal/platform/bluetooth_adapter.h" -#include "internal/platform/multi_thread_executor.h" #include "internal/platform/mutex.h" namespace nearby { namespace fastpair { +// Provides the operations that can be performed on the Bluetooth Low Energy +// (BLE) medium. class Ble { public: using DiscoveredPeripheralCallback = BleMedium::DiscoveredPeripheralCallback; - Ble() = default; - Ble(Ble&&) = default; - Ble& operator=(Ble&&) = default; + explicit Ble(BluetoothRadio& bluetooth_radio); + Ble(Ble&&) = delete; + Ble& operator=(Ble&&) = delete; + ~Ble() = default; - // Reverts the Ble to its original state. - ~Ble(); - - // Enables Bluetooth. Returns true if enabled successfully. - // This must be called before attempting to invoke any other methods of - // this class. - bool Enable(); - - // Disables Bluetooth. Returns true if disabled successfully. - bool Disable(); - - // Returns true if the Bluetooth radio is currently enabled. - bool IsEnabled() const; - - // Returns true if Ble communications are supported by a platform. + // Returns true, if Ble communications are supported by a platform. bool IsAvailable() const ABSL_LOCKS_EXCLUDED(mutex_); - // Returns true if this object owns a valid platform implementation. - bool IsMediumValid() const ABSL_LOCKS_EXCLUDED(mutex_) { - MutexLock lock(&mutex_); - return medium_.IsValid(); - } - - // Returns true if this object has a valid BluetoothAdapter reference. - bool IsAdapterValid() const { return adapter_.IsValid(); } - - // Return true if Ble is currenlty scanning. - bool IsScanning() ABSL_LOCKS_EXCLUDED(mutex_); - // Enables Ble scanning mode. Will report any discoverable peripherals in // range through a callback. Returns true, if scanning mode was enabled, // false otherwise. @@ -76,51 +47,29 @@ class Ble { DiscoveredPeripheralCallback callback) ABSL_LOCKS_EXCLUDED(mutex_); - // Returns a new GattClient connection to a gatt server. - std::unique_ptr ConnectToGattServer( - absl::string_view ble_address); - // Disables Ble discovery mode. bool StopScanning(const std::string& service_id) ABSL_LOCKS_EXCLUDED(mutex_); + // Return true if Ble is currenlty scanning. + bool IsScanning() ABSL_LOCKS_EXCLUDED(mutex_); + // Return BleMedium BleMedium& getMedium() { return medium_; } - // Return BluetoothAdapter - BluetoothAdapter& GetBluetoothAdapter() { return adapter_; } - private: - mutable Mutex mutex_; - // BluetoothAdapter::IsValid() will return false if BT is not supported. - BluetoothAdapter adapter_; - DiscoveredPeripheralCallback discovered_peripheral_callback_; - BleMedium medium_ ABSL_GUARDED_BY(mutex_){adapter_}; - BleV2Medium v2_medium_ ABSL_GUARDED_BY(mutex_){adapter_}; - bool is_scanning_ = false; - // Same as IsAvailable(), but must be called with mutex_ held. bool IsAvailableLocked() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); // Same as IsDiscovering(), but must be called with mutex_ held. bool IsScanningLocked() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - bool SetBluetoothState(bool enable); - - bool IsInDesiredState(bool should_be_enabled) const; - - // To be called in enable() and disable(). This will remember the - // original state of the ble before any ble state has been modified. - // Returns false if Bluetooth doesn't exist on the device and the state cannot - // be obtained. - bool SaveOriginalState(); - - // The Ble's original state, before we modified it. True if - // originally enabled, false if originally disabled. - // We restore the radio to its original state in the destructor. - AtomicBoolean originally_enabled_{false}; - - // false if we never modified the radio state, true otherwise. - AtomicBoolean ever_saved_state_{false}; + mutable Mutex mutex_; + DiscoveredPeripheralCallback discovered_peripheral_callback_; + BluetoothRadio& radio_ ABSL_GUARDED_BY(mutex_); + BluetoothAdapter& adapter_ ABSL_GUARDED_BY(mutex_){ + radio_.GetBluetoothAdapter()}; + BleMedium medium_ ABSL_GUARDED_BY(mutex_){adapter_}; + bool is_scanning_ = false; }; } // namespace fastpair diff --git a/fastpair/internal/ble/ble_test.cc b/fastpair/internal/mediums/ble_test.cc similarity index 59% rename from fastpair/internal/ble/ble_test.cc rename to fastpair/internal/mediums/ble_test.cc index f4d7c0d6..9fb93fd1 100644 --- a/fastpair/internal/ble/ble_test.cc +++ b/fastpair/internal/mediums/ble_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// 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. @@ -12,13 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "fastpair/internal/ble/ble.h" +#include "fastpair/internal/mediums/ble.h" #include #include "gtest/gtest.h" -#include "internal/platform/ble.h" -#include "internal/platform/bluetooth_adapter.h" #include "internal/platform/count_down_latch.h" #include "internal/platform/medium_environment.h" @@ -26,64 +24,27 @@ namespace nearby { namespace fastpair { namespace { -using FeatureFlags = FeatureFlags::Flags; - +using DiscoveredPeripheralCallback = BleMedium::DiscoveredPeripheralCallback; constexpr absl::Duration kWaitDuration = absl::Milliseconds(1000); constexpr absl::string_view kServiceID{"com.google.location.nearby.apps.test"}; constexpr absl::string_view kAdvertisementString{"96C12E"}; constexpr absl::string_view kFastPairServiceUuid{"\x2c\xfe"}; -class BleTest : public ::testing::TestWithParam { - protected: - using DiscoveredPeripheralCallback = BleMedium::DiscoveredPeripheralCallback; - - MediumEnvironment& env_{MediumEnvironment::Instance()}; -}; - -TEST_F(BleTest, ConstructorDestructorWorks) { - Ble ble; - EXPECT_TRUE(ble.IsAdapterValid()); +TEST(BleTest, ConstructorDestructorWorks) { + BluetoothRadio radio; + Ble ble(radio); + EXPECT_TRUE(ble.IsAvailable()); + EXPECT_FALSE(ble.IsScanning()); } -TEST_F(BleTest, CanEnable) { - Ble ble; - EXPECT_TRUE(ble.IsAdapterValid()); - EXPECT_TRUE(ble.IsEnabled()); - EXPECT_TRUE(ble.Disable()); - EXPECT_FALSE(ble.IsEnabled()); - EXPECT_TRUE(ble.Enable()); - EXPECT_TRUE(ble.IsEnabled()); -} - -TEST_F(BleTest, CanDisable) { - Ble ble; - EXPECT_TRUE(ble.IsAdapterValid()); - EXPECT_TRUE(ble.IsEnabled()); - EXPECT_TRUE(ble.Disable()); - EXPECT_FALSE(ble.IsEnabled()); -} - -TEST_F(BleTest, CanConstructValidObject) { - env_.Start(); - Ble ble_a; - Ble ble_b; - - EXPECT_TRUE(ble_a.IsMediumValid()); - EXPECT_TRUE(ble_a.IsAdapterValid()); - EXPECT_TRUE(ble_a.IsAvailable()); - EXPECT_TRUE(ble_b.IsMediumValid()); - EXPECT_TRUE(ble_b.IsAdapterValid()); - EXPECT_TRUE(ble_b.IsAvailable()); - EXPECT_NE(&ble_a.GetBluetoothAdapter(), &ble_b.GetBluetoothAdapter()); - env_.Stop(); -} - -TEST_F(BleTest, CanStartDiscovery) { - env_.Start(); - Ble ble_a; - Ble ble_b; - ble_a.Enable(); - ble_b.Enable(); +TEST(BleTest, CanStartDiscovery) { + MediumEnvironment::Instance().Start(); + BluetoothRadio radio_a; + BluetoothRadio radio_b; + Ble ble_a{radio_a}; + Ble ble_b{radio_b}; + radio_a.Enable(); + radio_b.Enable(); std::string service_id(kServiceID); ByteArray advertisement_bytes{std::string(kAdvertisementString)}; std::string fast_pair_service_uuid(kFastPairServiceUuid); @@ -112,16 +73,8 @@ TEST_F(BleTest, CanStartDiscovery) { EXPECT_TRUE(lost_latch.Await(kWaitDuration).result()); EXPECT_TRUE(ble_a.StopScanning(service_id)); EXPECT_FALSE(ble_a.IsScanning()); - env_.Stop(); + MediumEnvironment::Instance().Stop(); } - -TEST_F(BleTest, CannConnectToGattServer) { - env_.Start(); - Ble ble; - EXPECT_NE(ble.ConnectToGattServer("bleaddress"), nullptr); - env_.Stop(); -} - } // namespace } // namespace fastpair } // namespace nearby diff --git a/fastpair/internal/mediums/ble_v2.cc b/fastpair/internal/mediums/ble_v2.cc new file mode 100644 index 00000000..0fdc0c14 --- /dev/null +++ b/fastpair/internal/mediums/ble_v2.cc @@ -0,0 +1,69 @@ +// 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. + +#include "fastpair/internal/mediums/ble_v2.h" + +#include +#include + +#include "internal/platform/mutex_lock.h" + +namespace nearby { +namespace fastpair { +namespace { +// A stub BlePeripheral implementation. +class BlePeripheralStub : public api::ble_v2::BlePeripheral { + public: + explicit BlePeripheralStub(absl::string_view ble_address) { + ble_address_ = std::string(ble_address); + } + + std::string GetAddress() const override { return ble_address_; } + + private: + std::string ble_address_; +}; +} // namespace + +BleV2::BleV2(BluetoothRadio& radio) : radio_(radio) {} + +bool BleV2::IsAvailable() const { + MutexLock lock(&mutex_); + return IsAvailableLocked(); +} + +bool BleV2::IsAvailableLocked() const { + return medium_.IsValid() && adapter_.IsValid() && adapter_.IsEnabled(); +} + +std::unique_ptr BleV2::ConnectToGattServer( + absl::string_view ble_address) { + MutexLock lock(&mutex_); + if (!radio_.IsEnabled()) { + NEARBY_LOGS(INFO) + << "Can't connect to GattServer because Bluetooth was NOT enabled"; + return nullptr; + } + if (!IsAvailableLocked()) { + NEARBY_LOGS(VERBOSE) + << __func__ + << "Can't connect to GattServer because BleV2 isn't available."; + return nullptr; + } + auto v2_peripheral = std::make_unique(ble_address); + return medium_.ConnectToGattServer(BleV2Peripheral(v2_peripheral.get()), + api::ble_v2::TxPowerLevel::kUnknown, {}); +} +} // namespace fastpair +} // namespace nearby diff --git a/fastpair/internal/mediums/ble_v2.h b/fastpair/internal/mediums/ble_v2.h new file mode 100644 index 00000000..5a79e201 --- /dev/null +++ b/fastpair/internal/mediums/ble_v2.h @@ -0,0 +1,56 @@ +// 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. + +#ifndef THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_MEDIUMS_BLE_V2_H_ +#define THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_MEDIUMS_BLE_V2_H_ + +#include + +#include "fastpair/internal/mediums/bluetooth_radio.h" +#include "internal/platform/ble_v2.h" +#include "internal/platform/mutex.h" +#include "internal/platform/mutex_lock.h" + +namespace nearby { +namespace fastpair { + +// Provides the operations that can be performed on the Bluetooth Low Energy +// (BLE_V2) medium. +class BleV2 { + public: + explicit BleV2(BluetoothRadio& bluetooth_radio); + ~BleV2() = default; + + // Returns true, if BleV2 communications are supported by a platform. + bool IsAvailable() const ABSL_LOCKS_EXCLUDED(mutex_); + + // Returns a new GattClient connection to a gatt server. + std::unique_ptr ConnectToGattServer( + absl::string_view ble_address); + + private: + // Same as IsAvailable(), but must be called with mutex_ held. + bool IsAvailableLocked() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + mutable Mutex mutex_; + BluetoothRadio& radio_ ABSL_GUARDED_BY(mutex_); + BluetoothAdapter& adapter_ ABSL_GUARDED_BY(mutex_){ + radio_.GetBluetoothAdapter()}; + BleV2Medium medium_ ABSL_GUARDED_BY(mutex_){adapter_}; +}; + +} // namespace fastpair +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_MEDIUMS_BLE_V2_H_ diff --git a/fastpair/internal/mediums/ble_v2_test.cc b/fastpair/internal/mediums/ble_v2_test.cc new file mode 100644 index 00000000..26e9e52c --- /dev/null +++ b/fastpair/internal/mediums/ble_v2_test.cc @@ -0,0 +1,46 @@ +// 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. + +#include "fastpair/internal/mediums/ble_v2.h" + +#include "gtest/gtest.h" + +namespace nearby { +namespace fastpair { +namespace { + +TEST(BleV2Test, IsAvailable) { + BluetoothRadio radio; + BleV2 bleV2(radio); + EXPECT_TRUE(bleV2.IsAvailable()); + EXPECT_TRUE(radio.Disable()); + EXPECT_FALSE(bleV2.IsAvailable()); +} + +TEST(BleV2Test, CanConnectToGattServer) { + BluetoothRadio radio; + BleV2 bleV2(radio); + EXPECT_TRUE(bleV2.ConnectToGattServer("bleaddress")); +} + +TEST(BleV2Test, CannotConnectToGattServer) { + BluetoothRadio radio; + BleV2 bleV2(radio); + EXPECT_TRUE(radio.Disable()); + EXPECT_FALSE(bleV2.ConnectToGattServer("bleaddress")); +} + +} // namespace +} // namespace fastpair +} // namespace nearby diff --git a/fastpair/internal/mediums/bluetooth_radio.cc b/fastpair/internal/mediums/bluetooth_radio.cc new file mode 100644 index 00000000..b22895f4 --- /dev/null +++ b/fastpair/internal/mediums/bluetooth_radio.cc @@ -0,0 +1,86 @@ +// 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. + +#include "fastpair/internal/mediums/bluetooth_radio.h" + +#include "internal/platform/logging.h" + +namespace nearby { +namespace fastpair { + +BluetoothRadio::BluetoothRadio() { + if (!IsAdapterValid()) { + NEARBY_LOGS(ERROR) << "Bluetooth adapter is not valid: BT is not supported"; + } +} + +BluetoothRadio::~BluetoothRadio() { + // We never enabled Bluetooth, nothing to do. + if (!ever_saved_state_.Get()) { + NEARBY_LOGS(INFO) << "BT adapter was not used. Not touching HW."; + return; + } + + NEARBY_LOG(INFO, "Bring BT adapter to original state"); + if (!SetBluetoothState(originally_enabled_.Get())) { + NEARBY_LOGS(INFO) << "Failed to restore BT adapter original state."; + } +} + +bool BluetoothRadio::Enable() { + NEARBY_LOGS(INFO) << __func__; + if (!SaveOriginalState()) { + return false; + } + + return SetBluetoothState(true); +} + +bool BluetoothRadio::Disable() { + if (!SaveOriginalState()) { + return false; + } + + return SetBluetoothState(false); +} + +bool BluetoothRadio::IsEnabled() const { + return IsAdapterValid() && IsInDesiredState(true); +} + +bool BluetoothRadio::SetBluetoothState(bool enable) { + return bluetooth_adapter_.SetStatus( + enable ? BluetoothAdapter::Status::kEnabled + : BluetoothAdapter::Status::kDisabled); +} + +bool BluetoothRadio::IsInDesiredState(bool should_be_enabled) const { + return bluetooth_adapter_.IsEnabled() == should_be_enabled; +} + +bool BluetoothRadio::SaveOriginalState() { + if (!IsAdapterValid()) { + return false; + } + + // If we haven't saved the original state of the radio, save it. + if (!ever_saved_state_.Set(true)) { + originally_enabled_.Set(bluetooth_adapter_.IsEnabled()); + } + + return true; +} + +} // namespace fastpair +} // namespace nearby diff --git a/fastpair/internal/mediums/bluetooth_radio.h b/fastpair/internal/mediums/bluetooth_radio.h new file mode 100644 index 00000000..135d3731 --- /dev/null +++ b/fastpair/internal/mediums/bluetooth_radio.h @@ -0,0 +1,79 @@ +// 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. + +#ifndef THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_MEDIUMS_BLUETOOTH_RADIO_H_ +#define THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_MEDIUMS_BLUETOOTH_RADIO_H_ + +#include "internal/platform/atomic_boolean.h" +#include "internal/platform/bluetooth_adapter.h" + +namespace nearby { +namespace fastpair { + +// Provides the operations that can be performed on the Bluetooth radio. +class BluetoothRadio { + public: + BluetoothRadio(); + BluetoothRadio(BluetoothRadio&&) = default; + BluetoothRadio& operator=(BluetoothRadio&&) = default; + + // Reverts the Bluetooth radio to its original state. + ~BluetoothRadio(); + + // Enables Bluetooth. + // + // This must be called before attempting to invoke any other methods of + // this class. + // + // Returns true if enabled successfully. + bool Enable(); + + // Disables Bluetooth. + // + // Returns true if disabled successfully. + bool Disable(); + + // Returns true if the Bluetooth radio is currently enabled. + bool IsEnabled() const; + + // Returns result of BluetoothAdapter::IsValid() for private adapter instance. + bool IsAdapterValid() const { return bluetooth_adapter_.IsValid(); } + + BluetoothAdapter& GetBluetoothAdapter() { return bluetooth_adapter_; } + + private: + bool SetBluetoothState(bool enable); + bool IsInDesiredState(bool should_be_enabled) const; + // To be called in enable() and disable(). This will remember the + // original state of the radio before any radio state has been modified. + // Returns false if Bluetooth doesn't exist on the device and the state cannot + // be obtained. + bool SaveOriginalState(); + + // BluetoothAdapter::IsValid() will return false if BT is not supported. + BluetoothAdapter bluetooth_adapter_; + + // The Bluetooth radio's original state, before we modified it. True if + // originally enabled, false if originally disabled. + // We restore the radio to its original state in the destructor. + + AtomicBoolean originally_enabled_{false}; + // false if we never modified the radio state, true otherwise. + AtomicBoolean ever_saved_state_{false}; +}; + +} // namespace fastpair +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_MEDIUMS_BLUETOOTH_RADIO_H_ diff --git a/fastpair/internal/mediums/bluetooth_radio_test.cc b/fastpair/internal/mediums/bluetooth_radio_test.cc new file mode 100644 index 00000000..834aca8e --- /dev/null +++ b/fastpair/internal/mediums/bluetooth_radio_test.cc @@ -0,0 +1,48 @@ +// 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. + +#include "fastpair/internal/mediums/bluetooth_radio.h" + +#include "gtest/gtest.h" + +namespace nearby { +namespace fastpair { +namespace { + +TEST(BluetoothRadioTest, ConstructorDestructorWorks) { + BluetoothRadio radio; + EXPECT_TRUE(radio.IsAdapterValid()); +} + +TEST(BluetoothRadioTest, CanEnable) { + BluetoothRadio radio; + EXPECT_TRUE(radio.IsAdapterValid()); + EXPECT_TRUE(radio.IsEnabled()); + EXPECT_TRUE(radio.Disable()); + EXPECT_FALSE(radio.IsEnabled()); + EXPECT_TRUE(radio.Enable()); + EXPECT_TRUE(radio.IsEnabled()); +} + +TEST(BluetoothRadioTest, CanDisable) { + BluetoothRadio radio; + EXPECT_TRUE(radio.IsAdapterValid()); + EXPECT_TRUE(radio.IsEnabled()); + EXPECT_TRUE(radio.Disable()); + EXPECT_FALSE(radio.IsEnabled()); +} + +} // namespace +} // namespace fastpair +} // namespace nearby diff --git a/fastpair/internal/mediums/mediums.h b/fastpair/internal/mediums/mediums.h new file mode 100644 index 00000000..cf3f21a9 --- /dev/null +++ b/fastpair/internal/mediums/mediums.h @@ -0,0 +1,56 @@ +// 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. + +#ifndef THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_MEDIUMS_MEDIUMS_H_ +#define THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_MEDIUMS_MEDIUMS_H_ + +#include "fastpair/internal/mediums/ble.h" +#include "fastpair/internal/mediums/ble_v2.h" +#include "fastpair/internal/mediums/bluetooth_radio.h" + +namespace nearby { +namespace fastpair { + +// Facilitates convenient and reliable usage of various wireless mediums. +class Mediums { + public: + Mediums() = default; + ~Mediums() = default; + + // Returns a handle to the Bluetooth radio. + BluetoothRadio& GetBluetoothRadio() { return bluetooth_radio_; } + + // Returns a handle to the Ble medium. + Ble& GetBle() { return ble_; } + + // Returns a handle to the Ble medium. + BleV2& GetBleV2() { return ble_v2_; } + + private: + // The order of declaration is critical for both construction and + // destruction. + // + // 1) Construction: The individual mediums have a dependency on the + // corresponding radio, so the radio must be initialized first. + // + // 2) Destruction: The individual mediums should be shut down before the + // corresponding radio. + BluetoothRadio bluetooth_radio_; + Ble ble_{bluetooth_radio_}; + BleV2 ble_v2_{bluetooth_radio_}; +}; + +} // namespace fastpair +} // namespace nearby +#endif // THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_MEDIUMS_MEDIUMS_H_ diff --git a/fastpair/internal/mediums/mediums_test.cc b/fastpair/internal/mediums/mediums_test.cc new file mode 100644 index 00000000..2746abb3 --- /dev/null +++ b/fastpair/internal/mediums/mediums_test.cc @@ -0,0 +1,31 @@ +// 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. + +#include "fastpair/internal/mediums/mediums.h" + +#include "gtest/gtest.h" + +namespace nearby { +namespace fastpair { +namespace { + +TEST(MediumTest, ConstructorWorks) { + Mediums medium; + EXPECT_TRUE(medium.GetBluetoothRadio().IsAdapterValid()); + EXPECT_FALSE(medium.GetBle().IsScanning()); + EXPECT_TRUE(medium.GetBleV2().ConnectToGattServer("ble_address")); +} +} // namespace +} // namespace fastpair +} // namespace nearby diff --git a/fastpair/internal/test/BUILD b/fastpair/internal/test/BUILD index 3ea07c91..c7413fad 100644 --- a/fastpair/internal/test/BUILD +++ b/fastpair/internal/test/BUILD @@ -14,8 +14,6 @@ cc_library( deps = [ "//internal/network:types", "@com_google_absl//absl/functional:any_invocable", - "@com_google_absl//absl/status", - "@com_google_absl//absl/status:statusor", ], ) @@ -32,7 +30,6 @@ cc_test( deps = [ ":nearby_fastpair_test", "//internal/network:types", - "//internal/platform/implementation/g3", "@com_github_protobuf_matchers//protobuf-matchers", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/functional:any_invocable", diff --git a/fastpair/internal/test/fast_pair_fake_http_client.h b/fastpair/internal/test/fast_pair_fake_http_client.h index c5baccb6..84824a3a 100644 --- a/fastpair/internal/test/fast_pair_fake_http_client.h +++ b/fastpair/internal/test/fast_pair_fake_http_client.h @@ -21,7 +21,6 @@ #include #include -#include "absl/status/statusor.h" #include "internal/network/http_client.h" namespace nearby { @@ -52,11 +51,6 @@ class FastPairFakeHttpClient : public HttpClient { request_infos_.push_back(std::move(request_info)); } - absl::StatusOr GetResponse( - const HttpRequest& request) override { - return absl::UnimplementedError("unimplemented"); - } - // Mock methods void CompleteRequest(const absl::StatusOr& response, size_t pos = 0) { diff --git a/fastpair/keyed_service/fast_pair_mediator.cc b/fastpair/keyed_service/fast_pair_mediator.cc index d72d01c1..fee1b2c9 100644 --- a/fastpair/keyed_service/fast_pair_mediator.cc +++ b/fastpair/keyed_service/fast_pair_mediator.cc @@ -29,12 +29,12 @@ Mediator::Mediator(std::unique_ptr scanner_broker, scanner_broker_->AddObserver(this); } -void Mediator::OnDeviceFound(const FastPairDevice& device) { +void Mediator::OnDeviceFound(FastPairDevice& device) { NEARBY_LOGS(INFO) << __func__ << ": " << device; // Show discovery notification } -void Mediator::OnDeviceLost(const FastPairDevice& device) { +void Mediator::OnDeviceLost(FastPairDevice& device) { NEARBY_LOGS(INFO) << __func__ << ": " << device; } diff --git a/fastpair/keyed_service/fast_pair_mediator.h b/fastpair/keyed_service/fast_pair_mediator.h index c59857a1..499b6ea2 100644 --- a/fastpair/keyed_service/fast_pair_mediator.h +++ b/fastpair/keyed_service/fast_pair_mediator.h @@ -33,8 +33,8 @@ class Mediator final : public ScannerBroker::Observer { ~Mediator() override = default; // ScannerBroker::Observer - void OnDeviceFound(const FastPairDevice& device) override; - void OnDeviceLost(const FastPairDevice& device) override; + void OnDeviceFound(FastPairDevice& device) override; + void OnDeviceLost(FastPairDevice& device) override; void StartScanning(); diff --git a/fastpair/repository/BUILD b/fastpair/repository/BUILD index 5467d57b..6bfb3386 100644 --- a/fastpair/repository/BUILD +++ b/fastpair/repository/BUILD @@ -16,10 +16,7 @@ cc_library( copts = [ "-Ithird_party", ], - visibility = [ - "//fastpair:__subpackages__", - "//location/nearby/cpp/sharing:__subpackages__", - ], + visibility = ["//fastpair:__subpackages__"], deps = [ "//fastpair/common", "//fastpair/proto:fastpair_cc_proto", diff --git a/fastpair/repository/fast_pair_metadata_fetcher_impl_test.cc b/fastpair/repository/fast_pair_metadata_fetcher_impl_test.cc index ba76880c..43e0381b 100644 --- a/fastpair/repository/fast_pair_metadata_fetcher_impl_test.cc +++ b/fastpair/repository/fast_pair_metadata_fetcher_impl_test.cc @@ -26,7 +26,6 @@ #include "absl/status/statusor.h" #include "absl/strings/string_view.h" #include "fastpair/internal/test/fast_pair_fake_http_client.h" -#include "internal/network/http_client.h" namespace nearby { namespace fastpair { @@ -41,11 +40,6 @@ constexpr char kRequestUrl[] = "https://googleapis.com/nearbysharing/test"; constexpr char kQueryParameterAlternateOutputKey[] = "alt"; constexpr char kQueryParameterAlternateOutputProto[] = "proto"; -class MockHttpClient : public network::HttpClient { - public: - ~MockHttpClient() override = default; -}; - const FastPairMetadataFetcher::QueryParameters& GetTestRequestProtoAsQueryParameters() { static const FastPairMetadataFetcher::QueryParameters* @@ -130,11 +124,9 @@ class FastPairMetadataFetcherImplTest : public ::testing::Test { void CheckFastPairRepositoryGetUnauthRequest( const FastPairMetadataFetcher::QueryParameters& request_as_query_parameters) { - FastPairFakeHttpClient* fake_http_client = - reinterpret_cast(http_client_.get()); - EXPECT_EQ(fake_http_client->GetPendingRequest().size(), 1); + EXPECT_EQ(http_client_->GetPendingRequest().size(), 1); const network::HttpRequest& request = - fake_http_client->GetPendingRequest()[0].request; + http_client_->GetPendingRequest()[0].request; CheckPlatformTypeHeader(request.GetAllHeaders()); @@ -154,16 +146,14 @@ class FastPairMetadataFetcherImplTest : public ::testing::Test { int error, std::optional response_code = std::nullopt, const std::optional& response_string = std::nullopt) { - FastPairFakeHttpClient* client = - reinterpret_cast(http_client_.get()); - client->CompleteRequest(error, response_code, response_string); + http_client_->CompleteRequest(error, response_code, response_string); EXPECT_TRUE(result_ || network_error_); } std::unique_ptr result_; std::unique_ptr network_error_; private: - std::unique_ptr http_client_; + std::unique_ptr http_client_; FastPairMetadataFetcherImpl flow_{api::DeviceInfo::OsType::kChromeOs}; }; diff --git a/fastpair/scanning/BUILD b/fastpair/scanning/BUILD index bca6d0c3..e1dc5370 100644 --- a/fastpair/scanning/BUILD +++ b/fastpair/scanning/BUILD @@ -29,7 +29,7 @@ cc_library( ], deps = [ "//fastpair/common", - "//fastpair/internal/ble", + "//fastpair/internal/mediums", "//fastpair/scanning/fastpair:scanning", "//internal/base", "//internal/platform:base", @@ -68,14 +68,15 @@ cc_test( deps = [ ":scanner", "//fastpair/common", - "//fastpair/scanning/fastpair:scanning", - "//fastpair/scanning/fastpair:test_support", + "//fastpair/internal/mediums", + "//fastpair/proto:fastpair_cc_proto", + "//fastpair/server_access:test_support", + "//internal/platform:base", "//internal/platform:test_util", - "//internal/platform/implementation:types", + "//internal/platform:types", "//internal/platform/implementation/g3", # build_cleaner: keep "@com_github_protobuf_matchers//protobuf-matchers", "@com_google_absl//absl/strings", - "@com_google_absl//absl/time", "@com_google_googletest//:gtest_main", ], ) diff --git a/fastpair/scanning/fastpair/BUILD b/fastpair/scanning/fastpair/BUILD index a6e18638..2f3bd825 100644 --- a/fastpair/scanning/fastpair/BUILD +++ b/fastpair/scanning/fastpair/BUILD @@ -33,7 +33,7 @@ cc_library( deps = [ "//fastpair/common", "//fastpair/dataparser", - "//fastpair/internal/ble", + "//fastpair/internal/mediums", "//fastpair/proto:fastpair_cc_proto", "//fastpair/repository", "//fastpair/server_access", @@ -45,6 +45,8 @@ cc_library( "@com_google_absl//absl/functional:any_invocable", "@com_google_absl//absl/functional:bind_front", "@com_google_absl//absl/strings", + "@com_google_absl//absl/synchronization", + "@com_google_absl//absl/time", ], ) @@ -78,6 +80,7 @@ cc_test( deps = [ ":scanning", "//fastpair/common", + "//fastpair/internal/mediums", "//internal/platform:base", "//internal/platform:comm", "//internal/platform:test_util", diff --git a/fastpair/scanning/fastpair/fake_fast_pair_scanner.h b/fastpair/scanning/fastpair/fake_fast_pair_scanner.h index 960bf83f..ed9d9254 100644 --- a/fastpair/scanning/fastpair/fake_fast_pair_scanner.h +++ b/fastpair/scanning/fastpair/fake_fast_pair_scanner.h @@ -32,6 +32,7 @@ class FakeFastPairScanner final : public FastPairScanner { void RemoveObserver(Observer* observer) override; void NotifyDeviceFound(const BlePeripheral& peripheral); void NotifyDeviceLost(const BlePeripheral& peripheral); + void StartScanning() override {}; private: ObserverList observer_; diff --git a/fastpair/scanning/fastpair/fast_pair_discoverable_scanner_impl.cc b/fastpair/scanning/fastpair/fast_pair_discoverable_scanner_impl.cc index 8ab560cf..37fb3c62 100644 --- a/fastpair/scanning/fastpair/fast_pair_discoverable_scanner_impl.cc +++ b/fastpair/scanning/fastpair/fast_pair_discoverable_scanner_impl.cc @@ -25,6 +25,7 @@ #include "absl/functional/bind_front.h" #include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" #include "fastpair/common/constant.h" #include "fastpair/common/fast_pair_device.h" #include "fastpair/common/protocol.h" @@ -65,19 +66,16 @@ FastPairDiscoverableScannerImpl::Factory* FastPairDiscoverableScannerImpl::Factory::g_test_factory_ = nullptr; std::unique_ptr -FastPairDiscoverableScannerImpl::Factory::Create( - std::shared_ptr scanner, - std::shared_ptr adapter, DeviceCallback found_callback, - DeviceCallback lost_callback) { +FastPairDiscoverableScannerImpl::Factory::Create(FastPairScanner& scanner, + DeviceCallback found_callback, + DeviceCallback lost_callback) { if (g_test_factory_) { - return g_test_factory_->CreateInstance( - std::move(scanner), std::move(adapter), std::move(found_callback), - std::move(lost_callback)); + return g_test_factory_->CreateInstance(scanner, std::move(found_callback), + std::move(lost_callback)); } return std::make_unique( - std::move(scanner), std::move(adapter), std::move(found_callback), - std::move(lost_callback)); + scanner, std::move(found_callback), std::move(lost_callback)); } void FastPairDiscoverableScannerImpl::Factory::SetFactoryForTesting( @@ -89,14 +87,12 @@ FastPairDiscoverableScannerImpl::Factory::~Factory() = default; // FastPairScannerImpl FastPairDiscoverableScannerImpl::FastPairDiscoverableScannerImpl( - std::shared_ptr scanner, - std::shared_ptr adapter, DeviceCallback found_callback, + FastPairScanner& scanner, DeviceCallback found_callback, DeviceCallback lost_callback) - : scanner_(std::move(scanner)), - adapter_(std::move(adapter)), + : scanner_(scanner), found_callback_(std::move(found_callback)), lost_callback_(std::move(lost_callback)) { - scanner_->AddObserver(this); + scanner_.AddObserver(this); } void FastPairDiscoverableScannerImpl::OnDeviceFound( @@ -108,8 +104,10 @@ void FastPairDiscoverableScannerImpl::OnDeviceFound( << ": Device doesn't have any Fast Pair Service Data."; return; } - - model_id_parse_attempts_[peripheral.GetName()] = 1; + { + absl::MutexLock lock(&mutex_); + model_id_parse_attempts_[peripheral.GetName()] = 1; + } NEARBY_LOGS(INFO) << __func__ << ": Attempting to get model ID"; std::vector service_data; std::move(std::begin(fast_pair_service_data), @@ -125,18 +123,20 @@ void FastPairDiscoverableScannerImpl::OnDeviceFound( void FastPairDiscoverableScannerImpl::OnModelIdRetrieved( const std::string& address, const std::optional model_id) { - auto it = model_id_parse_attempts_.find(address); + { + absl::MutexLock lock(&mutex_); + auto it = model_id_parse_attempts_.find(address); - // If there's no entry in the map, the device was lost while parsing. - if (it == model_id_parse_attempts_.end()) { - NEARBY_LOGS(WARNING) - << __func__ - << ": Returning early because device as lost while parsing."; - return; + // If there's no entry in the map, the device was lost while parsing. + if (it == model_id_parse_attempts_.end()) { + NEARBY_LOGS(WARNING) + << __func__ + << ": Returning early because device as lost while parsing."; + return; + } + + model_id_parse_attempts_.erase(it); } - - model_id_parse_attempts_.erase(it); - if (!model_id.has_value()) { NEARBY_LOGS(INFO) << __func__ << ": Returning early because no model id was parsed."; @@ -182,9 +182,11 @@ void FastPairDiscoverableScannerImpl::OnDeviceMetadataRetrieved( "Ignoring this advertisement"; return; } - - FastPairDevice device(model_id, address, Protocol::kFastPairInitialPairing); - NotifyDeviceFound(device); + absl::MutexLock lock(&mutex_); + notified_devices_.insert_or_assign( + address, std::make_unique( + model_id, address, Protocol::kFastPairInitialPairing)); + NotifyDeviceFound(*notified_devices_[address]); } void FastPairDiscoverableScannerImpl::NotifyDeviceFound( @@ -192,23 +194,21 @@ void FastPairDiscoverableScannerImpl::NotifyDeviceFound( NEARBY_LOGS(VERBOSE) << "Notify Device found:" << "BluetoothAddress = " << device.GetBleAddress() << ", Model id = " << device.GetModelId(); - notified_devices_[device.GetBleAddress()] = &device; found_callback_(device); } void FastPairDiscoverableScannerImpl::OnDeviceLost( const BlePeripheral& peripheral) { NEARBY_LOGS(INFO) << __func__ << ": Running lost callback"; - + absl::MutexLock lock(&mutex_); model_id_parse_attempts_.erase(peripheral.GetName()); auto it = notified_devices_.find(peripheral.GetName()); // Don't invoke callback if we didn't notify this device. if (it == notified_devices_.end()) return; - FastPairDevice* notified_device = it->second; + lost_callback_(*it->second); notified_devices_.erase(it); - lost_callback_(*notified_device); } } // namespace fastpair diff --git a/fastpair/scanning/fastpair/fast_pair_discoverable_scanner_impl.h b/fastpair/scanning/fastpair/fast_pair_discoverable_scanner_impl.h index 6fec1981..d2f6aeb8 100644 --- a/fastpair/scanning/fastpair/fast_pair_discoverable_scanner_impl.h +++ b/fastpair/scanning/fastpair/fast_pair_discoverable_scanner_impl.h @@ -20,7 +20,9 @@ #include #include +#include "absl/synchronization/mutex.h" #include "fastpair/common/fast_pair_device.h" +#include "fastpair/internal/mediums/mediums.h" #include "fastpair/repository/device_metadata.h" #include "fastpair/scanning/fastpair/fast_pair_discoverable_scanner.h" #include "fastpair/scanning/fastpair/fast_pair_scanner.h" @@ -36,25 +38,22 @@ class FastPairDiscoverableScannerImpl : public FastPairDiscoverableScanner, class Factory { public: static std::unique_ptr Create( - std::shared_ptr scanner, - std::shared_ptr adapter, - DeviceCallback found_callback, DeviceCallback lost_callback); + FastPairScanner& scanner, DeviceCallback found_callback, + DeviceCallback lost_callback); static void SetFactoryForTesting(Factory* g_test_factory); protected: virtual ~Factory(); virtual std::unique_ptr CreateInstance( - std::shared_ptr scanner, - std::shared_ptr adapter, - DeviceCallback found_callback, DeviceCallback lost_callback) = 0; + FastPairScanner& scanner, DeviceCallback found_callback, + DeviceCallback lost_callback) = 0; private: static Factory* g_test_factory_; }; - FastPairDiscoverableScannerImpl(std::shared_ptr scanner, - std::shared_ptr adapter, + FastPairDiscoverableScannerImpl(FastPairScanner& scanner, DeviceCallback found_callback, DeviceCallback lost_callback); FastPairDiscoverableScannerImpl(const FastPairDiscoverableScannerImpl&) = @@ -75,12 +74,14 @@ class FastPairDiscoverableScannerImpl : public FastPairDiscoverableScanner, DeviceMetadata& device_metadata); void NotifyDeviceFound(FastPairDevice& device); - std::shared_ptr scanner_; - std::shared_ptr adapter_; + absl::Mutex mutex_; + FastPairScanner& scanner_; DeviceCallback found_callback_; DeviceCallback lost_callback_; - absl::flat_hash_map notified_devices_; - absl::flat_hash_map model_id_parse_attempts_; + absl::flat_hash_map> + notified_devices_ ABSL_GUARDED_BY(mutex_); + absl::flat_hash_map model_id_parse_attempts_ + ABSL_GUARDED_BY(mutex_); ObserverList observer_list_; }; diff --git a/fastpair/scanning/fastpair/fast_pair_discoverable_scanner_impl_test.cc b/fastpair/scanning/fastpair/fast_pair_discoverable_scanner_impl_test.cc index 883ff12f..42a891d8 100644 --- a/fastpair/scanning/fastpair/fast_pair_discoverable_scanner_impl_test.cc +++ b/fastpair/scanning/fastpair/fast_pair_discoverable_scanner_impl_test.cc @@ -19,8 +19,6 @@ #include #include -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" #include "absl/synchronization/notification.h" #include "fastpair/scanning/fastpair/fake_fast_pair_scanner.h" @@ -78,13 +76,11 @@ class FastPairDiscoverableScannerImplTest : public testing::Test { public: void SetUp() override { SetUpMetadata(); - scanner_ = std::make_shared(); - adapter_ = std::make_shared(); + scanner_ = std::make_unique(); } void TearDown() override { scanner_.reset(); - adapter_.reset(); repository_.reset(); } @@ -98,9 +94,8 @@ class FastPairDiscoverableScannerImplTest : public testing::Test { // void TearDown() override { discoverable_scanner_.reset(); } protected: - std::shared_ptr scanner_; + std::unique_ptr scanner_; std::unique_ptr repository_; - std::shared_ptr adapter_; DeviceCallback found_device_callback_; DeviceCallback lost_device_callback_; }; @@ -117,7 +112,7 @@ TEST_F(FastPairDiscoverableScannerImplTest, ValidModelId) { std::unique_ptr discoverable_scanner_from_factory = FastPairDiscoverableScannerImpl::Factory::Create( - scanner_, adapter_, std::move(found_device_callback_), + *scanner_, std::move(found_device_callback_), std::move(lost_device_callback_)); auto ble_peripheral = @@ -136,7 +131,7 @@ TEST_F(FastPairDiscoverableScannerImplTest, InvalidModelId) { std::unique_ptr discoverable_scanner_from_factory = FastPairDiscoverableScannerImpl::Factory::Create( - scanner_, adapter_, std::move(found_device_callback_), + *scanner_, std::move(found_device_callback_), std::move(lost_device_callback_)); auto ble_peripheral = std::make_unique( @@ -153,7 +148,7 @@ TEST_F(FastPairDiscoverableScannerImplTest, NoServiceData) { std::unique_ptr discoverable_scanner_from_factory = FastPairDiscoverableScannerImpl::Factory::Create( - scanner_, adapter_, std::move(found_device_callback_), + *scanner_, std::move(found_device_callback_), std::move(lost_device_callback_)); auto ble_peripheral = @@ -174,7 +169,7 @@ TEST_F(FastPairDiscoverableScannerImplTest, UnsupportedDeviceType) { std::unique_ptr discoverable_scanner_from_factory = FastPairDiscoverableScannerImpl::Factory::Create( - scanner_, adapter_, std::move(found_device_callback_), + *scanner_, std::move(found_device_callback_), std::move(lost_device_callback_)); auto ble_peripheral = @@ -196,7 +191,7 @@ TEST_F(FastPairDiscoverableScannerImplTest, UnsupportedNotifictionType) { std::unique_ptr discoverable_scanner_from_factory = FastPairDiscoverableScannerImpl::Factory::Create( - scanner_, adapter_, std::move(found_device_callback_), + *scanner_, std::move(found_device_callback_), std::move(lost_device_callback_)); auto ble_peripheral = @@ -222,7 +217,7 @@ TEST_F(FastPairDiscoverableScannerImplTest, UnspecifiedNotificationType) { std::unique_ptr discoverable_scanner_from_factory = FastPairDiscoverableScannerImpl::Factory::Create( - scanner_, adapter_, std::move(found_device_callback_), + *scanner_, std::move(found_device_callback_), std::move(lost_device_callback_)); auto ble_peripheral = @@ -246,7 +241,7 @@ TEST_F(FastPairDiscoverableScannerImplTest, V1NotificationType) { std::unique_ptr discoverable_scanner_from_factory = FastPairDiscoverableScannerImpl::Factory::Create( - scanner_, adapter_, std::move(found_device_callback_), + *scanner_, std::move(found_device_callback_), std::move(lost_device_callback_)); auto ble_peripheral = @@ -270,7 +265,7 @@ TEST_F(FastPairDiscoverableScannerImplTest, V2NotificationType) { std::unique_ptr discoverable_scanner_from_factory = FastPairDiscoverableScannerImpl::Factory::Create( - scanner_, adapter_, std::move(found_device_callback_), + *scanner_, std::move(found_device_callback_), std::move(lost_device_callback_)); auto ble_peripheral = @@ -287,7 +282,7 @@ TEST_F(FastPairDiscoverableScannerImplTest, NearbyShareModelId) { std::unique_ptr discoverable_scanner_from_factory = FastPairDiscoverableScannerImpl::Factory::Create( - scanner_, adapter_, std::move(found_device_callback_), + *scanner_, std::move(found_device_callback_), std::move(lost_device_callback_)); auto ble_peripheral = std::make_unique( @@ -309,7 +304,7 @@ TEST_F(FastPairDiscoverableScannerImplTest, std::unique_ptr discoverable_scanner_from_factory = FastPairDiscoverableScannerImpl::Factory::Create( - scanner_, adapter_, std::move(found_device_callback_), + *scanner_, std::move(found_device_callback_), std::move(lost_device_callback_)); auto ble_peripheral = diff --git a/fastpair/scanning/fastpair/fast_pair_scanner.h b/fastpair/scanning/fastpair/fast_pair_scanner.h index 48d54980..1ace2606 100644 --- a/fastpair/scanning/fastpair/fast_pair_scanner.h +++ b/fastpair/scanning/fastpair/fast_pair_scanner.h @@ -38,7 +38,8 @@ class FastPairScanner { virtual void AddObserver(Observer* observer) = 0; virtual void RemoveObserver(Observer* observer) = 0; - protected: + virtual void StartScanning() = 0; + virtual ~FastPairScanner() = default; }; diff --git a/fastpair/scanning/fastpair/fast_pair_scanner_impl.cc b/fastpair/scanning/fastpair/fast_pair_scanner_impl.cc index 4b4efc71..770cc092 100644 --- a/fastpair/scanning/fastpair/fast_pair_scanner_impl.cc +++ b/fastpair/scanning/fastpair/fast_pair_scanner_impl.cc @@ -17,6 +17,7 @@ #include #include +#include "absl/time/time.h" #include "fastpair/common/constant.h" #include "internal/platform/byte_array.h" #include "internal/platform/logging.h" @@ -31,31 +32,9 @@ constexpr absl::Duration kFastPairLowPowerInactiveSeconds = absl::Seconds(3); constexpr char kFastPairServiceUuid[] = "0000FE2C-0000-1000-8000-00805F9B34FB"; } // namespace -// static -FastPairScannerImpl::Factory* FastPairScannerImpl::Factory::g_test_factory_ = - nullptr; - -// static -std::shared_ptr FastPairScannerImpl::Factory::Create() { - if (g_test_factory_) { - return g_test_factory_->CreateInstance(); - } - - return std::make_shared(); -} - -// static -void FastPairScannerImpl::Factory::SetFactoryForTesting( - Factory* g_test_factory) { - g_test_factory_ = g_test_factory; -} - -FastPairScannerImpl::Factory::~Factory() = default; - // FastPairScannerImpl -FastPairScannerImpl::FastPairScannerImpl() { +FastPairScannerImpl::FastPairScannerImpl(Mediums& mediums) : mediums_(mediums) { task_runner_ = std::make_unique(1); - StartScanning(); } void FastPairScannerImpl::AddObserver(FastPairScanner::Observer* observer) { @@ -67,10 +46,12 @@ void FastPairScannerImpl::RemoveObserver(FastPairScanner::Observer* observer) { } void FastPairScannerImpl::StartScanning() { + NEARBY_LOGS(VERBOSE) << __func__; task_runner_->PostTask( [this]() { - if (ble_.Enable() && - ble_.StartScanning( + if (mediums_.GetBluetoothRadio().Enable() && + mediums_.GetBle().IsAvailable() && + mediums_.GetBle().StartScanning( kServiceId, kFastPairServiceUuid, { .peripheral_discovered_cb = @@ -108,7 +89,7 @@ void FastPairScannerImpl::StartScanning() { void FastPairScannerImpl::StopScanning() { DCHECK(IsFastPairLowPowerEnabled()); - ble_.StopScanning(kServiceId); + mediums_.GetBle().StopScanning(kServiceId); task_runner_->PostDelayedTask(kFastPairLowPowerInactiveSeconds, [this]() { StartScanning(); }); } diff --git a/fastpair/scanning/fastpair/fast_pair_scanner_impl.h b/fastpair/scanning/fastpair/fast_pair_scanner_impl.h index 9162a425..4a8a15a0 100644 --- a/fastpair/scanning/fastpair/fast_pair_scanner_impl.h +++ b/fastpair/scanning/fastpair/fast_pair_scanner_impl.h @@ -15,18 +15,14 @@ #ifndef THIRD_PARTY_NEARBY_FASTPAIR_SCANNING_FASTPAIR_FAST_PAIR_SCANNER_IMPL_H_ #define THIRD_PARTY_NEARBY_FASTPAIR_SCANNING_FASTPAIR_FAST_PAIR_SCANNER_IMPL_H_ -#include #include #include #include -#include -#include "absl/strings/string_view.h" -#include "fastpair/internal/ble/ble.h" +#include "fastpair/internal/mediums/mediums.h" #include "fastpair/scanning/fastpair/fast_pair_scanner.h" #include "internal/base/observer_list.h" #include "internal/platform/bluetooth_adapter.h" -#include "internal/platform/byte_array.h" #include "internal/platform/task_runner.h" namespace nearby { @@ -34,21 +30,7 @@ namespace fastpair { class FastPairScannerImpl : public FastPairScanner { public: - class Factory { - public: - static std::shared_ptr Create(); - - static void SetFactoryForTesting(Factory* g_test_factory); - - protected: - virtual ~Factory(); - virtual std::shared_ptr CreateInstance() = 0; - - private: - static Factory* g_test_factory_; - }; - - FastPairScannerImpl(); + explicit FastPairScannerImpl(Mediums& mediums); FastPairScannerImpl(const FastPairScannerImpl&) = delete; FastPairScannerImpl& operator=(const FastPairScannerImpl&) = delete; ~FastPairScannerImpl() override = default; @@ -65,11 +47,9 @@ class FastPairScannerImpl : public FastPairScanner { // Todo(b/267348348): Support Flags to control feature ramp bool IsFastPairLowPowerEnabled() const { return false; } - // For unit test - Ble& GetBle() { return ble_; } + void StartScanning() override; private: - void StartScanning(); void StopScanning(); std::unique_ptr task_runner_; @@ -80,7 +60,7 @@ class FastPairScannerImpl : public FastPairScanner { device_address_advertisement_data_map_; BluetoothAdapter bluetooth_adapter_; - Ble ble_; + Mediums& mediums_; ObserverList observer_; }; diff --git a/fastpair/scanning/fastpair/fast_pair_scanner_impl_test.cc b/fastpair/scanning/fastpair/fast_pair_scanner_impl_test.cc index d0e520a0..e91d5848 100644 --- a/fastpair/scanning/fastpair/fast_pair_scanner_impl_test.cc +++ b/fastpair/scanning/fastpair/fast_pair_scanner_impl_test.cc @@ -14,153 +14,86 @@ #include "fastpair/scanning/fastpair/fast_pair_scanner_impl.h" -#include #include #include -#include #include "gtest/gtest.h" +#include "absl/strings/escaping.h" #include "absl/strings/string_view.h" -#include "absl/time/time.h" +#include "fastpair/internal/mediums/mediums.h" #include "fastpair/scanning/fastpair/fast_pair_scanner.h" #include "internal/platform/bluetooth_adapter.h" #include "internal/platform/byte_array.h" +#include "internal/platform/count_down_latch.h" #include "internal/platform/medium_environment.h" namespace nearby { namespace fastpair { namespace { -// Below constants are used to construct MockBluetoothDevice for testing. -constexpr char kTestBleDeviceAddress[] = "11:12:13:14:15:16"; -constexpr char kTestModelId[] = "112233"; -constexpr absl::Duration kTaskWaitTimeout = absl::Milliseconds(200); - -class FakeBlePeripheral : public api::BlePeripheral { - public: - explicit FakeBlePeripheral(absl::string_view name, - absl::string_view service_data) { - name_ = std::string(name); - std::string service_data_str = std::string(service_data); - ByteArray advertisement_bytes(service_data_str); - advertisement_data_ = advertisement_bytes; - } - FakeBlePeripheral(const FakeBlePeripheral&) = default; - ~FakeBlePeripheral() override = default; - - std::string GetName() const override { return name_; } - - ByteArray GetAdvertisementBytes( - const std::string& service_id) const override { - return advertisement_data_; - } - - void SetName(const std::string& name) { name_ = name; } - - void SetAdvertisementBytes(ByteArray advertisement_bytes) { - advertisement_data_ = advertisement_bytes; - } - - private: - std::string name_; - ByteArray advertisement_data_; -}; +constexpr absl::Duration kTaskWaitTimeout = absl::Milliseconds(1000); +constexpr absl::string_view kServiceID{"Fast Pair"}; +constexpr absl::string_view kModelId{"718c17"}; +constexpr absl::string_view kFastPairServiceUuid{ + "0000FE2C-0000-1000-8000-00805F9B34FB"}; class FastPairScannerObserver : public FastPairScanner::Observer { public: + explicit FastPairScannerObserver(FastPairScanner* scanner, + CountDownLatch* accept_latch, + CountDownLatch* lost_latch) { + accept_latch_ = accept_latch; + lost_latch_ = lost_latch; + scanner->AddObserver(this); + } // FastPairScanner::Observer overrides void OnDeviceFound(const BlePeripheral& peripheral) override { - device_addreses_.push_back(peripheral.GetName()); - on_device_found_count_++; + accept_latch_->CountDown(); } void OnDeviceLost(const BlePeripheral& peripheral) override { - auto it = std::find(device_addreses_.begin(), device_addreses_.end(), - peripheral.GetName()); - if (it == device_addreses_.end()) return; - device_addreses_.erase(it); + lost_latch_->CountDown(); } - bool DoesDeviceListContainTestDevice(const std::string& address) { - auto it = - std::find(device_addreses_.begin(), device_addreses_.end(), address); - return it != device_addreses_.end(); - } - - int on_device_found_count() { return on_device_found_count_; } - - private: - std::vector device_addreses_; - - int on_device_found_count_ = 0; + CountDownLatch* accept_latch_ = nullptr; + CountDownLatch* lost_latch_ = nullptr; }; class FastPairScannerImplTest : public testing::Test { - public: - void SetUp() override { - env_.Start(); - scanner_ = std::make_shared(); - SystemClock::Sleep(kTaskWaitTimeout); - scanner_observer_ = std::make_unique(); - scanner_->AddObserver(scanner_observer_.get()); - } - - void TearDown() override { - scanner_->RemoveObserver(scanner_observer_.get()); - scanner_.reset(); - scanner_observer_.reset(); - env_.Stop(); - } - - void TriggerOnDeviceFound(absl::string_view address, absl::string_view data) { - auto ble_peripheral = std::make_unique(address, data); - scanner_->OnDeviceFound(BlePeripheral(ble_peripheral.get())); - } - - void TriggerOnDeviceLost(absl::string_view address, absl::string_view data) { - auto ble_peripheral = std::make_unique(address, data); - scanner_->OnDeviceLost(BlePeripheral(ble_peripheral.get())); - } - protected: MediumEnvironment& env_{MediumEnvironment::Instance()}; - std::shared_ptr scanner_; - std::unique_ptr scanner_observer_; }; -TEST_F(FastPairScannerImplTest, StartScanningSuccessfully) { - EXPECT_TRUE(scanner_->GetBle().IsScanning()); - // Not StopScanning as FastPairLowPowerDisabled -} +TEST_F(FastPairScannerImplTest, StartScanning) { + env_.Start(); -TEST_F(FastPairScannerImplTest, DeviceFoundNotifiesObservers) { - TriggerOnDeviceFound(kTestBleDeviceAddress, kTestModelId); - EXPECT_TRUE(scanner_observer_->DoesDeviceListContainTestDevice( - kTestBleDeviceAddress)); -} + // Create Fast Pair Scanner and add its observer + Mediums mediums_1; + auto scanner = std::make_unique(mediums_1); + CountDownLatch accept_latch(1); + CountDownLatch lost_latch(1); + FastPairScannerObserver observer(scanner.get(), &accept_latch, &lost_latch); -TEST_F(FastPairScannerImplTest, DeviceLostNotifiesObservers) { - TriggerOnDeviceFound(kTestBleDeviceAddress, kTestModelId); - EXPECT_TRUE(scanner_observer_->DoesDeviceListContainTestDevice( - kTestBleDeviceAddress)); - TriggerOnDeviceLost(kTestBleDeviceAddress, kTestModelId); - EXPECT_FALSE(scanner_observer_->DoesDeviceListContainTestDevice( - kTestBleDeviceAddress)); -} + // Create Advertiser and startAdvertising + Mediums mediums_2; + std::string service_id(kServiceID); + ByteArray advertisement_bytes{absl::HexStringToBytes(kModelId)}; + std::string fast_pair_service_uuid(kFastPairServiceUuid); + mediums_2.GetBle().getMedium().StartAdvertising( + service_id, advertisement_bytes, fast_pair_service_uuid); -TEST_F(FastPairScannerImplTest, DeviceFoundWithNoServiceData) {; - TriggerOnDeviceFound(kTestBleDeviceAddress, ""); - EXPECT_FALSE(scanner_observer_->DoesDeviceListContainTestDevice( - kTestBleDeviceAddress)); -} + // Fast Pair scanner startScanning + scanner->StartScanning(); + // Notify device found + EXPECT_TRUE(accept_latch.Await(kTaskWaitTimeout).result()); -TEST_F(FastPairScannerImplTest, RemoveObserver) { - scanner_->RemoveObserver(scanner_observer_.get()); - TriggerOnDeviceFound(kTestBleDeviceAddress, kTestModelId); - EXPECT_FALSE(scanner_observer_->DoesDeviceListContainTestDevice( - kTestBleDeviceAddress)); -} + // Advertiser stopAdvertising + mediums_2.GetBle().getMedium().StopAdvertising(service_id); + // Notify device lost + EXPECT_TRUE(lost_latch.Await(kTaskWaitTimeout).result()); + env_.Stop(); +} } // namespace } // namespace fastpair } // namespace nearby diff --git a/fastpair/scanning/mock_scanner_broker.h b/fastpair/scanning/mock_scanner_broker.h index 0df0bea0..29042c8d 100644 --- a/fastpair/scanning/mock_scanner_broker.h +++ b/fastpair/scanning/mock_scanner_broker.h @@ -35,13 +35,13 @@ class MockScannerBroker : public ScannerBroker { observers_.RemoveObserver(observer); } - void NotifyDeviceFound(const FastPairDevice& device) { + void NotifyDeviceFound(FastPairDevice& device) { for (auto& observer : observers_.GetObservers()) { observer->OnDeviceFound(device); } } - void NotifyDeviceLost(const FastPairDevice& device) { + void NotifyDeviceLost(FastPairDevice& device) { for (auto& observer : observers_.GetObservers()) { observer->OnDeviceLost(device); } diff --git a/fastpair/scanning/scanner_broker.h b/fastpair/scanning/scanner_broker.h index 0e191e28..3242e7b4 100644 --- a/fastpair/scanning/scanner_broker.h +++ b/fastpair/scanning/scanner_broker.h @@ -32,8 +32,8 @@ class ScannerBroker { public: virtual ~Observer() = default; - virtual void OnDeviceFound(const FastPairDevice& device) = 0; - virtual void OnDeviceLost(const FastPairDevice& device) = 0; + virtual void OnDeviceFound(FastPairDevice& device) = 0; + virtual void OnDeviceLost(FastPairDevice& device) = 0; }; virtual ~ScannerBroker() = default; diff --git a/fastpair/scanning/scanner_broker_impl.cc b/fastpair/scanning/scanner_broker_impl.cc index 36a60d80..372caddf 100644 --- a/fastpair/scanning/scanner_broker_impl.cc +++ b/fastpair/scanning/scanner_broker_impl.cc @@ -15,21 +15,17 @@ #include "fastpair/scanning/scanner_broker_impl.h" #include -#include #include "absl/functional/bind_front.h" #include "fastpair/common/fast_pair_device.h" -#include "fastpair/scanning/fastpair/fast_pair_discoverable_scanner.h" #include "fastpair/scanning/fastpair/fast_pair_discoverable_scanner_impl.h" #include "fastpair/scanning/fastpair/fast_pair_scanner_impl.h" -#include "internal/platform/bluetooth_adapter.h" #include "internal/platform/logging.h" #include "internal/platform/task_runner_impl.h" namespace nearby { namespace fastpair { -ScannerBrokerImpl::ScannerBrokerImpl() { - adapter_ = std::make_shared(); +ScannerBrokerImpl::ScannerBrokerImpl(Mediums& mediums) : mediums_(mediums) { task_runner_ = std::make_unique(1); } @@ -50,16 +46,17 @@ void ScannerBrokerImpl::StopScanning(Protocol protocol) { NEARBY_LOGS(VERBOSE) << __func__ << ": protocol=" << protocol; task_runner_->PostTask([this]() { StopFastPairScanning(); }); } + void ScannerBrokerImpl::StartFastPairScanning() { DCHECK(!fast_pair_discoverable_scanner_); - DCHECK(adapter_); NEARBY_LOGS(VERBOSE) << "Starting Fast Pair Scanning."; - scanner_ = std::make_shared(); + scanner_ = std::make_unique(mediums_); fast_pair_discoverable_scanner_ = FastPairDiscoverableScannerImpl::Factory::Create( - scanner_, adapter_, + *scanner_, absl::bind_front(&ScannerBrokerImpl::NotifyDeviceFound, this), absl::bind_front(&ScannerBrokerImpl::NotifyDeviceLost, this)); + scanner_->StartScanning(); } void ScannerBrokerImpl::StopFastPairScanning() { @@ -69,7 +66,7 @@ void ScannerBrokerImpl::StopFastPairScanning() { NEARBY_LOGS(VERBOSE) << __func__ << "Stopping Fast Pair Scanning."; } -void ScannerBrokerImpl::NotifyDeviceFound(const FastPairDevice& device) { +void ScannerBrokerImpl::NotifyDeviceFound(FastPairDevice& device) { NEARBY_LOGS(INFO) << __func__ << ": Notifying device found, model id = " << device.GetModelId(); for (auto& observer : observers_.GetObservers()) { @@ -77,7 +74,7 @@ void ScannerBrokerImpl::NotifyDeviceFound(const FastPairDevice& device) { } } -void ScannerBrokerImpl::NotifyDeviceLost(const FastPairDevice& device) { +void ScannerBrokerImpl::NotifyDeviceLost(FastPairDevice& device) { NEARBY_LOGS(INFO) << __func__ << ": Notifying device lost, model id = " << device.GetModelId(); for (auto& observer : observers_.GetObservers()) { diff --git a/fastpair/scanning/scanner_broker_impl.h b/fastpair/scanning/scanner_broker_impl.h index bb40d6b7..36774597 100644 --- a/fastpair/scanning/scanner_broker_impl.h +++ b/fastpair/scanning/scanner_broker_impl.h @@ -15,16 +15,14 @@ #ifndef THIRD_PARTY_NEARBY_FASTPAIR_SCANNING_SCANNER_BROKER_IMPL_H_ #define THIRD_PARTY_NEARBY_FASTPAIR_SCANNING_SCANNER_BROKER_IMPL_H_ -#include #include -#include #include "fastpair/common/fast_pair_device.h" +#include "fastpair/internal/mediums/mediums.h" #include "fastpair/scanning/fastpair/fast_pair_discoverable_scanner.h" #include "fastpair/scanning/fastpair/fast_pair_scanner.h" #include "fastpair/scanning/scanner_broker.h" #include "internal/base/observer_list.h" -#include "internal/platform/bluetooth_adapter.h" #include "internal/platform/task_runner.h" namespace nearby { @@ -32,7 +30,7 @@ namespace fastpair { class ScannerBrokerImpl : public ScannerBroker { public: - explicit ScannerBrokerImpl(); + explicit ScannerBrokerImpl(Mediums& mediums); ~ScannerBrokerImpl() override = default; // ScannerBroker: @@ -44,12 +42,12 @@ class ScannerBrokerImpl : public ScannerBroker { private: void StartFastPairScanning(); void StopFastPairScanning(); - void NotifyDeviceFound(const FastPairDevice& device); - void NotifyDeviceLost(const FastPairDevice& device); + void NotifyDeviceFound(FastPairDevice& device); + void NotifyDeviceLost(FastPairDevice& device); + Mediums& mediums_; std::unique_ptr task_runner_; - std::shared_ptr scanner_; - std::shared_ptr adapter_; + std::unique_ptr scanner_; std::unique_ptr fast_pair_discoverable_scanner_; ObserverList observers_; }; diff --git a/fastpair/scanning/scanner_broker_impl_test.cc b/fastpair/scanning/scanner_broker_impl_test.cc index 861d0f1d..273dcd04 100644 --- a/fastpair/scanning/scanner_broker_impl_test.cc +++ b/fastpair/scanning/scanner_broker_impl_test.cc @@ -16,190 +16,98 @@ #include #include -#include #include "gtest/gtest.h" +#include "absl/strings/escaping.h" #include "absl/strings/string_view.h" #include "fastpair/common/fast_pair_device.h" #include "fastpair/common/protocol.h" -#include "fastpair/scanning/fastpair/fake_fast_pair_discoverable_scanner.h" -#include "fastpair/scanning/fastpair/fake_fast_pair_scanner.h" -#include "fastpair/scanning/fastpair/fast_pair_discoverable_scanner.h" -#include "fastpair/scanning/fastpair/fast_pair_discoverable_scanner_impl.h" -#include "fastpair/scanning/fastpair/fast_pair_scanner.h" -#include "fastpair/scanning/fastpair/fast_pair_scanner_impl.h" -#include "internal/platform/implementation/system_clock.h" +#include "fastpair/internal/mediums/mediums.h" +#include "fastpair/proto/fastpair_rpcs.proto.h" +#include "fastpair/scanning/scanner_broker.h" +#include "fastpair/server_access/fake_fast_pair_repository.h" +#include "internal/platform/byte_array.h" +#include "internal/platform/count_down_latch.h" +#include "internal/platform/medium_environment.h" namespace nearby { namespace fastpair { namespace { -constexpr absl::string_view kTestDeviceAddress("11:12:13:14:15:16"); -constexpr absl::string_view kValidModelId("718c17"); -constexpr absl::Duration kTaskWaitTimeout = absl::Milliseconds(200); - -class FakeFastPairScannerFactory : public FastPairScannerImpl::Factory { +constexpr absl::Duration kTaskWaitTimeout = absl::Milliseconds(1000); +constexpr absl::string_view kServiceID{"Fast Pair"}; +constexpr absl::string_view kModelId{"718c17"}; +constexpr absl::string_view kFastPairServiceUuid{ + "0000FE2C-0000-1000-8000-00805F9B34FB"}; +constexpr absl::string_view kPublicAntiSpoof = + "Wuyr48lD3txnUhGiMF1IfzlTwRxxe+wMB1HLzP+" + "0wVcljfT3XPoiy1fntlneziyLD5knDVAJSE+RM/zlPRP/Jg=="; +class ScannerBrokerObserver : public ScannerBroker::Observer { public: - // FastPairScannerImpl::Factory: - std::shared_ptr CreateInstance() override { - auto fake_fast_pair_scanner = std::shared_ptr(); - fake_fast_pair_scanner_ = fake_fast_pair_scanner.get(); - return fake_fast_pair_scanner; + explicit ScannerBrokerObserver(ScannerBroker* scanner_broker, + CountDownLatch* accept_latch, + CountDownLatch* lost_latch) { + accept_latch_ = accept_latch; + lost_latch_ = lost_latch; + scanner_broker->AddObserver(this); } - ~FakeFastPairScannerFactory() override = default; - - FakeFastPairScanner* fake_fast_pair_scanner() { - return fake_fast_pair_scanner_; + void OnDeviceFound(FastPairDevice& device) override { + accept_latch_->CountDown(); } - private: - FakeFastPairScanner* fake_fast_pair_scanner_ = nullptr; + void OnDeviceLost(FastPairDevice& device) override { + lost_latch_->CountDown(); + } + + CountDownLatch* accept_latch_ = nullptr; + CountDownLatch* lost_latch_ = nullptr; }; -class FakeFastPairDiscoverableScannerFactory - : public FastPairDiscoverableScannerImpl::Factory { - public: - // FastPairDiscoverableScannerImpl::Factory: - std::unique_ptr CreateInstance( - std::shared_ptr scanner, - std::shared_ptr adapter, DeviceCallback found_callback, - DeviceCallback lost_callback) override { - create_instance_ = true; - auto fake_fast_pair_discoverable_scanner = - std::make_unique( - std::move(found_callback), std::move(lost_callback)); - fake_fast_pair_discoverable_scanner_ = - fake_fast_pair_discoverable_scanner.get(); - return fake_fast_pair_discoverable_scanner; - } - - FakeFastPairDiscoverableScanner* fake_fast_pair_discoverable_scanner() { - return fake_fast_pair_discoverable_scanner_; - } - - bool create_instance() { return create_instance_; } - +class ScannerBrokerImplTest : public testing::Test { protected: - bool create_instance_ = false; - FakeFastPairDiscoverableScanner* fake_fast_pair_discoverable_scanner_ = - nullptr; + MediumEnvironment& env_{MediumEnvironment::Instance()}; }; -class ScannerBrokerImplTest : public testing::Test, - public ScannerBroker::Observer { - public: - void SetUp() override { - adapter_ = std::shared_ptr(); +TEST_F(ScannerBrokerImplTest, CanStartScanning) { + env_.Start(); + // Setup FakeFastPairRepository + std::string decoded_key; + absl::Base64Unescape(kPublicAntiSpoof, &decoded_key); + proto::Device metadata; + auto repository_ = std::make_unique(); + metadata.mutable_anti_spoofing_key_pair()->set_public_key(decoded_key); + repository_->SetFakeMetadata(kModelId, metadata); - scanner_factory_ = std::make_unique(); - FastPairScannerImpl::Factory::SetFactoryForTesting(scanner_factory_.get()); + // Create Fast Pair Scanner and add its observer + Mediums mediums_1; + auto scanner_broker = std::make_unique(mediums_1); + CountDownLatch accept_latch(1); + CountDownLatch lost_latch(1); + ScannerBrokerObserver observer(scanner_broker.get(), &accept_latch, + &lost_latch); - discoverable_scanner_factory_ = - std::make_unique(); - FastPairDiscoverableScannerImpl::Factory::SetFactoryForTesting( - discoverable_scanner_factory_.get()); + // Create Advertiser and startAdvertising + Mediums mediums_2; + std::string service_id(kServiceID); + ByteArray advertisement_bytes{absl::HexStringToBytes(kModelId)}; + std::string fast_pair_service_uuid(kFastPairServiceUuid); + mediums_2.GetBle().getMedium().StartAdvertising( + service_id, advertisement_bytes, fast_pair_service_uuid); - scanner_broker_ = std::make_unique(); - scanner_broker_->AddObserver(this); - } + // Fast Pair scanner startScanning + scanner_broker->StartScanning(Protocol::kFastPairInitialPairing); - void TearDown() override { - scanner_broker_->RemoveObserver(this); - scanner_broker_.reset(); - scanner_factory_.reset(); - discoverable_scanner_factory_.reset(); - FastPairScannerImpl::Factory::SetFactoryForTesting(nullptr); - FastPairDiscoverableScannerImpl::Factory::SetFactoryForTesting(nullptr); - adapter_.reset(); - } + // Notify device found + EXPECT_TRUE(accept_latch.Await(kTaskWaitTimeout).result()); - void TriggerDiscoverableDeviceFound() { - FastPairDevice device(std::string(kValidModelId), - std::string(kTestDeviceAddress), - Protocol::kFastPairInitialPairing); - discoverable_scanner_factory_->fake_fast_pair_discoverable_scanner() - ->TriggerDeviceFoundCallback(device); - } + // Advertiser stopAdvertising + mediums_2.GetBle().getMedium().StopAdvertising(service_id); - void TriggerDiscoverableDeviceLost() { - FastPairDevice device(std::string(kValidModelId), - std::string(kTestDeviceAddress), - Protocol::kFastPairInitialPairing); - discoverable_scanner_factory_->fake_fast_pair_discoverable_scanner() - ->TriggerDeviceLostCallback(device); - } - - void OnDeviceFound(const FastPairDevice& device) override { - device_found_ = true; - } - - void OnDeviceLost(const FastPairDevice& device) override { - device_lost_ = true; - } - - protected: - bool device_found_ = false; - bool device_lost_ = false; - std::shared_ptr adapter_; - std::unique_ptr scanner_factory_; - std::unique_ptr - discoverable_scanner_factory_; - std::unique_ptr scanner_broker_; -}; - -TEST_F(ScannerBrokerImplTest, DiscoverableFound) { - EXPECT_FALSE(discoverable_scanner_factory_->create_instance()); - - scanner_broker_->StartScanning(Protocol::kFastPairInitialPairing); - SystemClock::Sleep(kTaskWaitTimeout); - EXPECT_FALSE(device_found_); - EXPECT_TRUE(discoverable_scanner_factory_->create_instance()); - - TriggerDiscoverableDeviceFound(); - EXPECT_TRUE(device_found_); + // Notify device lost + EXPECT_TRUE(lost_latch.Await(kTaskWaitTimeout).result()); + env_.Stop(); } - -TEST_F(ScannerBrokerImplTest, DiscoverableLost) { - EXPECT_FALSE(discoverable_scanner_factory_->create_instance()); - - scanner_broker_->StartScanning(Protocol::kFastPairInitialPairing); - SystemClock::Sleep(kTaskWaitTimeout); - EXPECT_FALSE(device_found_); - EXPECT_TRUE(discoverable_scanner_factory_->create_instance()); - - TriggerDiscoverableDeviceLost(); - EXPECT_TRUE(device_lost_); -} - -TEST_F(ScannerBrokerImplTest, RemoveObserver) { - EXPECT_FALSE(discoverable_scanner_factory_->create_instance()); - - scanner_broker_->StartScanning(Protocol::kFastPairInitialPairing); - SystemClock::Sleep(kTaskWaitTimeout); - EXPECT_FALSE(device_found_); - EXPECT_TRUE(discoverable_scanner_factory_->create_instance()); - - scanner_broker_->RemoveObserver(this); - TriggerDiscoverableDeviceLost(); - EXPECT_FALSE(device_lost_); -} - -TEST_F(ScannerBrokerImplTest, StopScanning) { - EXPECT_FALSE(discoverable_scanner_factory_->create_instance()); - - scanner_broker_->StartScanning(Protocol::kFastPairInitialPairing); - SystemClock::Sleep(kTaskWaitTimeout); - EXPECT_TRUE(discoverable_scanner_factory_->create_instance()); - - scanner_broker_->StopScanning(Protocol::kFastPairInitialPairing); - SystemClock::Sleep(kTaskWaitTimeout); - - scanner_broker_->StartScanning(Protocol::kFastPairInitialPairing); - SystemClock::Sleep(kTaskWaitTimeout); - EXPECT_TRUE(discoverable_scanner_factory_->create_instance()); -} - } // namespace } // namespace fastpair } // namespace nearby diff --git a/fastpair/ui/BUILD b/fastpair/ui/BUILD new file mode 100644 index 00000000..754f8eb9 --- /dev/null +++ b/fastpair/ui/BUILD @@ -0,0 +1,67 @@ +licenses(["notice"]) + +cc_library( + name = "fast_pair_ui", + srcs = [ + "fast_pair/fast_pair_notification_controller.cc", + "fast_pair/fast_pair_presenter_impl.cc", + "ui_broker_impl.cc", + ], + hdrs = [ + "actions.h", + "fast_pair/fast_pair_notification_controller.h", + "fast_pair/fast_pair_presenter.h", + "fast_pair/fast_pair_presenter_impl.h", + "ui_broker.h", + "ui_broker_impl.h", + ], + compatible_with = ["//buildenv/target:non_prod"], + visibility = [ + "//fastpair:__subpackages__", + ], + deps = [ + "//fastpair/common", + "//fastpair/repository", + "//fastpair/server_access", + "//internal/base", + "//internal/platform:logging", + "@com_google_absl//absl/functional:any_invocable", + "@com_google_absl//absl/strings", + ], +) + +cc_library( + name = "fake_fast_pair_ui", + hdrs = [ + "fast_pair/fake_fast_pair_notification_controller_observer.h", + "fast_pair/fake_fast_pair_presenter.h", + ], + visibility = [ + "//fastpair:__subpackages__", + ], + deps = [ + ":fast_pair_ui", + "//fastpair/repository", + ], +) + +cc_test( + name = "fast_pair_ui_test", + srcs = [ + "fast_pair/fast_pair_notification_controller_test.cc", + "fast_pair/fast_pair_presenter_impl_test.cc", + "ui_broker_impl_test.cc", + ], + deps = [ + ":fake_fast_pair_ui", + ":fast_pair_ui", + "//fastpair/common", + "//fastpair/proto:fastpair_cc_proto", + "//fastpair/repository", + "//fastpair/server_access:test_support", + "//internal/network:types", + "//internal/platform/implementation/g3", + "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/fastpair/ui/actions.h b/fastpair/ui/actions.h new file mode 100644 index 00000000..658100d0 --- /dev/null +++ b/fastpair/ui/actions.h @@ -0,0 +1,32 @@ +// 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. + +#ifndef THIRD_PARTY_NEARBY_FASTPAIR_UI_ACTIONS_H_ +#define THIRD_PARTY_NEARBY_FASTPAIR_UI_ACTIONS_H_ + +namespace nearby { +namespace fastpair { + +enum class DiscoveryAction { + kPairToDevice = 0, + kDismissedByUser = 1, + kDismissedByOs = 2, + kLearnMore = 3, + kDismissedByTimeout = 4, +}; + +} // namespace fastpair +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_FASTPAIR_UI_ACTIONS_H_ diff --git a/fastpair/ui/fast_pair/fake_fast_pair_presenter.h b/fastpair/ui/fast_pair/fake_fast_pair_presenter.h index f2fe938c..c5ee2038 100644 --- a/fastpair/ui/fast_pair/fake_fast_pair_presenter.h +++ b/fastpair/ui/fast_pair/fake_fast_pair_presenter.h @@ -17,6 +17,8 @@ #include +#include "fastpair/ui/actions.h" +#include "fastpair/ui/fast_pair/fast_pair_notification_controller.h" #include "fastpair/ui/fast_pair/fast_pair_presenter_impl.h" namespace nearby { @@ -24,10 +26,11 @@ namespace fastpair { class FakeFastPairPresenter : public FastPairPresenter { public: - void ShowDiscovery( - const FastPairDevice& device, - FastPairNotificationController& notification_controller) override { + void ShowDiscovery(const FastPairDevice& device, + FastPairNotificationController& notification_controller, + DiscoveryCallback callback) override { show_discovery_ = true; + callback(DiscoveryAction::kPairToDevice); } bool show_deiscovery() { return show_discovery_; } diff --git a/fastpair/ui/fast_pair/fast_pair_notification_controller.cc b/fastpair/ui/fast_pair/fast_pair_notification_controller.cc index 35f26de6..6f8ec747 100644 --- a/fastpair/ui/fast_pair/fast_pair_notification_controller.cc +++ b/fastpair/ui/fast_pair/fast_pair_notification_controller.cc @@ -14,7 +14,13 @@ #include "fastpair/ui/fast_pair/fast_pair_notification_controller.h" +#include + +#include "absl/functional/any_invocable.h" #include "fastpair/repository/device_metadata.h" +#include "fastpair/ui/actions.h" +#include "internal/platform/logging.h" + namespace nearby { namespace fastpair { void FastPairNotificationController::AddObserver(Observer* observer) { @@ -33,9 +39,19 @@ void FastPairNotificationController::NotifyShowDiscovery( } void FastPairNotificationController::ShowGuestDiscoveryNotification( - const DeviceMetadata& device) { + const DeviceMetadata& device, DiscoveryCallback callback) { + callback_ = std::move(callback); + NEARBY_LOGS(INFO) << __func__ << "Notify show guest discovery notification. "; NotifyShowDiscovery(device); } +void FastPairNotificationController::OnDiscoveryClicked( + DiscoveryAction action) { + NEARBY_LOGS(INFO) << __func__ + << "Discovery action button is clicked in the app."; + DCHECK(callback_); + callback_(action); +} + } // namespace fastpair } // namespace nearby diff --git a/fastpair/ui/fast_pair/fast_pair_notification_controller.h b/fastpair/ui/fast_pair/fast_pair_notification_controller.h index 7095aa17..5006c565 100644 --- a/fastpair/ui/fast_pair/fast_pair_notification_controller.h +++ b/fastpair/ui/fast_pair/fast_pair_notification_controller.h @@ -18,17 +18,19 @@ #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" #include "fastpair/repository/device_metadata.h" +#include "fastpair/ui/actions.h" #include "internal/base/observer_list.h" namespace nearby { namespace fastpair { -using RepeatingClosure = absl::AnyInvocable; + +using DiscoveryCallback = absl::AnyInvocable; + enum class FastPairNotificationDismissReason { kDismissedByUser, kDismissedByOs, kDismissedByTimeout, }; - // This controller creates and manages messages for each FastPair corresponding // notification event. class FastPairNotificationController { @@ -52,9 +54,14 @@ class FastPairNotificationController { void NotifyShowDiscovery(const DeviceMetadata& device); // Creates and displays corresponding notification. - void ShowGuestDiscoveryNotification(const DeviceMetadata& device_metadata); + void ShowGuestDiscoveryNotification(const DeviceMetadata& device_metadata, + DiscoveryCallback callback); + + // Triggers callback when the related action is clicked. + void OnDiscoveryClicked(DiscoveryAction action); private: + DiscoveryCallback callback_; ObserverList observers_; }; } // namespace fastpair diff --git a/fastpair/ui/fast_pair/fast_pair_notification_controller_test.cc b/fastpair/ui/fast_pair/fast_pair_notification_controller_test.cc index 997b8aa6..6142e2d2 100644 --- a/fastpair/ui/fast_pair/fast_pair_notification_controller_test.cc +++ b/fastpair/ui/fast_pair/fast_pair_notification_controller_test.cc @@ -14,15 +14,15 @@ #include "fastpair/ui/fast_pair/fast_pair_notification_controller.h" -#include -#include #include -#include +#include +#include #include "gmock/gmock.h" #include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" #include "fastpair/repository/device_metadata.h" +#include "fastpair/ui/actions.h" #include "fastpair/ui/fast_pair/fake_fast_pair_notification_controller_observer.h" namespace nearby { @@ -39,12 +39,19 @@ class FastPairNotificationControllerTest : public ::testing::Test { notification_controller_.AddObserver(¬ification_controller_obsesrver_); } - void TriggerOnUpdateDevice(DeviceMetadata& device) { - notification_controller_.ShowGuestDiscoveryNotification(device); + void TriggerOnUpdateDevice(DeviceMetadata& device, + DiscoveryCallback callback) { + notification_controller_.ShowGuestDiscoveryNotification( + device, std::move(callback)); + } + + void DiscoveryActionClicked(DiscoveryAction action) { + discovery_action_ = action; } FastPairNotificationController notification_controller_; FakeFastPairNotificationControllerObserver notification_controller_obsesrver_; + DiscoveryAction discovery_action_; }; TEST_F(FastPairNotificationControllerTest, ShowGuestDiscoveryNotification) { @@ -52,10 +59,14 @@ TEST_F(FastPairNotificationControllerTest, ShowGuestDiscoveryNotification) { response.mutable_device()->set_id(kDeviceId); response.mutable_device()->set_name(kDeviceName); DeviceMetadata device_metadata(response); - TriggerOnUpdateDevice(device_metadata); + TriggerOnUpdateDevice(device_metadata, [this](DiscoveryAction action) { + DiscoveryActionClicked(action); + }); EXPECT_TRUE(notification_controller_obsesrver_ .CheckDeviceMetadataListContainTestDevice(kDeviceName)); EXPECT_EQ(1, notification_controller_obsesrver_.on_update_device_count()); + notification_controller_.OnDiscoveryClicked(DiscoveryAction::kPairToDevice); + EXPECT_EQ(DiscoveryAction::kPairToDevice, discovery_action_); } } // namespace diff --git a/fastpair/ui/fast_pair/fast_pair_presenter.h b/fastpair/ui/fast_pair/fast_pair_presenter.h index 93313f7e..8b758531 100644 --- a/fastpair/ui/fast_pair/fast_pair_presenter.h +++ b/fastpair/ui/fast_pair/fast_pair_presenter.h @@ -15,7 +15,11 @@ #ifndef THIRD_PARTY_NEARBY_FASTPAIR_UI_FAST_PAIR_FAST_PAIR_PRESENTER_H_ #define THIRD_PARTY_NEARBY_FASTPAIR_UI_FAST_PAIR_FAST_PAIR_PRESENTER_H_ +#include + +#include "absl/functional/any_invocable.h" #include "fastpair/common/fast_pair_device.h" +#include "fastpair/ui/actions.h" #include "fastpair/ui/fast_pair/fast_pair_notification_controller.h" namespace nearby { @@ -27,7 +31,8 @@ class FastPairPresenter { // observer_list of notification_controller is updated virtual void ShowDiscovery( const FastPairDevice& device, - FastPairNotificationController& notification_controller) = 0; + FastPairNotificationController& notification_controller, + DiscoveryCallback callback) = 0; virtual ~FastPairPresenter() = default; }; diff --git a/fastpair/ui/fast_pair/fast_pair_presenter_impl.cc b/fastpair/ui/fast_pair/fast_pair_presenter_impl.cc index a5916cdf..78b80f71 100644 --- a/fastpair/ui/fast_pair/fast_pair_presenter_impl.cc +++ b/fastpair/ui/fast_pair/fast_pair_presenter_impl.cc @@ -15,10 +15,13 @@ #include "fastpair/ui/fast_pair/fast_pair_presenter_impl.h" #include +#include +#include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" #include "fastpair/repository/device_metadata.h" #include "fastpair/server_access/fast_pair_repository.h" +#include "fastpair/ui/actions.h" #include "fastpair/ui/fast_pair/fast_pair_notification_controller.h" #include "internal/platform/logging.h" @@ -47,22 +50,24 @@ FastPairPresenterImpl::Factory::~Factory() = default; void FastPairPresenterImpl::ShowDiscovery( const FastPairDevice& device, - FastPairNotificationController& notification_controller) { + FastPairNotificationController& notification_controller, + DiscoveryCallback callback) { + callback_ = std::move(callback); FastPairRepository::Get()->GetDeviceMetadata( - device.GetModelId(), - [¬ification_controller, this](const DeviceMetadata& device_metadata) { + device.GetModelId(), [&device, ¬ification_controller, + this](const DeviceMetadata& device_metadata) { NEARBY_LOGS(INFO) << __func__ << "Retrieved metadata to notification controller."; - FastPairPresenterImpl::OnDiscoveryMetadataRetrieved( - device_metadata, notification_controller); + OnDiscoveryMetadataRetrieved(device, device_metadata, + notification_controller); }); } void FastPairPresenterImpl::OnDiscoveryMetadataRetrieved( - const DeviceMetadata& device_metadata, + const FastPairDevice& device, const DeviceMetadata& device_metadata, FastPairNotificationController& notification_controller) { - notification_controller.ShowGuestDiscoveryNotification(device_metadata); + notification_controller.ShowGuestDiscoveryNotification(device_metadata, + std::move(callback_)); } - } // namespace fastpair } // namespace nearby diff --git a/fastpair/ui/fast_pair/fast_pair_presenter_impl.h b/fastpair/ui/fast_pair/fast_pair_presenter_impl.h index 10bd71b5..c5ee2d97 100644 --- a/fastpair/ui/fast_pair/fast_pair_presenter_impl.h +++ b/fastpair/ui/fast_pair/fast_pair_presenter_impl.h @@ -19,6 +19,7 @@ #include "fastpair/common/fast_pair_device.h" #include "fastpair/repository/device_metadata.h" +#include "fastpair/ui/actions.h" #include "fastpair/ui/fast_pair/fast_pair_notification_controller.h" #include "fastpair/ui/fast_pair/fast_pair_presenter.h" @@ -45,15 +46,16 @@ class FastPairPresenterImpl : public FastPairPresenter { FastPairPresenterImpl(const FastPairPresenterImpl&) = delete; FastPairPresenterImpl& operator=(const FastPairPresenterImpl&) = delete; - void ShowDiscovery( - const FastPairDevice& device, - FastPairNotificationController& notification_controller) override; + void ShowDiscovery(const FastPairDevice& device, + FastPairNotificationController& notification_controller, + DiscoveryCallback callback) override; private: // observer_list of notification_controller is updated void OnDiscoveryMetadataRetrieved( - const DeviceMetadata& device_metadata, + const FastPairDevice& device, const DeviceMetadata& device_metadata, FastPairNotificationController& notification_controller); + DiscoveryCallback callback_; }; } // namespace fastpair } // namespace nearby diff --git a/fastpair/ui/fast_pair/fast_pair_presenter_impl_test.cc b/fastpair/ui/fast_pair/fast_pair_presenter_impl_test.cc index 6929643e..9c33a7b8 100644 --- a/fastpair/ui/fast_pair/fast_pair_presenter_impl_test.cc +++ b/fastpair/ui/fast_pair/fast_pair_presenter_impl_test.cc @@ -22,9 +22,9 @@ #include "fastpair/common/fast_pair_device.h" #include "fastpair/proto/fastpair_rpcs.proto.h" #include "fastpair/server_access/fake_fast_pair_repository.h" +#include "fastpair/ui/actions.h" #include "fastpair/ui/fast_pair/fake_fast_pair_notification_controller_observer.h" #include "fastpair/ui/fast_pair/fast_pair_notification_controller.h" -#include "fastpair/ui/fast_pair/fast_pair_presenter.h" namespace nearby { namespace fastpair { @@ -42,7 +42,11 @@ class FastPairPresenterImplTest : public ::testing::Test { controller_.AddObserver(¬ification_controller_observer_); } + void OnDiscoveryAction(DiscoveryAction action) { discovery_action_ = action; } + protected: + DiscoveryAction discovery_action_; + FakeFastPairRepository repository_; FastPairPresenterImpl fast_pair_presenter_; FastPairNotificationController controller_; @@ -54,8 +58,12 @@ TEST_F(FastPairPresenterImplTest, ShowDiscovery) { Protocol::kFastPairInitialPairing); EXPECT_EQ(0, notification_controller_observer_.on_update_device_count()); - fast_pair_presenter_.ShowDiscovery(device, controller_); + fast_pair_presenter_.ShowDiscovery( + device, controller_, + [this](DiscoveryAction action) { OnDiscoveryAction(action); }); EXPECT_EQ(1, notification_controller_observer_.on_update_device_count()); + controller_.OnDiscoveryClicked(DiscoveryAction::kPairToDevice); + EXPECT_EQ(DiscoveryAction::kPairToDevice, discovery_action_); } } // namespace } // namespace fastpair diff --git a/fastpair/ui/ui_broker.h b/fastpair/ui/ui_broker.h new file mode 100644 index 00000000..046bceea --- /dev/null +++ b/fastpair/ui/ui_broker.h @@ -0,0 +1,50 @@ +// 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. + +#ifndef THIRD_PARTY_NEARBY_FASTPAIR_UI_UI_BROKER_H_ +#define THIRD_PARTY_NEARBY_FASTPAIR_UI_UI_BROKER_H_ + +#include "fastpair/common/fast_pair_device.h" +#include "fastpair/ui/actions.h" +#include "fastpair/ui/fast_pair/fast_pair_notification_controller.h" + +namespace nearby { +namespace fastpair { + +// The UIBroker is the entry point for the UI component in the FastPair system. +// It is responsible for brokering the 'show UI' calls to the correct Presenter +// implementation, and exposing user actions taken on that UI. +class UIBroker { + public: + class Observer { + public: + virtual ~Observer() = default; + virtual void OnDiscoveryAction(const FastPairDevice& device, + DiscoveryAction action) = 0; + }; + + virtual ~UIBroker() = default; + + virtual void AddObserver(Observer* observer) = 0; + virtual void RemoveObserver(Observer* observer) = 0; + + virtual void ShowDiscovery( + const FastPairDevice& device, + FastPairNotificationController& notification_controller) = 0; +}; + +} // namespace fastpair +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_FASTPAIR_UI_UI_BROKER_H_ diff --git a/fastpair/ui/ui_broker_impl.cc b/fastpair/ui/ui_broker_impl.cc new file mode 100644 index 00000000..41e33c2e --- /dev/null +++ b/fastpair/ui/ui_broker_impl.cc @@ -0,0 +1,69 @@ +// 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. + +#include "fastpair/ui/ui_broker_impl.h" + +#include + +#include "fastpair/common/fast_pair_device.h" +#include "fastpair/common/protocol.h" +#include "fastpair/ui/actions.h" +#include "fastpair/ui/fast_pair/fast_pair_presenter.h" +#include "fastpair/ui/fast_pair/fast_pair_presenter_impl.h" +#include "internal/platform/logging.h" + +namespace nearby { +namespace fastpair { + +UIBrokerImpl::UIBrokerImpl() + : fast_pair_presenter_(FastPairPresenterImpl::Factory::Create()) {} + +void UIBrokerImpl::AddObserver(Observer* observer) { + observers_.AddObserver(observer); +} + +void UIBrokerImpl::RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); +} + +void UIBrokerImpl::ShowDiscovery( + const FastPairDevice& device, + FastPairNotificationController& notification_controller) { + switch (device.GetProtocol()) { + case Protocol::kFastPairInitialPairing: + case Protocol::kFastPairSubsequentPairing: + fast_pair_presenter_->ShowDiscovery( + device, notification_controller, + [&device, this](DiscoveryAction action) { + NEARBY_LOGS(INFO) + << __func__ << ": Notify discovery action to all observers."; + NotifyDiscoveryAction(device, action); + }); + break; + case Protocol::kFastPairRetroactivePairing: + NEARBY_LOGS(ERROR) + << __func__ + << ": Retroactive Pairing should not show discovery Halfsheet."; + break; + } +} + +void UIBrokerImpl::NotifyDiscoveryAction(const FastPairDevice& device, + DiscoveryAction action) { + for (auto& observer : observers_.GetObservers()) + observer->OnDiscoveryAction(device, action); +} + +} // namespace fastpair +} // namespace nearby diff --git a/fastpair/ui/ui_broker_impl.h b/fastpair/ui/ui_broker_impl.h new file mode 100644 index 00000000..75bd7a78 --- /dev/null +++ b/fastpair/ui/ui_broker_impl.h @@ -0,0 +1,52 @@ +// 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. + +#ifndef THIRD_PARTY_NEARBY_FASTPAIR_UI_BROKER_IMPL_H_ +#define THIRD_PARTY_NEARBY_FASTPAIR_UI_BROKER_IMPL_H_ + +#include + +#include "fastpair/common/fast_pair_device.h" +#include "fastpair/ui/actions.h" +#include "fastpair/ui/fast_pair/fast_pair_presenter.h" +#include "fastpair/ui/ui_broker.h" +#include "internal/base/observer_list.h" + +namespace nearby { +namespace fastpair { + +class UIBrokerImpl : public UIBroker { + public: + UIBrokerImpl(); + UIBrokerImpl(const UIBrokerImpl &) = delete; + UIBrokerImpl &operator=(const UIBrokerImpl &) = delete; + + void AddObserver(Observer *observer) override; + void RemoveObserver(Observer *observer) override; + void ShowDiscovery( + const FastPairDevice &device, + FastPairNotificationController ¬ification_controller) override; + + private: + void NotifyDiscoveryAction(const FastPairDevice &device, + DiscoveryAction action); + + std::unique_ptr fast_pair_presenter_; + ObserverList observers_; +}; + +} // namespace fastpair +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_FASTPAIR_UI_BROKER_IMPL_H_ diff --git a/fastpair/ui/ui_broker_impl_test.cc b/fastpair/ui/ui_broker_impl_test.cc new file mode 100644 index 00000000..b93a74fa --- /dev/null +++ b/fastpair/ui/ui_broker_impl_test.cc @@ -0,0 +1,76 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "fastpair/ui/ui_broker_impl.h" + +#include +#include + +#include "gtest/gtest.h" +#include "fastpair/ui/actions.h" +#include "fastpair/ui/fast_pair/fake_fast_pair_presenter.h" +#include "fastpair/ui/fast_pair/fast_pair_notification_controller.h" +#include "fastpair/ui/ui_broker.h" + +namespace nearby { +namespace fastpair { +namespace { + +constexpr absl::string_view kModelId = "718C17"; +constexpr absl::string_view kAddress = "74:74:46:01:6C:21"; + +class UIBrokerImplTest : public ::testing::Test, public UIBroker::Observer { + protected: + UIBrokerImplTest() { + presenter_factory_ = std::make_unique(); + FastPairPresenterImpl::Factory::SetFactoryForTesting( + presenter_factory_.get()); + ui_broker_ = std::make_unique(); + ui_broker_->AddObserver(this); + } + + void OnDiscoveryAction(const FastPairDevice& device, + DiscoveryAction action) override { + on_discovery_action_notified_ = true; + discovery_action_ = action; + } + + std::unique_ptr ui_broker_; + FastPairNotificationController notification_controller_; + std::unique_ptr presenter_factory_; + DiscoveryAction discovery_action_; + bool on_discovery_action_notified_ = false; +}; + +TEST_F(UIBrokerImplTest, ShowDiscovery) { + FastPairDevice device(kModelId, kAddress, Protocol::kFastPairInitialPairing); + ui_broker_->ShowDiscovery(device, notification_controller_); + EXPECT_TRUE( + presenter_factory_->fake_fast_pair_presenter()->show_deiscovery()); + EXPECT_TRUE(on_discovery_action_notified_); + EXPECT_EQ(DiscoveryAction::kPairToDevice, discovery_action_); +} + +TEST_F(UIBrokerImplTest, ShowDiscoveryWithoutObserver) { + FastPairDevice device(kModelId, kAddress, Protocol::kFastPairInitialPairing); + ui_broker_->RemoveObserver(this); + ui_broker_->ShowDiscovery(device, notification_controller_); + EXPECT_TRUE( + presenter_factory_->fake_fast_pair_presenter()->show_deiscovery()); + EXPECT_FALSE(on_discovery_action_notified_); +} + +} // namespace +} // namespace fastpair +} // namespace nearby diff --git a/internal/base/BUILD b/internal/base/BUILD index a7cc6631..9e6b2da9 100644 --- a/internal/base/BUILD +++ b/internal/base/BUILD @@ -12,9 +12,8 @@ cc_library( ], visibility = [ "//fastpair:__subpackages__", - "//internal:__pkg__", + "//internal/interop:__pkg__", "//internal/platform:__pkg__", - "//location/nearby/cpp/fastpair:__subpackages__", "//location/nearby/cpp/sharing:__subpackages__", ], deps = [ @@ -38,7 +37,6 @@ cc_library( visibility = [ "//fastpair:__subpackages__", "//internal:__subpackages__", - "//location/nearby/cpp/fastpair:__subpackages__", "//location/nearby/cpp/sharing:__subpackages__", ], deps = [ diff --git a/internal/base/observer_list.h b/internal/base/observer_list.h index a4d44a2f..d91333cb 100644 --- a/internal/base/observer_list.h +++ b/internal/base/observer_list.h @@ -28,6 +28,11 @@ class ObserverList { using const_iterator = typename absl::flat_hash_set::const_iterator; + ~ObserverList() ABSL_LOCKS_EXCLUDED(mutex_) { + MutexLock lock(&mutex_); + observers_.clear(); + } + void AddObserver(ObserverType* observer) ABSL_LOCKS_EXCLUDED(mutex_) { MutexLock lock(&mutex_); observers_.insert(observer); diff --git a/internal/crypto/BUILD b/internal/crypto/BUILD index a9db74d2..e18aadea 100644 --- a/internal/crypto/BUILD +++ b/internal/crypto/BUILD @@ -98,6 +98,7 @@ cc_test( ], deps = [ ":crypto", + "//internal/platform/implementation:types", "@com_github_protobuf_matchers//protobuf-matchers", "@com_google_absl//absl/strings", "@com_google_absl//absl/types:span", diff --git a/internal/crypto/aead.cc b/internal/crypto/aead.cc index 41bedf2a..bbba65ce 100644 --- a/internal/crypto/aead.cc +++ b/internal/crypto/aead.cc @@ -22,7 +22,7 @@ #include #ifdef NEARBY_CHROMIUM -#include "base/check.h" +#include "internal/platform/logging.h" #elif defined(NEARBY_SWIFTPM) #include "internal/platform/logging.h" #else diff --git a/internal/crypto/ec_private_key.cc b/internal/crypto/ec_private_key.cc index 1281a70d..63471e02 100644 --- a/internal/crypto/ec_private_key.cc +++ b/internal/crypto/ec_private_key.cc @@ -24,7 +24,7 @@ #include #ifdef NEARBY_CHROMIUM -#include "base/check.h" +#include "internal/platform/logging.h" #elif defined(NEARBY_SWIFTPM) #include "internal/platform/logging.h" #else diff --git a/internal/crypto/encryptor.cc b/internal/crypto/encryptor.cc index dbb40cda..bc078ab2 100644 --- a/internal/crypto/encryptor.cc +++ b/internal/crypto/encryptor.cc @@ -23,7 +23,7 @@ #include #ifdef NEARBY_CHROMIUM -#include "base/check.h" +#include "internal/platform/logging.h" #elif defined(NEARBY_SWIFTPM) #include "internal/platform/logging.h" #else diff --git a/internal/crypto/hmac.cc b/internal/crypto/hmac.cc index e75c7f71..d9dfd8c6 100644 --- a/internal/crypto/hmac.cc +++ b/internal/crypto/hmac.cc @@ -20,7 +20,7 @@ #include #ifdef NEARBY_CHROMIUM -#include "base/check.h" +#include "internal/platform/logging.h" #elif defined(NEARBY_SWIFTPM) #include "internal/platform/logging.h" #else diff --git a/internal/crypto/nearby_base.cc b/internal/crypto/nearby_base.cc index 47292e24..1547ebaa 100644 --- a/internal/crypto/nearby_base.cc +++ b/internal/crypto/nearby_base.cc @@ -19,7 +19,7 @@ #include #ifdef NEARBY_CHROMIUM -#include "base/check.h" +#include "internal/platform/logging.h" #elif defined(NEARBY_SWIFTPM) #include "internal/platform/logging.h" #else diff --git a/internal/crypto/openssl_util.cc b/internal/crypto/openssl_util.cc index 98dd3b50..acac591d 100644 --- a/internal/crypto/openssl_util.cc +++ b/internal/crypto/openssl_util.cc @@ -19,7 +19,9 @@ #include -#ifdef NEARBY_SWIFTPM +#ifdef NEARBY_CHROMIUM +#include "internal/platform/logging.h" +#elif defined(NEARBY_SWIFTPM) #include "internal/platform/logging.h" #else #include "absl/log/log.h" // nogncheck @@ -30,24 +32,6 @@ namespace crypto { -namespace { - -// Callback routine for OpenSSL to print error messages. |str| is a -// NULL-terminated string of length |len| containing diagnostic information -// such as the library, function and reason for the error, the file and line -// where the error originated, plus potentially any context-specific -// information about the error. |context| contains a pointer to user-supplied -// data, which is currently unused. -// If this callback returns a value <= 0, OpenSSL will stop processing the -// error queue and return, otherwise it will continue calling this function -// until all errors have been removed from the queue. -int OpenSSLErrorCallback(const char* str, size_t len, void* context) { - LOG(INFO) << "\t" << absl::string_view(str, len); - return 1; -} - -} // namespace - void EnsureOpenSSLInit() { // CRYPTO_library_init may be safely called concurrently. CRYPTO_library_init(); diff --git a/internal/crypto/random.cc b/internal/crypto/random.cc index 07615b21..ee0ddf72 100644 --- a/internal/crypto/random.cc +++ b/internal/crypto/random.cc @@ -30,11 +30,4 @@ void RandBytes(absl::Span bytes) { RandBytes(bytes.data(), bytes.size()); } -std::string RandBytes(size_t length) { - std::string result(length, 0); - RandBytes(const_cast(result.data()), - result.size()); - return result; -} - } // namespace crypto diff --git a/internal/crypto/random.h b/internal/crypto/random.h index 213bedc8..4b70063e 100644 --- a/internal/crypto/random.h +++ b/internal/crypto/random.h @@ -12,6 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +//*** WARNING!!! Do not add more functions and Data types to this file. *** +// This file needs to be in sync with: +// https://source.chromium.org/chromium/chromium/src/+/main:crypto/random.h + #ifndef THIRD_PARTY_NEARBY_INTERNAL_CRYPTO_RANDOM_H_ #define THIRD_PARTY_NEARBY_INTERNAL_CRYPTO_RANDOM_H_ @@ -32,18 +36,6 @@ CRYPTO_EXPORT void RandBytes(void *bytes, size_t length); // Fills |bytes| with cryptographically-secure random bits. CRYPTO_EXPORT void RandBytes(absl::Span bytes); -// Returns |length| random bytes. -CRYPTO_EXPORT std::string RandBytes(size_t length); - -// Creates an object of type T initialized with random data. -// This template should be used for simple data types: int, char, etc. -template -T RandData() { - T data; - RandBytes(&data, sizeof(data)); - return data; -} - } // namespace crypto #endif // THIRD_PARTY_NEARBY_INTERNAL_CRYPTO_RANDOM_H_ diff --git a/internal/crypto/random_unittest.cc b/internal/crypto/random_unittest.cc index 0c30222e..4cf33ffa 100644 --- a/internal/crypto/random_unittest.cc +++ b/internal/crypto/random_unittest.cc @@ -20,6 +20,7 @@ #include "gtest/gtest.h" #include "internal/crypto/nearby_base.h" +#include "internal/platform/implementation/crypto.h" // Basic functionality tests. Does NOT test the security of the random data. @@ -46,15 +47,16 @@ TEST(RandBytes, RandBytes) { TEST(RandBytes, RandomString) { constexpr size_t kSize = 30; - std::string bytes = RandBytes(kSize); + std::string bytes(kSize, 0); + RandBytes(const_cast(bytes.data()), bytes.size()); EXPECT_EQ(bytes.size(), kSize); EXPECT_TRUE(!IsTrivial(bytes)); } TEST(RandBytes, RandData) { - uint64_t x = RandData(); - uint64_t y = RandData(); + uint64_t x = nearby::RandData(); + uint64_t y = nearby::RandData(); // Once in a billion years, consecutively generated random numbers will be // the same and the test will fail. diff --git a/internal/crypto/signature_verifier.cc b/internal/crypto/signature_verifier.cc index 363b0f60..8157a384 100644 --- a/internal/crypto/signature_verifier.cc +++ b/internal/crypto/signature_verifier.cc @@ -17,7 +17,7 @@ #include #ifdef NEARBY_CHROMIUM -#include "base/check.h" +#include "internal/platform/logging.h" #elif defined(NEARBY_SWIFTPM) #include "internal/platform/logging.h" #else diff --git a/internal/crypto/symmetric_key.cc b/internal/crypto/symmetric_key.cc index 7cdebcd6..a3beddfd 100644 --- a/internal/crypto/symmetric_key.cc +++ b/internal/crypto/symmetric_key.cc @@ -23,7 +23,7 @@ #include #ifdef NEARBY_CHROMIUM -#include "base/check.h" +#include "internal/platform/logging.h" #elif defined(NEARBY_SWIFTPM) #include "internal/platform/logging.h" #else diff --git a/internal/BUILD b/internal/interop/BUILD similarity index 83% rename from internal/BUILD rename to internal/interop/BUILD index 7d724792..f5423d06 100644 --- a/internal/BUILD +++ b/internal/interop/BUILD @@ -2,6 +2,7 @@ cc_library( name = "device", hdrs = [ "device.h", + "device_provider.h", ], visibility = [ "//connections:__subpackages__", @@ -9,6 +10,7 @@ cc_library( ], deps = [ "//internal/platform:connection_info", + "//internal/platform:types", "@com_google_absl//absl/strings", "@com_google_absl//absl/types:variant", ], diff --git a/internal/device.h b/internal/interop/device.h similarity index 90% rename from internal/device.h rename to internal/interop/device.h index 82f2f9b7..d13979f3 100644 --- a/internal/device.h +++ b/internal/interop/device.h @@ -41,10 +41,6 @@ class NearbyDevice { }; NearbyDevice() = default; virtual ~NearbyDevice() = default; - NearbyDevice(NearbyDevice&&) = default; - NearbyDevice& operator=(NearbyDevice&&) = default; - NearbyDevice(const NearbyDevice&) = delete; - NearbyDevice& operator=(const NearbyDevice&) = delete; virtual std::string GetEndpointId() const = 0; // We will be adding more ConnectionInfo types to this variant as they are // implemented. diff --git a/connections/device_provider.h b/internal/interop/device_provider.h similarity index 68% rename from connections/device_provider.h rename to internal/interop/device_provider.h index f46915ed..828b2613 100644 --- a/connections/device_provider.h +++ b/internal/interop/device_provider.h @@ -15,22 +15,21 @@ #ifndef THIRD_PARTY_NEARBY_CONNECTIONS_DEVICE_PROVIDER_H_ #define THIRD_PARTY_NEARBY_CONNECTIONS_DEVICE_PROVIDER_H_ -#include - -#include "internal/device.h" +#include "internal/interop/device.h" namespace nearby { -namespace connections { - -using ::nearby::NearbyDevice; +// The base device provider class for use with the Nearby Connections V3 APIs. +// This class currently provides a function to get the local device for whatever +// client implements it. +template ::value>::type* = nullptr> class NearbyDeviceProvider { + public: virtual ~NearbyDeviceProvider() = default; - virtual NearbyDevice* GetLocalDevice() = 0; - virtual std::string GetServiceId() = 0; -}; -} // namespace connections + const virtual T& GetLocalDevice() = 0; +}; } // namespace nearby #endif // THIRD_PARTY_NEARBY_CONNECTIONS_DEVICE_PROVIDER_H_ diff --git a/internal/network/BUILD b/internal/network/BUILD index 0aa6ae8e..228863bc 100644 --- a/internal/network/BUILD +++ b/internal/network/BUILD @@ -26,7 +26,6 @@ cc_library( "//location/nearby/cpp/sharing:__subpackages__", ], deps = [ - "//internal/platform:types", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", @@ -54,11 +53,11 @@ cc_library( deps = [ ":types", "//internal/platform:logging", - "//internal/platform:types", "//internal/platform/implementation:platform", "@com_google_absl//absl/base:core_headers", - "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/strings:str_format", + "@com_google_absl//absl/synchronization", ], ) diff --git a/internal/network/http_client.h b/internal/network/http_client.h index ac8e2bff..a99362c3 100644 --- a/internal/network/http_client.h +++ b/internal/network/http_client.h @@ -1,4 +1,4 @@ -// Copyright 2021-2023 Google LLC +// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -28,15 +28,10 @@ class HttpClient { public: virtual ~HttpClient() = default; - // Starts HTTP request in asynchronization mode. virtual void StartRequest( const HttpRequest& request, std::function&)> callback) = 0; - // Gets HTTP response in synchronization mode. - virtual absl::StatusOr GetResponse( - const HttpRequest& request) = 0; - // The error may be corrected if retried at a later time. static bool IsRetryableHttpError(absl::Status status) { return absl::IsUnavailable(status) || absl::IsFailedPrecondition(status); diff --git a/internal/network/http_client_impl.cc b/internal/network/http_client_impl.cc index ed59f5ee..3f7b4a77 100644 --- a/internal/network/http_client_impl.cc +++ b/internal/network/http_client_impl.cc @@ -1,4 +1,4 @@ -// Copyright 2021-2023 Google LLC +// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,135 +14,107 @@ #include "internal/network/http_client_impl.h" +#include // NOLINT #include -#include +#include // NOLINT #include #include #include -#include "absl/status/statusor.h" #include "internal/network/debug.h" #include "internal/platform/implementation/platform.h" #include "internal/platform/logging.h" -#include "internal/platform/mutex_lock.h" namespace nearby { namespace network { -namespace { - -// In nearby SDK, allowed maximum thread count. -constexpr int kMaxNetworkThreadCount = 3; - -} // namespace - -NearbyHttpClient::NearbyHttpClient() { - network_executor_ = - std::make_unique(kMaxNetworkThreadCount); -} void NearbyHttpClient::StartRequest( const HttpRequest& request, std::function&)> callback) { - MutexLock lock(&mutex_); - NEARBY_LOGS(INFO) << __func__ << ": Start async request to url=" - << request.GetUrl().GetUrlPath(); - if (network_executor_ == nullptr) { - callback(absl::ResourceExhaustedError("no available thread")); - return; - } + absl::MutexLock lock(&mutex_); + CleanThreads(); - network_executor_->Execute([&, request, callback]() { - absl::StatusOr response = InternalGetResponse(request); - if (response.ok()) { - NEARBY_LOGS(INFO) << __func__ << ": Got response from url=" - << request.GetUrl().GetUrlPath(); - } else { - NEARBY_LOGS(ERROR) << __func__ << ": Failed to get response from url=" - << request.GetUrl().GetUrlPath() << ", status" - << response.status(); + std::future http_thread = std::async(std::launch::async, [&, request, + callback]() { + api::WebRequest web_request; + web_request.url = request.GetUrl().GetUrlPath(); + web_request.method = absl::StrCat(request.GetMethodString()); + for (const auto& header : request.GetAllHeaders()) { + for (const auto& value : header.second) { + web_request.headers.emplace(header.first, value); + } + } + web_request.body = absl::StrCat(request.GetBody().GetRawData()); + + if (debug::kRequestEnabled) { + std::stringstream request_stream; + request_stream << "HTTP REQUEST====>" << std::endl; + request_stream << web_request.method << " " << web_request.url + << std::endl; + for (const auto& header : web_request.headers) { + request_stream << header.first << ": " << header.second << std::endl; + } + request_stream << std::endl; + request_stream << "body size: " << request.GetBody().GetRawData().size() + << std::endl; + NEARBY_LOGS(VERBOSE) << request_stream.str(); } - callback(response); - NEARBY_LOGS(INFO) << __func__ << ": Completed request to url=" - << request.GetUrl().GetUrlPath(); - }); -} + absl::StatusOr web_response = + api::ImplementationPlatform::SendRequest(web_request); -absl::StatusOr NearbyHttpClient::GetResponse( - const HttpRequest& request) { - NEARBY_LOGS(INFO) << __func__ << ": Start request to url=" - << request.GetUrl().GetUrlPath(); - - absl::StatusOr response = InternalGetResponse(request); - if (response.ok()) { - NEARBY_LOGS(INFO) << __func__ << ": Got response from url=" - << request.GetUrl().GetUrlPath(); - } else { - NEARBY_LOGS(ERROR) << __func__ << ": Failed to get response from url=" - << request.GetUrl().GetUrlPath() << ", status" - << response.status(); - } - - return response; -} - -absl::StatusOr NearbyHttpClient::InternalGetResponse( - const HttpRequest& request) { - api::WebRequest web_request; - web_request.url = request.GetUrl().GetUrlPath(); - web_request.method = absl::StrCat(request.GetMethodString()); - for (const auto& header : request.GetAllHeaders()) { - for (const auto& value : header.second) { - web_request.headers.emplace(header.first, value); + if (!web_response.ok()) { + if (callback != nullptr) { + callback(web_response.status()); + } + return; } - } - web_request.body = absl::StrCat(request.GetBody().GetRawData()); - if (debug::kRequestEnabled) { - std::stringstream request_stream; - request_stream << "HTTP REQUEST====>" << std::endl; - request_stream << web_request.method << " " << web_request.url << std::endl; - for (const auto& header : web_request.headers) { - request_stream << header.first << ": " << header.second << std::endl; + if (debug::kResponseEnabled) { + std::stringstream response_stream; + response_stream << "HTTP RESPONSE====>" << std::endl; + response_stream << "url: " << web_request.url << std::endl; + response_stream << web_response->status_code << " " + << web_response->status_text << std::endl; + for (const auto& header : web_response->headers) { + response_stream << header.first << ": " << header.second << std::endl; + } + response_stream << std::endl; + response_stream << "body size: " << web_response->body.size() + << std::endl; + NEARBY_LOGS(VERBOSE) << response_stream.str(); } - request_stream << std::endl; - request_stream << "body size: " << request.GetBody().GetRawData().size() - << std::endl; - NEARBY_LOGS(VERBOSE) << request_stream.str(); - } - absl::StatusOr web_response = - api::ImplementationPlatform::SendRequest(web_request); + HttpResponse response; - if (!web_response.ok()) { - return web_response.status(); - } - - if (debug::kResponseEnabled) { - std::stringstream response_stream; - response_stream << "HTTP RESPONSE====>" << std::endl; - response_stream << "url: " << web_request.url << std::endl; - response_stream << web_response->status_code << " " - << web_response->status_text << std::endl; + response.SetStatusCode( + static_cast(web_response->status_code)); + response.SetReasonPhrase(web_response->status_text); for (const auto& header : web_response->headers) { - response_stream << header.first << ": " << header.second << std::endl; + response.AddHeader(header.first, header.second); + } + response.SetBody(web_response->body); + + if (callback != nullptr) { + callback(response); + } + }); + + http_threads_.push_back(std::move(http_thread)); +} + +void NearbyHttpClient::CleanThreads() { + auto it = http_threads_.begin(); + + while (it != http_threads_.end()) { + // Delete the thread if it is ready + auto status = it->wait_for(std::chrono::seconds(0)); + if (status == std::future_status::ready) { + it = http_threads_.erase(it); + } else { + ++it; } - response_stream << std::endl; - response_stream << "body size: " << web_response->body.size() << std::endl; - NEARBY_LOGS(VERBOSE) << response_stream.str(); } - - HttpResponse response; - - response.SetStatusCode( - static_cast(web_response->status_code)); - response.SetReasonPhrase(web_response->status_text); - for (const auto& header : web_response->headers) { - response.AddHeader(header.first, header.second); - } - response.SetBody(web_response->body); - - return response; } } // namespace network diff --git a/internal/network/http_client_impl.h b/internal/network/http_client_impl.h index 6a350927..d0e0646c 100644 --- a/internal/network/http_client_impl.h +++ b/internal/network/http_client_impl.h @@ -1,4 +1,4 @@ -// Copyright 2022-2023 Google LLC +// 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. @@ -16,20 +16,21 @@ #define THIRD_PARTY_NEARBY_INTERNAL_NETWORK_HTTP_CLIENT_IMPL_H_ #include -#include +#include // NOLINT +#include // NOLINT +#include +#include #include "absl/base/thread_annotations.h" -#include "absl/status/statusor.h" +#include "absl/synchronization/mutex.h" #include "internal/network/http_client.h" -#include "internal/platform/multi_thread_executor.h" -#include "internal/platform/mutex.h" namespace nearby { namespace network { class NearbyHttpClient : public HttpClient { public: - NearbyHttpClient(); + NearbyHttpClient() = default; ~NearbyHttpClient() override = default; NearbyHttpClient(const NearbyHttpClient&) = default; @@ -37,19 +38,15 @@ class NearbyHttpClient : public HttpClient { NearbyHttpClient(NearbyHttpClient&&) = default; NearbyHttpClient& operator=(NearbyHttpClient&&) = default; - // Starts HTTP request in asynchronization mode. void StartRequest(const HttpRequest& request, std::function&)> callback) override ABSL_LOCKS_EXCLUDED(mutex_); - // Gets HTTP response in synchronization mode. - absl::StatusOr GetResponse(const HttpRequest& request) override; - private: - absl::StatusOr InternalGetResponse(const HttpRequest& request); + void CleanThreads() ABSL_SHARED_LOCKS_REQUIRED(mutex_); - Mutex mutex_; - std::unique_ptr network_executor_ = nullptr; + absl::Mutex mutex_; + std::vector> http_threads_ ABSL_GUARDED_BY(mutex_); }; } // namespace network diff --git a/internal/network/http_client_impl_test.cc b/internal/network/http_client_impl_test.cc index 887e2265..2f24e8ad 100644 --- a/internal/network/http_client_impl_test.cc +++ b/internal/network/http_client_impl_test.cc @@ -1,4 +1,4 @@ -// Copyright 2021-2023 Google LLC +// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -89,7 +89,7 @@ class NearbyHttpClientTest : public ::testing::Test { api::WebRequest GetWebRequest() { return api::GetContext()->web_request; } - absl::StatusOr GetResponseAsync( + absl::StatusOr GetResponse( absl::string_view url, HttpRequestMethod method, const std::multimap& headers, absl::string_view body) { @@ -121,28 +121,6 @@ class NearbyHttpClientTest : public ::testing::Test { return result; } - absl::StatusOr GetResponse( - absl::string_view url, HttpRequestMethod method, - const std::multimap& headers, - absl::string_view body) { - absl::StatusOr result; - absl::StatusOr request_url = Url::Create(url); - if (!request_url.ok()) { - return request_url.status(); - } - - HttpRequest request{request_url.value()}; - auto it = headers.begin(); - while (it != headers.end()) { - request.AddHeader(it->first, it->second); - ++it; - } - request.SetMethod(method); - request.SetBody(body); - - return client_.GetResponse(request); - } - void CheckHeader(const std::multimap& headers, absl::string_view key, absl::string_view expected_value) { auto it = headers.find(std::string(key)); @@ -168,8 +146,8 @@ namespace { TEST_F(NearbyHttpClientTest, TestGet) { MockResponse(HttpStatusCode::kHttpOk, "OK", {{"Content_Type", "text/html"}}, "web content"); - auto result = GetResponseAsync("http://www.google.com", - HttpRequestMethod::kGet, {}, ""); + auto result = + GetResponse("http://www.google.com", HttpRequestMethod::kGet, {}, ""); // Checks request. api::WebRequest web_request = GetWebRequest(); @@ -187,8 +165,8 @@ TEST_F(NearbyHttpClientTest, TestGet) { TEST_F(NearbyHttpClientTest, TestGetWithQuery) { MockResponse(HttpStatusCode::kHttpOk, "OK", {{"Content_Type", "text/html"}}, "web content"); - auto result = GetResponseAsync("http://www.google.com?name=name1&age=36", - HttpRequestMethod::kGet, {}, ""); + auto result = GetResponse("http://www.google.com?name=name1&age=36", + HttpRequestMethod::kGet, {}, ""); // Checks request. api::WebRequest web_request = GetWebRequest(); @@ -202,8 +180,8 @@ TEST_F(NearbyHttpClientTest, TestGetWithQuery) { TEST_F(NearbyHttpClientTest, TestGetWithErrorResult) { MockFailedResponse(absl::InternalError("no connection.")); - auto result = GetResponseAsync("http://www.google.com?name=name1&age=36", - HttpRequestMethod::kGet, {}, ""); + auto result = GetResponse("http://www.google.com?name=name1&age=36", + HttpRequestMethod::kGet, {}, ""); // Checks request. api::WebRequest web_request = GetWebRequest(); @@ -214,24 +192,6 @@ TEST_F(NearbyHttpClientTest, TestGetWithErrorResult) { EXPECT_FALSE(result.ok()); } -TEST_F(NearbyHttpClientTest, TestPostAsync) { - MockResponse(HttpStatusCode::kHttpNoContent, "OK", - {{"Content_Type", "text/html"}}, ""); - auto result = GetResponseAsync("http://www.google.com", - HttpRequestMethod::kPost, {}, ""); - - // Checks request. - api::WebRequest web_request = GetWebRequest(); - EXPECT_EQ(web_request.url, "http://www.google.com"); - EXPECT_EQ(web_request.method, "POST"); - - // Checks response. - ASSERT_TRUE(result.ok()); - EXPECT_EQ(result->GetStatusCode(), HttpStatusCode::kHttpNoContent); - HttpResponseBody body = result->GetBody(); - EXPECT_TRUE(body.empty()); -} - TEST_F(NearbyHttpClientTest, TestPost) { MockResponse(HttpStatusCode::kHttpNoContent, "OK", {{"Content_Type", "text/html"}}, ""); @@ -250,12 +210,12 @@ TEST_F(NearbyHttpClientTest, TestPost) { EXPECT_TRUE(body.empty()); } -TEST_F(NearbyHttpClientTest, TestPostWithHeaderAsync) { +TEST_F(NearbyHttpClientTest, TestPostWithHeader) { MockResponse(HttpStatusCode::kHttpNoContent, "OK", {{"Content_Type", "text/html"}}, ""); auto result = - GetResponseAsync("http://www.google.com", HttpRequestMethod::kPost, - {{"Content_Type", "text/json"}, {"size", "596"}}, ""); + GetResponse("http://www.google.com", HttpRequestMethod::kPost, + {{"Content_Type", "text/json"}, {"size", "596"}}, ""); // Checks request. api::WebRequest web_request = GetWebRequest(); @@ -271,11 +231,11 @@ TEST_F(NearbyHttpClientTest, TestPostWithHeaderAsync) { EXPECT_EQ(result->GetBody().GetRawData(), ""); } -TEST_F(NearbyHttpClientTest, TestPostWithErrorResultAsync) { +TEST_F(NearbyHttpClientTest, TestPostWithErrorResult) { MockFailedResponse(absl::UnauthenticatedError("no user.")); auto result = - GetResponseAsync("http://www.google.com", HttpRequestMethod::kPost, - {{"Content_Type", "text/json"}, {"size", "596"}}, ""); + GetResponse("http://www.google.com", HttpRequestMethod::kPost, + {{"Content_Type", "text/json"}, {"size", "596"}}, ""); // Checks request. api::WebRequest web_request = GetWebRequest(); @@ -289,11 +249,11 @@ TEST_F(NearbyHttpClientTest, TestPostWithErrorResultAsync) { ASSERT_FALSE(result.ok()); } -TEST_F(NearbyHttpClientTest, TestRequestWithCleanThreadsAsync) { +TEST_F(NearbyHttpClientTest, TestRequestWithCleanThreads) { MockResponse(HttpStatusCode::kHttpOk, "OK", {{"Content_Type", "text/html"}}, "web content"); - auto result = GetResponseAsync("http://www.google.com", - HttpRequestMethod::kGet, {}, ""); + auto result = + GetResponse("http://www.google.com", HttpRequestMethod::kGet, {}, ""); // Checks request. api::WebRequest web_request = GetWebRequest(); @@ -303,8 +263,8 @@ TEST_F(NearbyHttpClientTest, TestRequestWithCleanThreadsAsync) { // Checks response. ASSERT_TRUE(result.ok()); - result = GetResponseAsync("http://www.youtube.com", HttpRequestMethod::kGet, - {}, ""); + result = + GetResponse("http://www.youtube.com", HttpRequestMethod::kGet, {}, ""); ASSERT_TRUE(result.ok()); } diff --git a/internal/platform/BUILD b/internal/platform/BUILD index 7de29f48..ab66288c 100644 --- a/internal/platform/BUILD +++ b/internal/platform/BUILD @@ -46,9 +46,11 @@ cc_library( visibility = [ "//connections:__subpackages__", "//fastpair:__subpackages__", - "//internal:__pkg__", + "//internal/auth:__subpackages__", + "//internal/interop:__pkg__", "//internal/platform:__subpackages__", "//internal/platform/implementation:__subpackages__", + "//internal/preferences:__subpackages__", "//internal/weave:__pkg__", "//location/nearby/cpp:__subpackages__", "//presence:__subpackages__", @@ -99,7 +101,7 @@ cc_library( visibility = [ "//connections:__subpackages__", "//fastpair:__subpackages__", - "//internal:__pkg__", + "//internal/interop:__pkg__", "//internal/network:__subpackages__", "//internal/platform:__subpackages__", "//internal/proto/analytics:__subpackages__", @@ -149,7 +151,7 @@ cc_library( "wifi_lan_connection_info.h", ], visibility = [ - "//internal:__pkg__", + "//internal/interop:__pkg__", "//presence:__subpackages__", ], deps = [ @@ -346,8 +348,9 @@ cc_library( "//fastpair:__subpackages__", "//internal/base:__subpackages__", "//internal/flags:__subpackages__", - "//internal/network:__subpackages__", + "//internal/interop:__pkg__", "//internal/platform/implementation/windows:__subpackages__", + "//internal/preferences:__subpackages__", "//internal/test:__subpackages__", "//internal/weave:__pkg__", "//location/nearby/cpp:__subpackages__", diff --git a/internal/platform/ble_connection_info.cc b/internal/platform/ble_connection_info.cc index 1f4b573e..e5b0e611 100644 --- a/internal/platform/ble_connection_info.cc +++ b/internal/platform/ble_connection_info.cc @@ -15,10 +15,10 @@ #include "internal/platform/ble_connection_info.h" #include +#include #include "absl/status/status.h" #include "absl/status/statusor.h" -#include "absl/strings/str_format.h" #include "absl/strings/string_view.h" #include "internal/platform/connection_info.h" @@ -41,22 +41,22 @@ std::string BleConnectionInfo::ToDataElementBytes() const { mask |= has_psm ? kPsmMask : 0; payload_data.push_back(mask); if (has_mac) { - payload_data.insert(payload_data.end(), mac_address_.begin(), + payload_data.append(mac_address_.begin(), mac_address_.end()); } if (has_gatt) { payload_data.push_back(gatt_characteristic_.length()); - payload_data.insert(payload_data.end(), gatt_characteristic_.begin(), + payload_data.append(gatt_characteristic_.begin(), gatt_characteristic_.end()); } if (has_psm) { - payload_data.insert(payload_data.end(), psm_.begin(), psm_.end()); + payload_data.append(psm_.begin(), psm_.end()); } - payload_data.push_back(actions_); + payload_data.append(actions_.begin(), actions_.end()); std::string ret; ret.push_back(kDataElementFieldType); ret.push_back(payload_data.size()); - ret.insert(ret.end(), payload_data.begin(), payload_data.end()); + ret.append(payload_data.begin(), payload_data.end()); return ret; } @@ -111,12 +111,9 @@ absl::StatusOr BleConnectionInfo::FromDataElementBytes( return absl::InvalidArgumentError( "Insufficient remaining bytes to read action."); } - char action = bytes[position]; - // Check that we don't have any remaining bytes. - if (bytes.size() != ++position) { - return absl::InvalidArgumentError(absl::StrFormat( - "Nonzero remaining bytes: %d.", bytes.size() - position)); - } - return BleConnectionInfo(address, characteristic, psm, action); + auto action_str = bytes.substr(position); + return BleConnectionInfo( + address, characteristic, psm, + std::vector(action_str.begin(), action_str.end())); } } // namespace nearby diff --git a/internal/platform/ble_connection_info.h b/internal/platform/ble_connection_info.h index fee467fe..db5f7dc8 100644 --- a/internal/platform/ble_connection_info.h +++ b/internal/platform/ble_connection_info.h @@ -16,6 +16,7 @@ #define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_BLE_CONNECTION_INFO_H_ #include +#include #include "absl/status/statusor.h" #include "absl/strings/string_view.h" @@ -31,7 +32,7 @@ class BleConnectionInfo : public ConnectionInfo { BleConnectionInfo(absl::string_view mac_address, absl::string_view gatt_characteristic, - absl::string_view psm, char actions) + absl::string_view psm, std::vector actions) : mac_address_(std::string(mac_address)), gatt_characteristic_(std::string(gatt_characteristic)), psm_(std::string(psm)), @@ -45,18 +46,18 @@ class BleConnectionInfo : public ConnectionInfo { std::string GetMacAddress() const { return mac_address_; } std::string GetGattCharacteristic() const { return gatt_characteristic_; } std::string GetPsm() const { return psm_; } - char GetActions() const override { return actions_; } + std::vector GetActions() const override { return actions_; } private: std::string mac_address_; std::string gatt_characteristic_; std::string psm_; - char actions_ = 0; + std::vector actions_; }; inline bool operator==(const BleConnectionInfo& a, const BleConnectionInfo& b) { return a.GetMacAddress() == b.GetMacAddress() && - a.GetActions() == b.GetActions() && + a.GetActions() == b.GetActions() && a.GetPsm() == b.GetPsm() && a.GetGattCharacteristic() == b.GetGattCharacteristic(); } diff --git a/internal/platform/ble_connection_info_test.cc b/internal/platform/ble_connection_info_test.cc index 99a98f56..e27f2bd6 100644 --- a/internal/platform/ble_connection_info_test.cc +++ b/internal/platform/ble_connection_info_test.cc @@ -38,10 +38,11 @@ constexpr absl::string_view kPsm = "\x45\x56"; constexpr char kAction = 0x0F; TEST(BleConnectionInfoTest, TestGetFields) { - BleConnectionInfo info(kMacAddr, kGattCharacteristic, kPsm, kAction); + BleConnectionInfo info(kMacAddr, kGattCharacteristic, kPsm, {kAction}); EXPECT_EQ(info.GetMediumType(), Medium::BLE); EXPECT_EQ(info.GetGattCharacteristic(), kGattCharacteristic); - EXPECT_EQ(info.GetActions(), kAction); + ASSERT_EQ(info.GetActions().size(), 1); + ASSERT_EQ(info.GetActions()[0], kAction); EXPECT_EQ(info.GetMacAddress(), kMacAddr); EXPECT_EQ(info.GetPsm(), kPsm); } @@ -57,7 +58,7 @@ TEST(BleConnectionInfoTest, TestFromInvalidBytes) { } TEST(BleConnectionInfoTest, TestFromNoAction) { - BleConnectionInfo info(kMacAddr, kGattCharacteristic, kPsm, kAction); + BleConnectionInfo info(kMacAddr, kGattCharacteristic, kPsm, {kAction}); std::string serialized = info.ToDataElementBytes(); serialized[1] -= 1; auto result = BleConnectionInfo::FromDataElementBytes( @@ -66,7 +67,7 @@ TEST(BleConnectionInfoTest, TestFromNoAction) { } TEST(BleConnectionInfoTest, TestToFromBytes) { - BleConnectionInfo info(kMacAddr, kGattCharacteristic, kPsm, kAction); + BleConnectionInfo info(kMacAddr, kGattCharacteristic, kPsm, {kAction}); std::string serialized = info.ToDataElementBytes(); auto result = BleConnectionInfo::FromDataElementBytes(serialized); ASSERT_OK(result); @@ -78,7 +79,7 @@ TEST(BleConnectionInfoTest, TestToFromBytes) { } TEST(BleConnectionInfoTest, TestToFromBytesNoGattCharacteristic) { - BleConnectionInfo info(kMacAddr, "", kPsm, kAction); + BleConnectionInfo info(kMacAddr, "", kPsm, {kAction}); std::string serialized = info.ToDataElementBytes(); auto result = BleConnectionInfo::FromDataElementBytes(serialized); ASSERT_OK(result); @@ -90,7 +91,7 @@ TEST(BleConnectionInfoTest, TestToFromBytesNoGattCharacteristic) { } TEST(BleConnectionInfoTest, TestToFromBytesNoMac) { - BleConnectionInfo info("", kGattCharacteristic, kPsm, kAction); + BleConnectionInfo info("", kGattCharacteristic, kPsm, {kAction}); std::string serialized = info.ToDataElementBytes(); auto result = BleConnectionInfo::FromDataElementBytes(serialized); ASSERT_OK(result); @@ -103,7 +104,7 @@ TEST(BleConnectionInfoTest, TestToFromBytesNoMac) { TEST(BleConnectionInfoTest, TestToFromBytesLongMac) { BleConnectionInfo info(absl::StrCat(kMacAddr, kMacAddr), kGattCharacteristic, - kPsm, kAction); + kPsm, {kAction}); std::string serialized = info.ToDataElementBytes(); auto result = BleConnectionInfo::FromDataElementBytes(serialized); ASSERT_OK(result); @@ -115,7 +116,7 @@ TEST(BleConnectionInfoTest, TestToFromBytesLongMac) { } TEST(BleConnectionInfoTest, TestToFromBytesNoPsm) { - BleConnectionInfo info(kMacAddr, kGattCharacteristic, "", kAction); + BleConnectionInfo info(kMacAddr, kGattCharacteristic, "", {kAction}); std::string serialized = info.ToDataElementBytes(); auto result = BleConnectionInfo::FromDataElementBytes(serialized); ASSERT_OK(result); @@ -128,7 +129,7 @@ TEST(BleConnectionInfoTest, TestToFromBytesNoPsm) { TEST(BleConnectionInfoTest, TestToFromBytesLongPsm) { BleConnectionInfo info(kMacAddr, kGattCharacteristic, - absl::StrCat(kPsm, kPsm), kAction); + absl::StrCat(kPsm, kPsm), {kAction}); std::string serialized = info.ToDataElementBytes(); auto result = BleConnectionInfo::FromDataElementBytes(serialized); ASSERT_OK(result); @@ -145,186 +146,22 @@ TEST(BleConnectionInfoTest, TestFromEmpty) { } TEST(BleConnectionInfoTest, TestFromBadElementType) { - BleConnectionInfo info(kMacAddr, kGattCharacteristic, kPsm, kAction); + BleConnectionInfo info(kMacAddr, kGattCharacteristic, kPsm, {kAction}); std::string serialized = info.ToDataElementBytes(); serialized[0] = 0x56; auto result = BleConnectionInfo::FromDataElementBytes(serialized); EXPECT_THAT(result, StatusIs(absl::StatusCode::kInvalidArgument)); } -TEST(BleConnectionInfoTest, TestFromBadMask) { - BleConnectionInfo info(kMacAddr, kGattCharacteristic, kPsm, kAction); - std::string serialized = info.ToDataElementBytes(); - // Set mask for MAC address only. - serialized[3] = 0x40; - EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for GATT characteristic only. - serialized[3] = 0x20; - EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for PSM only. - serialized[3] = 0x10; - EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Remove all masks. - serialized[3] = 0x00; - EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for MAC address + GATT characteristic. - serialized[3] = 0x60; - EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for GATT characteristic + PSM. - serialized[3] = 0x30; - EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for MAC address and PSM. - serialized[3] = 0x50; - EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Verify OK with the correct mask. - serialized[3] = 0x70; - EXPECT_OK(BleConnectionInfo::FromDataElementBytes(serialized)); -} - -TEST(BleConnectionInfoTest, TestFromBadMaskNoMac) { - BleConnectionInfo info("", kGattCharacteristic, kPsm, kAction); - std::string serialized = info.ToDataElementBytes(); - // Set mask for MAC address only. - serialized[3] = 0x40; - EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for GATT characteristic only. - serialized[3] = 0x20; - EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for PSM only. - serialized[3] = 0x10; - EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for MAC address + GATT characteristic. - serialized[3] = 0x60; - EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for MAC address + PSM. - serialized[3] = 0x50; - EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Remove all masks. - serialized[3] = 0x00; - EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Verify OK with the correct mask. - serialized[3] = 0x30; - EXPECT_OK(BleConnectionInfo::FromDataElementBytes(serialized)); -} - -TEST(BleConnectionInfoTest, TestFromBadMaskNoGattCharacteristic) { - BleConnectionInfo info(kMacAddr, "", kPsm, kAction); - std::string serialized = info.ToDataElementBytes(); - // Set mask for MAC address only. - serialized[3] = 0x40; - EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for GATT characteristic only. - serialized[3] = 0x20; - EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for PSM only. - serialized[3] = 0x10; - EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for all fields. - serialized[3] = 0x70; - EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for MAC address + GATT characteristic. - serialized[3] = 0x60; - EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for GATT characteristic + PSM. - serialized[3] = 0x30; - EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Remove all masks. - serialized[3] = 0x00; - EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Verify OK with the correct mask. - serialized[3] = 0x50; - EXPECT_OK(BleConnectionInfo::FromDataElementBytes(serialized)); -} - -TEST(BleConnectionInfoTest, TestFromBadMaskNoPsm) { - BleConnectionInfo info(kMacAddr, kGattCharacteristic, "", kAction); - std::string serialized = info.ToDataElementBytes(); - // Set mask for MAC address only. - serialized[3] = 0x40; - EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for GATT characteristic only. - serialized[3] = 0x20; - EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for PSM only. - serialized[3] = 0x10; - EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Remove all masks. - serialized[3] = 0x00; - EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for all fields. - serialized[3] = 0x70; - EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for GATT characteristic + PSM. - serialized[3] = 0x30; - EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for MAC address + PSM. - serialized[3] = 0x50; - EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Verify OK with the correct mask. - serialized[3] = 0x60; - EXPECT_OK(BleConnectionInfo::FromDataElementBytes(serialized)); -} - -TEST(BleConnectionInfoTest, TestFromBadMaskEmpty) { - BleConnectionInfo info("", "", "", kAction); - std::string serialized = info.ToDataElementBytes(); - // Set mask for MAC address only. - serialized[3] = 0x40; - EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for GATT characteristic only. - serialized[3] = 0x20; - EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for PSM only. - serialized[3] = 0x10; - EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for all fields. - serialized[3] = 0x70; - EXPECT_THAT(BleConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Verify OK with correct mask. - serialized[3] = 0x00; - EXPECT_OK(BleConnectionInfo::FromDataElementBytes(serialized)); -} - TEST(BleConnectionInfoTest, TestCopy) { - BleConnectionInfo info(kMacAddr, kGattCharacteristic, kPsm, kAction); + BleConnectionInfo info(kMacAddr, kGattCharacteristic, kPsm, {kAction}); BleConnectionInfo copy(info); EXPECT_EQ(info, copy); } TEST(BleConnectionInfoTest, TestEquals) { - BleConnectionInfo info(kMacAddr, kGattCharacteristic, kPsm, kAction); - BleConnectionInfo info2(kMacAddr, kGattCharacteristic, kPsm, kAction); + BleConnectionInfo info(kMacAddr, kGattCharacteristic, kPsm, {kAction}); + BleConnectionInfo info2(kMacAddr, kGattCharacteristic, kPsm, {kAction}); EXPECT_EQ(info, info2); } diff --git a/internal/platform/ble_v2.cc b/internal/platform/ble_v2.cc index 6fd759c9..d6b5bff5 100644 --- a/internal/platform/ble_v2.cc +++ b/internal/platform/ble_v2.cc @@ -26,9 +26,15 @@ namespace nearby { +namespace { using ::nearby::api::ble_v2::BleAdvertisementData; using ::nearby::api::ble_v2::GattCharacteristic; using ::nearby::api::ble_v2::TxPowerLevel; +using ReadValueCallback = + ::nearby::api::ble_v2::ServerGattConnectionCallback::ReadValueCallback; +using WriteValueCallback = + ::nearby::api::ble_v2::ServerGattConnectionCallback::WriteValueCallback; +} // namespace bool BleV2Medium::StartAdvertising( const BleAdvertisementData& advertising_data, @@ -82,12 +88,9 @@ bool BleV2Medium::StartScanning(const Uuid& service_uuid, [this](api::ble_v2::BlePeripheral& peripheral, BleAdvertisementData advertisement_data) { MutexLock lock(&mutex_); - if (peripherals_.contains(&peripheral)) { - NEARBY_LOGS(INFO) - << "There is no need to callback due to peripheral impl=" - << &peripheral << ", which already exists."; - return; - } else { + if (!peripherals_.contains(&peripheral)) { + NEARBY_LOGS(INFO) << "Peripheral impl=" << &peripheral + << " is not existed; adds it to the map."; peripherals_.insert(&peripheral); } @@ -208,6 +211,23 @@ std::unique_ptr BleV2Medium::StartGattServer( server_gatt_connection_callback_ .characteristic_unsubscription_cb(characteristic); }, + .on_characteristic_read_cb = + [this](const api::ble_v2::BlePeripheral& remote_device, + const GattCharacteristic& characteristic, int offset, + ReadValueCallback callback) { + MutexLock lock(&mutex_); + server_gatt_connection_callback_.on_characteristic_read_cb( + remote_device, characteristic, offset, std::move(callback)); + }, + .on_characteristic_write_cb = + [this](const api::ble_v2::BlePeripheral& remote_device, + const GattCharacteristic& characteristic, int offset, + absl::string_view data, WriteValueCallback callback) { + MutexLock lock(&mutex_); + server_gatt_connection_callback_.on_characteristic_write_cb( + remote_device, characteristic, offset, data, + std::move(callback)); + }, }); return std::make_unique(std::move(api_gatt_server)); } diff --git a/internal/platform/ble_v2.h b/internal/platform/ble_v2.h index 8feca81c..dfdcb1ef 100644 --- a/internal/platform/ble_v2.h +++ b/internal/platform/ble_v2.h @@ -276,14 +276,28 @@ class BleV2Medium final { const api::ble_v2::BleAdvertisementData&>(); }; struct ServerGattConnectionCallback { - absl::AnyInvocable + using BlePeripheral = api::ble_v2::BlePeripheral; + using GattCharacteristic = api::ble_v2::GattCharacteristic; + using ReadValueCallback = + api::ble_v2::ServerGattConnectionCallback::ReadValueCallback; + using WriteValueCallback = + api::ble_v2::ServerGattConnectionCallback::WriteValueCallback; + + absl::AnyInvocable characteristic_subscription_cb = - nearby::DefaultCallback(); - absl::AnyInvocable + nearby::DefaultCallback(); + absl::AnyInvocable characteristic_unsubscription_cb = - nearby::DefaultCallback(); + nearby::DefaultCallback(); + absl::AnyInvocable + on_characteristic_read_cb; + absl::AnyInvocable + on_characteristic_write_cb; }; // TODO(b/231318879): Remove this wrapper callback and use impl callback if // there is only disconnect function here in the end. diff --git a/internal/platform/bluetooth_classic.h b/internal/platform/bluetooth_classic.h index 319d14f5..eee85f8c 100644 --- a/internal/platform/bluetooth_classic.h +++ b/internal/platform/bluetooth_classic.h @@ -16,7 +16,9 @@ #define PLATFORM_PUBLIC_BLUETOOTH_CLASSIC_H_ #include +#include #include +#include #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" @@ -126,6 +128,36 @@ class BluetoothServerSocket final { std::shared_ptr impl_; }; +// Opaque wrapper for a BluetoothPairing. +class BluetoothPairing final { + public: + explicit BluetoothPairing( + std::unique_ptr bluetooth_pairing) + : impl_(std::move(bluetooth_pairing)) {} + + bool InitiatePairing(api::BluetoothPairingCallback pairing_cb) { + return impl_->InitiatePairing(std::move(pairing_cb)); + } + + bool FinishPairing(std::optional pin_code) { + return impl_->FinishPairing(pin_code); + } + + bool CancelPairing() { return impl_->CancelPairing(); } + + bool Unpair() { return impl_->Unpair(); } + + bool IsPaired() { return impl_->IsPaired(); } + + // Returns reference to platform implementation. + // This is used to communicate with platform code, and for debugging + // purposes. + api::BluetoothPairing* GetImpl() { return impl_.get(); } + + private: + std::unique_ptr impl_; +}; + // Container of operations that can be performed over the Bluetooth Classic // medium. class BluetoothClassicMedium final @@ -147,6 +179,7 @@ class BluetoothClassicMedium final absl::AnyInvocable device_lost_cb = DefaultCallback(); }; + struct DeviceDiscoveryInfo { BluetoothDevice device; }; @@ -239,6 +272,15 @@ class BluetoothClassicMedium final impl_->ListenForService(service_name, service_uuid)); } + // Return a Bluetooth pairing instance to handle the pairing process with the + // remote device. + std::unique_ptr CreatePairing( + BluetoothDevice& remote_device) { + std::unique_ptr bluetooth_pairing = + impl_->CreatePairing(remote_device.GetImpl()); + return std::make_unique(std::move(bluetooth_pairing)); + } + bool IsValid() const { return impl_ != nullptr; } api::BluetoothClassicMedium& GetImpl() { return *impl_; } diff --git a/internal/platform/bluetooth_connection_info.cc b/internal/platform/bluetooth_connection_info.cc index ac4512a4..18d2a284 100644 --- a/internal/platform/bluetooth_connection_info.cc +++ b/internal/platform/bluetooth_connection_info.cc @@ -15,10 +15,10 @@ #include "internal/platform/bluetooth_connection_info.h" #include +#include #include "absl/status/status.h" #include "absl/status/statusor.h" -#include "absl/strings/str_format.h" #include "absl/strings/string_view.h" #include "internal/platform/connection_info.h" @@ -37,19 +37,17 @@ std::string BluetoothConnectionInfo::ToDataElementBytes() const { mask |= has_bluetooth_uuid ? kBluetoothUuidMask : 0; payload_data.push_back(mask); if (has_mac) { - payload_data.insert(payload_data.end(), mac_address_.begin(), - mac_address_.end()); + payload_data.append(mac_address_.begin(), mac_address_.end()); } if (has_bluetooth_uuid) { - payload_data.insert(payload_data.end(), bluetooth_uuid_.begin(), - bluetooth_uuid_.end()); + payload_data.append(bluetooth_uuid_.begin(), bluetooth_uuid_.end()); } - payload_data.push_back(actions_); + payload_data.append(actions_.begin(), actions_.end()); std::string ret; ret.push_back(kDataElementFieldType); ret.push_back(payload_data.size()); - ret.insert(ret.end(), payload_data.begin(), payload_data.end()); - return std::string(ret.data(), ret.size()); + ret.append(payload_data.begin(), payload_data.end()); + return ret; } absl::StatusOr @@ -90,14 +88,11 @@ BluetoothConnectionInfo::FromDataElementBytes(absl::string_view bytes) { } if (bytes.size() == position) { return absl::InvalidArgumentError( - "Insufficient remaining bytes to read action."); + "Insufficient remaining bytes to read actions."); } - char action = bytes[position]; - // Check that we don't have any remaining bytes. - if (bytes.size() != ++position) { - return absl::InvalidArgumentError(absl::StrFormat( - "Nonzero remaining bytes: %d.", bytes.size() - position)); - } - return BluetoothConnectionInfo(address, uuid, action); + auto action_str = bytes.substr(position); + return BluetoothConnectionInfo( + address, uuid, + std::vector(action_str.begin(), action_str.end())); } } // namespace nearby diff --git a/internal/platform/bluetooth_connection_info.h b/internal/platform/bluetooth_connection_info.h index 82afe528..838510e4 100644 --- a/internal/platform/bluetooth_connection_info.h +++ b/internal/platform/bluetooth_connection_info.h @@ -15,7 +15,9 @@ #ifndef THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_BLUETOOTH_CONNECTION_INFO_H_ #define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_BLUETOOTH_CONNECTION_INFO_H_ +#include #include +#include #include "absl/status/statusor.h" #include "absl/strings/string_view.h" @@ -30,7 +32,8 @@ class BluetoothConnectionInfo : public ConnectionInfo { absl::string_view bytes); BluetoothConnectionInfo(absl::string_view mac_address, - absl::string_view bluetooth_uuid, char actions) + absl::string_view bluetooth_uuid, + std::vector actions) : mac_address_(std::string(mac_address)), bluetooth_uuid_(std::string(bluetooth_uuid)), actions_(actions) {} @@ -42,12 +45,12 @@ class BluetoothConnectionInfo : public ConnectionInfo { std::string ToDataElementBytes() const override; std::string GetMacAddress() const { return mac_address_; } std::string GetBluetoothUuid() const { return bluetooth_uuid_; } - char GetActions() const override { return actions_; } + std::vector GetActions() const override { return actions_; } private: std::string mac_address_; std::string bluetooth_uuid_; - char actions_; + std::vector actions_; }; inline bool operator==(const BluetoothConnectionInfo& a, diff --git a/internal/platform/bluetooth_connection_info_test.cc b/internal/platform/bluetooth_connection_info_test.cc index 592b7a2e..072731d9 100644 --- a/internal/platform/bluetooth_connection_info_test.cc +++ b/internal/platform/bluetooth_connection_info_test.cc @@ -35,21 +35,22 @@ constexpr absl::string_view kBluetoothUuid{"test"}; constexpr char kAction = 0x0F; TEST(BluetoothConnectionInfoTest, TestGetFields) { - BluetoothConnectionInfo info(kMacAddr, kBluetoothUuid, kAction); + BluetoothConnectionInfo info(kMacAddr, kBluetoothUuid, {kAction}); EXPECT_EQ(info.GetMediumType(), Medium::BLUETOOTH); EXPECT_EQ(info.GetMacAddress(), kMacAddr); - EXPECT_EQ(info.GetActions(), kAction); + ASSERT_EQ(info.GetActions().size(), 1); + EXPECT_EQ(info.GetActions()[0], kAction); EXPECT_EQ(info.GetBluetoothUuid(), kBluetoothUuid); } TEST(BluetoothConnectionInfoTest, TestGetLongMacAddr) { BluetoothConnectionInfo info(absl::StrCat(kMacAddr, "\x56\x70\x89"), - kBluetoothUuid, kAction); + kBluetoothUuid, {kAction}); EXPECT_NE(info.GetMacAddress(), kMacAddr); } TEST(BluetoothConnectionInfoTest, TestToFromBytes) { - BluetoothConnectionInfo info(kMacAddr, kBluetoothUuid, kAction); + BluetoothConnectionInfo info(kMacAddr, kBluetoothUuid, {kAction}); std::string serialized = info.ToDataElementBytes(); auto result = BluetoothConnectionInfo::FromDataElementBytes(serialized); ASSERT_OK(result); @@ -57,18 +58,19 @@ TEST(BluetoothConnectionInfoTest, TestToFromBytes) { } TEST(BluetoothConnectionInfoTest, TestToFromNoMacAddress) { - BluetoothConnectionInfo info("", kBluetoothUuid, kAction); + BluetoothConnectionInfo info("", kBluetoothUuid, {kAction}); std::string serialized = info.ToDataElementBytes(); auto result = BluetoothConnectionInfo::FromDataElementBytes(serialized); ASSERT_OK(result); info = result.value(); EXPECT_EQ(info.GetMacAddress(), ""); EXPECT_EQ(info.GetBluetoothUuid(), kBluetoothUuid); - EXPECT_EQ(info.GetActions(), kAction); + ASSERT_EQ(info.GetActions().size(), 1); + EXPECT_EQ(info.GetActions()[0], kAction); } TEST(BluetoothConnectionInfoTest, TestToFromWrongLength) { - BluetoothConnectionInfo info(kMacAddr, kBluetoothUuid, kAction); + BluetoothConnectionInfo info(kMacAddr, kBluetoothUuid, {kAction}); std::string serialized = info.ToDataElementBytes(); ++serialized[1]; auto result = BluetoothConnectionInfo::FromDataElementBytes(serialized); @@ -76,7 +78,7 @@ TEST(BluetoothConnectionInfoTest, TestToFromWrongLength) { } TEST(BluetoothConnectionInfoTest, TestToFromNoBluetoothUuid) { - BluetoothConnectionInfo info(kMacAddr, "", kAction); + BluetoothConnectionInfo info(kMacAddr, "", {kAction}); std::string serialized = info.ToDataElementBytes(); auto result = BluetoothConnectionInfo::FromDataElementBytes(serialized); ASSERT_OK(result); @@ -89,7 +91,7 @@ TEST(BluetoothConnectionInfoTest, TestFromEmptyBytes) { } TEST(BluetoothConnectionInfoTest, TestFromNoAction) { - BluetoothConnectionInfo info("", "", kAction); + BluetoothConnectionInfo info("", "", {kAction}); std::string serialized = info.ToDataElementBytes(); serialized[1] -= 1; auto result = BluetoothConnectionInfo::FromDataElementBytes( @@ -103,106 +105,22 @@ TEST(BluetoothConnectionInfoTest, TestFromInvalidBytes) { } TEST(BluetoothConnectionInfoTest, TestFromBadElementType) { - BluetoothConnectionInfo info(kMacAddr, kBluetoothUuid, kAction); + BluetoothConnectionInfo info(kMacAddr, kBluetoothUuid, {kAction}); std::string serialized = info.ToDataElementBytes(); serialized[0] = 0x56; auto result = BluetoothConnectionInfo::FromDataElementBytes(serialized); EXPECT_THAT(result, StatusIs(absl::StatusCode::kInvalidArgument)); } -TEST(BluetoothConnectionInfoTest, TestFromBadMask) { - BluetoothConnectionInfo info(kMacAddr, kBluetoothUuid, kAction); - std::string serialized = info.ToDataElementBytes(); - // Remove the mask for UUID. - serialized[3] = 0x40; - EXPECT_THAT(BluetoothConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Remove the mask for MAC address and add back UUID. - serialized[3] = 0x20; - EXPECT_THAT(BluetoothConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Remove all masks. - serialized[3] = 0x00; - EXPECT_THAT(BluetoothConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set empty mask. - serialized[3] = 0x00; - EXPECT_THAT(BluetoothConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Verify OK with the correct mask. - serialized[3] = 0x60; - EXPECT_OK(BluetoothConnectionInfo::FromDataElementBytes(serialized)); -} - -TEST(BluetoothConnectionInfoTest, TestFromBadMaskNoUuid) { - BluetoothConnectionInfo info(kMacAddr, "", kAction); - std::string serialized = info.ToDataElementBytes(); - // Set the mask for UUID and MAC address. - serialized[3] = 0x60; - EXPECT_THAT(BluetoothConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set the mask for only UUID. - serialized[3] = 0x20; - EXPECT_THAT(BluetoothConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set empty mask. - serialized[3] = 0x00; - EXPECT_THAT(BluetoothConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Verify OK with the correct mask. - serialized[3] = 0x40; - EXPECT_OK(BluetoothConnectionInfo::FromDataElementBytes(serialized)); -} - -TEST(BluetoothConnectionInfoTest, TestFromBadMaskNoMac) { - BluetoothConnectionInfo info("", kBluetoothUuid, kAction); - std::string serialized = info.ToDataElementBytes(); - // Set the mask for UUID and MAC address. - serialized[3] = 0x60; - EXPECT_THAT(BluetoothConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set the mask for only MAC address. - serialized[3] = 0x40; - EXPECT_THAT(BluetoothConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set empty mask. - serialized[3] = 0x00; - EXPECT_THAT(BluetoothConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Verify OK with the correct mask. - serialized[3] = 0x20; - EXPECT_OK(BluetoothConnectionInfo::FromDataElementBytes(serialized)); -} - -TEST(BluetoothConnectionInfoTest, TestFromBadMaskEmpty) { - BluetoothConnectionInfo info("", "", kAction); - std::string serialized = info.ToDataElementBytes(); - // Set mask for UUID and MAC address. - serialized[3] = 0x60; - EXPECT_THAT(BluetoothConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for only UUID. - serialized[3] = 0x20; - EXPECT_THAT(BluetoothConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for only MAC address. - serialized[3] = 0x40; - EXPECT_THAT(BluetoothConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Verify OK with correct mask. - serialized[3] = 0x00; - EXPECT_OK(BluetoothConnectionInfo::FromDataElementBytes(serialized)); -} - TEST(BluetoothConnectionInfoTest, TestCopy) { - BluetoothConnectionInfo info(kMacAddr, kBluetoothUuid, kAction); + BluetoothConnectionInfo info(kMacAddr, kBluetoothUuid, {kAction}); BluetoothConnectionInfo copy(info); EXPECT_EQ(info, copy); } TEST(BluetoothConnectionInfoTest, TestEquals) { - BluetoothConnectionInfo info(kMacAddr, kBluetoothUuid, kAction); - BluetoothConnectionInfo info2(kMacAddr, kBluetoothUuid, kAction); + BluetoothConnectionInfo info(kMacAddr, kBluetoothUuid, {kAction}); + BluetoothConnectionInfo info2(kMacAddr, kBluetoothUuid, {kAction}); EXPECT_EQ(info, info2); } diff --git a/internal/platform/cancellation_flag.cc b/internal/platform/cancellation_flag.cc index c6b3bf5d..f9daa1ed 100644 --- a/internal/platform/cancellation_flag.cc +++ b/internal/platform/cancellation_flag.cc @@ -27,7 +27,10 @@ CancellationFlag::CancellationFlag(bool cancelled) { cancelled_ = cancelled; } -CancellationFlag::~CancellationFlag() { listeners_.clear(); } +CancellationFlag::~CancellationFlag() { + absl::MutexLock lock(mutex_.get()); + listeners_.clear(); +} void CancellationFlag::Cancel() { // Return immediately as no-op if feature flag is not enabled. diff --git a/internal/platform/connection_info.h b/internal/platform/connection_info.h index 804de0ad..bb102354 100644 --- a/internal/platform/connection_info.h +++ b/internal/platform/connection_info.h @@ -15,8 +15,11 @@ #ifndef THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_CONNECTION_INFO_H_ #define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_CONNECTION_INFO_H_ +#include #include +#include +#include "absl/status/statusor.h" #include "absl/strings/string_view.h" #include "absl/types/variant.h" #include "proto/connections_enums.pb.h" @@ -43,7 +46,7 @@ class ConnectionInfo { virtual ::location::nearby::proto::connections::Medium GetMediumType() const = 0; virtual std::string ToDataElementBytes() const = 0; - virtual char GetActions() const = 0; + virtual std::vector GetActions() const = 0; static ConnectionInfoVariant FromDataElementBytes( absl::string_view data_element_bytes); }; diff --git a/internal/platform/connection_info_test.cc b/internal/platform/connection_info_test.cc index ed8f9bab..ff4a6372 100644 --- a/internal/platform/connection_info_test.cc +++ b/internal/platform/connection_info_test.cc @@ -14,11 +14,16 @@ #include "internal/platform/connection_info.h" +#include +#include #include #include "gmock/gmock.h" #include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" +#include "absl/status/status.h" +#include "absl/strings/string_view.h" +#include "absl/types/variant.h" #include "internal/platform/ble_connection_info.h" #include "internal/platform/bluetooth_connection_info.h" #include "internal/platform/wifi_lan_connection_info.h" @@ -26,21 +31,28 @@ namespace nearby { namespace { +// Common +constexpr uint8_t kFirstAction = 0x0F; +constexpr uint8_t kSecondAction = 0x04; // BLE constexpr absl::string_view kMacAddr = "\x4C\x8B\x1D\xCE\xBA\xD1"; constexpr absl::string_view kGattCharacteristic = "\x03\x0a\x13\x56\x67\x21\x12\x45"; constexpr absl::string_view kPsm = "\x45\x56"; -constexpr char kAction = 0x0F; // Bluetooth constexpr absl::string_view kBluetoothUuid{"test"}; // WLAN constexpr absl::string_view kIpv4Addr = "\x4C\x8B\x1D\xCE"; -constexpr absl::string_view kPort = "\x12\x34\x56\x78"; +constexpr absl::string_view kPort = "\x12\x34"; constexpr absl::string_view kBssid = "\x0A\x1B\x2C\x34\x58\x7E"; +std::vector GetDefaultActions() { + return {kFirstAction, kSecondAction}; +} + TEST(ConnectionInfoTest, TestRestoreBle) { - BleConnectionInfo info(kMacAddr, kGattCharacteristic, kPsm, kAction); + BleConnectionInfo info(kMacAddr, kGattCharacteristic, kPsm, + GetDefaultActions()); auto serialized = info.ToDataElementBytes(); auto connection_info = ConnectionInfo::FromDataElementBytes(serialized); ASSERT_TRUE(absl::holds_alternative(connection_info)); @@ -49,7 +61,7 @@ TEST(ConnectionInfoTest, TestRestoreBle) { } TEST(ConnectionInfoTest, TestRestoreBluetooth) { - BluetoothConnectionInfo info(kMacAddr, kBluetoothUuid, kAction); + BluetoothConnectionInfo info(kMacAddr, kBluetoothUuid, GetDefaultActions()); auto serialized = info.ToDataElementBytes(); auto connection_info = ConnectionInfo::FromDataElementBytes(serialized); ASSERT_TRUE( @@ -59,7 +71,7 @@ TEST(ConnectionInfoTest, TestRestoreBluetooth) { } TEST(ConnectionInfoTest, TestRestoreMdns) { - WifiLanConnectionInfo info(kIpv4Addr, kPort, kBssid, kAction); + WifiLanConnectionInfo info(kIpv4Addr, kPort, kBssid, GetDefaultActions()); auto serialized = info.ToDataElementBytes(); auto connection_info = ConnectionInfo::FromDataElementBytes(serialized); ASSERT_TRUE(absl::holds_alternative(connection_info)); @@ -68,9 +80,12 @@ TEST(ConnectionInfoTest, TestRestoreMdns) { } TEST(ConnectionInfoTest, TestMonostate) { - WifiLanConnectionInfo wifi_info(kIpv4Addr, kPort, kBssid, kAction); - BluetoothConnectionInfo bt_info(kMacAddr, kBluetoothUuid, kAction); - BleConnectionInfo ble_info(kMacAddr, kGattCharacteristic, kPsm, kAction); + WifiLanConnectionInfo wifi_info(kIpv4Addr, kPort, kBssid, + GetDefaultActions()); + BluetoothConnectionInfo bt_info(kMacAddr, kBluetoothUuid, + GetDefaultActions()); + BleConnectionInfo ble_info(kMacAddr, kGattCharacteristic, kPsm, + GetDefaultActions()); std::vector infos = {&bt_info, &ble_info, &wifi_info}; for (auto info : infos) { auto serialized = info->ToDataElementBytes(); @@ -81,9 +96,12 @@ TEST(ConnectionInfoTest, TestMonostate) { } TEST(ConnectionInfoTest, TestCannotRestoreAsOtherInfos) { - WifiLanConnectionInfo wifi_info(kIpv4Addr, kPort, kBssid, kAction); - BluetoothConnectionInfo bt_info(kMacAddr, kBluetoothUuid, kAction); - BleConnectionInfo ble_info(kMacAddr, kGattCharacteristic, kPsm, kAction); + WifiLanConnectionInfo wifi_info(kIpv4Addr, kPort, kBssid, + GetDefaultActions()); + BluetoothConnectionInfo bt_info(kMacAddr, kBluetoothUuid, + GetDefaultActions()); + BleConnectionInfo ble_info(kMacAddr, kGattCharacteristic, kPsm, + GetDefaultActions()); EXPECT_THAT( BleConnectionInfo::FromDataElementBytes(wifi_info.ToDataElementBytes()), testing::status::StatusIs(absl::StatusCode::kInvalidArgument)); diff --git a/internal/platform/implementation/BUILD b/internal/platform/implementation/BUILD index 98f7b327..11f72960 100644 --- a/internal/platform/implementation/BUILD +++ b/internal/platform/implementation/BUILD @@ -31,6 +31,7 @@ cc_library( "log_message.h", "mutex.h", "output_file.h", + "preferences_repository.h", "scheduled_executor.h", "settable_future.h", "submittable_executor.h", @@ -39,8 +40,10 @@ cc_library( ], visibility = [ "//fastpair:__subpackages__", + "//internal/crypto:__pkg__", "//internal/platform:__pkg__", "//internal/platform/implementation:__subpackages__", + "//internal/preferences:__subpackages__", "//internal/test:__subpackages__", "//location/nearby/analytics/cpp:__subpackages__", "//location/nearby/cpp/common:__subpackages__", @@ -48,11 +51,13 @@ cc_library( "//presence:__subpackages__", ], deps = [ + "//internal/crypto", "//internal/platform:base", "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/functional:any_invocable", "@com_google_absl//absl/strings", "@com_google_absl//absl/time", + "@nlohmann_json//:json", ], ) @@ -76,7 +81,6 @@ cc_library( copts = ["-DNO_WEBRTC"], visibility = [ "//connections/implementation:__subpackages__", - "//internal:__pkg__", "//internal/network:__subpackages__", "//internal/platform:__pkg__", "//internal/platform/implementation:__subpackages__", @@ -111,13 +115,10 @@ cc_library( defines = ["NO_WEBRTC"], visibility = [ "//connections/implementation:__subpackages__", - "//fastpair:__subpackages__", - "//internal:__pkg__", "//internal:__subpackages__", "//internal/network:__subpackages__", "//internal/platform:__pkg__", "//internal/platform/implementation:__subpackages__", - "//location/nearby/cpp/fastpair:__subpackages__", "//location/nearby/cpp/sharing:__subpackages__", ], deps = [ diff --git a/internal/platform/implementation/apple/BUILD b/internal/platform/implementation/apple/BUILD index a1347496..2c6616d7 100644 --- a/internal/platform/implementation/apple/BUILD +++ b/internal/platform/implementation/apple/BUILD @@ -29,6 +29,7 @@ objc_library( "log_message.mm", "multi_thread_executor.mm", "platform.mm", + "preferences_repository.mm", "scheduled_executor.mm", "timer.mm", "utils.mm", @@ -40,6 +41,7 @@ objc_library( "device_info.h", "log_message.h", "multi_thread_executor.h", + "preferences_repository.h", "scheduled_executor.h", "single_thread_executor.h", "timer.h", @@ -63,12 +65,15 @@ objc_library( "//third_party/apple_frameworks:Foundation", "//third_party/apple_frameworks:Network", "//third_party/objective_c/google_toolbox_for_mac:GTM_Logger", + "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/strings", + "@com_google_absl//absl/synchronization", "@com_google_absl//absl/time", "@com_google_absl//absl/types:optional", + "@nlohmann_json//:json", ] + select({ - "//tools/cc_target_os:platform_ios": [ + "@platforms//os:platform_ios": [ "//third_party/apple_frameworks:UIKit", ], "//conditions:default": [], diff --git a/internal/platform/implementation/apple/Mediums/BUILD b/internal/platform/implementation/apple/Mediums/BUILD index 649bd04f..e5ec3b9f 100644 --- a/internal/platform/implementation/apple/Mediums/BUILD +++ b/internal/platform/implementation/apple/Mediums/BUILD @@ -25,6 +25,7 @@ objc_library( "GNCLeaks.h", "GNCLeaks.m", "GNCMConnection.m", + "WiFiLAN/GNCIPv4Address.m", "WiFiLAN/GNCWiFiLANError.m", "WiFiLAN/GNCWiFiLANMedium.m", "WiFiLAN/GNCWiFiLANServerSocket.m", @@ -37,6 +38,7 @@ objc_library( "Ble/GNCMBlePeripheral.h", "Ble/GNCMBleUtils.h", "GNCMConnection.h", + "WiFiLAN/GNCIPv4Address.h", "WiFiLAN/GNCWiFiLANError.h", "WiFiLAN/GNCWiFiLANMedium.h", "WiFiLAN/GNCWiFiLANServerSocket.h", diff --git a/internal/platform/implementation/apple/Mediums/Ble/Sockets/BUILD b/internal/platform/implementation/apple/Mediums/Ble/Sockets/BUILD index aef86932..255b6a1b 100644 --- a/internal/platform/implementation/apple/Mediums/Ble/Sockets/BUILD +++ b/internal/platform/implementation/apple/Mediums/Ble/Sockets/BUILD @@ -1,6 +1,4 @@ -load("//tools/build_defs/swift:swift_explicit_module_build_test.bzl", "swift_explicit_module_build_test") - -# Copyright 2020 Google LLC +# Copyright 2020-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. @@ -13,11 +11,13 @@ load("//tools/build_defs/swift:swift_explicit_module_build_test.bzl", "swift_exp # 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. + +load("//tools/build_defs/swift:swift_explicit_module_build_test.bzl", "swift_explicit_module_build_test") +load("//third_party/nearby:minimum_os.bzl", "IOS_MINIMUM_OS") + licenses(["notice"]) -package( - default_visibility = ["//visibility:public"], -) +package(default_visibility = ["//visibility:public"]) objc_library( name = "Central", @@ -57,7 +57,7 @@ objc_library( "//third_party/apple_frameworks:QuartzCore", "//third_party/objective_c/google_toolbox_for_mac:GTM_Logger", ] + select({ - "//tools/cc_target_os:platform_ios": [ + "@platforms//os:platform_ios": [ "//third_party/apple_frameworks:UIKit", ], "//conditions:default": [], @@ -86,6 +86,6 @@ objc_library( swift_explicit_module_build_test( name = "swift_explicit_module_build_test", ignore_headerless_targets = True, - minimum_os_version = "13.0", + minimum_os_version = IOS_MINIMUM_OS, platform_type = "ios", ) diff --git a/internal/platform/implementation/apple/Mediums/WiFiLAN/GNCIPv4Address.h b/internal/platform/implementation/apple/Mediums/WiFiLAN/GNCIPv4Address.h new file mode 100644 index 00000000..6ffce1cc --- /dev/null +++ b/internal/platform/implementation/apple/Mediums/WiFiLAN/GNCIPv4Address.h @@ -0,0 +1,60 @@ +// 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 + +/** A container for IPv4 address information. */ +@interface GNCIPv4Address : NSObject + +/** + * @remark init is not an available initializer. + */ +- (nonnull instancetype)init NS_UNAVAILABLE; + +/** + * Creates a container for IPv4 address information. + * + * @param byte1 The first byte of the IP address. + * @param byte2 The second byte of the IP address. + * @param byte3 The third byte of the IP address. + * @param byte4 The forth byte of the IP address. + */ +- (nonnull instancetype)initWithByte1:(uint8_t)byte1 + byte2:(uint8_t)byte2 + byte3:(uint8_t)byte3 + byte4:(uint8_t)byte4 NS_DESIGNATED_INITIALIZER; + ++ (nonnull instancetype)addressFromFourByteInt:(uint32_t)address; + ++ (nonnull instancetype)addressFromData:(nonnull NSData *)address; + +/** The first byte of the IP address. */ +@property(nonatomic, readonly) uint8_t byte1; + +/** The second byte of the IP address. */ +@property(nonatomic, readonly) uint8_t byte2; + +/** The third byte of the IP address. */ +@property(nonatomic, readonly) uint8_t byte3; + +/** The forth byte of the IP address. */ +@property(nonatomic, readonly) uint8_t byte4; + +/** The 4 byte binary representation for the IPv4 address. */ +@property(nonatomic, nonnull, readonly) NSData *binaryRepresentation; + +/** The human readable dotted representation for the IPv4 address. */ +@property(nonatomic, nonnull, readonly) NSString *dottedRepresentation; + +@end diff --git a/internal/platform/implementation/apple/Mediums/WiFiLAN/GNCIPv4Address.m b/internal/platform/implementation/apple/Mediums/WiFiLAN/GNCIPv4Address.m new file mode 100644 index 00000000..3333dda7 --- /dev/null +++ b/internal/platform/implementation/apple/Mediums/WiFiLAN/GNCIPv4Address.m @@ -0,0 +1,59 @@ +// 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/WiFiLAN/GNCIPv4Address.h" + +@implementation GNCIPv4Address + +- (instancetype)initWithByte1:(uint8_t)byte1 + byte2:(uint8_t)byte2 + byte3:(uint8_t)byte3 + byte4:(uint8_t)byte4 { + self = [super init]; + if (self) { + _byte1 = byte1; + _byte2 = byte2; + _byte3 = byte3; + _byte4 = byte4; + } + return self; +} + ++ (instancetype)addressFromFourByteInt:(uint32_t)address { + uint8_t byte1 = (address >> (8 * 0)) & 0xff; + uint8_t byte2 = (address >> (8 * 1)) & 0xff; + uint8_t byte3 = (address >> (8 * 2)) & 0xff; + uint8_t byte4 = (address >> (8 * 3)) & 0xff; + return [[GNCIPv4Address alloc] initWithByte1:byte1 byte2:byte2 byte3:byte3 byte4:byte4]; +} + ++ (instancetype)addressFromData:(NSData *)address { + NSAssert(address.length == 4, @"Address must be 4 bytes"); + const uint8_t *bytes = address.bytes; + return [[GNCIPv4Address alloc] initWithByte1:bytes[0] + byte2:bytes[1] + byte3:bytes[2] + byte4:bytes[3]]; +} + +- (NSData *)binaryRepresentation { + const uint8_t bytes[] = {_byte1, _byte2, _byte3, _byte4}; + return [NSData dataWithBytes:bytes length:4]; +} + +- (NSString *)dottedRepresentation { + return [NSString stringWithFormat:@"%d.%d.%d.%d", _byte1, _byte2, _byte3, _byte4]; +} + +@end diff --git a/internal/platform/implementation/apple/Mediums/WiFiLAN/GNCWiFiLANMedium.h b/internal/platform/implementation/apple/Mediums/WiFiLAN/GNCWiFiLANMedium.h index a6918f5a..9ac55431 100644 --- a/internal/platform/implementation/apple/Mediums/WiFiLAN/GNCWiFiLANMedium.h +++ b/internal/platform/implementation/apple/Mediums/WiFiLAN/GNCWiFiLANMedium.h @@ -14,6 +14,7 @@ #import +@class GNCIPv4Address; @class GNCWiFiLANServerSocket; @class GNCWiFiLANSocket; @@ -102,12 +103,12 @@ typedef void (^ServiceUpdateHandler)(NSString *_Nonnull serviceName, /** * Connects to an IP address and port. * - * @param host The 4 byte binary representation IPv4 address to connect to. + * @param host The IPv4 address to connect to. * @param port The port to connect to. * @param[out] error Error that will be populated on failure. * @return Returns a connected socket or nil if an error has occured. */ -- (nullable GNCWiFiLANSocket *)connectToHost:(nonnull NSString *)host +- (nullable GNCWiFiLANSocket *)connectToHost:(nonnull GNCIPv4Address *)host port:(NSInteger)port error:(NSError **_Nullable)error; diff --git a/internal/platform/implementation/apple/Mediums/WiFiLAN/GNCWiFiLANMedium.m b/internal/platform/implementation/apple/Mediums/WiFiLAN/GNCWiFiLANMedium.m index 202a95df..b1783208 100644 --- a/internal/platform/implementation/apple/Mediums/WiFiLAN/GNCWiFiLANMedium.m +++ b/internal/platform/implementation/apple/Mediums/WiFiLAN/GNCWiFiLANMedium.m @@ -17,6 +17,7 @@ #import #import +#import "internal/platform/implementation/apple/Mediums/WiFiLAN/GNCIPv4Address.h" #import "internal/platform/implementation/apple/Mediums/WiFiLAN/GNCWiFiLANError.h" #import "internal/platform/implementation/apple/Mediums/WiFiLAN/GNCWiFiLANServerSocket+Internal.h" #import "internal/platform/implementation/apple/Mediums/WiFiLAN/GNCWiFiLANServerSocket.h" @@ -258,9 +259,11 @@ NSDictionary *GNCTXTRecordForBrowseResult(nw_browse_resu return [self connectToEndpoint:endpoint error:error]; } -- (GNCWiFiLANSocket *)connectToHost:(NSString *)host port:(NSInteger)port error:(NSError **)error { +- (GNCWiFiLANSocket *)connectToHost:(GNCIPv4Address *)host + port:(NSInteger)port + error:(NSError **)error { nw_endpoint_t endpoint = - nw_endpoint_create_host([host UTF8String], [[@(port) stringValue] UTF8String]); + nw_endpoint_create_host(host.dottedRepresentation.UTF8String, @(port).stringValue.UTF8String); return [self connectToEndpoint:endpoint error:error]; } diff --git a/internal/platform/implementation/apple/Mediums/WiFiLAN/GNCWiFiLANServerSocket.h b/internal/platform/implementation/apple/Mediums/WiFiLAN/GNCWiFiLANServerSocket.h index 9acbc1c7..278193f5 100644 --- a/internal/platform/implementation/apple/Mediums/WiFiLAN/GNCWiFiLANServerSocket.h +++ b/internal/platform/implementation/apple/Mediums/WiFiLAN/GNCWiFiLANServerSocket.h @@ -14,6 +14,7 @@ #import +@class GNCIPv4Address; @class GNCWiFiLANSocket; @interface GNCWiFiLANServerSocket : NSObject @@ -31,9 +32,9 @@ - (nonnull instancetype)initWithPort:(NSInteger)port NS_DESIGNATED_INITIALIZER; /** - * The 4 byte binary representation for the IPv4 address of the physical network interface. + * The IPv4 address of the physical network interface. */ -@property(nonatomic, readonly, copy) NSString *ipAddress; +@property(nonatomic, readonly, copy) GNCIPv4Address *ipAddress; /** * The port of the server socket. diff --git a/internal/platform/implementation/apple/Mediums/WiFiLAN/GNCWiFiLANServerSocket.m b/internal/platform/implementation/apple/Mediums/WiFiLAN/GNCWiFiLANServerSocket.m index d4163457..66a6bac8 100644 --- a/internal/platform/implementation/apple/Mediums/WiFiLAN/GNCWiFiLANServerSocket.m +++ b/internal/platform/implementation/apple/Mediums/WiFiLAN/GNCWiFiLANServerSocket.m @@ -22,6 +22,7 @@ #include #include +#import "internal/platform/implementation/apple/Mediums/WiFiLAN/GNCIPv4Address.h" #import "internal/platform/implementation/apple/Mediums/WiFiLAN/GNCWiFiLANError.h" #import "internal/platform/implementation/apple/Mediums/WiFiLAN/GNCWiFiLANServerSocket+Internal.h" #import "internal/platform/implementation/apple/Mediums/WiFiLAN/GNCWiFiLANSocket.h" @@ -71,7 +72,7 @@ @synthesize ipAddress = _ipAddress; -- (NSString *)ipAddress { +- (GNCIPv4Address *)ipAddress { if (!_ipAddress) { _ipAddress = [GNCWiFiLANServerSocket lookupIpAddress]; } @@ -235,7 +236,7 @@ * Returns the IP address as a 4 byte string. If not available, this returns an empty string to * align with the Windows implementation. */ -+ (NSString *)lookupIpAddress { ++ (GNCIPv4Address *)lookupIpAddress { struct ifaddrs *ifaddr; // Note: The data returned by `getifaddrs()` is dynamically allocated and should be freed using @@ -243,7 +244,7 @@ // // See: https://linux.die.net/man/3/getifaddrs if (getifaddrs(&ifaddr) == -1) { - return @""; + return [GNCIPv4Address addressFromFourByteInt:0]; } // Walk through linked list, maintaining head pointer so we can free list later. @@ -264,22 +265,13 @@ continue; } - // Break the 4 byte binary representation of the hostname into 4 separate bytes. uint32_t host = ((struct sockaddr_in *)address)->sin_addr.s_addr; - uint8_t byte1 = (host >> (8 * 0)) & 0xff; - uint8_t byte2 = (host >> (8 * 1)) & 0xff; - uint8_t byte3 = (host >> (8 * 2)) & 0xff; - uint8_t byte4 = (host >> (8 * 3)) & 0xff; - - // Join the bytes into a 4 character string. - NSString *hostString = [NSString stringWithFormat:@"%c%c%c%c", byte1, byte2, byte3, byte4]; - freeifaddrs(ifaddr); - return hostString; + return [GNCIPv4Address addressFromFourByteInt:host]; } freeifaddrs(ifaddr); - return @""; + return [GNCIPv4Address addressFromFourByteInt:0]; } @end diff --git a/internal/platform/implementation/apple/Tests/BUILD b/internal/platform/implementation/apple/Tests/BUILD index 3607c383..16e7ef0d 100644 --- a/internal/platform/implementation/apple/Tests/BUILD +++ b/internal/platform/implementation/apple/Tests/BUILD @@ -1,6 +1,4 @@ -load("//tools/build_defs/apple:ios.bzl", "ios_unit_test") - -# Copyright 2020 Google LLC +# Copyright 2020-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. @@ -13,6 +11,10 @@ load("//tools/build_defs/apple:ios.bzl", "ios_unit_test") # 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. + +load("//tools/build_defs/apple:ios.bzl", "ios_unit_test") +load("//third_party/nearby:minimum_os.bzl", "IOS_MINIMUM_OS", "IOS_MINIMUM_TEST_RUNNER") + licenses(["notice"]) package(default_visibility = ["//visibility:public"]) @@ -24,6 +26,7 @@ objc_library( "GNCBleTest.mm", "GNCBluetoothAdapterTest.mm", "GNCCryptoTest.mm", + "GNCIPAddressTest.mm", "GNCMultiThreadExecutorTest.mm", "GNCScheduledExecutorTest.mm", "GNCSingleThreadExecutorTest.mm", @@ -31,16 +34,22 @@ objc_library( ], features = ["-layering_check"], deps = [ + "//internal/platform:base", + "//internal/platform/implementation:comm", + "//internal/platform/implementation:platform", + "//internal/platform/implementation:types", "//internal/platform/implementation/apple", - "//internal/platform/implementation/apple:Shared", + "//internal/platform/implementation/apple/Mediums", + "//third_party/apple_frameworks:Foundation", "//third_party/apple_frameworks:XCTest", + "@com_google_absl//absl/time", ], ) ios_unit_test( name = "PlatformTests", - minimum_os_version = "13.0", - runner = "//testing/utp/ios:IOS_13", + minimum_os_version = IOS_MINIMUM_OS, + runner = IOS_MINIMUM_TEST_RUNNER, deps = [ ":PlatformTestslib", ], diff --git a/internal/platform/implementation/apple/Tests/GNCIPAddressTest.mm b/internal/platform/implementation/apple/Tests/GNCIPAddressTest.mm new file mode 100644 index 00000000..65166717 --- /dev/null +++ b/internal/platform/implementation/apple/Tests/GNCIPAddressTest.mm @@ -0,0 +1,114 @@ +// 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 +#import + +#include + +#import "internal/platform/implementation/apple/Mediums/WiFiLAN/GNCIPv4Address.h" + +@interface GNCIPv4AddressTest : XCTestCase +@end + +@implementation GNCIPv4AddressTest + +- (void)testInitFromIntWithZero { + GNCIPv4Address *address = [GNCIPv4Address addressFromFourByteInt:0]; + XCTAssertEqual(address.byte1, 0); + XCTAssertEqual(address.byte2, 0); + XCTAssertEqual(address.byte3, 0); + XCTAssertEqual(address.byte4, 0); +} + +- (void)testInitFromIntWithRealIP { + GNCIPv4Address *address = [GNCIPv4Address addressFromFourByteInt:2869012672]; + XCTAssertEqual(address.byte1, 192); + XCTAssertEqual(address.byte2, 168); + XCTAssertEqual(address.byte3, 1); + XCTAssertEqual(address.byte4, 171); +} + +- (void)testInitFromDataWithZero { + std::string addressString = "\0\0\0\0"; + NSData *addressData = [NSData dataWithBytes:addressString.data() length:4]; + GNCIPv4Address *address = [GNCIPv4Address addressFromData:addressData]; + XCTAssertEqual(address.byte1, 0); + XCTAssertEqual(address.byte2, 0); + XCTAssertEqual(address.byte3, 0); + XCTAssertEqual(address.byte4, 0); +} + +- (void)testInitFromDataWithLeadingEmptyBytes { + std::string addressString = "\0\0\0\0"; + addressString[3] = static_cast(6); + + NSData *addressData = [NSData dataWithBytes:addressString.data() length:4]; + GNCIPv4Address *address = [GNCIPv4Address addressFromData:addressData]; + XCTAssertEqual(address.byte1, 0); + XCTAssertEqual(address.byte2, 0); + XCTAssertEqual(address.byte3, 0); + XCTAssertEqual(address.byte4, 6); +} + +- (void)testInitFromDataWithRealIP { + std::string addressString = "\0\0\0\0"; + addressString[0] = static_cast(192); + addressString[1] = static_cast(168); + addressString[2] = static_cast(1); + addressString[3] = static_cast(171); + + NSData *addressData = [NSData dataWithBytes:addressString.data() length:4]; + GNCIPv4Address *address = [GNCIPv4Address addressFromData:addressData]; + XCTAssertEqual(address.byte1, 192); + XCTAssertEqual(address.byte2, 168); + XCTAssertEqual(address.byte3, 1); + XCTAssertEqual(address.byte4, 171); +} + +- (void)testInitFromDataThrowsForWrongByteSize { + std::string addressString = "\0"; + NSData *addressData = [NSData dataWithBytes:addressString.data() length:addressString.size()]; + XCTAssertThrows([GNCIPv4Address addressFromData:addressData]); +} + +- (void)testDottedRepressentationWithZero { + GNCIPv4Address *address = [[GNCIPv4Address alloc] initWithByte1:0 byte2:0 byte3:0 byte4:0]; + XCTAssertEqualObjects(address.dottedRepresentation, @"0.0.0.0"); +} + +- (void)testDottedRepressentationWithRealIP { + GNCIPv4Address *address = [[GNCIPv4Address alloc] initWithByte1:192 byte2:168 byte3:1 byte4:171]; + XCTAssertEqualObjects(address.dottedRepresentation, @"192.168.1.171"); +} + +- (void)testBinaryRepressentationWithZero { + GNCIPv4Address *address = [[GNCIPv4Address alloc] initWithByte1:0 byte2:0 byte3:0 byte4:0]; + const uint8_t *bytes = (uint8_t *)address.binaryRepresentation.bytes; + XCTAssertEqual(bytes[0], 0); + XCTAssertEqual(bytes[1], 0); + XCTAssertEqual(bytes[2], 0); + XCTAssertEqual(bytes[3], 0); +} + +- (void)testBinaryRepressentationWithRealIP { + GNCIPv4Address *address = [[GNCIPv4Address alloc] initWithByte1:192 byte2:168 byte3:1 byte4:171]; + const uint8_t *bytes = (uint8_t *)address.binaryRepresentation.bytes; + XCTAssertEqual(bytes[0], 192); + XCTAssertEqual(bytes[1], 168); + XCTAssertEqual(bytes[2], 1); + XCTAssertEqual(bytes[3], 171); +} + +@end diff --git a/internal/platform/implementation/apple/platform.mm b/internal/platform/implementation/apple/platform.mm index d35d6b81..f8de03e6 100644 --- a/internal/platform/implementation/apple/platform.mm +++ b/internal/platform/implementation/apple/platform.mm @@ -28,6 +28,7 @@ #import "internal/platform/implementation/apple/log_message.h" #import "internal/platform/implementation/apple/multi_thread_executor.h" #include "internal/platform/implementation/apple/mutex.h" +#include "internal/platform/implementation/apple/preferences_repository.h" #import "internal/platform/implementation/apple/scheduled_executor.h" #import "internal/platform/implementation/apple/single_thread_executor.h" #include "internal/platform/implementation/apple/timer.h" @@ -246,5 +247,11 @@ std::unique_ptr ImplementationPlatform::CreateDeviceInf return std::make_unique(); } +// TODO(b/261503919): Add implementation. +std::unique_ptr +ImplementationPlatform::CreatePreferencesRepository(absl::string_view path) { + return std::make_unique(path); +} + } // namespace api } // namespace nearby diff --git a/internal/platform/implementation/apple/preferences_repository.h b/internal/platform/implementation/apple/preferences_repository.h new file mode 100644 index 00000000..af1430df --- /dev/null +++ b/internal/platform/implementation/apple/preferences_repository.h @@ -0,0 +1,40 @@ +// 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. + +#ifndef PLATFORM_IMPLEMENTATION_APPLE_PREFERENCES_REPOSITORY_H_ +#define PLATFORM_IMPLEMENTATION_APPLE_PREFERENCES_REPOSITORY_H_ + +#include "absl/base/thread_annotations.h" +#include "absl/synchronization/mutex.h" +#include "nlohmann/json_fwd.hpp" +#include "internal/platform/implementation/preferences_repository.h" + +namespace nearby::apple { + +class PreferencesRepository : public api::PreferencesRepository { + public: + explicit PreferencesRepository(absl::string_view path) + : api::PreferencesRepository(path) {} + + nlohmann::json LoadPreferences() override ABSL_LOCKS_EXCLUDED(&mutex_); + bool SavePreferences(nlohmann::json preferences) override + ABSL_LOCKS_EXCLUDED(&mutex_); + + private: + absl::Mutex mutex_; +}; + +} // namespace nearby::apple + +#endif // PLATFORM_IMPLEMENTATION_APPLE_PREFERENCES_REPOSITORY_H_ diff --git a/internal/platform/implementation/apple/preferences_repository.mm b/internal/platform/implementation/apple/preferences_repository.mm new file mode 100644 index 00000000..176afd24 --- /dev/null +++ b/internal/platform/implementation/apple/preferences_repository.mm @@ -0,0 +1,32 @@ +// 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/preferences_repository.h" + +#include "absl/synchronization/mutex.h" +#include "nlohmann/json.hpp" + +namespace nearby::apple { + +nlohmann::json PreferencesRepository::LoadPreferences() { + absl::MutexLock lock(&mutex_); + return nlohmann::json::object(); +} + +bool PreferencesRepository::SavePreferences(nlohmann::json preferences) { + absl::MutexLock lock(&mutex_); + return false; +} + +} // namespace nearby::apple \ No newline at end of file diff --git a/internal/platform/implementation/apple/wifi_lan.mm b/internal/platform/implementation/apple/wifi_lan.mm index 7cadd50a..5f2cc663 100644 --- a/internal/platform/implementation/apple/wifi_lan.mm +++ b/internal/platform/implementation/apple/wifi_lan.mm @@ -13,11 +13,13 @@ // limitations under the License. #import "internal/platform/implementation/apple/wifi_lan.h" +#import #include #include #include +#import "internal/platform/implementation/apple/Mediums/WiFiLAN/GNCIPv4Address.h" #import "internal/platform/implementation/apple/Mediums/WiFiLAN/GNCWiFiLANMedium.h" #import "internal/platform/implementation/apple/Mediums/WiFiLAN/GNCWiFiLANServerSocket.h" #import "internal/platform/implementation/apple/Mediums/WiFiLAN/GNCWiFiLANSocket.h" @@ -94,7 +96,8 @@ WifiLanServerSocket::WifiLanServerSocket(GNCWiFiLANServerSocket* server_socket) : server_socket_(server_socket) {} std::string WifiLanServerSocket::GetIPAddress() const { - return [server_socket_.ipAddress UTF8String]; + NSData* addressData = server_socket_.ipAddress.binaryRepresentation; + return std::string((char*)addressData.bytes, addressData.length); } int WifiLanServerSocket::GetPort() const { return server_socket_.port; } @@ -204,7 +207,12 @@ std::unique_ptr WifiLanMedium::ConnectToService( std::unique_ptr WifiLanMedium::ConnectToService( const std::string& ip_address, int port, CancellationFlag* cancellation_flag) { NSError* error = nil; - NSString* host = @(ip_address.c_str()); + if (ip_address.size() != 4) { + GTMLoggerError(@"Error IP address must be 4 bytes, but is %lu bytes", ip_address.size()); + return nil; + } + NSData* hostData = [NSData dataWithBytes:ip_address.data() length:ip_address.size()]; + GNCIPv4Address* host = [GNCIPv4Address addressFromData:hostData]; GNCWiFiLANSocket* socket = [medium_ connectToHost:host port:port error:&error]; if (socket != nil) { return std::make_unique(socket); diff --git a/internal/platform/implementation/ble_v2.h b/internal/platform/implementation/ble_v2.h index b2aaefea..8dd5ad50 100644 --- a/internal/platform/implementation/ble_v2.h +++ b/internal/platform/implementation/ble_v2.h @@ -27,6 +27,7 @@ #include "absl/container/flat_hash_map.h" #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/byte_array.h" #include "internal/platform/cancellation_flag.h" @@ -284,6 +285,9 @@ struct ClientGattConnectionCallback { // Callback for asynchronous events on the server side of a GATT connection. struct ServerGattConnectionCallback { + using ReadValueCallback = + absl::AnyInvocable data)>; + using WriteValueCallback = absl::AnyInvocable; // Called when a remote peripheral connected to us and subscribed to one of // our characteristics. absl::AnyInvocable @@ -293,6 +297,24 @@ struct ServerGattConnectionCallback { // characteristics. absl::AnyInvocable characteristic_unsubscription_cb; + + // Called when a gatt client is reading from the characteristic. + // Must call `callback` with the read result. + // When a characteristic has a static value set with + // `GattServer::UpdateCharacteristic()`, then reading from the characteristic + // yields that static value. The read callback is not called. + // Otherwise, the gatt server calls the read callback to get the value. + absl::AnyInvocable + on_characteristic_read_cb; + + // Called when a gatt client is writing to the characteristic. + // Must call `callback` with the write result. + absl::AnyInvocable + on_characteristic_write_cb; }; // A BLE GATT client socket for requesting GATT socket. diff --git a/internal/platform/implementation/bluetooth_classic.h b/internal/platform/implementation/bluetooth_classic.h index 7f8f9cf3..ae8da9f7 100644 --- a/internal/platform/implementation/bluetooth_classic.h +++ b/internal/platform/implementation/bluetooth_classic.h @@ -16,8 +16,11 @@ #define PLATFORM_API_BLUETOOTH_CLASSIC_H_ #include +#include #include +#include "absl/functional/any_invocable.h" +#include "absl/strings/string_view.h" #include "internal/platform/byte_array.h" #include "internal/platform/cancellation_flag.h" #include "internal/platform/exception.h" @@ -89,6 +92,105 @@ class BluetoothServerSocket { virtual Exception Close() = 0; }; +// https://developer.android.com/reference/com/google/android/things/bluetooth/PairingParams +// +// Encapsulates the data for a particular pairing attempt. +// The caller can use it to determine the pairing approach and choose a suitable +// way to obtain user consent to conclude the pairing process. +struct PairingParams { + // Pairing type for this pairing attempt. + // The Pairing type is based on the User Interface capabilities of both + // the pairing devices, and determines the process of pairing. + // `kConstent`: the user is expected to consent to the pairing process. + // `kDisplayPasskey`: the user is notified of a pairing passkey. + // `kDisplayPin`: same as kDisplayPasskey, but different pairing key format. + // `kConfirmPasskey`: the user must confirm pairing after verifying a passkey. + // `kRequestPin`: the user is supposed to enter a pin to confirm pairing. + enum class PairingType { + kUnknown = 0, + kConsent = 1, + kDisplayPasskey = 2, + kDisplayPin = 3, + kConfirmPasskey = 4, + kRequestPin = 5, + kLast, + }; + PairingType pairing_type; + + // Pairing pin to notify the user for the pairing process. + // If not relevant to the current pairing process, it's empty. + std::string passkey; +}; + +// https://developer.android.com/reference/com/google/android/things/bluetooth/BluetoothPairingCallback +// +// This callback is invoked during the Bluetooth pairing process and +// contains all the relevant pairing information required for pairing. +struct BluetoothPairingCallback { + // https://developer.android.com/reference/com/google/android/things/bluetooth/BluetoothPairingCallback.PairingError + enum class PairingError { + kUnknown = 0, + kAuthCanceled = 1, /* failed because we canceled the pairing process. */ + kAuthFailed = 2, /* failed with pins did not match, or no response. */ + kAuthRejected = 3, /* failed with the remote device rejected pairing. */ + kAuthTimeout = 4, /* failed with authentication timeout. */ + kFailed = 5, /* failed with no explicit reason. */ + kRepeatedAttempts = 6, /* failed with many repeated attempts. */ + kLast, + }; + + // Invoked when successfully paired with a device. + absl::AnyInvocable on_paired_cb = DefaultCallback<>(); + + // Invoked when pairing with a device is canceled or fails. + absl::AnyInvocable + on_pairing_error_cb = + DefaultCallback(); + + // Invoked when the pairing process has been initiated with a remote + // Bluetooth device. + absl::AnyInvocable + on_pairing_initiated_cb = DefaultCallback(); +}; + +// This class is responsible for handling Bluetooth pairing with a remote +// BluetoothDevice. +// DCHECK_CALLED_ON_VALID_SEQUENCE +class BluetoothPairing { + public: + virtual ~BluetoothPairing() = default; + + // https://developer.android.com/reference/com/google/android/things/bluetooth/BluetoothConnectionManager#initiatepairing + // https://developer.android.com/reference/com/google/android/things/bluetooth/BluetoothConnectionManager#registerpairingcallback + // + // Initiate Bluetooth pairing process with a remote device. + // Register a BluetoothPairingCallback to listen for Bluetooth pairing events + // Such as incoming pairing request, devices paired etc. + virtual bool InitiatePairing(BluetoothPairingCallback pairing_cb) = 0; + + // https://developer.android.com/reference/com/google/android/things/bluetooth/BluetoothConnectionManager#finishpairing + // + // Invoke this function to finish the pairing process with the remote device. + // Should be called only after receiving a callback from onPairingInitiated. + // Pin is needed for PairingType::kRequestPin + virtual bool FinishPairing(std::optional pin_code) = 0; + + // https://developer.android.com/reference/com/google/android/things/bluetooth/BluetoothConnectionManager#cancelpairing + // + // Cancel an ongoing pairing process with a remote device. + virtual bool CancelPairing() = 0; + + // https://developer.android.com/reference/com/google/android/things/bluetooth/BluetoothConnectionManager#unpair + // + // Destroys the existing pairing/bond with the remote device. + virtual bool Unpair() = 0; + + // https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#getBondState() + // + // Get the pairing state of the remote device. + virtual bool IsPaired() = 0; +}; + // Container of operations that can be performed over the Bluetooth Classic // medium. class BluetoothClassicMedium { @@ -174,6 +276,14 @@ class BluetoothClassicMedium { virtual std::unique_ptr ListenForService( const std::string& service_name, const std::string& service_uuid) = 0; + // https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#createBond() + // + // Start the bonding (pairing) process with the remote device. + // Return a Bluetooth pairing instance to handle the pairing process with the + // remote device. + virtual std::unique_ptr CreatePairing( + BluetoothDevice& remote_device) = 0; + virtual BluetoothDevice* GetRemoteDevice(const std::string& mac_address) = 0; virtual void AddObserver(Observer* observer) = 0; diff --git a/internal/platform/implementation/credential_storage.h b/internal/platform/implementation/credential_storage.h index 5c0af512..b8fb2459 100644 --- a/internal/platform/implementation/credential_storage.h +++ b/internal/platform/implementation/credential_storage.h @@ -21,6 +21,7 @@ #include "absl/strings/string_view.h" #include "internal/platform/implementation/credential_callbacks.h" #include "internal/proto/credential.pb.h" +#include "internal/proto/local_credential.pb.h" namespace nearby { namespace api { diff --git a/internal/platform/implementation/crypto.h b/internal/platform/implementation/crypto.h index 393f4d1f..ef53ff53 100644 --- a/internal/platform/implementation/crypto.h +++ b/internal/platform/implementation/crypto.h @@ -16,6 +16,11 @@ #define PLATFORM_API_CRYPTO_H_ #include "absl/strings/string_view.h" +#ifdef NEARBY_CHROMIUM +#include "crypto/random.h" +#else +#include "internal/crypto/random.h" +#endif #include "internal/platform/byte_array.h" namespace nearby { @@ -31,6 +36,15 @@ class Crypto { static ByteArray Sha256(absl::string_view input); }; +// Creates an object of type T initialized with random data. +// This template should be used for simple data types: int, char, etc. +template +T RandData() { + T data; + ::crypto::RandBytes(&data, sizeof(data)); + return data; +} + } // namespace nearby #endif // PLATFORM_API_CRYPTO_H_ diff --git a/internal/platform/implementation/g3/BUILD b/internal/platform/implementation/g3/BUILD index 1735101a..8dc1300b 100644 --- a/internal/platform/implementation/g3/BUILD +++ b/internal/platform/implementation/g3/BUILD @@ -18,6 +18,7 @@ cc_library( testonly = True, srcs = [ "log_message.cc", + "preferences_repository.cc", "scheduled_executor.cc", "system_clock.cc", ], @@ -30,11 +31,12 @@ cc_library( "multi_thread_executor.h", "mutex.h", "pipe.h", + "preferences_repository.h", "scheduled_executor.h", "single_thread_executor.h", "timer.h", ], - visibility = ["//visibility:private"], + visibility = ["//location/nearby/cpp:__subpackages__"], deps = [ "//internal/platform:base", "//internal/platform:logging", @@ -52,6 +54,7 @@ cc_library( "@com_google_absl//absl/time", "@com_google_glog//:glog", "@com_google_nisaba//nisaba/port:thread_pool", + "@nlohmann_json//:json", ], alwayslink = 1, ) @@ -136,6 +139,7 @@ cc_library( "//internal/flags:__subpackages__", "//internal/network:__subpackages__", "//internal/platform:__subpackages__", + "//internal/preferences:__subpackages__", "//internal/proto/analytics:__subpackages__", "//internal/test:__subpackages__", "//internal/weave:__subpackages__", diff --git a/internal/platform/implementation/g3/bluetooth_classic.cc b/internal/platform/implementation/g3/bluetooth_classic.cc index b8544aba..b7732b5e 100644 --- a/internal/platform/implementation/g3/bluetooth_classic.cc +++ b/internal/platform/implementation/g3/bluetooth_classic.cc @@ -271,6 +271,12 @@ BluetoothClassicMedium::ListenForService(const std::string& service_name, return socket; } +std::unique_ptr BluetoothClassicMedium::CreatePairing( + api::BluetoothDevice& remote_device) { + // TODO(b/279964840): Add g3 implementation for BluetoothPairing. + return nullptr; +} + api::BluetoothDevice* BluetoothClassicMedium::GetRemoteDevice( const std::string& mac_address) { auto& env = MediumEnvironment::Instance(); diff --git a/internal/platform/implementation/g3/bluetooth_classic.h b/internal/platform/implementation/g3/bluetooth_classic.h index 1db7af17..966615cd 100644 --- a/internal/platform/implementation/g3/bluetooth_classic.h +++ b/internal/platform/implementation/g3/bluetooth_classic.h @@ -233,6 +233,11 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium { const std::string& service_name, const std::string& service_uuid) override ABSL_LOCKS_EXCLUDED(mutex_); + // Return a Bluetooth pairing instance to handle the pairing process with the + // remote device. + std::unique_ptr CreatePairing( + api::BluetoothDevice& remote_device) override; + api::BluetoothDevice* GetRemoteDevice( const std::string& mac_address) override; diff --git a/internal/platform/implementation/g3/platform.cc b/internal/platform/implementation/g3/platform.cc index e4056d14..f8fc4fbb 100644 --- a/internal/platform/implementation/g3/platform.cc +++ b/internal/platform/implementation/g3/platform.cc @@ -32,6 +32,7 @@ #include "internal/platform/implementation/condition_variable.h" #include "internal/platform/implementation/log_message.h" #include "internal/platform/implementation/mutex.h" +#include "internal/platform/implementation/preferences_repository.h" #include "internal/platform/implementation/scheduled_executor.h" #include "internal/platform/implementation/server_sync.h" #include "internal/platform/implementation/shared/count_down_latch.h" @@ -52,6 +53,7 @@ #include "internal/platform/implementation/g3/log_message.h" #include "internal/platform/implementation/g3/multi_thread_executor.h" #include "internal/platform/implementation/g3/mutex.h" +#include "internal/platform/implementation/g3/preferences_repository.h" #include "internal/platform/implementation/g3/scheduled_executor.h" #include "internal/platform/implementation/g3/single_thread_executor.h" #include "internal/platform/implementation/g3/timer.h" @@ -235,5 +237,10 @@ ImplementationPlatform::CreateDeviceInfo() { return std::make_unique(); } +std::unique_ptr +ImplementationPlatform::CreatePreferencesRepository(absl::string_view path) { + return std::make_unique(path); +} + } // namespace api } // namespace nearby diff --git a/internal/platform/implementation/g3/preferences_repository.cc b/internal/platform/implementation/g3/preferences_repository.cc new file mode 100644 index 00000000..0700743c --- /dev/null +++ b/internal/platform/implementation/g3/preferences_repository.cc @@ -0,0 +1,70 @@ +// Copyright 2021 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/g3/preferences_repository.h" + +#include // NOLINT(build/c++17) +#include + +#include "absl/synchronization/mutex.h" +#include "nlohmann/json.hpp" +#include "nlohmann/json_fwd.hpp" + +namespace nearby { +namespace g3 { +namespace { +using json = nlohmann::json; +} // namespace + +json PreferencesRepository::LoadPreferences() { + absl::MutexLock lock(&mutex_); + + // Emulate Windows implementation + try { + // settings.json is used for testing, but we should look at having + // an implementation override for G3 in PreferencesManager to override + // the path for testing. + std::filesystem::path path = + std::filesystem::temp_directory_path() / "settings.json"; + if (!std::filesystem::exists(path)) { + return value_; + } + + std::ifstream preferences_file(path.c_str()); + if (!preferences_file.good()) { + return value_; + } + json preferences = json::parse(preferences_file, nullptr, false); + preferences_file.close(); + + if (preferences.is_discarded()) { + return value_; + } + + value_ = preferences; + } catch (...) { + return value_; + } + + return value_; +} + +bool PreferencesRepository::SavePreferences(json preferences) { + absl::MutexLock lock(&mutex_); + value_ = preferences; + return true; +} + +} // namespace g3 +} // namespace nearby diff --git a/internal/platform/implementation/g3/preferences_repository.h b/internal/platform/implementation/g3/preferences_repository.h new file mode 100644 index 00000000..960c89ea --- /dev/null +++ b/internal/platform/implementation/g3/preferences_repository.h @@ -0,0 +1,47 @@ +// Copyright 2021 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_IMPLEMENTATION_G3_PREFERENCES_REPOSITORY_H_ +#define PLATFORM_IMPLEMENTATION_G3_PREFERENCES_REPOSITORY_H_ + +#include "absl/base/thread_annotations.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" +#include "nlohmann/json.hpp" +#include "nlohmann/json_fwd.hpp" +#include "internal/platform/implementation/preferences_repository.h" + +namespace nearby { +namespace g3 { + +class PreferencesRepository : public api::PreferencesRepository { + public: + explicit PreferencesRepository(absl::string_view path) + : api::PreferencesRepository(path) {} + + nlohmann::json LoadPreferences() override ABSL_LOCKS_EXCLUDED(&mutex_); + bool SavePreferences(nlohmann::json preferences) override + ABSL_LOCKS_EXCLUDED(&mutex_); + + private: + // Avoid to write in google3, just create a memory value to simulate a + // preferences storage + nlohmann::json value_ = nlohmann::json::object(); + absl::Mutex mutex_; +}; + +} // namespace g3 +} // namespace nearby + +#endif // PLATFORM_IMPLEMENTATION_G3_PREFERENCES_REPOSITORY_H_ diff --git a/internal/platform/implementation/g3/preferences_repository_test.cc b/internal/platform/implementation/g3/preferences_repository_test.cc new file mode 100644 index 00000000..b407f64f --- /dev/null +++ b/internal/platform/implementation/g3/preferences_repository_test.cc @@ -0,0 +1,68 @@ +// 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. + +#include "internal/platform/implementation/g3/preferences_repository.h" + +#include // NOLINT(build/c++17) +#include + +#include "gtest/gtest.h" +#include "nlohmann/json.hpp" +#include "nlohmann/json_fwd.hpp" + +namespace nearby { +namespace platform { +namespace g3 { +namespace { +using json = ::nlohmann::json; +} // namespace + +TEST(Preferences, TestSaveAndGetPreferences) { + PreferencesRepository preferences_repository{ + std::filesystem::temp_directory_path().string()}; + std::string string_key = "string_value"; + std::string string_value = "hello world"; + std::string int_key = "int_value"; + json prefs = {{int_key, 345}, {string_key, string_value}}; + + EXPECT_TRUE(preferences_repository.SavePreferences(prefs)); + + auto result = preferences_repository.LoadPreferences(); + EXPECT_EQ(result[string_key].get(), string_value); + EXPECT_EQ(result[int_key].get(), 345); +} + +TEST(Preferences, TestMultipleSaveAndGetPreferences) { + PreferencesRepository preferences_repository{ + std::filesystem::temp_directory_path().string()}; + std::string string_key = "string_value"; + std::string string_value = "hello world"; + std::string string_new_value = "again"; + std::string int_key = "int_value"; + json prefs = {{int_key, 345}, {string_key, string_value}}; + EXPECT_TRUE(preferences_repository.SavePreferences(prefs)); + + json result = preferences_repository.LoadPreferences(); + EXPECT_EQ(result[string_key].get(), string_value); + prefs[string_key] = string_new_value; + prefs[int_key] = 456; + EXPECT_TRUE(preferences_repository.SavePreferences(prefs)); + result = preferences_repository.LoadPreferences(); + EXPECT_EQ(result[string_key].get(), string_new_value); + EXPECT_EQ(result[int_key].get(), 456); +} + +} // namespace g3 +} // namespace platform +} // namespace nearby diff --git a/internal/platform/implementation/platform.h b/internal/platform/implementation/platform.h index 862bae8f..6860d15f 100644 --- a/internal/platform/implementation/platform.h +++ b/internal/platform/implementation/platform.h @@ -46,6 +46,9 @@ #ifndef NO_WEBRTC #include "internal/platform/implementation/webrtc.h" #endif +#ifndef NEARBY_CHROMIUM +#include "internal/platform/implementation/preferences_repository.h" +#endif #include "internal/platform/implementation/wifi.h" #include "internal/platform/implementation/wifi_direct.h" #include "internal/platform/implementation/wifi_hotspot.h" @@ -148,6 +151,11 @@ class ImplementationPlatform { // return WebResponse if HTTP status code between 200 and 300. // other cases will return absl Status in error. static absl::StatusOr SendRequest(const WebRequest& request); + +#ifndef NEARBY_CHROMIUM + static std::unique_ptr + CreatePreferencesRepository(absl::string_view path); +#endif }; } // namespace api diff --git a/internal/platform/implementation/preferences_repository.h b/internal/platform/implementation/preferences_repository.h new file mode 100644 index 00000000..d3840ded --- /dev/null +++ b/internal/platform/implementation/preferences_repository.h @@ -0,0 +1,47 @@ +// 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. + +#ifndef PLATFORM_IMPLEMENTATION_PREFERENCE_REPOSITORY_H_ +#define PLATFORM_IMPLEMENTATION_PREFERENCE_REPOSITORY_H_ + +#include + +#include "absl/strings/string_view.h" +#include "nlohmann/json_fwd.hpp" + +namespace nearby { +namespace api { + +// A repository for preferences. The implementations are different on different +// platforms. +// +// The data in preferences has multiple types, such as int, bool, string and +// dictionary. Using proto to describe it is a little complicated. In the +// repository, we use json as the parser for now. +class PreferencesRepository { + public: + explicit PreferencesRepository(absl::string_view path) : path_(path) {} + virtual ~PreferencesRepository() = default; + + virtual nlohmann::json LoadPreferences() = 0; + virtual bool SavePreferences(nlohmann::json preferences) = 0; + + protected: + std::string path_; +}; + +} // namespace api +} // namespace nearby + +#endif // PLATFORM_IMPLEMENTATION_PREFERENCE_REPOSITORY_H_ diff --git a/internal/platform/implementation/windows/BUILD b/internal/platform/implementation/windows/BUILD index e76c9872..38bdfc5b 100644 --- a/internal/platform/implementation/windows/BUILD +++ b/internal/platform/implementation/windows/BUILD @@ -33,6 +33,7 @@ cc_library( "log_message.h", "mutex.h", "output_file.h", + "preferences_repository.h", "scheduled_executor.h", "settable_future.h", "submittable_executor.h", @@ -148,6 +149,7 @@ cc_library( "file_path.cc", "http_loader.cc", "platform.cc", + "preferences_repository.cc", "scheduled_executor.cc", "submittable_executor.cc", "system_clock.cc", @@ -239,6 +241,7 @@ cc_test( "executor_test.cc", "file_path_test.cc", "http_loader_test.cc", + "preferences_repository_test.cc", "scheduled_executor_test.cc", "submittable_executor_test.cc", "thread_pool_test.cc", diff --git a/internal/platform/implementation/windows/ble_gatt_client.cc b/internal/platform/implementation/windows/ble_gatt_client.cc index 9bfb6b02..0ba470ff 100644 --- a/internal/platform/implementation/windows/ble_gatt_client.cc +++ b/internal/platform/implementation/windows/ble_gatt_client.cc @@ -26,9 +26,12 @@ #include #include +#include "absl/functional/any_invocable.h" #include "absl/strings/escaping.h" #include "absl/strings/str_format.h" #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/byte_array.h" #include "internal/platform/implementation/ble_v2.h" @@ -213,10 +216,11 @@ bool BleGattClient::DiscoverServiceAndCharacteristics( // found all characteristics. NEARBY_LOGS(VERBOSE) << __func__ << ": Found all characteristics."; - break; + return true; } - return true; + NEARBY_LOGS(VERBOSE) << __func__ + << ": Failed to find service and all characteristics."; } catch (std::exception exception) { NEARBY_LOGS(ERROR) << __func__ << ": Failed to get GATT services. exception: " @@ -623,10 +627,30 @@ void BleGattClient::OnCharacteristicValueChanged( } NEARBY_LOGS(VERBOSE) << __func__ << ": Got characteristic value length= " << data.size(); - absl::MutexLock lock(&mutex_); - DCHECK( - native_characteristic_map_[characteristic].on_characteristic_changed_cb); - native_characteristic_map_[characteristic].on_characteristic_changed_cb(data); + + absl::AnyInvocable + on_characteristic_changed_cb; + { + absl::MutexLock lock(&mutex_); + if (!native_characteristic_map_.contains(characteristic) || + !native_characteristic_map_[characteristic] + .on_characteristic_changed_cb) { + NEARBY_LOGS(INFO) << __func__ + << ": No registered callback for characteristic."; + return; + } + on_characteristic_changed_cb = + std::move(native_characteristic_map_[characteristic] + .on_characteristic_changed_cb); + } + + on_characteristic_changed_cb(std::move(data)); + + { + absl::MutexLock lock(&mutex_); + native_characteristic_map_[characteristic].on_characteristic_changed_cb = + std::move(on_characteristic_changed_cb); + } } } // namespace windows diff --git a/internal/platform/implementation/windows/bluetooth_classic_medium.cc b/internal/platform/implementation/windows/bluetooth_classic_medium.cc index 2a14c61e..6f914208 100644 --- a/internal/platform/implementation/windows/bluetooth_classic_medium.cc +++ b/internal/platform/implementation/windows/bluetooth_classic_medium.cc @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -29,10 +30,12 @@ #include "internal/platform/cancellation_flag.h" #include "internal/platform/cancellation_flag_listener.h" #include "internal/platform/exception.h" +#include "internal/platform/implementation/bluetooth_classic.h" #include "internal/platform/implementation/windows/bluetooth_adapter.h" #include "internal/platform/implementation/windows/bluetooth_classic_device.h" #include "internal/platform/implementation/windows/bluetooth_classic_server_socket.h" #include "internal/platform/implementation/windows/bluetooth_classic_socket.h" +#include "internal/platform/implementation/windows/bluetooth_pairing.h" #include "internal/platform/implementation/windows/generated/winrt/Windows.Devices.Bluetooth.Rfcomm.h" #include "internal/platform/implementation/windows/generated/winrt/Windows.Devices.Bluetooth.h" #include "internal/platform/implementation/windows/generated/winrt/Windows.Devices.Enumeration.h" @@ -45,7 +48,6 @@ namespace nearby { namespace windows { namespace { - using winrt::Windows::Foundation::IInspectable; using winrt::Windows::Foundation::Collections::IMapView; @@ -310,7 +312,6 @@ std::unique_ptr BluetoothClassicMedium::ConnectToService( } nearby::CancellationFlagListener cancellation_flag_listener( cancellation_flag, [&rfcomm_socket]() { - rfcomm_socket->CancelIOAsync().get(); rfcomm_socket->Close(); }); @@ -337,6 +338,37 @@ std::unique_ptr BluetoothClassicMedium::ConnectToService( } } +std::unique_ptr BluetoothClassicMedium::CreatePairing( + api::BluetoothDevice& remote_device) { + NEARBY_LOGS(VERBOSE) << __func__ << ": Start to createPairing with device: " + << remote_device.GetMacAddress(); + try { + winrt::Windows::Devices::Bluetooth::BluetoothDevice bluetooth_device = + winrt::Windows::Devices::Bluetooth::BluetoothDevice:: + FromBluetoothAddressAsync( + mac_address_string_to_uint64(remote_device.GetMacAddress())) + .get(); + winrt::Windows::Devices::Enumeration::DeviceInformationCustomPairing + custom_pairing = + bluetooth_device.DeviceInformation().Pairing().Custom(); + if (custom_pairing) { + return std::make_unique(bluetooth_device, + custom_pairing); + } + NEARBY_LOGS(VERBOSE) << __func__ + << ": Failed to get DeviceInformationCustomPairing."; + } catch (std::exception exception) { + NEARBY_LOGS(ERROR) << __func__ << " : Failed to create pairing. exception: " + << exception.what(); + } catch (const winrt::hresult_error& error) { + NEARBY_LOGS(ERROR) << __func__ + << ": Failed to create pairing. WinRT exception: " + << error.code() << ": " + << winrt::to_string(error.message()); + } + return nullptr; +} + bool BluetoothClassicMedium::HaveAccess(winrt::hstring device_id) { if (device_id.empty()) { return false; diff --git a/internal/platform/implementation/windows/bluetooth_classic_medium.h b/internal/platform/implementation/windows/bluetooth_classic_medium.h index 40d1f933..bfaa00a6 100644 --- a/internal/platform/implementation/windows/bluetooth_classic_medium.h +++ b/internal/platform/implementation/windows/bluetooth_classic_medium.h @@ -146,6 +146,11 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium { api::BluetoothDevice* GetRemoteDevice( const std::string& mac_address) override; + // Return a Bluetooth pairing instance to handle the pairing process with the + // remote device. + std::unique_ptr CreatePairing( + api::BluetoothDevice& remote_device) override; + void AddObserver(Observer* observer) override { // TODO(b/269521993): Implement. } diff --git a/internal/platform/implementation/windows/bluetooth_classic_socket.cc b/internal/platform/implementation/windows/bluetooth_classic_socket.cc index 71250a57..5eedb94b 100644 --- a/internal/platform/implementation/windows/bluetooth_classic_socket.cc +++ b/internal/platform/implementation/windows/bluetooth_classic_socket.cc @@ -156,12 +156,18 @@ BluetoothSocket::BluetoothInputStream::BluetoothInputStream( ExceptionOr BluetoothSocket::BluetoothInputStream::Read( std::int64_t size) { try { - if (size <= 0 || size > kMaxTransmitPacketSize) { + if (size <= 0) { NEARBY_LOGS(ERROR) << __func__ << ": Invalid transmit packet size: " << size; return {Exception::kIo}; } + if (size > read_buffer_.Capacity()) { + NEARBY_LOGS(WARNING) << __func__ + << ": resize receive buffer to packet size: " << size; + read_buffer_ = Buffer(size); + } + // Init the read buffer. read_buffer_.Length(0); @@ -189,12 +195,6 @@ ExceptionOr BluetoothSocket::BluetoothInputStream::Read( } } -IAsyncAction BluetoothSocket::CancelIOAsync() { - // Cancels pending reads and writes over a StreamSocket object. - // https://docs.microsoft.com/en-us/uwp/api/windows.networking.sockets.streamsocket.cancelioasync?view=winrt-20348 - return windows_socket_.CancelIOAsync(); -} - Exception BluetoothSocket::BluetoothInputStream::Close() { NEARBY_LOGS(INFO) << __func__ << ": Close bluetooth input stream."; @@ -223,10 +223,11 @@ BluetoothSocket::BluetoothOutputStream::BluetoothOutputStream( Exception BluetoothSocket::BluetoothOutputStream::Write(const ByteArray& data) { try { - if (data.size() > kMaxTransmitPacketSize) { - NEARBY_LOGS(ERROR) << __func__ << ": Transmit packet size " << data.size() - << " is too big."; - return {Exception::kIo}; + if (data.size() > write_buffer_.Capacity()) { + NEARBY_LOGS(WARNING) << __func__ + << ": resize write buffer to packet size: " + << data.size(); + write_buffer_ = Buffer(data.size()); } std::memcpy(write_buffer_.data(), data.data(), data.size()); diff --git a/internal/platform/implementation/windows/bluetooth_classic_socket.h b/internal/platform/implementation/windows/bluetooth_classic_socket.h index 786c2104..19e2de3b 100644 --- a/internal/platform/implementation/windows/bluetooth_classic_socket.h +++ b/internal/platform/implementation/windows/bluetooth_classic_socket.h @@ -98,10 +98,8 @@ class BluetoothSocket : public api::BluetoothSocket { bool Connect(HostName connectionHostName, winrt::hstring connectionServiceName); - IAsyncAction CancelIOAsync(); - private: - static constexpr int kMaxTransmitPacketSize = 4096; + static constexpr int kInitialTransmitPacketSize = 4096; class BluetoothInputStream : public InputStream { public: @@ -113,7 +111,7 @@ class BluetoothSocket : public api::BluetoothSocket { private: IInputStream winrt_input_stream_{nullptr}; - Buffer read_buffer_{kMaxTransmitPacketSize}; + Buffer read_buffer_{kInitialTransmitPacketSize}; }; class BluetoothOutputStream : public OutputStream { @@ -128,11 +126,11 @@ class BluetoothSocket : public api::BluetoothSocket { private: IOutputStream winrt_output_stream_{nullptr}; - Buffer write_buffer_{kMaxTransmitPacketSize}; + Buffer write_buffer_{kInitialTransmitPacketSize}; }; bool InternalConnect(HostName connectionHostName, - winrt::hstring connectionServiceName); + winrt::hstring connectionServiceName); winrt::fire_and_forget Listener_ConnectionStatusChanged( winrt::Windows::Devices::Bluetooth::BluetoothDevice device, diff --git a/internal/platform/implementation/windows/bluetooth_pairing.cc b/internal/platform/implementation/windows/bluetooth_pairing.cc index f038fa4d..f43d1193 100644 --- a/internal/platform/implementation/windows/bluetooth_pairing.cc +++ b/internal/platform/implementation/windows/bluetooth_pairing.cc @@ -14,7 +14,20 @@ #include "internal/platform/implementation/windows/bluetooth_pairing.h" +#include + +#include +#include +#include +#include + +#include "absl/log/check.h" +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "internal/platform/implementation/bluetooth_classic.h" +#include "internal/platform/implementation/windows/generated/winrt/impl/Windows.Devices.Enumeration.0.h" #include "internal/platform/logging.h" +#include "winrt/Windows.Devices.Bluetooth.h" #include "winrt/Windows.Devices.Enumeration.h" #include "winrt/Windows.Foundation.Collections.h" #include "winrt/base.h" @@ -23,93 +36,294 @@ namespace nearby { namespace windows { namespace { +using ::winrt::Windows::Devices::Bluetooth::BluetoothDevice; using ::winrt::Windows::Devices::Enumeration::DeviceInformationCustomPairing; using ::winrt::Windows::Devices::Enumeration::DevicePairingKinds; using ::winrt::Windows::Devices::Enumeration::DevicePairingProtectionLevel; using ::winrt::Windows::Devices::Enumeration::DevicePairingRequestedEventArgs; using ::winrt::Windows::Devices::Enumeration::DevicePairingResult; using ::winrt::Windows::Devices::Enumeration::DevicePairingResultStatus; +using ::winrt::Windows::Devices::Enumeration::DeviceUnpairingResult; +using ::winrt::Windows::Devices::Enumeration::DeviceUnpairingResultStatus; using ::winrt::Windows::Foundation::IAsyncOperation; +using PairingError = ::nearby::api::BluetoothPairingCallback::PairingError; +using PairingType = ::nearby::api::PairingParams::PairingType; } // namespace BluetoothPairing::BluetoothPairing( - DeviceInformationCustomPairing& custom_pairing) - : custom_pairing_(custom_pairing) {} + BluetoothDevice bluetooth_device, + DeviceInformationCustomPairing custom_pairing) + : bluetooth_device_(bluetooth_device), custom_pairing_(custom_pairing) { + NEARBY_LOGS(VERBOSE) << __func__ + << ": BluetoothPairing is created for device."; +} -BluetoothPairing::~BluetoothPairing() = default; - -void BluetoothPairing::StartPairing() { - NEARBY_LOGS(VERBOSE) << "Bluetooth_pairing start pairing"; - pairing_requested_token_ = custom_pairing_.PairingRequested( - {this, &BluetoothPairing::OnPairingRequested}); - IAsyncOperation pairing_operation = - custom_pairing_.PairAsync(DevicePairingKinds::ConfirmOnly | - DevicePairingKinds::ProvidePin | - DevicePairingKinds::ConfirmPinMatch, - DevicePairingProtectionLevel::None); - DevicePairingResult pairing_result = pairing_operation.get(); - if (pairing_result != nullptr) { - OnPair(pairing_result); +BluetoothPairing::~BluetoothPairing() { + if (pairing_requested_token_) { + custom_pairing_.PairingRequested( + std::exchange(pairing_requested_token_, {})); } + CancelPairing(); + NEARBY_LOGS(VERBOSE) << __func__ + << ": BluetoothPairing is destroyed for device."; +} + +bool BluetoothPairing::InitiatePairing( + api::BluetoothPairingCallback pairing_cb) { + NEARBY_LOGS(VERBOSE) << __func__ << ": Start to initiate pairing process."; + try { + pairing_requested_token_ = custom_pairing_.PairingRequested( + {this, &BluetoothPairing::OnPairingRequested}); + if (!pairing_requested_token_) { + NEARBY_LOGS(VERBOSE) << __func__ + << " Failed to registered pairing callback."; + return false; + } + pairing_callback_ = std::move(pairing_cb); + DevicePairingResult pairing_result = + custom_pairing_ + .PairAsync(DevicePairingKinds::ConfirmOnly | + DevicePairingKinds::ProvidePin | + DevicePairingKinds::ConfirmPinMatch | + DevicePairingKinds::DisplayPin, + DevicePairingProtectionLevel::None) + .get(); + OnPair(pairing_result); + return true; + } catch (std::exception exception) { + NEARBY_LOGS(ERROR) << __func__ + << ": Failed to initiate pairing. exception: " + << exception.what(); + } catch (const winrt::hresult_error& error) { + NEARBY_LOGS(ERROR) << __func__ + << ": Failed to initiate pairing. WinRT exception: " + << error.code() << ": " + << winrt::to_string(error.message()); + } + return false; +} + +bool BluetoothPairing::FinishPairing( + std::optional pin_code) { + NEARBY_LOGS(VERBOSE) << __func__ << "Start to finish pairing."; + try { + if (!pairing_requested_) { + NEARBY_LOGS(VERBOSE) << __func__ << "No pairing requested."; + return false; + } + if (!pairing_deferral_) { + NEARBY_LOGS(VERBOSE) << __func__ << "No ongoing pairing process."; + return false; + } + if (expecting_pin_code_) { + if (!pin_code.has_value()) { + NEARBY_LOGS(INFO) << __func__ << " Failed to get pin code"; + return false; + } + expecting_pin_code_ = false; + auto pin_hstring = winrt::to_hstring(std::string(pin_code.value())); + pairing_requested_.Accept(pin_hstring); + } else { + pairing_requested_.Accept(); + } + pairing_deferral_.Complete(); + NEARBY_LOGS(VERBOSE) << "Successfully finished pairing."; + return true; + } catch (std::exception exception) { + NEARBY_LOGS(ERROR) << __func__ << ": Failed to finish pairing. exception: " + << exception.what(); + } catch (const winrt::hresult_error& error) { + NEARBY_LOGS(ERROR) << __func__ + << ": Failed to finish pairing. WinRT exception: " + << error.code() << ": " + << winrt::to_string(error.message()); + } + return false; +} + +bool BluetoothPairing::CancelPairing() { + NEARBY_LOGS(VERBOSE) << __func__ + << "Start to cancel ongoing pairing process."; + try { + if (!pairing_deferral_) { + NEARBY_LOGS(VERBOSE) << __func__ << "No ongoing pairing process."; + return true; + } + // There is no way to explicitly cancel an in-progress pairing on Windows as + // DevicePairingRequestedEventArgs has no Cancel() method. + // Our approach is to complete the deferral, without accepting, + // which results in a RejectedByHandler result status. + // |was_cancelled_| is set so that OnPair(), which is called when the + // deferral is completed, will know that cancellation was the actual result. + was_cancelled_ = true; + pairing_deferral_.Complete(); + NEARBY_LOGS(VERBOSE) << __func__ << "Canceled ongoing pairing process."; + return true; + } catch (std::exception exception) { + NEARBY_LOGS(ERROR) << __func__ << ": Failed to cancel ongoing pairing " + << "process. exception: " << exception.what(); + } catch (const winrt::hresult_error& error) { + NEARBY_LOGS(ERROR) << __func__ + << ": Failed to cancel ongoing pairing process. " + << "WinRT exception: " << error.code() << ": " + << winrt::to_string(error.message()); + } + return false; +} + +bool BluetoothPairing::Unpair() { + NEARBY_LOGS(VERBOSE) << __func__ << ": Start to unpair with remote device."; + try { + if (!IsPaired()) { + NEARBY_LOGS(VERBOSE) << __func__ << " : Remote device Was not paired."; + return true; + } + DeviceUnpairingResult unpairing_result = + bluetooth_device_.DeviceInformation().Pairing().UnpairAsync().get(); + if (unpairing_result.Status() == DeviceUnpairingResultStatus::Unpaired) { + NEARBY_LOGS(VERBOSE) << __func__ << ": Unpaired with remote device."; + return true; + } + NEARBY_LOGS(VERBOSE) << __func__ + << ": Failed to unpaired with remote device."; + } catch (std::exception exception) { + NEARBY_LOGS(ERROR) << __func__ + << ": Failed to unpaired with device. exception: " + << exception.what(); + } catch (const winrt::hresult_error& error) { + NEARBY_LOGS(ERROR) << __func__ + << ": Failed to unpaired with device. WinRT exception: " + << error.code() << ": " + << winrt::to_string(error.message()); + } + return false; +} + +bool BluetoothPairing::IsPaired() { + try { + bool is_paired = bluetooth_device_.DeviceInformation().Pairing().IsPaired(); + NEARBY_LOGS(INFO) << __func__ << (is_paired ? "True" : "False"); + return is_paired; + } catch (std::exception exception) { + NEARBY_LOGS(ERROR) << __func__ << ": Failed to get IsPaired. exception: " + << exception.what(); + } catch (const winrt::hresult_error& error) { + NEARBY_LOGS(ERROR) << __func__ + << ": Failed to get IsPaired. WinRT exception: " + << error.code() << ": " + << winrt::to_string(error.message()); + } + return false; } void BluetoothPairing::OnPairingRequested( DeviceInformationCustomPairing custom_pairing, DevicePairingRequestedEventArgs pairing_requested) { - NEARBY_LOGS(INFO) << "BluetoothPairing::OnPairingRequested()"; - DevicePairingKinds pairing_kind = pairing_requested.PairingKind(); - switch (pairing_kind) { - case DevicePairingKinds::ProvidePin: - NEARBY_LOGS(INFO) << "DevicePairingKind: RequestPinCode."; - pairing_requested.Accept(); - return; - case DevicePairingKinds::ConfirmOnly: - NEARBY_LOGS(INFO) << "DevicePairingKind: ConfirmOnly."; - pairing_requested.Accept(); - break; - case DevicePairingKinds::ConfirmPinMatch: - NEARBY_LOGS(INFO) << "DevicePairingKind: Confirm Pin Match: " - << pairing_requested.Pin().c_str(); - pairing_requested.Accept(); - break; - default: - NEARBY_LOGS(INFO) << "Unsupported DevicePairingKind = " - << static_cast(pairing_kind); - break; + NEARBY_LOGS(VERBOSE) << __func__ << "Requested to pair."; + try { + DevicePairingKinds pairing_kind = pairing_requested.PairingKind(); + pairing_requested_ = pairing_requested; + pairing_deferral_ = pairing_requested.GetDeferral(); + api::PairingParams params; + switch (pairing_kind) { + case DevicePairingKinds::ProvidePin: + NEARBY_LOGS(INFO) << __func__ << "DevicePairingKind: RequestPinCode."; + expecting_pin_code_ = true; + params.pairing_type = PairingType::kRequestPin; + pairing_callback_.on_pairing_initiated_cb(params); + return; + case DevicePairingKinds::ConfirmOnly: + NEARBY_LOGS(INFO) << __func__ << "DevicePairingKind: ConfirmOnly."; + params.pairing_type = PairingType::kConsent; + pairing_callback_.on_pairing_initiated_cb(params); + return; + case DevicePairingKinds::ConfirmPinMatch: + NEARBY_LOGS(INFO) << __func__ + << "DevicePairingKind: Confirm Pin Match."; + params.pairing_type = PairingType::kConfirmPasskey; + params.passkey = winrt::to_string(pairing_requested.Pin()); + pairing_callback_.on_pairing_initiated_cb(params); + return; + default: + params.pairing_type = PairingType::kUnknown; + NEARBY_LOGS(INFO) << __func__ << "Unsupported DevicePairingKind:" + << static_cast(pairing_kind); + break; + } + } catch (std::exception exception) { + NEARBY_LOGS(ERROR) << __func__ + << ": Failed to request to pair with device. exception: " + << exception.what(); + } catch (const winrt::hresult_error& error) { + NEARBY_LOGS(ERROR) + << __func__ + << ": Failed to request to pair with device. WinRT exception: " + << error.code() << ": " << winrt::to_string(error.message()); } + pairing_callback_.on_pairing_error_cb(PairingError::kFailed); } void BluetoothPairing::OnPair(DevicePairingResult& pairing_result) { - DevicePairingResultStatus status = pairing_result.Status(); - - switch (status) { - case DevicePairingResultStatus::AlreadyPaired: - case DevicePairingResultStatus::Paired: - NEARBY_LOGS(ERROR) << "Pairing Result Status: Paired."; - return; - case DevicePairingResultStatus::PairingCanceled: - NEARBY_LOGS(ERROR) << "Pairing Result Status: Pairing Canceled."; - return; - case DevicePairingResultStatus::AuthenticationFailure: - NEARBY_LOGS(ERROR) << "Pairing Result Status: Authentication Failure."; - return; - case DevicePairingResultStatus::ConnectionRejected: - case DevicePairingResultStatus::RejectedByHandler: - NEARBY_LOGS(ERROR) << "Pairing Result Status: Authentication Rejected."; - return; - case DevicePairingResultStatus::AuthenticationTimeout: - NEARBY_LOGS(ERROR) << "Pairing Result Status: Authentication Timeout."; - return; - case DevicePairingResultStatus::Failed: - NEARBY_LOGS(ERROR) << "Pairing Result Status: Failed."; - return; - case DevicePairingResultStatus::OperationAlreadyInProgress: - NEARBY_LOGS(ERROR) << "Pairing Result Status: Operatio In Progress."; - return; - default: - NEARBY_LOGS(ERROR) << "Pairing Result Status: Failed."; - return; + try { + DevicePairingResultStatus status = pairing_result.Status(); + NEARBY_LOGS(INFO) << __func__ + << "Pairing Result Status: " << static_cast(status); + if (was_cancelled_ && + status == DevicePairingResultStatus::RejectedByHandler) { + // See comment in CancelPairing() for explanation of why was_cancelled_ + // is used. + status = DevicePairingResultStatus::PairingCanceled; + } + switch (status) { + case DevicePairingResultStatus::AlreadyPaired: + case DevicePairingResultStatus::Paired: + NEARBY_LOGS(ERROR) << __func__ << "Pairing Result Status: Paired."; + pairing_callback_.on_paired_cb(); + return; + case DevicePairingResultStatus::PairingCanceled: + NEARBY_LOGS(ERROR) << __func__ + << "Pairing Result Status: Pairing Canceled."; + pairing_callback_.on_pairing_error_cb(PairingError::kAuthCanceled); + return; + case DevicePairingResultStatus::AuthenticationFailure: + NEARBY_LOGS(ERROR) << __func__ + << "Pairing Result Status: Authentication Failure."; + pairing_callback_.on_pairing_error_cb(PairingError::kAuthFailed); + return; + case DevicePairingResultStatus::ConnectionRejected: + case DevicePairingResultStatus::RejectedByHandler: + NEARBY_LOGS(ERROR) << __func__ + << "Pairing Result Status: Authentication Rejected."; + pairing_callback_.on_pairing_error_cb(PairingError::kAuthRejected); + return; + case DevicePairingResultStatus::AuthenticationTimeout: + NEARBY_LOGS(ERROR) << __func__ + << "Pairing Result Status: Authentication Timeout."; + pairing_callback_.on_pairing_error_cb(PairingError::kAuthTimeout); + return; + case DevicePairingResultStatus::Failed: + NEARBY_LOGS(ERROR) << __func__ << "Pairing Result Status: Failed."; + pairing_callback_.on_pairing_error_cb(PairingError::kFailed); + return; + case DevicePairingResultStatus::OperationAlreadyInProgress: + NEARBY_LOGS(ERROR) << __func__ + << "Pairing Result Status: Operation In Progress."; + pairing_callback_.on_pairing_error_cb(PairingError::kRepeatedAttempts); + return; + default: + break; + } + NEARBY_LOGS(ERROR) << __func__ << "Pairing Result Status: Failed."; + } catch (std::exception exception) { + NEARBY_LOGS(ERROR) << __func__ + << ": Failed to get Pairing Result Status. exception: " + << exception.what(); + } catch (const winrt::hresult_error& error) { + NEARBY_LOGS(ERROR) << __func__ << ": Failed to get Pairing Result Status." + << " WinRT exception: " << error.code() << ": " + << winrt::to_string(error.message()); } + pairing_callback_.on_pairing_error_cb(PairingError::kFailed); } } // namespace windows diff --git a/internal/platform/implementation/windows/bluetooth_pairing.h b/internal/platform/implementation/windows/bluetooth_pairing.h index 05cf5c75..4ffd8d04 100644 --- a/internal/platform/implementation/windows/bluetooth_pairing.h +++ b/internal/platform/implementation/windows/bluetooth_pairing.h @@ -15,27 +15,35 @@ #ifndef THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_WINDOWS_BLUETOOTH_PAIRING_H_ #define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_WINDOWS_BLUETOOTH_PAIRING_H_ -#include +#include -#include +#include +#include "absl/strings/string_view.h" +#include "internal/platform/implementation/bluetooth_classic.h" +#include "winrt/Windows.Devices.Bluetooth.h" #include "winrt/Windows.Devices.Enumeration.h" +#include "winrt/Windows.Foundation.Collections.h" +#include "winrt/base.h" namespace nearby { namespace windows { -class BluetoothPairing { +class BluetoothPairing : public api::BluetoothPairing { public: explicit BluetoothPairing( - ::winrt::Windows::Devices::Enumeration::DeviceInformationCustomPairing& + ::winrt::Windows::Devices::Bluetooth::BluetoothDevice bluetooth_device, + ::winrt::Windows::Devices::Enumeration::DeviceInformationCustomPairing custom_pairing); BluetoothPairing(const BluetoothPairing&) = default; BluetoothPairing& operator=(const BluetoothPairing&) = default; + ~BluetoothPairing() override; - ~BluetoothPairing(); - - // Initiates the pairing procedure. - void StartPairing(); + bool InitiatePairing(api::BluetoothPairingCallback pairing_cb) override; + bool FinishPairing(std::optional pin_code) override; + bool CancelPairing() override; + bool Unpair() override; + bool IsPaired() override; private: void OnPairingRequested( @@ -48,9 +56,20 @@ class BluetoothPairing { pairing_result); // WinRT objects + ::winrt::Windows::Devices::Bluetooth::BluetoothDevice bluetooth_device_; ::winrt::Windows::Devices::Enumeration::DeviceInformationCustomPairing custom_pairing_; + ::winrt::event_token pairing_requested_token_; + ::winrt::Windows::Devices::Enumeration::DevicePairingRequestedEventArgs + pairing_requested_ = nullptr; + ::winrt::Windows::Foundation::Deferral pairing_deferral_ = nullptr; + api::BluetoothPairingCallback pairing_callback_; + + // Boolean indicating whether the device is currently pairing and expecting a + // PIN Code to be returned. + bool expecting_pin_code_ = false; + bool was_cancelled_ = false; }; } // namespace windows diff --git a/internal/platform/implementation/windows/generated/BUILD b/internal/platform/implementation/windows/generated/BUILD index 4451d2e2..da742cb1 100644 --- a/internal/platform/implementation/windows/generated/BUILD +++ b/internal/platform/implementation/windows/generated/BUILD @@ -45,7 +45,6 @@ cc_library( "//internal:__subpackages__", "//internal/platform/implementation/windows:__subpackages__", "//location/nearby/apps/better_together/windows:__subpackages__", - "//location/nearby/cpp/fastpair/internal:__subpackages__", "//location/nearby/cpp/sharing/implementation/internal:__subpackages__", ], ) diff --git a/internal/platform/implementation/windows/platform.cc b/internal/platform/implementation/windows/platform.cc index 70047776..b7956fe8 100644 --- a/internal/platform/implementation/windows/platform.cc +++ b/internal/platform/implementation/windows/platform.cc @@ -53,6 +53,7 @@ #include "internal/platform/implementation/windows/listenable_future.h" #include "internal/platform/implementation/windows/log_message.h" #include "internal/platform/implementation/windows/mutex.h" +#include "internal/platform/implementation/windows/preferences_repository.h" #include "internal/platform/implementation/windows/scheduled_executor.h" #include "internal/platform/implementation/windows/server_sync.h" #include "internal/platform/implementation/windows/settable_future.h" @@ -141,7 +142,7 @@ std::string ImplementationPlatform::GetAppDataPath( FOLDERID_LocalAppData, // rfid: A reference to the KNOWNFOLDERID that // identifies the folder. 0, // dwFlags: Flags that specify special retrieval options. - NULL, // hToken: An access token that represents a particular user. + nullptr, // hToken: An access token that represents a particular user. &basePath); // ppszPath: When this method returns, contains the address // of a pointer to a null-terminated Unicode string that // specifies the path of the known folder. The calling @@ -149,7 +150,7 @@ std::string ImplementationPlatform::GetAppDataPath( // is no longer needed by calling CoTaskMemFree, whether // SHGetKnownFolderPath succeeds or not. size_t bufferSize; - wcstombs_s(&bufferSize, NULL, 0, basePath, 0); + wcstombs_s(&bufferSize, nullptr, 0, basePath, 0); std::string fullpathUTF8(bufferSize - 1, '\0'); wcstombs_s(&bufferSize, fullpathUTF8.data(), bufferSize, basePath, _TRUNCATE); CoTaskMemFree(basePath); @@ -320,5 +321,10 @@ std::unique_ptr ImplementationPlatform::CreateDeviceInfo() { return std::make_unique(); } +std::unique_ptr +ImplementationPlatform::CreatePreferencesRepository(absl::string_view path) { + return std::make_unique(path); +} + } // namespace api } // namespace nearby diff --git a/internal/platform/implementation/windows/preferences_repository.cc b/internal/platform/implementation/windows/preferences_repository.cc new file mode 100644 index 00000000..5c323c20 --- /dev/null +++ b/internal/platform/implementation/windows/preferences_repository.cc @@ -0,0 +1,145 @@ +// 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. + +#include "internal/platform/implementation/windows/preferences_repository.h" + +#include +#include // NOLINT(build/c++17) +#include +#include + +#include "nlohmann/json.hpp" +#include "nlohmann/json_fwd.hpp" +#include "internal/platform/logging.h" + +namespace nearby { +namespace windows { +namespace { +using json = ::nlohmann::json; + +constexpr char kPreferencesFileName[] = "preferences.json"; +constexpr char kPreferencesBackupFileName[] = "preferences_bak.json"; + +} // namespace + +json PreferencesRepository::LoadPreferences() { + absl::MutexLock lock(&mutex_); + std::optional preferences = AttemptLoad(); + if (preferences.has_value()) { + return preferences.value(); + } + + NEARBY_LOGS(ERROR) << "Could not load preferences file, trying backup."; + + // In the future we should switch to using a transaction log or another + // stable method which doesn't pose a risk of losing settings + preferences = RestoreFromBackup(); + if (preferences.has_value()) { + NEARBY_LOGS(ERROR) << "Successfully recovered from backup."; + return preferences.value(); + } + + NEARBY_LOGS(ERROR) << "Failed to load preferences file from back up."; + + return json::object(); +} + +bool PreferencesRepository::SavePreferences(json preferences) { + absl::MutexLock lock(&mutex_); + try { + std::filesystem::path path = path_; + if (!std::filesystem::exists(path) && + !std::filesystem::create_directories(path)) { + NEARBY_LOGS(ERROR) << "Failed to create preferences path."; + return false; + } + + std::filesystem::path full_name = path / kPreferencesFileName; + std::filesystem::path full_name_backup = path / kPreferencesBackupFileName; + + // Create a backup without moving the bytes on disk + if (std::filesystem::exists(full_name)) { + NEARBY_LOGS(INFO) << "Making backup of preferences file."; + std::filesystem::rename(full_name, full_name_backup); + } + + std::ofstream preferences_file(full_name.c_str()); + preferences_file << preferences; + preferences_file.close(); + + // Make sure the file wasn't saved in a corrupted state + if (!AttemptLoad().has_value()) { + NEARBY_LOGS(ERROR) << "Preferences saved to disk in corrupted state. " + "Restoring from backup."; + + if (!RestoreFromBackup().has_value()) { + NEARBY_LOGS(ERROR) << "Failed to restore preferences file."; + return false; + } + } + } catch (const std::exception& e) { + NEARBY_LOGS(ERROR) << "Failed to save preferences file: " << e.what(); + return false; + } + + return true; +} + +std::optional PreferencesRepository::AttemptLoad() { + std::filesystem::path path = path_; + std::filesystem::path full_name = path / kPreferencesFileName; + if (!std::filesystem::exists(path) || !std::filesystem::exists(full_name)) { + return std::nullopt; + } + + try { + std::ifstream preferences_file(full_name.c_str()); + if (!preferences_file.good()) { + return std::nullopt; + } + + json preferences = json::parse(preferences_file, nullptr, false); + preferences_file.close(); + + if (preferences.is_discarded()) { + NEARBY_LOGS(ERROR) << "Preferences file corrupted."; + return std::nullopt; + } + + return preferences; + } catch (const std::exception& e) { + NEARBY_LOGS(ERROR) << "Exception while loading preferences: " << e.what(); + return std::nullopt; + } +} + +std::optional PreferencesRepository::RestoreFromBackup() { + std::filesystem::path path = path_; + std::filesystem::path full_name = path / kPreferencesFileName; + std::filesystem::path full_name_backup = path / kPreferencesBackupFileName; + + if (!std::filesystem::exists(full_name_backup)) { + NEARBY_LOGS(WARNING) + << "Backup requested but no backup preferences file found."; + return std::nullopt; + } + + std::filesystem::rename(full_name_backup, full_name); + + NEARBY_LOGS(INFO) << "Attempting load from backup preferences."; + return AttemptLoad(); +} + +} // namespace windows +} // namespace nearby diff --git a/internal/platform/implementation/windows/preferences_repository.h b/internal/platform/implementation/windows/preferences_repository.h new file mode 100644 index 00000000..1ed532a9 --- /dev/null +++ b/internal/platform/implementation/windows/preferences_repository.h @@ -0,0 +1,48 @@ +// Copyright 2021 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_IMPLEMENTATION_WINDOWS_PREFERENCES_REPOSITORY_H_ +#define PLATFORM_IMPLEMENTATION_WINDOWS_PREFERENCES_REPOSITORY_H_ + +#include + +#include "absl/base/thread_annotations.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" +#include "nlohmann/json_fwd.hpp" +#include "internal/platform/implementation/preferences_repository.h" + +namespace nearby { +namespace windows { + +class PreferencesRepository : public api::PreferencesRepository { + public: + explicit PreferencesRepository(absl::string_view path) + : api::PreferencesRepository(path) {} + + nlohmann::json LoadPreferences() override ABSL_LOCKS_EXCLUDED(&mutex_); + bool SavePreferences(nlohmann::json preferences) override + ABSL_LOCKS_EXCLUDED(&mutex_); + + std::optional AttemptLoad(); + std::optional RestoreFromBackup(); + + private: + absl::Mutex mutex_; +}; + +} // namespace windows +} // namespace nearby + +#endif // PLATFORM_IMPLEMENTATION_WINDOWS_PREFERENCES_REPOSITORY_H_ diff --git a/internal/platform/implementation/windows/preferences_repository_test.cc b/internal/platform/implementation/windows/preferences_repository_test.cc new file mode 100644 index 00000000..78794a00 --- /dev/null +++ b/internal/platform/implementation/windows/preferences_repository_test.cc @@ -0,0 +1,142 @@ +// 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. + +#include "internal/platform/implementation/windows/preferences_repository.h" + +#include // NOLINT(build/c++17) +#include +#include + +#include "gtest/gtest.h" +#include "nlohmann/json.hpp" +#include "nlohmann/json_fwd.hpp" +#include "internal/platform/implementation/device_info.h" +#include "internal/platform/implementation/platform.h" + +namespace nearby { +namespace windows { +namespace { + +using json = ::nlohmann::json; + +constexpr char kPreferencesFileName[] = "preferences.json"; +constexpr char kPreferencesBackupFileName[] = "preferences_bak.json"; +constexpr char kPreferencesPath[] = "Google/Nearby/Sharing"; + +TEST(PreferencesRepository, LoadWithBadPath) { + PreferencesRepository preferences_repository{"c:\\users\\a\\b\\c\\d\\e\\f"}; + json result = preferences_repository.LoadPreferences(); + EXPECT_TRUE(result.empty()); +} + +TEST(PreferencesRepository, SaveAndLoadPreferences) { + std::optional app_data_path = + api::ImplementationPlatform::CreateDeviceInfo()->GetLocalAppDataPath(); + ASSERT_TRUE(app_data_path.has_value()); + std::filesystem::path full_path = *app_data_path / kPreferencesPath; + std::filesystem::path full_name = full_path / kPreferencesFileName; + + if (std::filesystem::exists(full_name)) { + std::filesystem::remove(full_name); + } + + PreferencesRepository preferences_repository{full_path.string()}; + json data; + data["key1"] = "value1"; + data["key2"] = "value2"; + EXPECT_TRUE(preferences_repository.SavePreferences(data)); + json result = preferences_repository.LoadPreferences(); + EXPECT_EQ(result.size(), 2); + EXPECT_EQ(result["key1"], "value1"); + EXPECT_EQ(result["key2"], "value2"); + std::filesystem::remove(full_name); +} + +TEST(PreferencesRepository, LoadFromBackup) { + std::optional app_data_path = + api::ImplementationPlatform::CreateDeviceInfo()->GetLocalAppDataPath(); + ASSERT_TRUE(app_data_path.has_value()); + std::filesystem::path full_path = *app_data_path / kPreferencesPath; + std::filesystem::path full_name = full_path / kPreferencesFileName; + std::filesystem::path full_name_backup = + full_path / kPreferencesBackupFileName; + + if (std::filesystem::exists(full_name)) { + std::filesystem::remove(full_name); + } + + if (std::filesystem::exists(full_name_backup)) { + std::filesystem::remove(full_name_backup); + } + + PreferencesRepository preferences_repository{full_path.string()}; + json data; + data["key1"] = "value1"; + data["key2"] = "value2"; + + std::ofstream backup_file(full_name_backup.c_str()); + backup_file << data; + backup_file.close(); + + std::optional result; + result = preferences_repository.AttemptLoad(); + EXPECT_FALSE(result.has_value()); + result = preferences_repository.RestoreFromBackup(); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(result.value()["key1"], "value1"); + EXPECT_EQ(result.value()["key2"], "value2"); + std::filesystem::remove(full_name); + EXPECT_FALSE(std::filesystem::exists(full_name_backup)); +} + +TEST(PreferencesRepository, RecoverFromCorruption) { + std::optional app_data_path = + api::ImplementationPlatform::CreateDeviceInfo()->GetLocalAppDataPath(); + ASSERT_TRUE(app_data_path.has_value()); + std::filesystem::path full_path = *app_data_path / kPreferencesPath; + std::filesystem::path full_name = full_path / kPreferencesFileName; + std::filesystem::path full_name_backup = + full_path / kPreferencesBackupFileName; + + if (std::filesystem::exists(full_name)) { + std::filesystem::remove(full_name); + } + + if (std::filesystem::exists(full_name_backup)) { + std::filesystem::remove(full_name_backup); + } + + PreferencesRepository preferences_repository{full_path.string()}; + json data; + data["key1"] = "value1"; + data["key2"] = "value2"; + + std::ofstream preferences_file(full_name_backup.c_str()); + preferences_file << data; + preferences_file.close(); + + std::ofstream backup_file(full_name.c_str()); + backup_file << "[BAD JSON FILE]"; + backup_file.close(); + + std::optional result = preferences_repository.LoadPreferences(); + EXPECT_EQ(result.value()["key1"], "value1"); + EXPECT_EQ(result.value()["key2"], "value2"); + std::filesystem::remove(full_name); + EXPECT_FALSE(std::filesystem::exists(full_name_backup)); +} + +} // namespace +} // namespace windows +} // namespace nearby diff --git a/internal/platform/implementation/windows/wifi_direct.h b/internal/platform/implementation/windows/wifi_direct.h index 561ed4c4..c44787ed 100644 --- a/internal/platform/implementation/windows/wifi_direct.h +++ b/internal/platform/implementation/windows/wifi_direct.h @@ -29,7 +29,6 @@ #include // Nearby connections headers -#include "internal/platform/cancellation_flag_listener.h" #include "internal/platform/implementation/wifi_direct.h" #include "internal/platform/implementation/windows/scheduled_executor.h" @@ -314,10 +313,6 @@ class WifiDirectMedium : public api::WifiDirectMedium { // Scheduled task for connection timeout. std::shared_ptr connection_timeout_ = nullptr; - - // Listener to connect cancellation. - std::unique_ptr - connection_cancellation_listener_ = nullptr; }; } // namespace windows diff --git a/internal/platform/implementation/windows/wifi_direct_medium.cc b/internal/platform/implementation/windows/wifi_direct_medium.cc index 3f0ff532..c27595d0 100644 --- a/internal/platform/implementation/windows/wifi_direct_medium.cc +++ b/internal/platform/implementation/windows/wifi_direct_medium.cc @@ -107,6 +107,9 @@ std::unique_ptr WifiDirectMedium::ConnectToService( try { StreamSocket socket{}; + std::unique_ptr + connection_cancellation_listener = nullptr; + // setup cancel listener if (cancellation_flag != nullptr) { if (cancellation_flag->Cancelled()) { @@ -115,7 +118,7 @@ std::unique_ptr WifiDirectMedium::ConnectToService( return nullptr; } - connection_cancellation_listener_ = + connection_cancellation_listener = std::make_unique( cancellation_flag, [socket]() { NEARBY_LOGS(WARNING) @@ -132,9 +135,6 @@ std::unique_ptr WifiDirectMedium::ConnectToService( kWifiDirectClientSocketConnectTimeoutMillis); socket.ConnectAsync(host_name, service_name).get(); - if (connection_cancellation_listener_ != nullptr) { - connection_cancellation_listener_ = nullptr; - } if (connection_timeout_ != nullptr) { connection_timeout_->Cancel(); @@ -151,10 +151,6 @@ std::unique_ptr WifiDirectMedium::ConnectToService( << ":" << port << " for the " << i + 1 << " time"; } - if (connection_cancellation_listener_ != nullptr) { - connection_cancellation_listener_ = nullptr; - } - if (connection_timeout_ != nullptr) { connection_timeout_->Cancel(); connection_timeout_ = nullptr; @@ -165,8 +161,8 @@ std::unique_ptr WifiDirectMedium::ConnectToService( return nullptr; } -std::unique_ptr -WifiDirectMedium::ListenForService(int port) { +std::unique_ptr WifiDirectMedium::ListenForService( + int port) { absl::MutexLock lock(&mutex_); // check current status diff --git a/internal/platform/implementation/windows/wifi_hotspot.h b/internal/platform/implementation/windows/wifi_hotspot.h index c6253f63..162c843b 100644 --- a/internal/platform/implementation/windows/wifi_hotspot.h +++ b/internal/platform/implementation/windows/wifi_hotspot.h @@ -27,7 +27,6 @@ #include // Nearby connections headers -#include "internal/platform/cancellation_flag_listener.h" #include "internal/platform/implementation/wifi_hotspot.h" #include "internal/platform/implementation/windows/scheduled_executor.h" #include "internal/platform/implementation/windows/submittable_executor.h" @@ -325,10 +324,6 @@ class WifiHotspotMedium : public api::WifiHotspotMedium { // Scheduled task for connection timeout. std::shared_ptr connection_timeout_ = nullptr; - - // Listener to connect cancellation. - std::unique_ptr - connection_cancellation_listener_ = nullptr; }; } // namespace windows diff --git a/internal/platform/implementation/windows/wifi_hotspot_medium.cc b/internal/platform/implementation/windows/wifi_hotspot_medium.cc index 0ec43ac6..8a3532f7 100644 --- a/internal/platform/implementation/windows/wifi_hotspot_medium.cc +++ b/internal/platform/implementation/windows/wifi_hotspot_medium.cc @@ -109,6 +109,9 @@ std::unique_ptr WifiHotspotMedium::ConnectToService( for (int i = 0; i < kWifiHotspotMaxConnectionRetries; i++) { try { StreamSocket socket{}; + // Listener to connect cancellation. + std::unique_ptr + connection_cancellation_listener = nullptr; // setup cancel listener if (cancellation_flag != nullptr) { @@ -118,7 +121,7 @@ std::unique_ptr WifiHotspotMedium::ConnectToService( return nullptr; } - connection_cancellation_listener_ = + connection_cancellation_listener = std::make_unique( cancellation_flag, [socket]() { NEARBY_LOGS(WARNING) @@ -137,9 +140,6 @@ std::unique_ptr WifiHotspotMedium::ConnectToService( } socket.ConnectAsync(host_name, service_name).get(); - if (connection_cancellation_listener_ != nullptr) { - connection_cancellation_listener_ = nullptr; - } if (connection_timeout_ != nullptr) { connection_timeout_->Cancel(); @@ -166,10 +166,6 @@ std::unique_ptr WifiHotspotMedium::ConnectToService( << " time due to unknown reason."; } - if (connection_cancellation_listener_ != nullptr) { - connection_cancellation_listener_ = nullptr; - } - if (connection_timeout_ != nullptr) { connection_timeout_->Cancel(); connection_timeout_ = nullptr; diff --git a/internal/platform/implementation/windows/wifi_lan.h b/internal/platform/implementation/windows/wifi_lan.h index 1e33f0fc..dcb4e765 100644 --- a/internal/platform/implementation/windows/wifi_lan.h +++ b/internal/platform/implementation/windows/wifi_lan.h @@ -34,7 +34,6 @@ #include "absl/synchronization/mutex.h" #include "absl/time/time.h" #include "absl/types/optional.h" -#include "internal/platform/cancellation_flag_listener.h" #include "internal/platform/count_down_latch.h" #include "internal/platform/exception.h" #include "internal/platform/implementation/wifi_lan.h" @@ -368,10 +367,6 @@ class WifiLanMedium : public api::WifiLanMedium { // Scheduled task for connection timeout. std::shared_ptr connection_timeout_ = nullptr; - - // Listener to connect cancellation. - std::unique_ptr - connection_cancellation_listener_ = nullptr; }; } // namespace windows diff --git a/internal/platform/implementation/windows/wifi_lan_medium.cc b/internal/platform/implementation/windows/wifi_lan_medium.cc index ea2923d9..b98b164c 100644 --- a/internal/platform/implementation/windows/wifi_lan_medium.cc +++ b/internal/platform/implementation/windows/wifi_lan_medium.cc @@ -350,6 +350,9 @@ std::unique_ptr WifiLanMedium::ConnectToService( return nullptr; } + std::unique_ptr connection_cancellation_listener = + nullptr; + HostName host_name{string_to_wstring(std::string(ipv4_address))}; winrt::hstring service_name{winrt::to_hstring(port)}; @@ -363,7 +366,7 @@ std::unique_ptr WifiLanMedium::ConnectToService( return nullptr; } - connection_cancellation_listener_ = + connection_cancellation_listener = std::make_unique( cancellation_flag, [socket]() { NEARBY_LOGS(WARNING) @@ -384,9 +387,6 @@ std::unique_ptr WifiLanMedium::ConnectToService( } socket.ConnectAsync(host_name, service_name).get(); - if (connection_cancellation_listener_ != nullptr) { - connection_cancellation_listener_ = nullptr; - } if (connection_timeout_ != nullptr) { connection_timeout_->Cancel(); @@ -409,10 +409,6 @@ std::unique_ptr WifiLanMedium::ConnectToService( << ":" << port; } - if (connection_cancellation_listener_ != nullptr) { - connection_cancellation_listener_ = nullptr; - } - if (connection_timeout_ != nullptr) { connection_timeout_->Cancel(); connection_timeout_ = nullptr; diff --git a/internal/platform/implementation/windows/wifi_medium.cc b/internal/platform/implementation/windows/wifi_medium.cc index 0ca62ec8..f35bf8e5 100644 --- a/internal/platform/implementation/windows/wifi_medium.cc +++ b/internal/platform/implementation/windows/wifi_medium.cc @@ -192,7 +192,7 @@ api::WifiInformation& WifiMedium::GetInformation() { p_connect_info->wlanAssociationAttributes.dot11Bssid), kMacAddrLen); wifi_information_.bssid = absl::StrFormat( - "%2llx:%2llx:%2llx:%2llx:%2llx:%2llx", str_tmp[0], str_tmp[1], + "%02llx:%02llx:%02llx:%02llx:%02llx:%02llx", str_tmp[0], str_tmp[1], str_tmp[2], str_tmp[3], str_tmp[4], str_tmp[5]); NEARBY_LOGS(INFO) << "wifi bssid is: " << wifi_information_.bssid; } @@ -241,7 +241,7 @@ std::string WifiMedium::InternalGetWifiIpAddress() { winrt::to_string(profile_details.GetConnectedSsid())) { NEARBY_LOGS(INFO) << "SSID of this IP matches with this WiFi interface's SSID:" - << wifi_information_.ssid << ", return this IP"; + << wifi_information_.ssid << ", return this IP: " << ipv4_s; return ipv4_s; } } diff --git a/internal/platform/listeners.h b/internal/platform/listeners.h index e4ef54bd..f1143a13 100644 --- a/internal/platform/listeners.h +++ b/internal/platform/listeners.h @@ -27,6 +27,11 @@ constexpr absl::AnyInvocable DefaultCallback() { return absl::AnyInvocable{[](Args...) {}}; } +template +constexpr std::function DefaultFuncCallback() { + return [](Args...) {}; +} + } // namespace nearby #endif // PLATFORM_BASE_LISTENERS_H_ diff --git a/internal/platform/medium_environment.cc b/internal/platform/medium_environment.cc index 600ce8fd..ead9fcc6 100644 --- a/internal/platform/medium_environment.cc +++ b/internal/platform/medium_environment.cc @@ -50,6 +50,7 @@ void MediumEnvironment::Start(EnvironmentConfig config) { NEARBY_LOGS(INFO) << "MediumEnvironment::Start()"; config_ = std::move(config); if (config_.use_simulated_clock) { + MutexLock lock(&mutex_); simulated_clock_ = std::make_unique(); } Reset(); @@ -60,8 +61,11 @@ void MediumEnvironment::Stop() { if (enabled_.exchange(false)) { NEARBY_LOGS(INFO) << "MediumEnvironment::Stop()"; Sync(false); + if (config_.use_simulated_clock) { + MutexLock lock(&mutex_); + simulated_clock_.reset(); + } config_ = {}; - simulated_clock_.reset(); } } @@ -1205,6 +1209,7 @@ void MediumEnvironment::SetFeatureFlags(const FeatureFlags::Flags& flags) { } absl::optional MediumEnvironment::GetSimulatedClock() { + MutexLock lock(&mutex_); if (simulated_clock_) { return absl::optional(simulated_clock_.get()); } diff --git a/internal/platform/medium_environment.h b/internal/platform/medium_environment.h index 4bbd2d86..b0865e33 100644 --- a/internal/platform/medium_environment.h +++ b/internal/platform/medium_environment.h @@ -511,7 +511,7 @@ class MediumEnvironment { bool use_valid_peer_connection_ = true; absl::Duration peer_connection_latency_ = absl::ZeroDuration(); - std::unique_ptr simulated_clock_; + std::unique_ptr simulated_clock_ ABSL_GUARDED_BY(mutex_); }; } // namespace nearby diff --git a/internal/platform/task_runner_impl.cc b/internal/platform/task_runner_impl.cc index ac400d1f..587f5319 100644 --- a/internal/platform/task_runner_impl.cc +++ b/internal/platform/task_runner_impl.cc @@ -18,7 +18,7 @@ #include #include "absl/functional/any_invocable.h" -#include "internal/crypto/random.h" +#include "internal/platform/implementation/crypto.h" #include "internal/platform/timer_impl.h" namespace nearby { @@ -66,6 +66,6 @@ bool TaskRunnerImpl::PostDelayedTask(absl::Duration delay, return false; } -uint64_t TaskRunnerImpl::GenerateId() { return ::crypto::RandData(); } +uint64_t TaskRunnerImpl::GenerateId() { return nearby::RandData(); } } // namespace nearby diff --git a/internal/platform/wifi_lan_connection_info.cc b/internal/platform/wifi_lan_connection_info.cc index 361ddabe..424f2396 100644 --- a/internal/platform/wifi_lan_connection_info.cc +++ b/internal/platform/wifi_lan_connection_info.cc @@ -15,12 +15,13 @@ #include "internal/platform/wifi_lan_connection_info.h" #include +#include #include "absl/status/status.h" #include "absl/status/statusor.h" -#include "absl/strings/str_format.h" #include "absl/strings/string_view.h" #include "internal/platform/connection_info.h" +#include "internal/platform/logging.h" namespace nearby { namespace { @@ -42,19 +43,18 @@ std::string WifiLanConnectionInfo::ToDataElementBytes() const { mask |= has_bssid ? kBssidMask : 0; payload_data.push_back(mask); if (has_ipv4 || has_ipv6) { - payload_data.insert(payload_data.end(), ip_address_.begin(), - ip_address_.end()); + payload_data.append(ip_address_.begin(), ip_address_.end()); } - payload_data.insert(payload_data.end(), port_.begin(), port_.end()); + payload_data.append(port_.begin(), port_.end()); if (has_bssid) { - payload_data.insert(payload_data.end(), bssid_.begin(), bssid_.end()); + payload_data.append(bssid_.begin(), bssid_.end()); } - payload_data.push_back(actions_); + payload_data.append(actions_.begin(), actions_.end()); std::string ret; ret.push_back(kDataElementFieldType); ret.push_back(payload_data.size()); - ret.insert(ret.end(), payload_data.begin(), payload_data.end()); - return std::string(ret.data(), ret.size()); + ret.append(payload_data.begin(), payload_data.end()); + return ret; } absl::StatusOr @@ -121,13 +121,10 @@ WifiLanConnectionInfo::FromDataElementBytes(absl::string_view bytes) { return absl::InvalidArgumentError( "Insufficient remaining bytes to read action."); } - char action = bytes[position]; - // Check that we don't have any remaining bytes. - if (bytes.size() != ++position) { - return absl::InvalidArgumentError(absl::StrFormat( - "Nonzero remaining bytes: %d.", bytes.size() - position)); - } - return WifiLanConnectionInfo(address, port, bssid, action); + auto action_str = bytes.substr(position); + return WifiLanConnectionInfo( + address, port, bssid, + std::vector(action_str.begin(), action_str.end())); } } // namespace nearby diff --git a/internal/platform/wifi_lan_connection_info.h b/internal/platform/wifi_lan_connection_info.h index a31bf6b2..32c58ca2 100644 --- a/internal/platform/wifi_lan_connection_info.h +++ b/internal/platform/wifi_lan_connection_info.h @@ -17,6 +17,7 @@ #include #include +#include #include "absl/status/statusor.h" #include "absl/strings/string_view.h" @@ -25,10 +26,10 @@ namespace nearby { -constexpr int kIpv4AddressLength = 4; -constexpr int kIpv6AddressLength = 16; -constexpr int kPortLength = 4; -constexpr int kBssidLength = 6; +inline constexpr int kIpv4AddressLength = 4; +inline constexpr int kIpv6AddressLength = 16; +inline constexpr int kPortLength = 2; +inline constexpr int kBssidLength = 6; class WifiLanConnectionInfo : public ConnectionInfo { public: @@ -36,10 +37,10 @@ class WifiLanConnectionInfo : public ConnectionInfo { absl::string_view bytes); WifiLanConnectionInfo(absl::string_view ip_address, absl::string_view port, - char actions) + std::vector actions) : ip_address_(ip_address), port_(port), bssid_(""), actions_(actions) {} WifiLanConnectionInfo(absl::string_view ip_address, absl::string_view port, - absl::string_view bssid, char actions) + absl::string_view bssid, std::vector actions) : ip_address_(ip_address), port_(port), bssid_(std::string(bssid)), @@ -51,15 +52,18 @@ class WifiLanConnectionInfo : public ConnectionInfo { } std::string ToDataElementBytes() const override; std::string GetIpAddress() const { return ip_address_; } + // This port is expected to be in hex form, such as \xFF\xFF for a value of + // 65535, 2 bytes in length. This field will be represented in network byte + // order (aka big-endian), so \x12\x34 will correspond to port 4660 (0x1234). std::string GetPort() const { return port_; } std::string GetBssid() const { return bssid_; } - char GetActions() const override { return actions_; } + std::vector GetActions() const override { return actions_; } private: std::string ip_address_; std::string port_; std::string bssid_; - char actions_; + std::vector actions_; }; inline bool operator==(const WifiLanConnectionInfo& a, diff --git a/internal/platform/wifi_lan_connection_info_test.cc b/internal/platform/wifi_lan_connection_info_test.cc index 817e47ff..3e5473c6 100644 --- a/internal/platform/wifi_lan_connection_info_test.cc +++ b/internal/platform/wifi_lan_connection_info_test.cc @@ -30,7 +30,7 @@ namespace { constexpr absl::string_view kIpv4Addr = "\x4C\x8B\x1D\xCE"; constexpr absl::string_view kIpv6Addr = "\x4C\x8B\x1D\xCE\x4C\x8B\x1D\xCE\x4C\x8B\x1D\xCE\x4C\x8B\x1D\xCE"; -constexpr absl::string_view kPort = "\x12\x34\x56\x78"; +constexpr absl::string_view kPort = "\x12\x34"; constexpr absl::string_view kBssid = "\x0A\x1B\x2C\x34\x58\x7E"; constexpr char kAction = 0x0F; @@ -38,20 +38,21 @@ using ::testing::status::StatusIs; using Medium = ::location::nearby::proto::connections::Medium; TEST(WifiLanConnectionInfoTest, TestMediumType) { - WifiLanConnectionInfo info(kIpv4Addr, kPort, kAction); + WifiLanConnectionInfo info(kIpv4Addr, kPort, {kAction}); EXPECT_EQ(info.GetMediumType(), Medium::WIFI_LAN); } TEST(WifiLanConnectionInfoTest, TestGetMembers) { - WifiLanConnectionInfo info(kIpv4Addr, kPort, kBssid, kAction); + WifiLanConnectionInfo info(kIpv4Addr, kPort, kBssid, {kAction}); EXPECT_EQ(info.GetIpAddress(), kIpv4Addr); EXPECT_EQ(info.GetPort(), kPort); EXPECT_EQ(info.GetBssid(), kBssid); - EXPECT_EQ(info.GetActions(), kAction); + ASSERT_EQ(info.GetActions().size(), 1); + EXPECT_EQ(info.GetActions()[0], kAction); } TEST(WifiLanConnectionInfoTest, TestToFromBytesIpv4) { - WifiLanConnectionInfo info(kIpv4Addr, kPort, kBssid, kAction); + WifiLanConnectionInfo info(kIpv4Addr, kPort, kBssid, {kAction}); std::string serialized = info.ToDataElementBytes(); auto result = WifiLanConnectionInfo::FromDataElementBytes(serialized); ASSERT_OK(result); @@ -59,7 +60,7 @@ TEST(WifiLanConnectionInfoTest, TestToFromBytesIpv4) { } TEST(WifiLanConnectionInfoTest, TestToFromBytesIpv6) { - WifiLanConnectionInfo info(kIpv6Addr, kPort, kBssid, kAction); + WifiLanConnectionInfo info(kIpv6Addr, kPort, kBssid, {kAction}); std::string serialized = info.ToDataElementBytes(); auto result = WifiLanConnectionInfo::FromDataElementBytes(serialized); ASSERT_OK(result); @@ -67,19 +68,19 @@ TEST(WifiLanConnectionInfoTest, TestToFromBytesIpv6) { } TEST(WifiLanConnectionInfoTest, TestCopy) { - WifiLanConnectionInfo info(kIpv4Addr, kPort, kBssid, kAction); + WifiLanConnectionInfo info(kIpv4Addr, kPort, kBssid, {kAction}); WifiLanConnectionInfo copy(info); EXPECT_EQ(info, copy); } TEST(WifiLanConnectionInfoTest, TestEquals) { - WifiLanConnectionInfo info(kIpv4Addr, kPort, kBssid, kAction); - WifiLanConnectionInfo info2(kIpv4Addr, kPort, kBssid, kAction); + WifiLanConnectionInfo info(kIpv4Addr, kPort, kBssid, {kAction}); + WifiLanConnectionInfo info2(kIpv4Addr, kPort, kBssid, {kAction}); EXPECT_EQ(info, info2); } TEST(WifiLanConnectionInfoTest, TestFromNoAction) { - WifiLanConnectionInfo info(kIpv4Addr, kPort, kBssid, kAction); + WifiLanConnectionInfo info(kIpv4Addr, kPort, kBssid, {kAction}); std::string serialized = info.ToDataElementBytes(); serialized[1] -= 1; auto result = WifiLanConnectionInfo::FromDataElementBytes( @@ -89,37 +90,40 @@ TEST(WifiLanConnectionInfoTest, TestFromNoAction) { TEST(WifiLanConnectionInfoTest, TestToFromShortBssid) { std::string shortBssid = "\x0A\x1B\x2C"; - WifiLanConnectionInfo info(kIpv4Addr, kPort, shortBssid, kAction); + WifiLanConnectionInfo info(kIpv4Addr, kPort, shortBssid, {kAction}); auto bytes = info.ToDataElementBytes(); auto result = WifiLanConnectionInfo::FromDataElementBytes(bytes); ASSERT_OK(result); EXPECT_EQ(result->GetIpAddress(), kIpv4Addr); EXPECT_EQ(result->GetPort(), kPort); EXPECT_TRUE(result->GetBssid().empty()); - EXPECT_EQ(result->GetActions(), kAction); + ASSERT_EQ(result->GetActions().size(), 1); + EXPECT_EQ(result->GetActions()[0], kAction); } TEST(WifiLanConnectionInfoTest, TestToFromNoIp) { - WifiLanConnectionInfo info("", kPort, kBssid, kAction); + WifiLanConnectionInfo info("", kPort, kBssid, {kAction}); auto bytes = info.ToDataElementBytes(); auto result = WifiLanConnectionInfo::FromDataElementBytes(bytes); ASSERT_OK(result); EXPECT_TRUE(result->GetIpAddress().empty()); EXPECT_EQ(result->GetPort(), kPort); EXPECT_EQ(result->GetBssid(), kBssid); - EXPECT_EQ(result->GetActions(), kAction); + ASSERT_EQ(result->GetActions().size(), 1); + EXPECT_EQ(result->GetActions()[0], kAction); } TEST(WifiLanConnectionInfoTest, TestToFromLongIp) { WifiLanConnectionInfo info(absl::StrCat(kIpv4Addr, kIpv6Addr), kPort, kBssid, - kAction); + {kAction}); auto bytes = info.ToDataElementBytes(); auto result = WifiLanConnectionInfo::FromDataElementBytes(bytes); ASSERT_OK(result); EXPECT_TRUE(result->GetIpAddress().empty()); EXPECT_EQ(result->GetPort(), kPort); EXPECT_EQ(result->GetBssid(), kBssid); - EXPECT_EQ(result->GetActions(), kAction); + ASSERT_EQ(result->GetActions().size(), 1); + EXPECT_EQ(result->GetActions()[0], kAction); } TEST(WifiLanConnectionInfoTest, TestFromIp) { @@ -130,7 +134,7 @@ TEST(WifiLanConnectionInfoTest, TestFromIp) { } TEST(WifiLanConnectionInfoTest, TestBadBytesLength) { - WifiLanConnectionInfo info(kIpv4Addr, kPort, kBssid, kAction); + WifiLanConnectionInfo info(kIpv4Addr, kPort, kBssid, {kAction}); std::string serialized = info.ToDataElementBytes(); std::string modified_short( serialized.substr(0, kIpv4AddressLength + kPortLength)); @@ -144,128 +148,12 @@ TEST(WifiLanConnectionInfoTest, TestBadBytesLength) { } TEST(WifiLanConnectionInfoTest, TestFromBadElementType) { - WifiLanConnectionInfo info(kIpv4Addr, kPort, kBssid, kAction); + WifiLanConnectionInfo info(kIpv4Addr, kPort, kBssid, {kAction}); std::string serialized = info.ToDataElementBytes(); serialized[0] = 0x56; auto result = WifiLanConnectionInfo::FromDataElementBytes(serialized); EXPECT_THAT(result, StatusIs(absl::StatusCode::kInvalidArgument)); } -TEST(WifiLanConnectionInfoTest, TestFromBadMaskIpv4) { - WifiLanConnectionInfo info(kIpv4Addr, kPort, kBssid, kAction); - std::string serialized = info.ToDataElementBytes(); - // Set mask for IPV4 address only. - serialized[3] = 0x40; - EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for IPV6 address only. - serialized[3] = 0x20; - EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for port only. - serialized[3] = 0x10; - EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for BSSID only. - serialized[3] = 0x08; - EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Remove all masks. - serialized[3] = 0x00; - EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for IPV4 + IPV6 addresses. - serialized[3] = 0x60; - EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for IPV4 + port. - serialized[3] = 0x50; - EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for IPV4 + BSSID. - serialized[3] = 0x48; - EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for all fields present. - serialized[3] = 0x78; - EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Verify OK with the correct mask. - serialized[3] = 0x58; - EXPECT_OK(WifiLanConnectionInfo::FromDataElementBytes(serialized)); -} - -TEST(WifiLanConnectionInfoTest, TestFromBadMaskIpv6) { - WifiLanConnectionInfo info(kIpv6Addr, kPort, kBssid, kAction); - std::string serialized = info.ToDataElementBytes(); - // Set mask for IPV4 address only. - serialized[3] = 0x40; - EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for IPV6 address only. - serialized[3] = 0x20; - EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for port only. - serialized[3] = 0x10; - EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for BSSID only. - serialized[3] = 0x08; - EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Remove all masks. - serialized[3] = 0x00; - EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for IPV4 + IPV6 addresses. - serialized[3] = 0x60; - EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for IPV6 + port. - serialized[3] = 0x30; - EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for IPV6 + BSSID. - serialized[3] = 0x28; - EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for all fields present. - serialized[3] = 0x78; - EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Verify OK with the correct mask. - serialized[3] = 0x38; - EXPECT_OK(WifiLanConnectionInfo::FromDataElementBytes(serialized)); -} - -TEST(WifiLanConnectionInfoTest, TestFromBadMaskEmpty) { - WifiLanConnectionInfo info("", "", "", kAction); - std::string serialized = info.ToDataElementBytes(); - // Set mask for IPV4 address only. - serialized[3] = 0x40; - EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for IPV6 address only. - serialized[3] = 0x20; - EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for port only. - serialized[3] = 0x10; - EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for BSSID only. - serialized[3] = 0x08; - EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Set mask for all fields. - serialized[3] = 0x78; - EXPECT_THAT(WifiLanConnectionInfo::FromDataElementBytes(serialized), - StatusIs(absl::StatusCode::kInvalidArgument)); - // Verify OK with correct mask. - serialized[3] = 0x00; - EXPECT_OK(WifiLanConnectionInfo::FromDataElementBytes(serialized)); -} - } // namespace } // namespace nearby diff --git a/internal/proto/metadata.proto b/internal/proto/metadata.proto index 91fdbeb9..819a68b7 100644 --- a/internal/proto/metadata.proto +++ b/internal/proto/metadata.proto @@ -66,4 +66,11 @@ enum DeviceType { // The device is a watch. DEVICE_TYPE_WATCH = 6; + + // The device is a ChromeOS device. ChromeOS can be a laptop, desktop, or + // convertible (tablet + clamshell). + DEVICE_TYPE_CHROMEOS = 7; + + // The device is a foldable. + DEVICE_TYPE_FOLDABLE = 8; } diff --git a/internal/test/BUILD b/internal/test/BUILD index 618ee62f..dc5407b7 100644 --- a/internal/test/BUILD +++ b/internal/test/BUILD @@ -1,3 +1,17 @@ +# 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. + licenses(["notice"]) cc_library( @@ -50,6 +64,7 @@ cc_test( "//internal/platform/implementation:types", "//internal/platform/implementation/g3", "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_absl//absl/synchronization", "@com_google_absl//absl/time", "@com_google_googletest//:gtest_main", ], diff --git a/internal/test/fake_clock.cc b/internal/test/fake_clock.cc index 418863b6..3f464ea6 100644 --- a/internal/test/fake_clock.cc +++ b/internal/test/fake_clock.cc @@ -21,7 +21,15 @@ namespace nearby { -absl::Time FakeClock::Now() const { return now_; } +FakeClock::~FakeClock() { + absl::MutexLock lock(&mutex_); + observers_.clear(); +} + +absl::Time FakeClock::Now() const { + absl::MutexLock lock(&mutex_); + return now_; +} void FakeClock::AddObserver(absl::string_view name, std::function observer) { @@ -36,9 +44,9 @@ void FakeClock::RemoveObserver(absl::string_view name) { void FakeClock::FastForward(absl::Duration duration) { std::vector timer_callback_ids; - now_ += duration; { absl::MutexLock lock(&mutex_); + now_ += duration; for (const auto& observer : observers_) { timer_callback_ids.push_back(observer.first); } diff --git a/internal/test/fake_clock.h b/internal/test/fake_clock.h index dc64aae0..8c077e18 100644 --- a/internal/test/fake_clock.h +++ b/internal/test/fake_clock.h @@ -33,6 +33,7 @@ class FakeClock : public Clock { FakeClock() { now_ = absl::Now(); } FakeClock(FakeClock&&) = default; FakeClock& operator=(FakeClock&&) = default; + ~FakeClock() override ABSL_LOCKS_EXCLUDED(mutex_); absl::Time Now() const override; @@ -45,8 +46,8 @@ class FakeClock : public Clock { int GetObserversCount() ABSL_LOCKS_EXCLUDED(mutex_); private: - absl::Time now_; mutable absl::Mutex mutex_; + absl::Time now_ ABSL_GUARDED_BY(mutex_); absl::flat_hash_map> observers_ ABSL_GUARDED_BY(mutex_); }; diff --git a/internal/test/fake_task_runner.cc b/internal/test/fake_task_runner.cc index 316e74cf..cd35a9a1 100644 --- a/internal/test/fake_task_runner.cc +++ b/internal/test/fake_task_runner.cc @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2022-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. @@ -14,21 +14,36 @@ #include "internal/test/fake_task_runner.h" -#include +#include #include // NOLINT #include #include #include +#include "absl/synchronization/mutex.h" +#include "absl/synchronization/notification.h" #include "absl/time/time.h" +#include "internal/test/fake_timer.h" namespace nearby { -std::atomic_uint FakeTaskRunner::running_thread_count_ = 0; +std::atomic_uint FakeTaskRunner::total_running_thread_count_ = 0; + +FakeTaskRunner::~FakeTaskRunner() { + absl::MutexLock lock(&mutex_); + CleanThreads(); +} bool FakeTaskRunner::PostTask(absl::AnyInvocable task) { - if (mode_ == Mode::kNoPending) { - run(std::move(task)); + absl::MutexLock lock(&mutex_); + if (mode_ == Mode::kActive) { + if (running_thread_count_ >= count_) { + queued_tasks_.push_back(std::move(task)); + return true; + } + + ++running_thread_count_; + Run(std::move(task)); return true; } pending_tasks_.push_back(std::move(task)); @@ -37,56 +52,80 @@ bool FakeTaskRunner::PostTask(absl::AnyInvocable task) { bool FakeTaskRunner::PostDelayedTask(absl::Duration delay, absl::AnyInvocable task) { + absl::MutexLock lock(&mutex_); std::unique_ptr timer = std::make_unique(clock_); Timer* timer_ptr = timer.get(); uint32_t id = GenerateId(); - pending_delayed_tasks_.emplace(id, std::move(timer)); + queued_delayed_tasks_.emplace(id, std::move(timer)); timer_ptr->Start(delay / absl::Milliseconds(1), 0, [this, task = std::move(task), id]() mutable { PostTask(std::move(task)); - completed_delayed_tasks_.push_back(id); + { + absl::MutexLock lock(&mutex_); + completed_delayed_tasks_.push_back(id); + } }); return true; } -void FakeTaskRunner::RunNextTask() { - if (pending_tasks_.empty()) { - return; - } +void FakeTaskRunner::SetMode(Mode mode) { + absl::MutexLock lock(&mutex_); + mode_ = mode; +} - run(std::move(pending_tasks_.front())); - pending_tasks_.erase(pending_tasks_.begin()); +FakeTaskRunner::Mode FakeTaskRunner::GetMode() const { + absl::MutexLock lock(&mutex_); + return mode_; +} + +void FakeTaskRunner::RunNextPendingTask() { + absl::MutexLock lock(&mutex_); + InternalRunNextPendingTask(); } void FakeTaskRunner::RunAllPendingTasks() { + absl::MutexLock lock(&mutex_); while (!pending_tasks_.empty()) { - RunNextTask(); + InternalRunNextPendingTask(); } } -const std::vector>& FakeTaskRunner::GetPendingTasks() - const { +void FakeTaskRunner::Sync() { + absl::Notification notification; + PostTask([&] { notification.Notify(); }); + notification.WaitForNotification(); +} + +const std::vector>& +FakeTaskRunner::GetAllPendingTasks() const { + absl::MutexLock lock(&mutex_); return pending_tasks_; } const absl::flat_hash_map>& -FakeTaskRunner::GetPendingDelayedTask() { +FakeTaskRunner::GetAllDelayedTasks() { + absl::MutexLock lock(&mutex_); if (!completed_delayed_tasks_.empty()) { for (uint32_t id : completed_delayed_tasks_) { - pending_delayed_tasks_.erase(id); + queued_delayed_tasks_.erase(id); } } - return pending_delayed_tasks_; + return queued_delayed_tasks_; +} + +int FakeTaskRunner::GetConcurrentCount() const { + absl::MutexLock lock(&mutex_); + return count_; } bool FakeTaskRunner::WaitForRunningTasksWithTimeout(absl::Duration timeout) { int i = (timeout / absl::Milliseconds(1)) / 50; - while (running_thread_count_ != 0 && i > 0) { + while (total_running_thread_count_ != 0 && i > 0) { absl::SleepFor(absl::Milliseconds(50)); --i; } - return running_thread_count_ == 0; + return total_running_thread_count_ == 0; } uint32_t FakeTaskRunner::GenerateId() { @@ -107,17 +146,39 @@ void FakeTaskRunner::CleanThreads() { } } -void FakeTaskRunner::run(absl::AnyInvocable task) { - absl::MutexLock lock(&mutex_); +void FakeTaskRunner::Run(absl::AnyInvocable task) { CleanThreads(); - ++running_thread_count_; + ++total_running_thread_count_; // Run the task in a new thread, to simulate the real environment. std::future thread = std::async(std::launch::async, [&, task = std::move(task)]() mutable { task(); - --running_thread_count_; + RunNextQueueTask(); + --total_running_thread_count_; }); threads_.push_back(std::move(thread)); } +void FakeTaskRunner::InternalRunNextPendingTask() { + if (pending_tasks_.empty()) { + return; + } + + Run(std::move(pending_tasks_.front())); + pending_tasks_.erase(pending_tasks_.begin()); +} + +void FakeTaskRunner::RunNextQueueTask() { + absl::MutexLock lock(&mutex_); + --running_thread_count_; + if (queued_tasks_.empty()) { + return; + } + + auto task = std::move(queued_tasks_.front()); + queued_tasks_.erase(queued_tasks_.begin()); + ++running_thread_count_; + Run(std::move(task)); +} + } // namespace nearby diff --git a/internal/test/fake_task_runner.h b/internal/test/fake_task_runner.h index 4b433e83..f92b07f9 100644 --- a/internal/test/fake_task_runner.h +++ b/internal/test/fake_task_runner.h @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2022-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. @@ -19,8 +19,6 @@ #include #include //NOLINT #include -#include //NOLINT -#include #include #include "absl/base/thread_annotations.h" @@ -34,52 +32,68 @@ namespace nearby { class FakeTaskRunner : public TaskRunner { public: - enum class Mode { kNoPending, kPending }; + enum class Mode { kActive, kPending }; FakeTaskRunner(FakeClock* clock, uint32_t count) : clock_(clock), count_(count) {} - ~FakeTaskRunner() override = default; + ~FakeTaskRunner() override ABSL_LOCKS_EXCLUDED(mutex_); - bool PostTask(absl::AnyInvocable task) override; + bool PostTask(absl::AnyInvocable task) override + ABSL_LOCKS_EXCLUDED(mutex_); // No matter the mode is pending or not, always put the task in timer control. // Caller can move forward time to trigger it. bool PostDelayedTask(absl::Duration delay, - absl::AnyInvocable task) override; + absl::AnyInvocable task) override + ABSL_LOCKS_EXCLUDED(mutex_); // Mocked methods. - void SetMode(Mode mode) { mode_ = mode; } - Mode GetMode() const { return mode_; } + void SetMode(Mode mode) ABSL_LOCKS_EXCLUDED(mutex_); + Mode GetMode() const ABSL_LOCKS_EXCLUDED(mutex_); - void RunNextTask(); - void RunAllPendingTasks(); + void RunNextPendingTask() ABSL_LOCKS_EXCLUDED(mutex_); + void RunAllPendingTasks() ABSL_LOCKS_EXCLUDED(mutex_); + void Sync(); - const std::vector>& GetPendingTasks() const; + const std::vector>& GetAllPendingTasks() const + ABSL_LOCKS_EXCLUDED(mutex_); const absl::flat_hash_map>& - GetPendingDelayedTask(); + GetAllDelayedTasks() ABSL_LOCKS_EXCLUDED(mutex_); - int GetConcurrentCount() const { return count_; } + int GetConcurrentCount() const ABSL_LOCKS_EXCLUDED(mutex_); - // In some testcases, we needs to make sure all running tasks completion + // In some test cases, we needs to make sure all running tasks completion // before go to next task. This method can be used for the purpose. static bool WaitForRunningTasksWithTimeout(absl::Duration timeout); + static int GetTotalRunningThreadCount() { + return total_running_thread_count_; + } private: uint32_t GenerateId(); void CleanThreads() ABSL_SHARED_LOCKS_REQUIRED(mutex_); - void run(absl::AnyInvocable task) ABSL_LOCKS_EXCLUDED(mutex_); + void Run(absl::AnyInvocable task) ABSL_SHARED_LOCKS_REQUIRED(mutex_); + void InternalRunNextPendingTask() ABSL_SHARED_LOCKS_REQUIRED(mutex_); + void RunNextQueueTask() ABSL_LOCKS_EXCLUDED(mutex_); - Mode mode_ = Mode::kNoPending; + mutable absl::Mutex mutex_; + mutable absl::Mutex thread_mutex_; + Mode mode_ ABSL_GUARDED_BY(mutex_) = Mode::kActive; std::atomic_uint current_id_ = 0; FakeClock* clock_ = nullptr; - uint32_t count_; - std::vector> pending_tasks_; - std::vector completed_delayed_tasks_; - absl::flat_hash_map> pending_delayed_tasks_; - absl::Mutex mutex_; - std::vector> threads_ ABSL_GUARDED_BY(mutex_); + uint32_t count_ ABSL_GUARDED_BY(mutex_); - static std::atomic_uint running_thread_count_; + // Used for pending mode + std::vector> pending_tasks_ + ABSL_GUARDED_BY(mutex_); + std::vector> queued_tasks_ ABSL_GUARDED_BY(mutex_); + std::vector completed_delayed_tasks_ ABSL_GUARDED_BY(mutex_); + absl::flat_hash_map> queued_delayed_tasks_ + ABSL_GUARDED_BY(mutex_); + std::vector> threads_ ABSL_GUARDED_BY(mutex_); + int running_thread_count_ ABSL_GUARDED_BY(mutex_) = 0; + + static std::atomic_uint total_running_thread_count_; }; } // namespace nearby diff --git a/internal/test/fake_task_runner_test.cc b/internal/test/fake_task_runner_test.cc index fd2b0554..e778d0ae 100644 --- a/internal/test/fake_task_runner_test.cc +++ b/internal/test/fake_task_runner_test.cc @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2022-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. @@ -14,7 +14,11 @@ #include "internal/test/fake_task_runner.h" +#include + #include "gtest/gtest.h" +#include "absl/synchronization/mutex.h" +#include "absl/time/clock.h" #include "absl/time/time.h" #include "internal/test/fake_clock.h" @@ -28,7 +32,7 @@ TEST(FakeTaskRunner, PostTask) { task_runner.PostTask([&count] { ++count; }); ASSERT_TRUE( FakeTaskRunner::WaitForRunningTasksWithTimeout(absl::Milliseconds(100))); - EXPECT_EQ(task_runner.GetPendingTasks().size(), 0); + EXPECT_EQ(task_runner.GetAllPendingTasks().size(), 0); EXPECT_EQ(count, 1); } @@ -37,14 +41,14 @@ TEST(FakeTaskRunner, PostDelayedTask) { int count = 0; FakeTaskRunner task_runner{&clock, 1}; task_runner.PostDelayedTask(absl::Seconds(10), [&count] { ++count; }); - EXPECT_EQ(task_runner.GetPendingTasks().size(), 0); - EXPECT_EQ(task_runner.GetPendingDelayedTask().size(), 1); + EXPECT_EQ(task_runner.GetAllPendingTasks().size(), 0); + EXPECT_EQ(task_runner.GetAllDelayedTasks().size(), 1); EXPECT_EQ(count, 0); clock.FastForward(absl::Seconds(10)); ASSERT_TRUE( FakeTaskRunner::WaitForRunningTasksWithTimeout(absl::Milliseconds(100))); EXPECT_EQ(count, 1); - EXPECT_EQ(task_runner.GetPendingDelayedTask().size(), 0); + EXPECT_EQ(task_runner.GetAllDelayedTasks().size(), 0); } TEST(FakeTaskRunner, PostTasksInPendingMode) { @@ -54,11 +58,11 @@ TEST(FakeTaskRunner, PostTasksInPendingMode) { EXPECT_EQ(task_runner.GetConcurrentCount(), 1); task_runner.PostTask([]() {}); task_runner.PostTask([]() {}); - EXPECT_EQ(task_runner.GetPendingTasks().size(), 2); - task_runner.RunNextTask(); - EXPECT_EQ(task_runner.GetPendingTasks().size(), 1); - task_runner.RunNextTask(); - EXPECT_EQ(task_runner.GetPendingTasks().size(), 0); + EXPECT_EQ(task_runner.GetAllPendingTasks().size(), 2); + task_runner.RunNextPendingTask(); + EXPECT_EQ(task_runner.GetAllPendingTasks().size(), 1); + task_runner.RunNextPendingTask(); + EXPECT_EQ(task_runner.GetAllPendingTasks().size(), 0); } TEST(FakeTaskRunner, RunAllPostedTasksInPendingMode) { @@ -67,11 +71,11 @@ TEST(FakeTaskRunner, RunAllPostedTasksInPendingMode) { task_runner.SetMode(FakeTaskRunner::Mode::kPending); task_runner.PostTask([]() {}); task_runner.PostTask([]() {}); - EXPECT_EQ(task_runner.GetPendingTasks().size(), 2); + EXPECT_EQ(task_runner.GetAllPendingTasks().size(), 2); task_runner.RunAllPendingTasks(); ASSERT_TRUE( FakeTaskRunner::WaitForRunningTasksWithTimeout(absl::Milliseconds(100))); - EXPECT_EQ(task_runner.GetPendingTasks().size(), 0); + EXPECT_EQ(task_runner.GetAllPendingTasks().size(), 0); } TEST(FakeTaskRunner, PostDelayedTaskInPendingMode) { @@ -80,17 +84,36 @@ TEST(FakeTaskRunner, PostDelayedTaskInPendingMode) { FakeTaskRunner task_runner{&clock, 1}; task_runner.SetMode(FakeTaskRunner::Mode::kPending); task_runner.PostDelayedTask(absl::Seconds(1), [&called]() { called = true; }); - EXPECT_EQ(task_runner.GetPendingDelayedTask().size(), 1); + EXPECT_EQ(task_runner.GetAllDelayedTasks().size(), 1); clock.FastForward(absl::Seconds(1)); - EXPECT_EQ(task_runner.GetPendingDelayedTask().size(), 0); - EXPECT_EQ(task_runner.GetPendingTasks().size(), 1); + EXPECT_EQ(task_runner.GetAllDelayedTasks().size(), 0); + EXPECT_EQ(task_runner.GetAllPendingTasks().size(), 1); task_runner.RunAllPendingTasks(); ASSERT_TRUE( FakeTaskRunner::WaitForRunningTasksWithTimeout(absl::Milliseconds(100))); - EXPECT_EQ(task_runner.GetPendingTasks().size(), 0); + EXPECT_EQ(task_runner.GetAllPendingTasks().size(), 0); EXPECT_TRUE(called); } +TEST(FakeTaskRunner, PostTasksRunInSequence) { + std::list result; + absl::Mutex mutex; + FakeClock clock; + FakeTaskRunner task_runner{&clock, 1}; + for (int i = 0; i < 100; ++i) { + task_runner.PostTask([&, i]() { + absl::MutexLock lock(&mutex); + absl::SleepFor(absl::Milliseconds(40)); + result.push_back(i); + }); + } + task_runner.Sync(); + for (int i = 0; i < 100; ++i) { + EXPECT_EQ(result.front(), i); + result.pop_front(); + } +} + TEST(FakeTaskRunner, PostDelayedTaskInDelayedTask) { FakeClock clock; int called_count = 0; diff --git a/minimum_os.bzl b/minimum_os.bzl new file mode 100644 index 00000000..c51a86e2 --- /dev/null +++ b/minimum_os.bzl @@ -0,0 +1,4 @@ +"""Minimum OS version definitions and related test setup""" + +IOS_MINIMUM_OS = "13.7" +IOS_MINIMUM_TEST_RUNNER = "//testing/utp/ios:IPHONE_6S_13_7" diff --git a/presence/BUILD b/presence/BUILD index 8b417166..6d48be7f 100644 --- a/presence/BUILD +++ b/presence/BUILD @@ -23,11 +23,14 @@ cc_library( ], hdrs = [ "presence_client.h", + "presence_device_provider.h", "presence_service.h", ], deps = [ ":types", + "//internal/interop:device", "//internal/platform:types", + "//internal/proto:metadata_cc_proto", "//presence/implementation:internal", # build_cleaner: keep "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", @@ -60,10 +63,11 @@ cc_library( "scan_request_builder.h", ], deps = [ - "//internal:device", "//internal/crypto", + "//internal/interop:device", "//internal/platform:connection_info", "//internal/platform:logging", + "//internal/platform:types", "//internal/platform/implementation:types", "//internal/proto:credential_cc_proto", "//internal/proto:metadata_cc_proto", @@ -100,7 +104,7 @@ cc_test( "@com_google_absl//absl/types:variant", "@com_google_googletest//:gtest_main", ] + select({ - "//tools/cc_target_os:windows": [ + "@platforms//os:windows": [ "//internal/platform/implementation/windows", ], "//conditions:default": [ @@ -124,7 +128,7 @@ cc_test( "@com_github_protobuf_matchers//protobuf-matchers", "@com_google_googletest//:gtest_main", ] + select({ - "//tools/cc_target_os:windows": [ + "@platforms//os:windows": [ "//internal/platform/implementation/windows", ], "//conditions:default": [ @@ -138,6 +142,7 @@ cc_test( size = "small", srcs = [ "presence_client_test.cc", + "presence_device_provider_test.cc", "presence_service_test.cc", ], shard_count = 6, @@ -145,11 +150,14 @@ cc_test( ":presence", ":types", "//internal/platform:test_util", + "//internal/platform:types", + "//internal/proto:metadata_cc_proto", "@com_github_protobuf_matchers//protobuf-matchers", "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", "@com_google_googletest//:gtest_main", ] + select({ - "//tools/cc_target_os:windows": [ + "@platforms//os:windows": [ "//internal/platform/implementation/windows", ], "//conditions:default": [ diff --git a/presence/data_types.h b/presence/data_types.h index 8333e7ff..21700bb9 100644 --- a/presence/data_types.h +++ b/presence/data_types.h @@ -59,6 +59,15 @@ struct BroadcastCallback { }; }; +// Chromium uses its own crypto library instead of nearby/internal/crypto, +// in which base::span is used instead of absl::Span. See b/276368162. +#ifdef NEARBY_CHROMIUM +template +using CryptoSpan = base::span; +#else +template +using CryptoSpan = absl::Span; +#endif } // namespace presence } // namespace nearby diff --git a/presence/implementation/BUILD b/presence/implementation/BUILD index 3985f6f1..dec31ae5 100644 --- a/presence/implementation/BUILD +++ b/presence/implementation/BUILD @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. load("@bazel_skylib//rules:common_settings.bzl", "bool_flag") -load("//third_party/bazel_skylib/lib:selects.bzl", "selects") +load("@bazel_skylib//lib:selects.bzl", "selects") licenses(["notice"]) @@ -36,8 +36,8 @@ selects.config_setting_group( name = "norust_or_windows_or_android", match_any = [ ":no_link_with_rust", - "//tools/cc_target_os:windows", - "//tools/cc_target_os:android", + "@platforms//os:windows", + "@platforms//os:android", ], ) @@ -159,7 +159,7 @@ cc_test( "@com_google_absl//absl/strings", "@com_google_googletest//:gtest_main", ] + select({ - "//tools/cc_target_os:windows": [ + "@platforms//os:windows": [ "//internal/platform/implementation/windows", ], "//conditions:default": [ @@ -183,7 +183,7 @@ cc_test( "@com_google_absl//absl/strings", "@com_google_googletest//:gtest_main", ] + select({ - "//tools/cc_target_os:windows": [ + "@platforms//os:windows": [ "//internal/platform/implementation/windows", ], "//conditions:default": [ @@ -206,7 +206,7 @@ cc_test( "@com_github_protobuf_matchers//protobuf-matchers", "@com_google_googletest//:gtest_main", ] + select({ - "//tools/cc_target_os:windows": [ + "@platforms//os:windows": [ "//internal/platform/implementation/windows", ], "//conditions:default": [ @@ -227,7 +227,7 @@ cc_test( "@com_google_absl//absl/strings", "@com_google_googletest//:gtest_main", ] + select({ - "//tools/cc_target_os:windows": [ + "@platforms//os:windows": [ "//internal/platform/implementation/windows", ], "//conditions:default": [ @@ -247,7 +247,7 @@ cc_test( "@com_google_absl//absl/types:variant", "@com_google_googletest//:gtest_main", ] + select({ - "//tools/cc_target_os:windows": [ + "@platforms//os:windows": [ "//internal/platform/implementation/windows", ], "//conditions:default": [ @@ -267,7 +267,7 @@ cc_test( "@com_google_absl//absl/strings", "@com_google_googletest//:gtest_main", ] + select({ - "//tools/cc_target_os:windows": [ + "@platforms//os:windows": [ "//internal/platform/implementation/windows", ], "//conditions:default": [ @@ -294,7 +294,7 @@ cc_test( "@com_google_absl//absl/time", "@com_google_googletest//:gtest_main", ] + select({ - "//tools/cc_target_os:windows": [ + "@platforms//os:windows": [ "//internal/platform/implementation/windows", ], "//conditions:default": [ @@ -317,7 +317,7 @@ cc_test( "@com_google_absl//absl/time", "@com_google_googletest//:gtest_main", ] + select({ - "//tools/cc_target_os:windows": [ + "@platforms//os:windows": [ "//internal/platform/implementation/windows", ], "//conditions:default": [ diff --git a/presence/implementation/action_factory_test.cc b/presence/implementation/action_factory_test.cc index 02b46434..5eb57076 100644 --- a/presence/implementation/action_factory_test.cc +++ b/presence/implementation/action_factory_test.cc @@ -35,7 +35,7 @@ constexpr uint32_t kFastPairBitMask = 1 << 14; TEST(ActionFactory, CreateActiveUnlockAction) { std::vector data_elements; - data_elements.emplace_back(DataElement(ActionBit::kActiveUnlockAction)); + data_elements.emplace_back(ActionBit::kActiveUnlockAction); Action action = ActionFactory::CreateAction(data_elements); @@ -44,10 +44,10 @@ TEST(ActionFactory, CreateActiveUnlockAction) { TEST(ActionFactory, CreateActiveIgnoresUnsupportedActions) { std::vector data_elements; - data_elements.emplace_back(DataElement(ActionBit::kActiveUnlockAction)); + data_elements.emplace_back(ActionBit::kActiveUnlockAction); // The action is 32 bit, so the valid range is [0-31] - data_elements.emplace_back(DataElement(ActionBit(-1))); - data_elements.emplace_back(DataElement(ActionBit(32))); + data_elements.emplace_back(ActionBit(-1)); + data_elements.emplace_back(ActionBit(32)); Action action = ActionFactory::CreateAction(data_elements); EXPECT_EQ(action.action, kActiveUnlockBitMask); @@ -71,7 +71,7 @@ TEST(ActionFactory, CreateContextTimestampAndFastPair) { std::vector data_elements; data_elements.emplace_back(DataElement::kContextTimestampFieldType, kTimestamp); - data_elements.emplace_back(DataElement(ActionBit::kFastPairAction)); + data_elements.emplace_back(ActionBit::kFastPairAction); Action action = ActionFactory::CreateAction(data_elements); diff --git a/presence/implementation/advertisement_factory_test.cc b/presence/implementation/advertisement_factory_test.cc index 1847d8aa..f87aa433 100644 --- a/presence/implementation/advertisement_factory_test.cc +++ b/presence/implementation/advertisement_factory_test.cc @@ -61,7 +61,7 @@ TEST(AdvertisementFactory, CreateAdvertisementFromPrivateIdentity) { std::string salt = "AB"; constexpr IdentityType kIdentity = IdentityType::IDENTITY_TYPE_PRIVATE; std::vector data_elements; - data_elements.emplace_back(DataElement(ActionBit::kActiveUnlockAction)); + data_elements.emplace_back(ActionBit::kActiveUnlockAction); Action action = ActionFactory::CreateAction(data_elements); BaseBroadcastRequest request = BaseBroadcastRequest(BasePresenceRequestBuilder(kIdentity) @@ -85,8 +85,8 @@ TEST(AdvertisementFactory, CreateAdvertisementFromTrustedIdentity) { std::string salt = "AB"; constexpr IdentityType kIdentity = IdentityType::IDENTITY_TYPE_TRUSTED; std::vector data_elements; - data_elements.emplace_back(DataElement(ActionBit::kActiveUnlockAction)); - data_elements.emplace_back(DataElement(ActionBit::kFitCastAction)); + data_elements.emplace_back(ActionBit::kActiveUnlockAction); + data_elements.emplace_back(ActionBit::kFitCastAction); Action action = ActionFactory::CreateAction(data_elements); BaseBroadcastRequest request = BaseBroadcastRequest(BasePresenceRequestBuilder(kIdentity) @@ -110,8 +110,8 @@ TEST(AdvertisementFactory, CreateAdvertisementFromProvisionedIdentity) { std::string salt = "AB"; constexpr IdentityType kIdentity = IdentityType::IDENTITY_TYPE_PROVISIONED; std::vector data_elements; - data_elements.emplace_back(DataElement(ActionBit::kActiveUnlockAction)); - data_elements.emplace_back(DataElement(ActionBit::kFitCastAction)); + data_elements.emplace_back(ActionBit::kActiveUnlockAction); + data_elements.emplace_back(ActionBit::kFitCastAction); Action action = ActionFactory::CreateAction(data_elements); BaseBroadcastRequest request = BaseBroadcastRequest(BasePresenceRequestBuilder(kIdentity) @@ -135,7 +135,7 @@ TEST(AdvertisementFactory, CreateAdvertisementFromPublicIdentity) { std::string salt = "AB"; constexpr IdentityType kIdentity = IdentityType::IDENTITY_TYPE_PUBLIC; std::vector data_elements; - data_elements.emplace_back(DataElement(ActionBit::kActiveUnlockAction)); + data_elements.emplace_back(ActionBit::kActiveUnlockAction); Action action = ActionFactory::CreateAction(data_elements); BaseBroadcastRequest request = BaseBroadcastRequest(BasePresenceRequestBuilder(kIdentity) @@ -155,7 +155,7 @@ TEST(AdvertisementFactory, CreateAdvertisementFailsWhenSaltIsTooShort) { std::string salt = "AB"; constexpr IdentityType kIdentity = internal::IDENTITY_TYPE_PRIVATE; std::vector data_elements; - data_elements.emplace_back(DataElement(ActionBit::kActiveUnlockAction)); + data_elements.emplace_back(ActionBit::kActiveUnlockAction); Action action = ActionFactory::CreateAction(data_elements); BaseBroadcastRequest request = BaseBroadcastRequest(BasePresenceRequestBuilder(kIdentity) diff --git a/presence/implementation/base_broadcast_request.cc b/presence/implementation/base_broadcast_request.cc index 2171c8f5..935d7ccc 100644 --- a/presence/implementation/base_broadcast_request.cc +++ b/presence/implementation/base_broadcast_request.cc @@ -19,7 +19,7 @@ #include "absl/status/status.h" #include "absl/strings/string_view.h" -#include "internal/crypto/random.h" +#include "internal/platform/implementation/crypto.h" #include "internal/platform/logging.h" #include "presence/broadcast_request.h" #include "presence/implementation/action_factory.h" @@ -72,9 +72,14 @@ BasePresenceRequestBuilder::operator BaseBroadcastRequest() const { .account_name = account_name_, .identity_type = identity_}, .action = action_}; + + std::string bytes(kSaltSize, 0); + crypto::RandBytes(const_cast(bytes.data()), + bytes.size()); + BaseBroadcastRequest broadcast_request{ .variant = presence, - .salt = salt_.size() == kSaltSize ? salt_ : crypto::RandBytes(kSaltSize), + .salt = salt_.size() == kSaltSize ? salt_ : bytes, .tx_power = tx_power_, .power_mode = power_mode_}; return broadcast_request; diff --git a/presence/implementation/broadcast_manager.cc b/presence/implementation/broadcast_manager.cc index 33e0edcc..5bc5ed3a 100644 --- a/presence/implementation/broadcast_manager.cc +++ b/presence/implementation/broadcast_manager.cc @@ -23,7 +23,7 @@ #include #include "absl/time/time.h" -#include "internal/crypto/random.h" +#include "internal/platform/implementation/crypto.h" #include "internal/platform/implementation/system_clock.h" #include "internal/platform/logging.h" #include "presence/implementation/advertisement_factory.h" @@ -66,7 +66,7 @@ std::string SelectSalt(LocalCredential& credential, if (!credential.consumed_salts().contains(s)) { break; } - s = crypto::RandData(); + s = nearby::RandData(); } credential.mutable_consumed_salts()->insert({s, true}); return SaltFromInt(s); @@ -235,7 +235,7 @@ void BroadcastManager::StopBroadcast(BroadcastSessionId id) { } BroadcastSessionId BroadcastManager::GenerateBroadcastSessionId() { - return ::crypto::RandData(); + return nearby::RandData(); } void BroadcastManager::BroadcastSessionState::SetAdvertisingSession( diff --git a/presence/implementation/credential_manager.h b/presence/implementation/credential_manager.h index a758ae20..01cadc6c 100644 --- a/presence/implementation/credential_manager.h +++ b/presence/implementation/credential_manager.h @@ -95,6 +95,18 @@ class CredentialManager { virtual std::string DecryptMetadata(absl::string_view metadata_encryption_key, absl::string_view key_seed, absl::string_view metadata_string) = 0; + + // Sets the NP service's device metadata, regenerating credentials if + // `regen_credentials` is set to true. + virtual void SetLocalDeviceMetadata( + const ::nearby::internal::Metadata& metadata, bool regen_credentials, + absl::string_view manager_app_id, + const std::vector& identity_types, + int credential_life_cycle_days, int contiguous_copy_of_credentials, + GenerateCredentialsResultCallback credentials_generated_cb) = 0; + + // Gets the NP service's device metadata. + virtual ::nearby::internal::Metadata GetLocalDeviceMetadata() = 0; }; } // namespace presence diff --git a/presence/implementation/credential_manager_impl.cc b/presence/implementation/credential_manager_impl.cc index 63e73199..67800870 100644 --- a/presence/implementation/credential_manager_impl.cc +++ b/presence/implementation/credential_manager_impl.cc @@ -25,10 +25,17 @@ #include "absl/status/status.h" #include "absl/strings/string_view.h" #include "absl/time/time.h" +#ifdef NEARBY_CHROMIUM +#include "crypto/aead.h" +#include "crypto/ec_private_key.h" +#include "crypto/hkdf.h" +#include "crypto/random.h" +#else #include "internal/crypto/aead.h" #include "internal/crypto/ec_private_key.h" #include "internal/crypto/hkdf.h" #include "internal/crypto/random.h" +#endif #include "internal/platform/base64_utils.h" #include "internal/platform/future.h" #include "internal/platform/implementation/credential_callbacks.h" @@ -36,6 +43,7 @@ #include "internal/platform/logging.h" #include "internal/proto/credential.pb.h" #include "internal/proto/local_credential.pb.h" +#include "presence/data_types.h" #include "presence/implementation/base_broadcast_request.h" #include "presence/implementation/ldt.h" @@ -56,13 +64,13 @@ constexpr char kPairedKeyAliasPrefix[] = "nearby_presence_paired_key_alias_"; // Returns a random duration in [0, max_duration] range. absl::Duration RandomDuration(absl::Duration max_duration) { - uint32_t random = ::crypto::RandData(); + uint32_t random = nearby::RandData(); return max_duration * random / std::numeric_limits::max(); } std::string CustomizeBytesSize(absl::string_view bytes, size_t len) { return ::crypto::HkdfSha256( - /*ikm=*/bytes, + /*ikm=*/std::string(bytes), // NOLINT /*salt=*/std::string(CredentialManagerImpl::kAuthenticityKeyByteSize, 0), /*info=*/"", /*derived_key_size=*/len); } @@ -164,7 +172,9 @@ CredentialManagerImpl::CreateLocalCredential(const Metadata& metadata, private_credential.set_identity_type(identity_type); // Creates an AES key to encrypt the whole broadcast. - std::string secret_key = crypto::RandBytes(kAuthenticityKeyByteSize); + std::string secret_key(kAuthenticityKeyByteSize, 0); + crypto::RandBytes(const_cast(secret_key.data()), + secret_key.size()); private_credential.set_key_seed(secret_key); // Uses SHA-256 algorithm to generate the credential ID from the @@ -186,7 +196,9 @@ CredentialManagerImpl::CreateLocalCredential(const Metadata& metadata, private_credential.mutable_connection_signing_key()->set_key( std::string(private_key.begin(), private_key.end())); // Create an AES key to encrypt the device metadata. - auto metadata_key = crypto::RandBytes(kBaseMetadataSize); + std::string metadata_key(kBaseMetadataSize, 0); + crypto::RandBytes(const_cast(metadata_key.data()), + metadata_key.size()); private_credential.set_metadata_encryption_key(metadata_key); // Generate the public credential @@ -260,7 +272,7 @@ std::string CredentialManagerImpl::DecryptMetadata( auto result = aead.Open(encrypted_metadata_bytes, /*nonce=*/ iv_bytes, - /*additional_data=*/absl::Span()); + /*additional_data=*/CryptoSpan()); return std::string(result.value().begin(), result.value().end()); } @@ -285,7 +297,7 @@ std::string CredentialManagerImpl::EncryptMetadata( auto encrypted = aead.Seal(metadata_bytes, /*nonce=*/ iv_bytes, - /*additional_data=*/absl::Span()); + /*additional_data=*/CryptoSpan()); return std::string(encrypted.begin(), encrypted.end()); } @@ -295,8 +307,8 @@ std::vector CredentialManagerImpl::ExtendMetadataEncryptionKey( return crypto::HkdfSha256( std::vector(metadata_encryption_key.begin(), metadata_encryption_key.end()), - /*salt=*/absl::Span(), - /*info=*/absl::Span(), kNearbyPresenceNumBytesAesGcmKeySize); + /*salt=*/CryptoSpan(), + /*info=*/CryptoSpan(), kNearbyPresenceNumBytesAesGcmKeySize); } void CredentialManagerImpl::GetLocalCredentials( @@ -354,7 +366,7 @@ SubscriberId CredentialManagerImpl::SubscribeForPublicCredentials( const CredentialSelector& credential_selector, PublicCredentialType public_credential_type, GetPublicCredentialsResultCallback callback) { - SubscriberId id = ::crypto::RandData(); + SubscriberId id = nearby::RandData(); RunOnServiceControllerThread( "add-subscriber", [this, key = SubscriberKey{credential_selector, public_credential_type}, diff --git a/presence/implementation/credential_manager_impl.h b/presence/implementation/credential_manager_impl.h index 673de9f8..00779132 100644 --- a/presence/implementation/credential_manager_impl.h +++ b/presence/implementation/credential_manager_impl.h @@ -31,6 +31,7 @@ #include "internal/platform/runnable.h" #include "internal/platform/single_thread_executor.h" #include "internal/proto/credential.pb.h" +#include "internal/proto/metadata.pb.h" #include "presence/implementation/credential_manager.h" namespace nearby { @@ -127,6 +128,24 @@ class CredentialManagerImpl : public CredentialManager { std::vector ExtendMetadataEncryptionKey( absl::string_view metadata_encryption_key); + void SetLocalDeviceMetadata( + const Metadata& metadata, bool regen_credentials, + absl::string_view manager_app_id, + const std::vector& identity_types, + int credential_life_cycle_days, int contiguous_copy_of_credentials, + GenerateCredentialsResultCallback credentials_generated_cb) override { + metadata_ = metadata; + if (regen_credentials) { + GenerateCredentials( + metadata, manager_app_id, identity_types, credential_life_cycle_days, + contiguous_copy_of_credentials, std::move(credentials_generated_cb)); + } + } + + ::nearby::internal::Metadata GetLocalDeviceMetadata() override { + return metadata_; + } + private: struct SubscriberKey { CredentialSelector credential_selector; @@ -185,6 +204,7 @@ class CredentialManagerImpl : public CredentialManager { ABSL_GUARDED_BY(*executor_); SingleThreadExecutor* executor_; std::unique_ptr credential_storage_ptr_; + Metadata metadata_; }; } // namespace presence diff --git a/presence/implementation/mediums/BUILD b/presence/implementation/mediums/BUILD index c8ebe2df..e30cdad4 100644 --- a/presence/implementation/mediums/BUILD +++ b/presence/implementation/mediums/BUILD @@ -50,7 +50,7 @@ cc_test( "@com_github_protobuf_matchers//protobuf-matchers", "@com_google_googletest//:gtest_main", ] + select({ - "//tools/cc_target_os:windows": [ + "@platforms//os:windows": [ "//internal/platform/implementation/windows", ], "//conditions:default": [ diff --git a/presence/implementation/scan_manager.cc b/presence/implementation/scan_manager.cc index 31b98bf9..3d209e1c 100644 --- a/presence/implementation/scan_manager.cc +++ b/presence/implementation/scan_manager.cc @@ -23,7 +23,7 @@ #include "absl/status/status.h" #include "absl/types/variant.h" -#include "internal/crypto/random.h" +#include "internal/platform/implementation/crypto.h" #include "internal/platform/future.h" #include "internal/platform/implementation/ble_v2.h" #include "internal/platform/implementation/credential_callbacks.h" @@ -46,7 +46,7 @@ using ScanningCallback = ::nearby::api::ble_v2::BleMedium::ScanningCallback; ScanSessionId ScanManager::StartScan(ScanRequest scan_request, ScanCallback cb) { - ScanSessionId id = ::crypto::RandData(); + ScanSessionId id = nearby::RandData(); RunOnServiceControllerThread( "start-scan", [this, id, scan_request, scan_callback = std::move(cb)]() diff --git a/presence/implementation/service_controller.h b/presence/implementation/service_controller.h index 34185cdc..ab041728 100644 --- a/presence/implementation/service_controller.h +++ b/presence/implementation/service_controller.h @@ -16,8 +16,10 @@ #define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_SERVICE_CONTROLLER_H_ #include +#include #include "absl/status/statusor.h" +#include "internal/proto/metadata.pb.h" #include "presence/broadcast_request.h" #include "presence/data_types.h" #include "presence/scan_request.h" @@ -40,6 +42,13 @@ class ServiceController { virtual absl::StatusOr StartBroadcast( BroadcastRequest broadcast_request, BroadcastCallback callback) = 0; virtual void StopBroadcast(BroadcastSessionId session_id) = 0; + virtual void UpdateLocalDeviceMetadata( + const ::nearby::internal::Metadata& metadata, bool regen_credentials, + absl::string_view manager_app_id, + const std::vector& identity_types, + int credential_life_cycle_days, int contiguous_copy_of_credentials, + GenerateCredentialsResultCallback credentials_generated_cb) = 0; + virtual ::nearby::internal::Metadata GetLocalDeviceMetadata() = 0; }; } // namespace presence diff --git a/presence/implementation/service_controller_impl.cc b/presence/implementation/service_controller_impl.cc index c49e035f..efeed31d 100644 --- a/presence/implementation/service_controller_impl.cc +++ b/presence/implementation/service_controller_impl.cc @@ -15,6 +15,7 @@ #include "presence/implementation/service_controller_impl.h" #include +#include #include "absl/status/statusor.h" #include "presence/data_types.h" @@ -40,5 +41,17 @@ void ServiceControllerImpl::StopBroadcast(BroadcastSessionId id) { broadcast_manager_.StopBroadcast(id); } +void ServiceControllerImpl::UpdateLocalDeviceMetadata( + const ::nearby::internal::Metadata& metadata, bool regen_credentials, + absl::string_view manager_app_id, + const std::vector& identity_types, + int credential_life_cycle_days, int contiguous_copy_of_credentials, + GenerateCredentialsResultCallback credentials_generated_cb) { + credential_manager_.SetLocalDeviceMetadata( + metadata, regen_credentials, manager_app_id, identity_types, + credential_life_cycle_days, contiguous_copy_of_credentials, + std::move(credentials_generated_cb)); +} + } // namespace presence } // namespace nearby diff --git a/presence/implementation/service_controller_impl.h b/presence/implementation/service_controller_impl.h index e36ff749..6233b772 100644 --- a/presence/implementation/service_controller_impl.h +++ b/presence/implementation/service_controller_impl.h @@ -20,6 +20,7 @@ #include #include "absl/status/statusor.h" +#include "internal/proto/metadata.pb.h" #include "presence/implementation/broadcast_manager.h" #include "presence/implementation/credential_manager_impl.h" #include "presence/implementation/mediums/mediums.h" @@ -46,6 +47,16 @@ class ServiceControllerImpl : public ServiceController { absl::StatusOr StartBroadcast( BroadcastRequest broadcast_request, BroadcastCallback callback) override; void StopBroadcast(BroadcastSessionId) override; + void UpdateLocalDeviceMetadata( + const ::nearby::internal::Metadata& metadata, bool regen_credentials, + absl::string_view manager_app_id, + const std::vector& identity_types, + int credential_life_cycle_days, int contiguous_copy_of_credentials, + GenerateCredentialsResultCallback credentials_generated_cb) override; + + ::nearby::internal::Metadata GetLocalDeviceMetadata() override { + return credential_manager_.GetLocalDeviceMetadata(); + } SingleThreadExecutor& GetBackgroundExecutor() { return executor_; } diff --git a/presence/presence_client.cc b/presence/presence_client.cc index 32418486..b011590e 100644 --- a/presence/presence_client.cc +++ b/presence/presence_client.cc @@ -14,13 +14,13 @@ #include "presence/presence_client.h" -#include +#include #include -#include #include "absl/status/status.h" #include "internal/platform/borrowable.h" #include "internal/platform/logging.h" +#include "presence/presence_device.h" #include "presence/presence_service.h" namespace nearby { @@ -62,5 +62,13 @@ void PresenceClient::StopBroadcast(BroadcastSessionId session_id) { } } +std::optional PresenceClient::GetLocalDevice() { + ::nearby::Borrowed borrowed = service_.Borrow(); + if (borrowed) { + return (*borrowed)->GetLocalDeviceProvider()->GetLocalDevice(); + } + return std::nullopt; +} + } // namespace presence } // namespace nearby diff --git a/presence/presence_client.h b/presence/presence_client.h index 23b70791..16845b47 100644 --- a/presence/presence_client.h +++ b/presence/presence_client.h @@ -23,6 +23,7 @@ #include "internal/platform/borrowable.h" #include "presence/broadcast_request.h" #include "presence/data_types.h" +#include "presence/presence_device.h" #include "presence/scan_request.h" namespace nearby { @@ -78,6 +79,11 @@ class PresenceClient { // terminated. void StopBroadcast(BroadcastSessionId session_id); + // Returns the local PresenceDevice describing the current device's actions, + // connectivity info and unique identifier for use in Connections and + // Presence. + std::optional GetLocalDevice(); + private: BorrowablePresenceService service_; }; diff --git a/presence/presence_client_test.cc b/presence/presence_client_test.cc index 2403ad15..9a4585cd 100644 --- a/presence/presence_client_test.cc +++ b/presence/presence_client_test.cc @@ -20,14 +20,18 @@ #include "absl/status/status.h" #include "internal/platform/medium_environment.h" #include "presence/data_types.h" +#include "presence/presence_device.h" #include "presence/presence_service.h" namespace nearby { namespace presence { namespace { +using ::nearby::internal::Metadata; using ::testing::status::StatusIs; +constexpr absl::string_view kMacAddr = "\x4C\x8B\x1D\xCE\xBA\xD1"; + // Creates a PresenceClient and destroys PresenceService that was used to create // it. PresenceClient CreateDefunctPresenceClient() { @@ -35,6 +39,17 @@ PresenceClient CreateDefunctPresenceClient() { return presence_service.CreatePresenceClient(); } +Metadata CreateTestMetadata() { + Metadata metadata; + metadata.set_device_type(internal::DEVICE_TYPE_PHONE); + metadata.set_account_name("test_account"); + metadata.set_device_name("NP test device"); + metadata.set_user_name("Test user"); + metadata.set_device_profile_url("test_image.test.com"); + metadata.set_bluetooth_mac_address(kMacAddr); + return metadata; +} + class PresenceClientTest : public testing::Test { protected: nearby::MediumEnvironment& env_{nearby::MediumEnvironment::Instance()}; @@ -104,6 +119,23 @@ TEST_F(PresenceClientTest, StartScanFailsWhenPresenceServiceIsGone) { env_.Stop(); } +TEST_F(PresenceClientTest, GettingDeviceWorks) { + PresenceService presence_service; + PresenceClient presence_client = presence_service.CreatePresenceClient(); + presence_service.UpdateLocalDeviceMetadata(CreateTestMetadata(), false, "", + {}, 0, 0, {}); + auto device = presence_client.GetLocalDevice(); + ASSERT_NE(device, std::nullopt); + EXPECT_EQ(device->GetEndpointId().length(), kEndpointIdLength); + EXPECT_EQ(device->GetMetadata().SerializeAsString(), + CreateTestMetadata().SerializeAsString()); +} + +TEST_F(PresenceClientTest, TestGettingDeviceDefunct) { + PresenceClient presence_client = CreateDefunctPresenceClient(); + auto device = presence_client.GetLocalDevice(); + EXPECT_EQ(device, std::nullopt); +} } // namespace } // namespace presence } // namespace nearby diff --git a/presence/presence_device.cc b/presence/presence_device.cc index 474a9d20..585d92c4 100644 --- a/presence/presence_device.cc +++ b/presence/presence_device.cc @@ -17,8 +17,8 @@ #include #include -#include "internal/crypto/random.h" -#include "internal/device.h" +#include "internal/platform/implementation/crypto.h" +#include "internal/interop/device.h" #include "internal/platform/ble_connection_info.h" #include "internal/platform/implementation/system_clock.h" #include "presence/device_motion.h" @@ -28,7 +28,10 @@ namespace presence { namespace { std::string GenerateRandomEndpointId() { - return crypto::RandBytes(kEndpointIdLength); + std::string result(kEndpointIdLength, 0); + crypto::RandBytes(const_cast(result.data()), + result.size()); + return result; } } // namespace @@ -48,8 +51,14 @@ PresenceDevice::PresenceDevice(DeviceMotion device_motion, std::vector PresenceDevice::GetConnectionInfos() const { + std::vector transformed_actions; + transformed_actions.reserve(actions_.size()); + for (const auto& action : actions_) { + transformed_actions.push_back(action.GetActionIdentifier()); + } return {nearby::BleConnectionInfo(metadata_.bluetooth_mac_address(), - /*gatt_characteristic=*/"", /*psm=*/"", 0)}; + /*gatt_characteristic=*/"", /*psm=*/"", + transformed_actions)}; } } // namespace presence } // namespace nearby diff --git a/presence/presence_device.h b/presence/presence_device.h index 94fd14eb..3d56b5db 100644 --- a/presence/presence_device.h +++ b/presence/presence_device.h @@ -21,7 +21,7 @@ #include "absl/time/time.h" #include "absl/types/variant.h" -#include "internal/device.h" +#include "internal/interop/device.h" #include "internal/proto/metadata.pb.h" #include "presence/data_element.h" #include "presence/device_motion.h" @@ -60,12 +60,13 @@ class PresenceDevice : public nearby::NearbyDevice { const override; DeviceMotion GetDeviceMotion() const { return device_motion_; } Metadata GetMetadata() const { return metadata_; } + void SetMetadata(const Metadata metadata) { metadata_ = metadata; } absl::Time GetDiscoveryTimestamp() const { return discovery_timestamp_; } private: const absl::Time discovery_timestamp_; const DeviceMotion device_motion_; - const Metadata metadata_; + Metadata metadata_; std::vector extended_properties_; std::vector actions_; std::string endpoint_id_; @@ -77,7 +78,9 @@ class PresenceDevice : public nearby::NearbyDevice { inline bool operator==(const PresenceDevice& d1, const PresenceDevice& d2) { return d1.GetDeviceMotion() == d2.GetDeviceMotion() && d1.GetMetadata().SerializeAsString() == - d2.GetMetadata().SerializeAsString(); + d2.GetMetadata().SerializeAsString() && + d1.GetActions() == d2.GetActions() && + d1.GetExtendedProperties() == d2.GetExtendedProperties(); } inline bool operator!=(const PresenceDevice& d1, const PresenceDevice& d2) { diff --git a/presence/presence_device_provider.h b/presence/presence_device_provider.h new file mode 100644 index 00000000..31d65e4e --- /dev/null +++ b/presence/presence_device_provider.h @@ -0,0 +1,42 @@ +// 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. + +#ifndef THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_DEVICE_PROVIDER_H_ +#define THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_DEVICE_PROVIDER_H_ + +#include "internal/interop/device_provider.h" +#include "internal/proto/metadata.pb.h" +#include "presence/presence_device.h" + +namespace nearby { +namespace presence { + +class PresenceDeviceProvider : public NearbyDeviceProvider { + public: + explicit PresenceDeviceProvider(::nearby::internal::Metadata metadata) + : device_{metadata} {} + + const PresenceDevice& GetLocalDevice() override { return device_; } + + void UpdateMetadata(const ::nearby::internal::Metadata& metadata) { + device_.SetMetadata(metadata); + } + + private: + PresenceDevice device_; +}; +} // namespace presence +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_DEVICE_PROVIDER_H_ diff --git a/presence/presence_device_provider_test.cc b/presence/presence_device_provider_test.cc new file mode 100644 index 00000000..cf7b3e22 --- /dev/null +++ b/presence/presence_device_provider_test.cc @@ -0,0 +1,72 @@ +// 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. + +#include "presence/presence_device_provider.h" + +#include + +#include "gmock/gmock.h" +#include "protobuf-matchers/protocol-buffer-matchers.h" +#include "gtest/gtest.h" +#include "absl/strings/string_view.h" +#include "internal/proto/metadata.pb.h" +#include "internal/proto/metadata.proto.h" +#include "presence/presence_device.h" + +namespace nearby { +namespace presence { +namespace { +using ::nearby::internal::Metadata; + +constexpr absl::string_view kMacAddr = "\x4C\x8B\x1D\xCE\xBA\xD1"; + +Metadata CreateTestMetadata() { + Metadata metadata; + metadata.set_device_type(internal::DEVICE_TYPE_PHONE); + metadata.set_account_name("test_account"); + metadata.set_device_name("NP test device"); + metadata.set_user_name("Test user"); + metadata.set_device_profile_url("test_image.test.com"); + metadata.set_bluetooth_mac_address(kMacAddr); + return metadata; +} + +TEST(PresenceDeviceProviderTest, ProviderIsNotTriviallyConstructible) { + EXPECT_FALSE(std::is_trivially_constructible::value); +} + +TEST(PresenceDeviceProviderTest, DeviceProviderWorks) { + PresenceDeviceProvider provider(CreateTestMetadata()); + auto device = provider.GetLocalDevice(); + EXPECT_EQ(device.GetMetadata().SerializeAsString(), + CreateTestMetadata().SerializeAsString()); +} + +TEST(PresenceDeviceProviderTest, DeviceProviderCanUpdateDevice) { + PresenceDeviceProvider provider(CreateTestMetadata()); + auto device = provider.GetLocalDevice(); + EXPECT_EQ(device.GetMetadata().SerializeAsString(), + CreateTestMetadata().SerializeAsString()); + Metadata new_metadata = CreateTestMetadata(); + new_metadata.set_device_name("NP interop device"); + provider.UpdateMetadata(new_metadata); + EXPECT_NE(device.GetMetadata().SerializeAsString(), + new_metadata.SerializeAsString()); + EXPECT_EQ(provider.GetLocalDevice().GetMetadata().SerializeAsString(), + new_metadata.SerializeAsString()); +} + +} // namespace +} // namespace presence +} // namespace nearby diff --git a/presence/presence_device_test.cc b/presence/presence_device_test.cc index b6c1ff13..0ad5c8d5 100644 --- a/presence/presence_device_test.cc +++ b/presence/presence_device_test.cc @@ -72,13 +72,16 @@ TEST(PresenceDeviceTest, ExplicitInitNotEquals) { EXPECT_NE(device1, device2); } -TEST(PresenceDeviceTest, TestGetBluetoothAddress) { +TEST(PresenceDeviceTest, TestGetBleConnectionInfo) { Metadata metadata = CreateTestMetadata(); PresenceDevice device = PresenceDevice({kDefaultMotionType}, metadata); + device.AddAction(PresenceAction(kTestAction)); auto info = (device.GetConnectionInfos().at(0)); ASSERT_TRUE(absl::holds_alternative(info)); - EXPECT_EQ(absl::get(info).GetMacAddress(), + auto ble_info = absl::get(info); + EXPECT_EQ(ble_info.GetMacAddress(), kMacAddr); + EXPECT_EQ(ble_info.GetActions(), std::vector{kTestAction}); } TEST(PresenceDevicetest, TestGetAddExtendedProperties) { diff --git a/presence/presence_service.cc b/presence/presence_service.cc index e4d4c3b8..5fee1268 100644 --- a/presence/presence_service.cc +++ b/presence/presence_service.cc @@ -24,7 +24,9 @@ namespace nearby { namespace presence { PresenceService::PresenceService() { - this->service_controller_ = std::make_unique(); + service_controller_ = std::make_unique(); + provider_ = std::make_unique( + service_controller_->GetLocalDeviceMetadata()); } PresenceClient PresenceService::CreatePresenceClient() { @@ -35,6 +37,7 @@ absl::StatusOr PresenceService::StartScan( ScanRequest scan_request, ScanCallback callback) { return service_controller_->StartScan(scan_request, std::move(callback)); } + void PresenceService::StopScan(ScanSessionId id) { service_controller_->StopScan(id); } diff --git a/presence/presence_service.h b/presence/presence_service.h index 972e6487..623e6169 100644 --- a/presence/presence_service.h +++ b/presence/presence_service.h @@ -16,11 +16,15 @@ #define THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_SERVICE_H_ #include +#include +#include #include "internal/platform/borrowable.h" +#include "internal/proto/metadata.pb.h" #include "presence/data_types.h" #include "presence/implementation/service_controller.h" #include "presence/presence_client.h" +#include "presence/presence_device_provider.h" namespace nearby { namespace presence { @@ -46,9 +50,30 @@ class PresenceService { void StopBroadcast(BroadcastSessionId session_id); + void UpdateLocalDeviceMetadata( + const ::nearby::internal::Metadata& metadata, bool regen_credentials, + absl::string_view manager_app_id, + const std::vector& identity_types, + int credential_life_cycle_days, int contiguous_copy_of_credentials, + GenerateCredentialsResultCallback credentials_generated_cb) { + provider_->UpdateMetadata(metadata); + service_controller_->UpdateLocalDeviceMetadata( + metadata, regen_credentials, manager_app_id, identity_types, + credential_life_cycle_days, contiguous_copy_of_credentials, + std::move(credentials_generated_cb)); + } + + PresenceDeviceProvider* GetLocalDeviceProvider() { return provider_.get(); } + + // Testing only. + ::nearby::internal::Metadata GetLocalDeviceMetadata() { + return service_controller_->GetLocalDeviceMetadata(); + } + private: std::unique_ptr service_controller_; ::nearby::Lender lender_{this}; + std::unique_ptr provider_; }; } // namespace presence diff --git a/presence/presence_service_test.cc b/presence/presence_service_test.cc index 190301b8..564754d1 100644 --- a/presence/presence_service_test.cc +++ b/presence/presence_service_test.cc @@ -24,11 +24,22 @@ namespace nearby { namespace presence { namespace { +using Metadata = ::nearby::internal::Metadata; + class PresenceServiceTest : public testing::Test { protected: nearby::MediumEnvironment& env_{nearby::MediumEnvironment::Instance()}; }; +Metadata CreateTestMetadata(absl::string_view account_name) { + Metadata metadata; + metadata.set_account_name(account_name); + metadata.set_device_name("NP test device"); + metadata.set_device_profile_url("test_image.test.com"); + metadata.set_bluetooth_mac_address("\xFF\xFF\xFF\xFF\xFF\xFF"); + return metadata; +} + TEST_F(PresenceServiceTest, DefaultConstructorWorks) { PresenceService presence_service; } @@ -59,6 +70,19 @@ TEST_F(PresenceServiceTest, StartThenStopScan) { env_.Stop(); } +TEST_F(PresenceServiceTest, UpdatingLocalMetadataWorks) { + PresenceService presence_service; + presence_service.UpdateLocalDeviceMetadata(CreateTestMetadata("Test account"), + false, "Test app", {}, 3, 1, {}); + EXPECT_EQ(presence_service.GetLocalDeviceMetadata().SerializeAsString(), + CreateTestMetadata("Test account").SerializeAsString()); +} + +TEST_F(PresenceServiceTest, TestGetDeviceProvider) { + PresenceService presence_service; + EXPECT_NE(presence_service.GetLocalDeviceProvider(), nullptr); +} + } // namespace } // namespace presence } // namespace nearby diff --git a/proto/sharing_enums.proto b/proto/sharing_enums.proto index e001bba6..442bc4f0 100644 --- a/proto/sharing_enums.proto +++ b/proto/sharing_enums.proto @@ -513,6 +513,7 @@ enum PermissionRequestType { PERMISSION_WIFI = 2; PERMISSION_BLUETOOTH = 3; PERMISSION_LOCATION = 4; + PERMISSION_WIFI_HOTSPOT = 5; } enum SharingUseCase { diff --git a/third_party/absl b/third_party/absl index 29273402..9336be04 160000 --- a/third_party/absl +++ b/third_party/absl @@ -1 +1 @@ -Subproject commit 2927340217c37328319b5869285a6dcdbc13e7a7 +Subproject commit 9336be04a242237cd41a525bedfcf3be1bb55377 diff --git a/third_party/depot_tools b/third_party/depot_tools index 95a28c5a..e1197f06 160000 --- a/third_party/depot_tools +++ b/third_party/depot_tools @@ -1 +1 @@ -Subproject commit 95a28c5a14cf0c7040794e10e9404b270784010c +Subproject commit e1197f06a8f45c0328d341b30e337d3a4b609716 diff --git a/third_party/google-toolbox-for-mac/google-toolbox-for-mac b/third_party/google-toolbox-for-mac/google-toolbox-for-mac index 1b2da5e6..33941504 160000 --- a/third_party/google-toolbox-for-mac/google-toolbox-for-mac +++ b/third_party/google-toolbox-for-mac/google-toolbox-for-mac @@ -1 +1 @@ -Subproject commit 1b2da5e6e6b5edb1fa1427cc77e23f04069c04ad +Subproject commit 339415048005a9eba957357a02459a977a2e3007 diff --git a/third_party/gtest b/third_party/gtest index 8fa9461c..d61d4d8e 160000 --- a/third_party/gtest +++ b/third_party/gtest @@ -1 +1 @@ -Subproject commit 8fa9461cc28e053d66f17132808d287ae51575e2 +Subproject commit d61d4d8e64c08a662055e82904bbf90e108a704f diff --git a/third_party/mbedtls b/third_party/mbedtls index 6a327a5f..3e0418fe 160000 --- a/third_party/mbedtls +++ b/third_party/mbedtls @@ -1 +1 @@ -Subproject commit 6a327a5fdc2786cb50b4dbe5e3a75884a1f8435a +Subproject commit 3e0418fe502b2a2194e118d362efdfc0e558be73 diff --git a/third_party/protobuf b/third_party/protobuf index 53515ead..4812107b 160000 --- a/third_party/protobuf +++ b/third_party/protobuf @@ -1 +1 @@ -Subproject commit 53515eadce8d732bc0b2d0f0c8ec20ddd55ef86b +Subproject commit 4812107b9d0fb9fdcca933766c237c38f2150379