blob: d72f2f1bcabe5c1afbaf7c59bbb3231ce74e4b3d [file] [log] [blame]
// Copyright 2014 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 "components/pairing/fake_controller_pairing_controller.h"
#include <map>
#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/rand_util.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/threading/thread_task_runner_handle.h"
namespace pairing_chromeos {
FakeControllerPairingController::FakeControllerPairingController(
const std::string& config)
: current_stage_(STAGE_NONE),
should_fail_on_connecting_(false),
connection_lost_begin_(STAGE_NONE),
connection_lost_end_(STAGE_NONE),
enrollment_should_fail_(false) {
ApplyConfig(config);
AddObserver(this);
}
FakeControllerPairingController::~FakeControllerPairingController() {
RemoveObserver(this);
}
void FakeControllerPairingController::ApplyConfig(const std::string& config) {
typedef std::vector<std::string> Tokens;
base::StringPairs kv_pairs;
CHECK(base::SplitStringIntoKeyValuePairs(config, ':', ',', &kv_pairs))
<< "Wrong config format.";
std::map<std::string, std::string> dict(kv_pairs.begin(), kv_pairs.end());
if (dict.count("async_duration")) {
int ms = 0;
CHECK(base::StringToInt(dict["async_duration"], &ms))
<< "Wrong 'async_duration' format.";
async_duration_ = base::TimeDelta::FromMilliseconds(ms);
} else {
async_duration_ = base::TimeDelta::FromMilliseconds(3000);
}
should_fail_on_connecting_ =
dict.count("fail_connecting") && (dict["fail_connecting"] == "1");
enrollment_should_fail_ =
dict.count("fail_enrollment") && (dict["fail_enrollment"] == "1");
if (dict.count("connection_lost")) {
Tokens lost_begin_end = base::SplitString(
dict["connection_lost"], "-", base::KEEP_WHITESPACE,
base::SPLIT_WANT_NONEMPTY);
CHECK_EQ(2u, lost_begin_end.size()) << "Wrong 'connection_lost' format.";
int begin = 0;
int end = 0;
CHECK(base::StringToInt(lost_begin_end[0], &begin) &&
base::StringToInt(lost_begin_end[1], &end))
<< "Wrong 'connection_lost' format.";
CHECK((begin == 0 && end == 0) ||
(STAGE_WAITING_FOR_CODE_CONFIRMATION <= begin && begin <= end &&
end <= STAGE_HOST_ENROLLMENT_ERROR))
<< "Wrong 'connection_lost' interval.";
connection_lost_begin_ = static_cast<Stage>(begin);
connection_lost_end_ = static_cast<Stage>(end);
} else {
connection_lost_begin_ = connection_lost_end_ = STAGE_NONE;
}
if (!dict.count("discovery")) {
dict["discovery"] =
"F-Device_1~F-Device_5~F-Device_3~L-Device_3~L-Device_1~F-Device_1";
}
base::StringPairs events;
CHECK(
base::SplitStringIntoKeyValuePairs(dict["discovery"], '-', '~', &events))
<< "Wrong 'discovery' format.";
DiscoveryScenario scenario;
for (const auto& event : events) {
const std::string& type = event.first;
const std::string& device_id = event.second;
CHECK(type == "F" || type == "L" || type == "N")
<< "Wrong discovery event type.";
CHECK(!device_id.empty() || type == "N") << "Empty device ID.";
scenario.push_back(DiscoveryEvent(
type == "F" ? DEVICE_FOUND : type == "L" ? DEVICE_LOST : NOTHING_FOUND,
device_id));
}
SetDiscoveryScenario(scenario);
preset_confirmation_code_ = dict["code"];
CHECK(preset_confirmation_code_.empty() ||
(preset_confirmation_code_.length() == 6 &&
preset_confirmation_code_.find_first_not_of("0123456789") ==
std::string::npos))
<< "Wrong 'code' format.";
}
void FakeControllerPairingController::SetShouldFailOnConnecting() {
should_fail_on_connecting_ = true;
}
void FakeControllerPairingController::SetShouldLoseConnection(Stage stage_begin,
Stage stage_end) {
connection_lost_begin_ = stage_begin;
connection_lost_end_ = stage_end;
}
void FakeControllerPairingController::SetEnrollmentShouldFail() {
enrollment_should_fail_ = true;
}
void FakeControllerPairingController::SetDiscoveryScenario(
const DiscoveryScenario& discovery_scenario) {
discovery_scenario_ = discovery_scenario;
// Check that scenario is valid.
std::set<std::string> devices;
for (DiscoveryScenario::const_iterator event = discovery_scenario_.begin();
event != discovery_scenario_.end();
++event) {
switch (event->first) {
case DEVICE_FOUND: {
devices.insert(event->second);
break;
}
case DEVICE_LOST: {
CHECK(devices.count(event->second));
devices.erase(event->second);
break;
}
case NOTHING_FOUND: {
CHECK(++event == discovery_scenario_.end());
return;
}
}
}
}
void FakeControllerPairingController::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void FakeControllerPairingController::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
ControllerPairingController::Stage
FakeControllerPairingController::GetCurrentStage() {
return current_stage_;
}
void FakeControllerPairingController::StartPairing() {
CHECK(current_stage_ == STAGE_NONE);
ChangeStage(STAGE_DEVICES_DISCOVERY);
}
ControllerPairingController::DeviceIdList
FakeControllerPairingController::GetDiscoveredDevices() {
CHECK(current_stage_ == STAGE_DEVICES_DISCOVERY);
return DeviceIdList(discovered_devices_.begin(), discovered_devices_.end());
}
void FakeControllerPairingController::ChooseDeviceForPairing(
const std::string& device_id) {
CHECK(current_stage_ == STAGE_DEVICES_DISCOVERY);
CHECK(discovered_devices_.count(device_id));
choosen_device_ = device_id;
ChangeStage(STAGE_ESTABLISHING_CONNECTION);
}
void FakeControllerPairingController::RepeatDiscovery() {
CHECK(current_stage_ == STAGE_DEVICE_NOT_FOUND ||
current_stage_ == STAGE_ESTABLISHING_CONNECTION_ERROR ||
current_stage_ == STAGE_HOST_ENROLLMENT_ERROR);
ChangeStage(STAGE_DEVICES_DISCOVERY);
}
std::string FakeControllerPairingController::GetConfirmationCode() {
CHECK(current_stage_ == STAGE_WAITING_FOR_CODE_CONFIRMATION);
if (confirmation_code_.empty()) {
if (preset_confirmation_code_.empty()) {
for (int i = 0; i < 6; ++i)
confirmation_code_.push_back(base::RandInt('0', '9'));
} else {
confirmation_code_ = preset_confirmation_code_;
}
}
return confirmation_code_;
}
void FakeControllerPairingController::SetConfirmationCodeIsCorrect(
bool correct) {
CHECK(current_stage_ == STAGE_WAITING_FOR_CODE_CONFIRMATION);
if (correct)
ChangeStage(STAGE_HOST_UPDATE_IN_PROGRESS);
else
ChangeStage(STAGE_DEVICES_DISCOVERY);
}
void FakeControllerPairingController::SetHostNetwork(
const std::string& onc_spec) {}
void FakeControllerPairingController::SetHostConfiguration(
bool accepted_eula,
const std::string& lang,
const std::string& timezone,
bool send_reports,
const std::string& keyboard_layout) {
}
void FakeControllerPairingController::OnAuthenticationDone(
const std::string& domain,
const std::string& auth_token) {
CHECK(current_stage_ == STAGE_WAITING_FOR_CREDENTIALS);
ChangeStage(STAGE_HOST_ENROLLMENT_IN_PROGRESS);
}
void FakeControllerPairingController::StartSession() {
CHECK(current_stage_ == STAGE_HOST_ENROLLMENT_SUCCESS);
ChangeStage(STAGE_FINISHED);
}
void FakeControllerPairingController::ChangeStage(Stage new_stage) {
if (current_stage_ == new_stage)
return;
current_stage_ = new_stage;
for (Observer& observer : observers_)
observer.PairingStageChanged(new_stage);
}
void FakeControllerPairingController::ChangeStageLater(Stage new_stage) {
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&FakeControllerPairingController::ChangeStage,
base::Unretained(this), new_stage),
async_duration_);
}
void FakeControllerPairingController::ExecuteDiscoveryEvent(
size_t event_position) {
if (current_stage_ != STAGE_DEVICES_DISCOVERY)
return;
CHECK(event_position < discovery_scenario_.size());
const DiscoveryEvent& event = discovery_scenario_[event_position];
switch (event.first) {
case DEVICE_FOUND: {
DeviceFound(event.second);
break;
}
case DEVICE_LOST: {
DeviceLost(event.second);
break;
}
case NOTHING_FOUND: {
ChangeStage(STAGE_DEVICE_NOT_FOUND);
break;
}
}
if (++event_position == discovery_scenario_.size()) {
return;
}
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&FakeControllerPairingController::ExecuteDiscoveryEvent,
base::Unretained(this), event_position),
async_duration_);
}
void FakeControllerPairingController::DeviceFound(
const std::string& device_id) {
CHECK(current_stage_ == STAGE_DEVICES_DISCOVERY);
discovered_devices_.insert(device_id);
for (Observer& observer : observers_)
observer.DiscoveredDevicesListChanged();
}
void FakeControllerPairingController::DeviceLost(const std::string& device_id) {
CHECK(current_stage_ == STAGE_DEVICES_DISCOVERY);
discovered_devices_.erase(device_id);
for (Observer& observer : observers_)
observer.DiscoveredDevicesListChanged();
}
void FakeControllerPairingController::PairingStageChanged(Stage new_stage) {
Stage next_stage = STAGE_NONE;
switch (new_stage) {
case STAGE_DEVICES_DISCOVERY: {
discovered_devices_.clear();
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(
&FakeControllerPairingController::ExecuteDiscoveryEvent,
base::Unretained(this), 0),
async_duration_);
break;
}
case STAGE_ESTABLISHING_CONNECTION: {
if (should_fail_on_connecting_) {
next_stage = STAGE_ESTABLISHING_CONNECTION_ERROR;
should_fail_on_connecting_ = false;
} else {
confirmation_code_.clear();
next_stage = STAGE_WAITING_FOR_CODE_CONFIRMATION;
}
break;
}
case STAGE_HOST_UPDATE_IN_PROGRESS: {
next_stage = STAGE_WAITING_FOR_CREDENTIALS;
break;
}
case STAGE_HOST_ENROLLMENT_IN_PROGRESS: {
if (enrollment_should_fail_) {
enrollment_should_fail_ = false;
next_stage = STAGE_HOST_ENROLLMENT_ERROR;
} else {
next_stage = STAGE_HOST_ENROLLMENT_SUCCESS;
}
break;
}
case STAGE_HOST_CONNECTION_LOST: {
next_stage = connection_lost_end_;
connection_lost_end_ = STAGE_NONE;
break;
}
default:
break;
}
if (new_stage == connection_lost_begin_) {
connection_lost_begin_ = STAGE_NONE;
next_stage = STAGE_HOST_CONNECTION_LOST;
}
if (next_stage != STAGE_NONE)
ChangeStageLater(next_stage);
}
void FakeControllerPairingController::DiscoveredDevicesListChanged() {
}
} // namespace pairing_chromeos