Commit 394cf68b authored by Makoto Shimazu's avatar Makoto Shimazu Committed by Commit Bot

Revert "[CrOS MultiDevice] Add HostVerifierOperation."

This reverts commit a69a2eb1.

Reason for revert: MultiDeviceSetupHostVerifierOperationImplTest.ReceivedInvalidResponse_InvalidResultCode
 keeps failing on linux-chromeos-dbg bot.
https://ci.chromium.org/p/chromium/builders/luci.chromium.ci/linux-chromeos-dbg/6639

Original change's description:
> [CrOS MultiDevice] Add HostVerifierOperation.
> 
> This class represents an operation for completing the verification step
> for the current host device. A HostVerifierOperation instance is meant
> to be used for a single verification attempt; if verification needs to
> be retried, a new instance should be created for the next attempt.
> 
> This class completes the following steps to verify a device:
> (1) Call FindEligibleDevices(). This step sends a message to the host
>     device, which in turn enables background advertising.
> (2) Creates a connection to the device using the BLE listener role.
> (3) Sends an EnableBetterTogetherRequest message to the host device.
> (4) Waits for an EnableBetterTogetherResponse messages to be returned by
>     the host device.
> 
> Bug: 824568
> Change-Id: I520ff9388334467831342af4d0e270cb4e351eeb
> Reviewed-on: https://chromium-review.googlesource.com/1119466
> Reviewed-by: Ryan Hansberry <hansberry@chromium.org>
> Commit-Queue: Kyle Horimoto <khorimoto@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#572339}

TBR=khorimoto@chromium.org,hansberry@chromium.org

