#include "chromeos/ash/components/data_migration/testing/fake_nearby_connections.h"
#include <utility>
#include "base/check.h"
#include "base/containers/contains.h"
#include "base/containers/extend.h"
#include "base/functional/callback_helpers.h"
#include "base/notimplemented.h"
#include "base/rand_util.h"
#include "chromeos/ash/components/data_migration/constants.h"
#include "chromeos/ash/services/nearby/public/mojom/nearby_connections_types.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace data_migration {
namespace {
constexpr std::string_view kTestAuthToken = "test-auth-token";
}
FakeNearbyConnections::RegisteredFilePayload::RegisteredFilePayload() = default;
FakeNearbyConnections::RegisteredFilePayload::RegisteredFilePayload(
base::File input_file_in,
base::File output_file_in)
: input_file(std::move(input_file_in)),
output_file(std::move(output_file_in)) {}
FakeNearbyConnections::RegisteredFilePayload::RegisteredFilePayload(
RegisteredFilePayload&&) = default;
FakeNearbyConnections::RegisteredFilePayload&
FakeNearbyConnections::RegisteredFilePayload::operator=(
RegisteredFilePayload&&) = default;
FakeNearbyConnections::RegisteredFilePayload::~RegisteredFilePayload() =
default;
FakeNearbyConnections::FakeNearbyConnections(
std::string_view remote_endpoint_id)
: remote_endpoint_id_(remote_endpoint_id) {
CHECK(!remote_endpoint_id_.empty());
}
FakeNearbyConnections::~FakeNearbyConnections() = default;
bool FakeNearbyConnections::SendFile(int64_t payload_id,
std::vector<uint8_t>* transferred_bytes) {
if (transferred_bytes) {
transferred_bytes->clear();
}
if (!remote_to_local_payload_listener_.is_bound()) {
LOG(ERROR) << "Payload listener not bound. Cannot send file yet.";
return false;
}
auto registered_files_iter = registered_files_.find(payload_id);
if (registered_files_iter == registered_files_.end()) {
LOG(ERROR) << "Payload id " << payload_id
<< " and its corresponding path not registered yet.";
return false;
}
RegisteredFilePayload file_handles = std::move(registered_files_iter->second);
registered_files_.erase(registered_files_iter);
remote_to_local_payload_listener_->OnPayloadReceived(
remote_endpoint_id_,
::nearby::connections::mojom::Payload::New(
payload_id, ::nearby::connections::mojom::PayloadContent::NewFile(
::nearby::connections::mojom::FilePayload::New(
std::move(file_handles.input_file)))));
PayloadStatus final_status = final_file_payload_status_.contains(payload_id)
? final_file_payload_status_.at(payload_id)
: PayloadStatus::kSuccess;
size_t num_chunks_to_transfer =
final_status == PayloadStatus::kSuccess ? test_file_num_chunks_ : 1;
size_t total_bytes_transferred = 0;
for (size_t chunk_idx = 0; chunk_idx < num_chunks_to_transfer; ++chunk_idx) {
const std::vector<uint8_t> new_chunk = base::RandBytesAsVector(
test_file_size_in_bytes_ / test_file_num_chunks_);
CHECK(!new_chunk.empty());
base::File& output_file = file_handles.output_file;
if (!output_file.WriteAtCurrentPosAndCheck(new_chunk) ||
!output_file.Flush()) {
LOG(ERROR) << "Failed to write file chunk for payload " << payload_id;
return false;
}
if (transferred_bytes) {
base::Extend(*transferred_bytes, new_chunk);
}
total_bytes_transferred += new_chunk.size();
remote_to_local_payload_listener_->OnPayloadTransferUpdate(
remote_endpoint_id_,
::nearby::connections::mojom::PayloadTransferUpdate::New(
payload_id, PayloadStatus::kInProgress, test_file_size_in_bytes_,
total_bytes_transferred));
}
remote_to_local_payload_listener_->OnPayloadTransferUpdate(
remote_endpoint_id_,
::nearby::connections::mojom::PayloadTransferUpdate::New(
payload_id, final_status, test_file_size_in_bytes_,
total_bytes_transferred));
return true;
}
void FakeNearbyConnections::SetFinalFilePayloadStatus(PayloadStatus status,
int64_t payload_id) {
final_file_payload_status_[payload_id] = status;
}
bool FakeNearbyConnections::SendBytesPayload(int64_t payload_id,
const std::string& bytes) {
if (!remote_to_local_payload_listener_.is_bound()) {
LOG(ERROR) << "Payload listener not bound. Cannot send bytes yet.";
return false;
}
remote_to_local_payload_listener_->OnPayloadReceived(
remote_endpoint_id_,
::nearby::connections::mojom::Payload::New(
payload_id,
::nearby::connections::mojom::PayloadContent::NewBytes(
::nearby::connections::mojom::BytesPayload::New(
std::vector<uint8_t>(bytes.begin(), bytes.end())))));
remote_to_local_payload_listener_->OnPayloadTransferUpdate(
remote_endpoint_id_,
::nearby::connections::mojom::PayloadTransferUpdate::New(
payload_id, PayloadStatus::kSuccess, bytes.size(), bytes.size()));
return true;
}
bool FakeNearbyConnections::SimulateRemoteDisconnect() {
if (!connection_listener_.is_bound()) {
return false;
}
auto connection_listener = std::move(connection_listener_);
DisconnectFromEndpoint(kServiceId, remote_endpoint_id_, base::DoNothing());
connection_listener->OnDisconnected(remote_endpoint_id_);
return true;
}
void FakeNearbyConnections::StartAdvertising(
const std::string& service_id,
const std::vector<uint8_t>& endpoint_info,
::nearby::connections::mojom::AdvertisingOptionsPtr options,
mojo::PendingRemote<
::nearby::connections::mojom::ConnectionLifecycleListener> listener,
StartAdvertisingCallback callback) {
if (service_id != kServiceId) {
GTEST_FAIL() << "StartAdvertising() call invalid. service_id="
<< service_id;
}
if (is_advertising_) {
std::move(callback).Run(Status::kAlreadyAdvertising);
return;
}
is_advertising_ = true;
connection_listener_.reset();
connection_listener_.Bind(std::move(listener));
std::move(callback).Run(Status::kSuccess);
if (options->strategy ==
::nearby::connections::mojom::Strategy::kP2pPointToPoint &&
options->allowed_mediums->bluetooth) {
connection_listener_->OnConnectionInitiated(
remote_endpoint_id_,
::nearby::connections::mojom::ConnectionInfo::New(
kTestAuthToken.data(),
base::RandBytesAsVector(64),
std::vector<uint8_t>(64, 0),
true));
} else {
GTEST_FAIL() << "Invalid advertising options. strategy="
<< options->strategy
<< " bluetooth=" << options->allowed_mediums->bluetooth;
}
}
void FakeNearbyConnections::StopAdvertising(const std::string& service_id,
StopAdvertisingCallback callback) {
if (service_id != kServiceId || !is_advertising_) {
GTEST_FAIL() << "StopAdvertising() call invalid. service_id=" << service_id
<< " is_advertising_=" << is_advertising_;
}
is_advertising_ = false;
std::move(callback).Run(Status::kSuccess);
}
void FakeNearbyConnections::StartDiscovery(
const std::string& service_id,
::nearby::connections::mojom::DiscoveryOptionsPtr options,
mojo::PendingRemote<::nearby::connections::mojom::EndpointDiscoveryListener>
listener,
StartDiscoveryCallback callback) {
NOTIMPLEMENTED();
}
void FakeNearbyConnections::StopDiscovery(const std::string& service_id,
StopDiscoveryCallback callback) {
NOTIMPLEMENTED();
}
void FakeNearbyConnections::InjectBluetoothEndpoint(
const std::string& service_id,
const std::string& endpoint_id,
const std::vector<uint8_t>& endpoint_info,
const std::vector<uint8_t>& remote_bluetooth_mac_address,
InjectBluetoothEndpointCallback callback) {
NOTIMPLEMENTED();
}
void FakeNearbyConnections::RequestConnection(
const std::string& service_id,
const std::vector<uint8_t>& endpoint_info,
const std::string& endpoint_id,
::nearby::connections::mojom::ConnectionOptionsPtr options,
mojo::PendingRemote<
::nearby::connections::mojom::ConnectionLifecycleListener> listener,
RequestConnectionCallback callback) {
NOTIMPLEMENTED();
}
void FakeNearbyConnections::DisconnectFromEndpoint(
const std::string& service_id,
const std::string& endpoint_id,
DisconnectFromEndpointCallback callback) {
if (service_id == kServiceId && endpoint_id == remote_endpoint_id_) {
connection_listener_.reset();
remote_to_local_payload_listener_.reset();
registered_files_.clear();
} else {
GTEST_FAIL() << "DisconnectFromEndpoint() call invalid. service_id="
<< service_id << " endpoint_id=" << endpoint_id;
}
std::move(callback).Run(Status::kSuccess);
}
void FakeNearbyConnections::AcceptConnection(
const std::string& service_id,
const std::string& endpoint_id,
mojo::PendingRemote<::nearby::connections::mojom::PayloadListener> listener,
AcceptConnectionCallback callback) {
if (service_id != kServiceId || !connection_listener_.is_bound()) {
GTEST_FAIL() << "AcceptConnection() call invalid. service_id=" << service_id
<< " connection_listener_=" << connection_listener_.is_bound();
}
if (remote_to_local_payload_listener_.is_bound()) {
std::move(callback).Run(Status::kAlreadyConnectedToEndpoint);
return;
}
remote_to_local_payload_listener_.Bind(std::move(listener));
std::move(callback).Run(Status::kSuccess);
connection_listener_->OnConnectionAccepted(remote_endpoint_id_);
if (connection_established_listener_) {
connection_established_listener_.Run();
}
}
void FakeNearbyConnections::RejectConnection(
const std::string& service_id,
const std::string& endpoint_id,
RejectConnectionCallback callback) {
NOTIMPLEMENTED();
}
void FakeNearbyConnections::SendPayload(
const std::string& service_id,
const std::vector<std::string>& endpoint_ids,
::nearby::connections::mojom::PayloadPtr payload,
SendPayloadCallback callback) {
if (service_id != kServiceId) {
GTEST_FAIL() << "Sending payload to unexpected service_id " << service_id;
}
if (!base::Contains(endpoint_ids, remote_endpoint_id_)) {
std::move(callback).Run(Status::kEndpointUnknown);
return;
}
if (!remote_to_local_payload_listener_.is_bound()) {
std::move(callback).Run(Status::kOutOfOrderApiCall);
return;
}
if (local_to_remote_payload_listener_) {
local_to_remote_payload_listener_.Run(std::move(payload));
}
std::move(callback).Run(Status::kSuccess);
}
void FakeNearbyConnections::CancelPayload(const std::string& service_id,
int64_t payload_id,
CancelPayloadCallback callback) {
registered_files_.erase(payload_id);
std::move(callback).Run(Status::kSuccess);
}
void FakeNearbyConnections::StopAllEndpoints(
const std::string& service_id,
StopAllEndpointsCallback callback) {
DisconnectFromEndpoint(service_id, remote_endpoint_id_, std::move(callback));
}
void FakeNearbyConnections::InitiateBandwidthUpgrade(
const std::string& service_id,
const std::string& endpoint_id,
InitiateBandwidthUpgradeCallback callback) {
NOTIMPLEMENTED();
}
void FakeNearbyConnections::RegisterPayloadFile(
const std::string& service_id,
int64_t payload_id,
base::File input_file,
base::File output_file,
RegisterPayloadFileCallback callback) {
if (service_id != kServiceId) {
GTEST_FAIL() << "RegisterPayloadFile() call invalid. service_id="
<< service_id;
}
Status result = Status::kSuccess;
if (register_payload_file_result_generator_) {
result = register_payload_file_result_generator_.Run();
}
if (result == Status::kSuccess) {
registered_files_[payload_id] =
RegisteredFilePayload(std::move(input_file), std::move(output_file));
}
std::move(callback).Run(result);
}
void FakeNearbyConnections::RequestConnectionV3(
const std::string& service_id,
ash::nearby::presence::mojom::PresenceDevicePtr remote_device,
::nearby::connections::mojom::ConnectionOptionsPtr connection_options,
mojo::PendingRemote<::nearby::connections::mojom::ConnectionListenerV3>
listener,
RequestConnectionV3Callback callback) {
NOTIMPLEMENTED();
}
void FakeNearbyConnections::AcceptConnectionV3(
const std::string& service_id,
ash::nearby::presence::mojom::PresenceDevicePtr remote_device,
mojo::PendingRemote<::nearby::connections::mojom::PayloadListenerV3>
listener,
AcceptConnectionV3Callback callback) {
NOTIMPLEMENTED();
}
void FakeNearbyConnections::RejectConnectionV3(
const std::string& service_id,
ash::nearby::presence::mojom::PresenceDevicePtr remote_device,
RejectConnectionV3Callback callback) {
NOTIMPLEMENTED();
}
void FakeNearbyConnections::DisconnectFromDeviceV3(
const std::string& service_id,
ash::nearby::presence::mojom::PresenceDevicePtr remote_device,
DisconnectFromDeviceV3Callback callback) {
NOTIMPLEMENTED();
}
void FakeNearbyConnections::RegisterServiceWithPresenceDeviceProvider(
const std::string& service_id) {
NOTIMPLEMENTED();
}
}