| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ash/secure_channel/nearby_endpoint_finder_impl.h" |
| |
| #include "base/base64.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/rand_util.h" |
| #include "chromeos/ash/components/multidevice/logging/logging.h" |
| #include "chromeos/ash/services/secure_channel/public/mojom/nearby_connector.mojom.h" |
| #include "chromeos/ash/services/secure_channel/util/histogram_util.h" |
| |
| namespace ash { |
| namespace secure_channel { |
| namespace { |
| |
| using ::nearby::connections::mojom::DiscoveredEndpointInfoPtr; |
| using ::nearby::connections::mojom::DiscoveryOptions; |
| using ::nearby::connections::mojom::MediumSelection; |
| using ::nearby::connections::mojom::Status; |
| using ::nearby::connections::mojom::Strategy; |
| |
| NearbyEndpointFinderImpl::Factory* g_test_factory = nullptr; |
| |
| const size_t kEndpointIdLength = 4u; |
| const size_t kEndpointInfoLength = 4u; |
| |
| void OnStopDiscoveryDestructorResult(Status status) { |
| util::RecordStopDiscoveryResult(status); |
| |
| if (status != Status::kSuccess) |
| PA_LOG(WARNING) << "Failed to stop discovery as part of destructor"; |
| } |
| |
| std::string GenerateEndpointId() { |
| // Generate a random array of bytes; as long as it is of size of at least |
| // 3/4 kEndpointInfoLength, the final substring of the Base64-encoded array |
| // will be of size kEndpointInfoLength. |
| std::vector<uint8_t> raw_endpoint_info = |
| base::RandBytesAsVector(kEndpointIdLength); |
| |
| // Return the first kEndpointIdLength characters of the Base64-encoded string. |
| return base::Base64Encode(raw_endpoint_info).substr(0, kEndpointIdLength); |
| } |
| |
| std::vector<uint8_t> GenerateEndpointInfo(const std::vector<uint8_t>& eid) { |
| if (eid.size() < 2) { |
| return base::RandBytesAsVector(kEndpointInfoLength); |
| } |
| |
| std::vector<uint8_t> endpoint_info = { |
| // version number |
| 1, |
| // 2 bytes indicating the EID |
| eid[0], |
| eid[1], |
| }; |
| |
| return endpoint_info; |
| } |
| |
| } // namespace |
| |
| // static |
| std::unique_ptr<NearbyEndpointFinder> NearbyEndpointFinderImpl::Factory::Create( |
| const mojo::SharedRemote<::nearby::connections::mojom::NearbyConnections>& |
| nearby_connections) { |
| if (g_test_factory) |
| return g_test_factory->CreateInstance(nearby_connections); |
| |
| return base::WrapUnique(new NearbyEndpointFinderImpl(nearby_connections)); |
| } |
| |
| // static |
| void NearbyEndpointFinderImpl::Factory::SetFactoryForTesting( |
| Factory* test_factory) { |
| g_test_factory = test_factory; |
| } |
| |
| NearbyEndpointFinderImpl::NearbyEndpointFinderImpl( |
| const mojo::SharedRemote<::nearby::connections::mojom::NearbyConnections>& |
| nearby_connections) |
| : nearby_connections_(nearby_connections), |
| endpoint_id_(GenerateEndpointId()) {} |
| |
| NearbyEndpointFinderImpl::~NearbyEndpointFinderImpl() { |
| if (is_discovery_active_) { |
| nearby_connections_->StopDiscovery( |
| mojom::kServiceId, base::BindOnce(&OnStopDiscoveryDestructorResult)); |
| } |
| } |
| |
| void NearbyEndpointFinderImpl::PerformFindEndpoint() { |
| is_discovery_active_ = true; |
| endpoint_info_ = GenerateEndpointInfo(eid()); |
| nearby_connections_->StartDiscovery( |
| mojom::kServiceId, |
| DiscoveryOptions::New(Strategy::kP2pPointToPoint, |
| MediumSelection::New(/*bluetooth=*/true, |
| /*ble=*/false, |
| /*webrtc=*/false, |
| /*wifi_lan=*/false, |
| /*wifi_direct=*/false), |
| /*fast_advertisement_service_uuid=*/std::nullopt, |
| /*is_out_of_band_connection=*/true), |
| endpoint_discovery_listener_receiver_.BindNewPipeAndPassRemote(), |
| base::BindOnce(&NearbyEndpointFinderImpl::OnStartDiscoveryResult, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void NearbyEndpointFinderImpl::OnEndpointFound(const std::string& endpoint_id, |
| DiscoveredEndpointInfoPtr info) { |
| // Only look for endpoints whose endpoint metadata field matches the |
| // parameters passed to the InjectEndpoint() call. |
| if (endpoint_id_ != endpoint_id || endpoint_info_ != info->endpoint_info) |
| return; |
| |
| PA_LOG(VERBOSE) << "Found endpoint with ID " << endpoint_id_ |
| << ", stopping discovery"; |
| nearby_connections_->StopDiscovery( |
| mojom::kServiceId, |
| base::BindOnce(&NearbyEndpointFinderImpl::OnStopDiscoveryResult, |
| weak_ptr_factory_.GetWeakPtr(), std::move(info))); |
| } |
| |
| void NearbyEndpointFinderImpl::OnStartDiscoveryResult(Status status) { |
| util::RecordStartDiscoveryResult(status); |
| |
| if (status != Status::kSuccess) { |
| PA_LOG(WARNING) << "Failed to start Nearby discovery: " << status; |
| is_discovery_active_ = false; |
| NotifyEndpointDiscoveryFailure(status); |
| return; |
| } |
| |
| PA_LOG(VERBOSE) << "Started Nearby discovery"; |
| |
| nearby_connections_->InjectBluetoothEndpoint( |
| mojom::kServiceId, endpoint_id_, endpoint_info_, |
| remote_device_bluetooth_address(), |
| base::BindOnce(&NearbyEndpointFinderImpl::OnInjectBluetoothEndpointResult, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void NearbyEndpointFinderImpl::OnInjectBluetoothEndpointResult(Status status) { |
| util::RecordInjectEndpointResult(status); |
| |
| if (status != Status::kSuccess) { |
| PA_LOG(WARNING) << "Failed to inject Bluetooth endpoint: " << status; |
| NotifyEndpointDiscoveryFailure(status); |
| return; |
| } |
| |
| PA_LOG(VERBOSE) << "Injected Bluetooth endpoint"; |
| } |
| |
| void NearbyEndpointFinderImpl::OnStopDiscoveryResult( |
| ::nearby::connections::mojom::DiscoveredEndpointInfoPtr info, |
| Status status) { |
| util::RecordStopDiscoveryResult(status); |
| |
| is_discovery_active_ = false; |
| |
| if (status != Status::kSuccess) { |
| PA_LOG(WARNING) << "Failed to stop Nearby discovery: " << status; |
| NotifyEndpointDiscoveryFailure(status); |
| return; |
| } |
| |
| NotifyEndpointFound(endpoint_id_, std::move(info)); |
| } |
| |
| } // namespace secure_channel |
| } // namespace ash |