Change-Id: I3f74224c953fbca3932ce178367824342ddf06e8
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: 824568
Reviewed-on: https://chromium-review.googlesource.com/1125479Reviewed-by: default avatarMakoto Shimazu <shimazu@chromium.org>
Commit-Queue: Makoto Shimazu <shimazu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#572407}
parent 2bbe8e93
...@@ -29,10 +29,6 @@ static_library("multidevice_setup") { ...@@ -29,10 +29,6 @@ static_library("multidevice_setup") {
"host_verifier.h", "host_verifier.h",
"host_verifier_impl.cc", "host_verifier_impl.cc",
"host_verifier_impl.h", "host_verifier_impl.h",
"host_verifier_operation.cc",
"host_verifier_operation.h",
"host_verifier_operation_impl.cc",
"host_verifier_operation_impl.h",
"multidevice_setup_base.cc", "multidevice_setup_base.cc",
"multidevice_setup_base.h", "multidevice_setup_base.h",
"multidevice_setup_impl.cc", "multidevice_setup_impl.cc",
...@@ -51,7 +47,6 @@ static_library("multidevice_setup") { ...@@ -51,7 +47,6 @@ static_library("multidevice_setup") {
"//chromeos/components/proximity_auth/logging", "//chromeos/components/proximity_auth/logging",
"//chromeos/services/device_sync/public/cpp", "//chromeos/services/device_sync/public/cpp",
"//chromeos/services/device_sync/public/mojom", "//chromeos/services/device_sync/public/mojom",
"//chromeos/services/multidevice_setup/proto",
"//chromeos/services/multidevice_setup/public/mojom", "//chromeos/services/multidevice_setup/public/mojom",
"//chromeos/services/secure_channel/public/cpp/client", "//chromeos/services/secure_channel/public/cpp/client",
"//chromeos/services/secure_channel/public/mojom", "//chromeos/services/secure_channel/public/mojom",
...@@ -82,8 +77,6 @@ static_library("test_support") { ...@@ -82,8 +77,6 @@ static_library("test_support") {
"fake_host_status_provider.h", "fake_host_status_provider.h",
"fake_host_verifier.cc", "fake_host_verifier.cc",
"fake_host_verifier.h", "fake_host_verifier.h",
"fake_host_verifier_operation.cc",
"fake_host_verifier_operation.h",
"fake_setup_flow_completion_recorder.cc", "fake_setup_flow_completion_recorder.cc",
"fake_setup_flow_completion_recorder.h", "fake_setup_flow_completion_recorder.h",
] ]
...@@ -107,7 +100,6 @@ source_set("unit_tests") { ...@@ -107,7 +100,6 @@ source_set("unit_tests") {
"host_backend_delegate_impl_unittest.cc", "host_backend_delegate_impl_unittest.cc",
"host_status_provider_impl_unittest.cc", "host_status_provider_impl_unittest.cc",
"host_verifier_impl_unittest.cc", "host_verifier_impl_unittest.cc",
"host_verifier_operation_impl_unittest.cc",
"multidevice_setup_impl_unittest.cc", "multidevice_setup_impl_unittest.cc",
"multidevice_setup_service_unittest.cc", "multidevice_setup_service_unittest.cc",
"setup_flow_completion_recorder_impl_unittest.cc", "setup_flow_completion_recorder_impl_unittest.cc",
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/services/multidevice_setup/fake_host_verifier_operation.h"
namespace chromeos {
namespace multidevice_setup {
FakeHostVerifierOperation::FakeHostVerifierOperation(Delegate* delegate)
: HostVerifierOperation(delegate) {}
FakeHostVerifierOperation::~FakeHostVerifierOperation() = default;
void FakeHostVerifierOperation::PerformCancelOperation() {}
FakeHostVerifierOperationDelegate::FakeHostVerifierOperationDelegate() =
default;
FakeHostVerifierOperationDelegate::~FakeHostVerifierOperationDelegate() =
default;
void FakeHostVerifierOperationDelegate::OnOperationFinished(
HostVerifierOperation::Result result) {
DCHECK(!result_);
result_ = result;
}
} // namespace multidevice_setup
} // namespace chromeos
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROMEOS_SERVICES_MULTIDEVICE_SETUP_FAKE_HOST_VERIFIER_OPERATION_H_
#define CHROMEOS_SERVICES_MULTIDEVICE_SETUP_FAKE_HOST_VERIFIER_OPERATION_H_
#include "base/macros.h"
#include "base/optional.h"
#include "chromeos/services/multidevice_setup/host_verifier_operation.h"
namespace chromeos {
namespace multidevice_setup {
// Test HostVerifierOperation implementation.
class FakeHostVerifierOperation : public HostVerifierOperation {
public:
FakeHostVerifierOperation(Delegate* delegate);
~FakeHostVerifierOperation() override;
using HostVerifierOperation::NotifyOperationFinished;
private:
// HostVerifierOperation:
void PerformCancelOperation() override;
DISALLOW_COPY_AND_ASSIGN(FakeHostVerifierOperation);
};
// Test HostVerifierOperation::Delegate implementation.
class FakeHostVerifierOperationDelegate
: public HostVerifierOperation::Delegate {
public:
FakeHostVerifierOperationDelegate();
~FakeHostVerifierOperationDelegate() override;
const base::Optional<HostVerifierOperation::Result>& result() const {
return result_;
}
private:
// HostVerifierOperation::Delegate:
void OnOperationFinished(HostVerifierOperation::Result result) override;
base::Optional<HostVerifierOperation::Result> result_;
DISALLOW_COPY_AND_ASSIGN(FakeHostVerifierOperationDelegate);
};
} // namespace multidevice_setup
} // namespace chromeos
#endif // CHROMEOS_SERVICES_MULTIDEVICE_SETUP_FAKE_HOST_VERIFIER_OPERATION_H_
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/services/multidevice_setup/host_verifier_operation.h"
#include "chromeos/components/proximity_auth/logging/logging.h"
namespace chromeos {
namespace multidevice_setup {
HostVerifierOperation::HostVerifierOperation(Delegate* delegate)
: delegate_(delegate) {}
HostVerifierOperation::~HostVerifierOperation() = default;
void HostVerifierOperation::CancelOperation() {
if (result_) {
PA_LOG(ERROR) << "HostVerifierOperation::CancelOperation(): Tried to "
<< "cancel operation, but it was already finished. Result: "
<< *result_;
NOTREACHED();
}
PerformCancelOperation();
NotifyOperationFinished(Result::kCanceled);
}
void HostVerifierOperation::NotifyOperationFinished(Result result) {
if (result_) {
PA_LOG(ERROR) << "HostVerifierOperation::NotifyOperationFinished(): Tried "
<< "to finish operation, but it was already finished. "
<< "Result: " << *result_;
}
result_ = result;
delegate_->OnOperationFinished(*result_);
}
std::ostream& operator<<(std::ostream& stream,
const HostVerifierOperation::Result& result) {
switch (result) {
case HostVerifierOperation::Result::kTimeoutFindingEligibleDevices:
stream << "[timeout calling FindEligibleDevices()]";
break;
case HostVerifierOperation::Result::kErrorCallingFindEligibleDevices:
stream << "[error calling FindEligibleDevices()]";
break;
case HostVerifierOperation::Result::kDeviceToVerifyIsNotEligible:
stream << "[device to verify was not included in FindEligibleDevices() "
<< "response];";
break;
case HostVerifierOperation::Result::kTimeoutFindingConnection:
stream << "[timeout finding connection]";
break;
case HostVerifierOperation::Result::kConnectionAttemptFailed:
stream << "[connection attempt failed]";
break;
case HostVerifierOperation::Result::kConnectionDisconnectedUnexpectedly:
stream << "[connection disconnected unexpectedly]";
break;
case HostVerifierOperation::Result::kTimeoutReceivingResponse:
stream << "[timeout receiving EnableBetterTogetherResponse]";
break;
case HostVerifierOperation::Result::kReceivedInvalidResponse:
stream << "[received invalid EnableBetterTogetherResponse message]";
break;
case HostVerifierOperation::Result::kReceivedErrorResponse:
stream << "[received EnableBetterTogetherResponse with error]";
break;
case HostVerifierOperation::Result::kCanceled:
stream << "[request canceled]";
break;
case HostVerifierOperation::Result::kSuccess:
stream << "[success]";
break;
}
return stream;
}
} // namespace multidevice_setup
} // namespace chromeos
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROMEOS_SERVICES_MULTIDEVICE_SETUP_HOST_VERIFIER_OPERATION_H_
#define CHROMEOS_SERVICES_MULTIDEVICE_SETUP_HOST_VERIFIER_OPERATION_H_
#include <ostream>
#include "base/macros.h"
#include "base/optional.h"
namespace chromeos {
namespace multidevice_setup {
// Operation for completing the verification step for the current host device.
// A HostVerifierOperation instance is meant to be used for a single
// verification attempt; if verification needs to be retried, a new instance
// should be created for the next attempt.
class HostVerifierOperation {
public:
enum class Result {
kTimeoutFindingEligibleDevices,
kErrorCallingFindEligibleDevices,
kDeviceToVerifyIsNotEligible,
kTimeoutFindingConnection,
kConnectionAttemptFailed,
kConnectionDisconnectedUnexpectedly,
kTimeoutReceivingResponse,
kReceivedInvalidResponse,
kReceivedErrorResponse,
kCanceled,
kSuccess
};
class Delegate {
public:
virtual ~Delegate() = default;
virtual void OnOperationFinished(Result result) = 0;
};
virtual ~HostVerifierOperation();
// Cancels the operation, triggering a delegate callback with the kCanceled
// result.
//
// It is invalid to call this function after the operation has already
// completed.
void CancelOperation();
// Returns the result of the operation. If the operation has not yet finished,
// null is returned.
const base::Optional<Result>& result() const { return result_; }
protected:
HostVerifierOperation(Delegate* delegate);
// Derived types should use this function to cancel the operation, but they
// should not call NotifyOperationFinished() during cancellation.
virtual void PerformCancelOperation() = 0;
void NotifyOperationFinished(Result result);
private:
Delegate* delegate_;
base::Optional<Result> result_;
DISALLOW_COPY_AND_ASSIGN(HostVerifierOperation);
};
std::ostream& operator<<(std::ostream& stream,
const HostVerifierOperation::Result& result);
} // namespace multidevice_setup
} // namespace chromeos
#endif // CHROMEOS_SERVICES_MULTIDEVICE_SETUP_HOST_VERIFIER_OPERATION_H_
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/services/multidevice_setup/host_verifier_operation_impl.h"
#include <sstream>
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "base/no_destructor.h"
#include "base/stl_util.h"
#include "base/time/time.h"
#include "chromeos/components/proximity_auth/logging/logging.h"
#include "chromeos/services/device_sync/public/cpp/device_sync_client.h"
#include "chromeos/services/secure_channel/public/cpp/client/secure_channel_client.h"
namespace chromeos {
namespace multidevice_setup {
namespace {
const char kFeature[] = "better_together_setup";
const int kNumMinutesForTimeout = 1;
const char kEligibleDeviceIdsLogString[] = "Eligible device IDs";
const char kIneligibleDeviceIdsLogString[] = "Ineligible device IDs";
void LogDeviceIds(const cryptauth::RemoteDeviceRefList& device_list,
const std::string& device_type_name,
std::stringstream* ss) {
*ss << device_type_name << ": [";
if (!device_list.empty()) {
for (const auto& device : device_list)
*ss << "\"" << device.GetTruncatedDeviceIdForLogs() << "\", ";
ss->seekp(-2, ss->cur); // Remove last ", " from the stream.
}
*ss << "]";
}
std::string CreateLogString(
const cryptauth::RemoteDeviceRefList& eligible_devices,
const cryptauth::RemoteDeviceRefList& ineligible_devices) {
std::stringstream ss;
LogDeviceIds(eligible_devices, kEligibleDeviceIdsLogString, &ss);
ss << ", ";
LogDeviceIds(ineligible_devices, kIneligibleDeviceIdsLogString, &ss);
return ss.str();
}
base::Optional<EnableBetterTogetherResponse> DeserializePossibleResponse(
const std::string& payload) {
BetterTogetherSetupMessageWrapper wrapper;
// If |payload| does not correspond to a BetterTogetherSetupMessageWrapper,
// return null.
if (!wrapper.ParseFromString(payload))
return base::nullopt;
// If |wrapper|'s type indicates that it does not contain a
// EnableBetterTogetherResponse, return null.
if (!wrapper.has_type() ||
wrapper.type() != MessageType::ENABLE_BETTER_TOGETHER_RESPONSE) {
return base::nullopt;
}
EnableBetterTogetherResponse response;
// If |wrapper|'s payload does not represent an EnableBetterTogetherResponse,
// return null.
if (!wrapper.has_payload() || !response.ParseFromString(wrapper.payload()))
return base::nullopt;
return response;
}
} // namespace
// static
HostVerifierOperationImpl::Factory*
HostVerifierOperationImpl::Factory::test_factory_ = nullptr;
// static
HostVerifierOperationImpl::Factory* HostVerifierOperationImpl::Factory::Get() {
if (test_factory_)
return test_factory_;
static base::NoDestructor<Factory> factory;
return factory.get();
}
// static
void HostVerifierOperationImpl::Factory::SetFactoryForTesting(
Factory* test_factory) {
test_factory_ = test_factory;
}
HostVerifierOperationImpl::Factory::~Factory() = default;
std::unique_ptr<HostVerifierOperation>
HostVerifierOperationImpl::Factory::BuildInstance(
HostVerifierOperation::Delegate* delegate,
cryptauth::RemoteDeviceRef device_to_connect,
cryptauth::RemoteDeviceRef local_device,
device_sync::DeviceSyncClient* device_sync_client,
secure_channel::SecureChannelClient* secure_channel_client,
std::unique_ptr<base::OneShotTimer> timer) {
return base::WrapUnique(new HostVerifierOperationImpl(
delegate, device_to_connect, local_device, device_sync_client,
secure_channel_client, std::move(timer)));
}
// static
BetterTogetherSetupMessageWrapper
HostVerifierOperationImpl::CreateWrappedEnableBetterTogetherRequest() {
BetterTogetherSetupMessageWrapper wrapper;
wrapper.set_type(MessageType::ENABLE_BETTER_TOGETHER_REQUEST);
EnableBetterTogetherRequest request;
wrapper.set_payload(request.SerializeAsString());
return wrapper;
}
HostVerifierOperationImpl::HostVerifierOperationImpl(
HostVerifierOperation::Delegate* delegate,
cryptauth::RemoteDeviceRef device_to_connect,
cryptauth::RemoteDeviceRef local_device,
device_sync::DeviceSyncClient* device_sync_client,
secure_channel::SecureChannelClient* secure_channel_client,
std::unique_ptr<base::OneShotTimer> timer)
: HostVerifierOperation(delegate),
device_to_connect_(device_to_connect),
local_device_(local_device),
device_sync_client_(device_sync_client),
secure_channel_client_(secure_channel_client),
timer_(std::move(timer)),
weak_ptr_factory_(this) {
timer_->Start(FROM_HERE, base::TimeDelta::FromMinutes(kNumMinutesForTimeout),
base::Bind(&HostVerifierOperationImpl::OnTimeout,
base::Unretained(this)));
device_sync_client_->FindEligibleDevices(
cryptauth::SoftwareFeature::BETTER_TOGETHER_HOST,
base::BindOnce(&HostVerifierOperationImpl::OnFindEligibleDevicesResponse,
weak_ptr_factory_.GetWeakPtr()));
}
HostVerifierOperationImpl::~HostVerifierOperationImpl() = default;
void HostVerifierOperationImpl::PerformCancelOperation() {
FinishOperation(status_, Result::kCanceled);
}
void HostVerifierOperationImpl::OnConnectionAttemptFailure(
secure_channel::mojom::ConnectionAttemptFailureReason reason) {
PA_LOG(WARNING) << "HostVerifierOperationImpl::OnConnectionAttemptFailure(): "
<< "Failed to establish connection to device with ID \""
<< device_to_connect_.GetTruncatedDeviceIdForLogs() << "\". "
<< "Reason: " << reason;
FinishOperation(Status::kWaitingForConnection,
Result::kConnectionAttemptFailed);
}
void HostVerifierOperationImpl::OnConnection(
std::unique_ptr<secure_channel::ClientChannel> channel) {
client_channel_ = std::move(channel);
client_channel_->AddObserver(this);
TransitionStatus(Status::kWaitingForConnection, Status::kWaitingForResponse);
client_channel_->SendMessage(
CreateWrappedEnableBetterTogetherRequest().SerializeAsString(),
base::DoNothing() /* on_sent_callback */);
}
void HostVerifierOperationImpl::OnDisconnected() {
// Disconnections may occur after the operation is finished.
if (status_ == Status::kFinished)
return;
PA_LOG(WARNING) << "HostVerifierOperationImpl::OnDisconnected(): "
<< "Channel disconnected unexpectedly; could not complete "
<< "verification of device with ID \""
<< device_to_connect_.GetTruncatedDeviceIdForLogs() << "\". ";
FinishOperation(Status::kWaitingForResponse,
Result::kConnectionDisconnectedUnexpectedly);
}
void HostVerifierOperationImpl::OnMessageReceived(const std::string& payload) {
base::Optional<EnableBetterTogetherResponse> possible_response =
DeserializePossibleResponse(payload);
// The message could have been unrelated; continue waiting.
if (!possible_response)
return;
// If the received message is malformed, fail the operation.
if (!possible_response->has_result_code() ||
!EnableBetterTogetherResponse::ResultCode_IsValid(
possible_response->result_code())) {
FinishOperation(Status::kWaitingForResponse,
Result::kReceivedInvalidResponse);
return;
}
// If the received message includes an error, fail the operation.
if (possible_response->result_code() == EnableBetterTogetherResponse::ERROR) {
FinishOperation(Status::kWaitingForResponse,
Result::kReceivedErrorResponse);
return;
}
FinishOperation(Status::kWaitingForResponse, Result::kSuccess);
}
void HostVerifierOperationImpl::OnTimeout() {
switch (status_) {
case Status::kWaitingForFindEligibleDevicesResponse:
FinishOperation(status_, Result::kTimeoutFindingEligibleDevices);
break;
case Status::kWaitingForConnection:
FinishOperation(status_, Result::kTimeoutFindingConnection);
break;
case Status::kWaitingForResponse:
FinishOperation(status_, Result::kTimeoutReceivingResponse);
break;
case Status::kFinished:
PA_LOG(ERROR) << "HostVerifierOperationImpl::OnTimeout(): Timeout "
<< "occurred, but the operation had already finished.";
NOTREACHED();
break;
}
}
void HostVerifierOperationImpl::OnFindEligibleDevicesResponse(
const base::Optional<std::string>& error_code,
cryptauth::RemoteDeviceRefList eligible_devices,
cryptauth::RemoteDeviceRefList ineligible_devices) {
// A response may be received after the operation is finished.
if (status_ == Status::kFinished)
return;
if (error_code) {
PA_LOG(WARNING) << "HostVerifierOperationImpl::"
<< "OnFindEligibleDevicesResponse(): Failed to complete "
<< "FindEligibleDevices() call. Error code: "
<< *error_code;
FinishOperation(Status::kWaitingForFindEligibleDevicesResponse,
Result::kErrorCallingFindEligibleDevices);
return;
}
PA_LOG(INFO) << "HostVerifierOperationImpl::OnFindEligibleDevicesResponse(): "
<< "Received FindEligibleDevices() response. "
<< CreateLogString(eligible_devices, ineligible_devices);
if (!base::ContainsValue(eligible_devices, device_to_connect_)) {
PA_LOG(WARNING) << "HostVerifierOperationImpl::"
<< "OnFindEligibleDevicesResponse(): FindEligibleDevices() "
<< "response does not include the device to connect. ID: "
<< device_to_connect_.GetTruncatedDeviceIdForLogs();
FinishOperation(Status::kWaitingForFindEligibleDevicesResponse,
Result::kDeviceToVerifyIsNotEligible);
return;
}
TransitionStatus(Status::kWaitingForFindEligibleDevicesResponse,
Status::kWaitingForConnection);
connection_attempt_ = secure_channel_client_->ListenForConnectionFromDevice(
device_to_connect_, local_device_, kFeature,
secure_channel::ConnectionPriority::kHigh);
connection_attempt_->SetDelegate(this);
}
void HostVerifierOperationImpl::FinishOperation(Status expected_current_status,
Result result) {
TransitionStatus(expected_current_status, Status::kFinished);
if (client_channel_) {
client_channel_->RemoveObserver(this);
client_channel_.reset();
}
connection_attempt_.reset();
timer_->Stop();
if (result == Result::kCanceled)
return;
NotifyOperationFinished(result);
}
void HostVerifierOperationImpl::TransitionStatus(Status expected_current_status,
Status new_status) {
if (status_ != expected_current_status) {
PA_LOG(ERROR) << "HostVerifierOperationImpl::VerifyCurrentStatus(): "
<< "Current status is unexpected. Current: " << status_
<< ", Expected: " << expected_current_status
<< ", Attempted new status: " << new_status;
NOTREACHED();
}
PA_LOG(INFO) << "HostVerifierOperationImpl::TransitionStatus(): "
<< "Transitioning from " << status_ << " to " << new_status
<< ".";
status_ = new_status;
}
std::ostream& operator<<(std::ostream& stream,
const HostVerifierOperationImpl::Status& status) {
switch (status) {
case HostVerifierOperationImpl::Status::
kWaitingForFindEligibleDevicesResponse:
stream << "[waiting for FindEligibleDevices() response]";
break;
case HostVerifierOperationImpl::Status::kWaitingForConnection:
stream << "[waiting for connection]";
break;
case HostVerifierOperationImpl::Status::kWaitingForResponse:
stream << "[waiting for response]";
break;
case HostVerifierOperationImpl::Status::kFinished:
stream << "[finished]";
break;
}
return stream;
}
} // namespace multidevice_setup
} // namespace chromeos
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROMEOS_SERVICES_MULTIDEVICE_SETUP_HOST_VERIFIER_OPERATION_IMPL_H_
#define CHROMEOS_SERVICES_MULTIDEVICE_SETUP_HOST_VERIFIER_OPERATION_IMPL_H_
#include <memory>
#include <ostream>
#include <string>
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/timer/timer.h"
#include "chromeos/services/multidevice_setup/host_verifier_operation.h"
#include "chromeos/services/multidevice_setup/proto/multidevice_setup.pb.h"
#include "chromeos/services/secure_channel/public/cpp/client/client_channel.h"
#include "chromeos/services/secure_channel/public/cpp/client/connection_attempt.h"
#include "chromeos/services/secure_channel/public/mojom/secure_channel.mojom.h"
#include "components/cryptauth/remote_device_ref.h"
namespace chromeos {
namespace device_sync {
class DeviceSyncClient;
} // namespace device_sync
namespace secure_channel {
class SecureChannelClient;
} // namespace secure_channel
namespace multidevice_setup {
// Concrete HostVerifierOperation implementation. To verify the host, this class
// performs the following steps:
// (1) Call FindEligibleDevices(). This step sends a message to the host device,
// which in turn enables background advertising.
// (2) Creates a connection to the device using the BLE listener role.
// (3) Sends an EnableBetterTogetherRequest message to the host device.
// (4) Waits for an EnableBetterTogetherResponse messages to be returned by the
// host device.
class HostVerifierOperationImpl
: public HostVerifierOperation,
public secure_channel::ConnectionAttempt::Delegate,
public secure_channel::ClientChannel::Observer {
public:
class Factory {
public:
static Factory* Get();
static void SetFactoryForTesting(Factory* test_factory);
virtual ~Factory();
virtual std::unique_ptr<HostVerifierOperation> BuildInstance(
HostVerifierOperation::Delegate* delegate,
cryptauth::RemoteDeviceRef device_to_connect,
cryptauth::RemoteDeviceRef local_device,
device_sync::DeviceSyncClient* device_sync_client,
secure_channel::SecureChannelClient* secure_channel_client,
std::unique_ptr<base::OneShotTimer> timer =
std::make_unique<base::OneShotTimer>());
private:
static Factory* test_factory_;
};
~HostVerifierOperationImpl() override;
private:
friend class MultiDeviceSetupHostVerifierOperationImplTest;
enum class Status {
kWaitingForFindEligibleDevicesResponse,
kWaitingForConnection,
kWaitingForResponse,
kFinished
};
friend std::ostream& operator<<(std::ostream& stream, const Status& status);
HostVerifierOperationImpl(
HostVerifierOperation::Delegate* delegate,
cryptauth::RemoteDeviceRef device_to_connect,
cryptauth::RemoteDeviceRef local_device,
device_sync::DeviceSyncClient* device_sync_client,
secure_channel::SecureChannelClient* secure_channel_client,
std::unique_ptr<base::OneShotTimer> timer);
static BetterTogetherSetupMessageWrapper
CreateWrappedEnableBetterTogetherRequest();
// HostVerifierOperation:
void PerformCancelOperation() override;
// secure_channel::ConnectionAttempt::Delegate:
void OnConnectionAttemptFailure(
secure_channel::mojom::ConnectionAttemptFailureReason reason) override;
void OnConnection(
std::unique_ptr<secure_channel::ClientChannel> channel) override;
// secure_channel::ClientChannel::Observer:
void OnDisconnected() override;
void OnMessageReceived(const std::string& payload) override;
void OnTimeout();
void OnFindEligibleDevicesResponse(
const base::Optional<std::string>& error_code,
cryptauth::RemoteDeviceRefList eligible_devices,
cryptauth::RemoteDeviceRefList ineligible_devices);
void FinishOperation(Status expected_current_status, Result result);
void TransitionStatus(Status expected_current_status, Status new_status);
cryptauth::RemoteDeviceRef device_to_connect_;
cryptauth::RemoteDeviceRef local_device_;
device_sync::DeviceSyncClient* device_sync_client_;
secure_channel::SecureChannelClient* secure_channel_client_;
std::unique_ptr<base::OneShotTimer> timer_;
Status status_ = Status::kWaitingForFindEligibleDevicesResponse;
std::unique_ptr<secure_channel::ConnectionAttempt> connection_attempt_;
std::unique_ptr<secure_channel::ClientChannel> client_channel_;
base::WeakPtrFactory<HostVerifierOperationImpl> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(HostVerifierOperationImpl);
};
std::ostream& operator<<(std::ostream& stream,
const HostVerifierOperationImpl::Status& status);
} // namespace multidevice_setup
} // namespace chromeos
#endif // CHROMEOS_SERVICES_MULTIDEVICE_SETUP_HOST_VERIFIER_OPERATION_IMPL_H_
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/services/multidevice_setup/host_verifier_operation_impl.h"
#include <memory>
#include "base/macros.h"
#include "base/timer/mock_timer.h"
#include "chromeos/services/device_sync/public/cpp/fake_device_sync_client.h"
#include "chromeos/services/multidevice_setup/fake_host_verifier_operation.h"
#include "chromeos/services/secure_channel/public/cpp/client/fake_client_channel.h"
#include "chromeos/services/secure_channel/public/cpp/client/fake_connection_attempt.h"
#include "chromeos/services/secure_channel/public/cpp/client/fake_secure_channel_client.h"
#include "components/cryptauth/remote_device_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chromeos {
namespace multidevice_setup {
namespace {
const size_t kNumTestDevices = 5;
enum class ResponseType {
kUnrelated,
kNoResultCode,
kInvalidResultCode,
kErrorResultCode,
kSuccess
};
std::string CreatePayloadForResponseType(ResponseType response_type) {
BetterTogetherSetupMessageWrapper wrapper;
wrapper.set_type(MessageType::ENABLE_BETTER_TOGETHER_RESPONSE);
switch (response_type) {
case ResponseType::kUnrelated: {
return "unrelated";
}
case ResponseType::kNoResultCode: {
EnableBetterTogetherResponse response;
wrapper.set_payload(response.SerializeAsString());
return wrapper.SerializeAsString();
}
case ResponseType::kInvalidResultCode: {
EnableBetterTogetherResponse response;
response.set_result_code(
static_cast<EnableBetterTogetherResponse_ResultCode>(1337));
wrapper.set_payload(response.SerializeAsString());
return wrapper.SerializeAsString();
}
case ResponseType::kErrorResultCode: {
EnableBetterTogetherResponse response;
response.set_result_code(EnableBetterTogetherResponse::ERROR);
wrapper.set_payload(response.SerializeAsString());
return wrapper.SerializeAsString();
}
case ResponseType::kSuccess: {
EnableBetterTogetherResponse response;
response.set_result_code(EnableBetterTogetherResponse::NORMAL);
wrapper.set_payload(response.SerializeAsString());
return wrapper.SerializeAsString();
}
}
}
} // namespace
class MultiDeviceSetupHostVerifierOperationImplTest : public testing::Test {
protected:
MultiDeviceSetupHostVerifierOperationImplTest()
: test_devices_(
cryptauth::CreateRemoteDeviceRefListForTest(kNumTestDevices)) {}
~MultiDeviceSetupHostVerifierOperationImplTest() override = default;
// testing::Test:
void SetUp() override {
fake_delegate_ = std::make_unique<FakeHostVerifierOperationDelegate>();
fake_device_sync_client_ =
std::make_unique<device_sync::FakeDeviceSyncClient>();
fake_secure_channel_client_ =
std::make_unique<secure_channel::FakeSecureChannelClient>();
auto mock_timer = std::make_unique<base::MockOneShotTimer>();
mock_timer_ = mock_timer.get();
operation_ = HostVerifierOperationImpl::Factory::Get()->BuildInstance(
fake_delegate_.get(), remote_device(), local_device(),
fake_device_sync_client_.get(), fake_secure_channel_client_.get(),
std::move(mock_timer));
// The operation should have started its timer immediately.
EXPECT_TRUE(mock_timer_->IsRunning());
}
void Timeout() { mock_timer_->Fire(); }
void CancelAndVerifyResult() {
operation_->CancelOperation();
EXPECT_EQ(HostVerifierOperation::Result::kCanceled, GetOperationResult());
}
// Note: If |error_code| is set, then |should_remote_device_be_eligible| is
// ignored. The eligible/ineligible device lists are only provided if there
// was no error.
void CompletePendingFindEligibleDevicesResponse(
const base::Optional<std::string>& error_code = base::nullopt,
bool should_remote_device_be_eligible = true) {
cryptauth::RemoteDeviceRefList eligible_devices;
cryptauth::RemoteDeviceRefList ineligible_devices;
if (!error_code) {
// Always make device 2 eligible.
eligible_devices.push_back(test_devices_[2]);
if (should_remote_device_be_eligible)
eligible_devices.push_back(remote_device());
else
ineligible_devices.push_back(remote_device());
// Always make the local device as well as devices 3 and 4 ineligible.
ineligible_devices.push_back(local_device());
ineligible_devices.push_back(test_devices_[3]);
ineligible_devices.push_back(test_devices_[4]);
if (should_remote_device_be_eligible) {
auto fake_connection_attempt =
std::make_unique<secure_channel::FakeConnectionAttempt>();
fake_connection_attempt_ = fake_connection_attempt.get();
fake_secure_channel_client_->set_next_listen_connection_attempt(
remote_device(), local_device(),
std::move(fake_connection_attempt));
}
}
EXPECT_FALSE(GetOperationResult());
fake_device_sync_client_->InvokePendingFindEligibleDevicesCallback(
error_code, eligible_devices, ineligible_devices);
if (error_code) {
EXPECT_EQ(HostVerifierOperation::Result::kErrorCallingFindEligibleDevices,
GetOperationResult());
} else if (!should_remote_device_be_eligible) {
EXPECT_EQ(HostVerifierOperation::Result::kDeviceToVerifyIsNotEligible,
GetOperationResult());
} else {
EXPECT_FALSE(GetOperationResult());
}
}
void FailToCreateConnectionAndVerifyState(
secure_channel::mojom::ConnectionAttemptFailureReason failure_reason) {
EXPECT_FALSE(GetOperationResult());
fake_connection_attempt_->NotifyConnectionAttemptFailure(failure_reason);
EXPECT_EQ(HostVerifierOperation::Result::kConnectionAttemptFailed,
GetOperationResult());
}
void CreateConnectionSuccessfully() {
EXPECT_FALSE(GetOperationResult());
auto fake_client_channel =
std::make_unique<secure_channel::FakeClientChannel>();
fake_client_channel_ = fake_client_channel.get();
fake_connection_attempt_->NotifyConnection(std::move(fake_client_channel));
EXPECT_EQ(1u, fake_client_channel_->sent_messages().size());
EXPECT_EQ(
HostVerifierOperationImpl::CreateWrappedEnableBetterTogetherRequest()
.SerializeAsString(),
fake_client_channel_->sent_messages()[0].first);
}
void ReceiveResponseAndVerifyState(ResponseType response_type) {
fake_client_channel_->NotifyMessageReceived(
CreatePayloadForResponseType(response_type));
switch (response_type) {
case ResponseType::kUnrelated:
EXPECT_FALSE(GetOperationResult());
break;
case ResponseType::kNoResultCode:
EXPECT_EQ(HostVerifierOperation::Result::kReceivedInvalidResponse,
GetOperationResult());
break;
case ResponseType::kInvalidResultCode:
EXPECT_EQ(HostVerifierOperation::Result::kReceivedInvalidResponse,
GetOperationResult());
break;
case ResponseType::kErrorResultCode:
EXPECT_EQ(HostVerifierOperation::Result::kReceivedErrorResponse,
GetOperationResult());
break;
case ResponseType::kSuccess:
EXPECT_EQ(HostVerifierOperation::Result::kSuccess,
GetOperationResult());
break;
}
}
base::Optional<HostVerifierOperation::Result> GetOperationResult() {
// Both |operation_| and |fake_delegate_| should have identical result
// values.
EXPECT_EQ(operation_->result(), fake_delegate_->result());
return operation_->result();
}
secure_channel::FakeClientChannel* fake_client_channel() {
return fake_client_channel_;
}
const cryptauth::RemoteDeviceRef& local_device() { return test_devices_[0]; }
const cryptauth::RemoteDeviceRef& remote_device() { return test_devices_[1]; }
private:
const cryptauth::RemoteDeviceRefList test_devices_;
std::unique_ptr<FakeHostVerifierOperationDelegate> fake_delegate_;
std::unique_ptr<device_sync::FakeDeviceSyncClient> fake_device_sync_client_;
std::unique_ptr<secure_channel::FakeSecureChannelClient>
fake_secure_channel_client_;
base::MockOneShotTimer* mock_timer_ = nullptr;
secure_channel::FakeConnectionAttempt* fake_connection_attempt_ = nullptr;
secure_channel::FakeClientChannel* fake_client_channel_ = nullptr;
std::unique_ptr<HostVerifierOperation> operation_;
DISALLOW_COPY_AND_ASSIGN(MultiDeviceSetupHostVerifierOperationImplTest);
};
TEST_F(MultiDeviceSetupHostVerifierOperationImplTest,
TimeoutFindingEligibleDevices) {
Timeout();
EXPECT_EQ(HostVerifierOperation::Result::kTimeoutFindingEligibleDevices,
GetOperationResult());
}
TEST_F(MultiDeviceSetupHostVerifierOperationImplTest,
CancelWhileFindingEligibleDevices) {
CancelAndVerifyResult();
}
TEST_F(MultiDeviceSetupHostVerifierOperationImplTest,
ErrorCallingFindEligibleDevices) {
CompletePendingFindEligibleDevicesResponse("errorCode");
}
TEST_F(MultiDeviceSetupHostVerifierOperationImplTest,
DeviceToVerifyIsNotEligible) {
CompletePendingFindEligibleDevicesResponse(
base::nullopt /* error_code */,
false /* should_remote_device_be_eligible */);
}
TEST_F(MultiDeviceSetupHostVerifierOperationImplTest,
TimeoutFindingConnection) {
CompletePendingFindEligibleDevicesResponse();
Timeout();
EXPECT_EQ(HostVerifierOperation::Result::kTimeoutFindingConnection,
GetOperationResult());
}
TEST_F(MultiDeviceSetupHostVerifierOperationImplTest,
CancelWhileFindingConnection) {
CompletePendingFindEligibleDevicesResponse();
CancelAndVerifyResult();
}
TEST_F(MultiDeviceSetupHostVerifierOperationImplTest, ConnectionAttemptFailed) {
CompletePendingFindEligibleDevicesResponse();
FailToCreateConnectionAndVerifyState(
secure_channel::mojom::ConnectionAttemptFailureReason::
AUTHENTICATION_ERROR);
}
TEST_F(MultiDeviceSetupHostVerifierOperationImplTest,
ConnectionDisconnectedUnexpectedly) {
CompletePendingFindEligibleDevicesResponse();
CreateConnectionSuccessfully();
fake_client_channel()->NotifyDisconnected();
EXPECT_EQ(HostVerifierOperation::Result::kConnectionDisconnectedUnexpectedly,
GetOperationResult());
}
TEST_F(MultiDeviceSetupHostVerifierOperationImplTest,
TimeoutReceivingResponse) {
CompletePendingFindEligibleDevicesResponse();
CreateConnectionSuccessfully();
Timeout();
EXPECT_EQ(HostVerifierOperation::Result::kTimeoutReceivingResponse,
GetOperationResult());
}
TEST_F(MultiDeviceSetupHostVerifierOperationImplTest,
CancelWhileWaitingForResponse) {
CompletePendingFindEligibleDevicesResponse();
CreateConnectionSuccessfully();
CancelAndVerifyResult();
}
TEST_F(MultiDeviceSetupHostVerifierOperationImplTest,
ReceivedInvalidResponse_NoResultCode) {
CompletePendingFindEligibleDevicesResponse();
CreateConnectionSuccessfully();
ReceiveResponseAndVerifyState(ResponseType::kNoResultCode);
}
TEST_F(MultiDeviceSetupHostVerifierOperationImplTest,
ReceivedInvalidResponse_InvalidResultCode) {
CompletePendingFindEligibleDevicesResponse();
CreateConnectionSuccessfully();
ReceiveResponseAndVerifyState(ResponseType::kInvalidResultCode);
}
TEST_F(MultiDeviceSetupHostVerifierOperationImplTest,
ReceivedInvalidResponse_ErrorResultCode) {
CompletePendingFindEligibleDevicesResponse();
CreateConnectionSuccessfully();
ReceiveResponseAndVerifyState(ResponseType::kErrorResultCode);
}
TEST_F(MultiDeviceSetupHostVerifierOperationImplTest, Success) {
CompletePendingFindEligibleDevicesResponse();
CreateConnectionSuccessfully();
ReceiveResponseAndVerifyState(ResponseType::kSuccess);
}
TEST_F(MultiDeviceSetupHostVerifierOperationImplTest,
ReceiveUnrelatedMessageThenSuccess) {
CompletePendingFindEligibleDevicesResponse();
CreateConnectionSuccessfully();
ReceiveResponseAndVerifyState(ResponseType::kUnrelated);
ReceiveResponseAndVerifyState(ResponseType::kSuccess);
}
} // namespace multidevice_setup
} // namespace chromeos
# Copyright 2018 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//third_party/protobuf/proto_library.gni")
proto_library("proto") {
sources = [
"multidevice_setup.proto",
]
}
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
syntax = "proto2";
package chromeos.multidevice_setup;
option optimize_for = LITE_RUNTIME;
enum MessageType {
UNKNOWN_TYPE = 0;
ENABLE_BETTER_TOGETHER_REQUEST = 1;
ENABLE_BETTER_TOGETHER_RESPONSE = 2;
}
// Client to host, indicating that the client is requesting Better Together
// setup.
message EnableBetterTogetherRequest {}
// Host to client, indicating that Better Together setup was completed.
// Next id: 2
message EnableBetterTogetherResponse {
enum ResultCode {
NORMAL = 0;
ERROR = 1;
}
optional ResultCode result_code = 1;
}
// Wrapper that Better Together setup messages use to explicitly indicate
// message type.
// Next id: 3
message BetterTogetherSetupMessageWrapper {
required MessageType type = 1;
optional bytes payload = 2;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment