Home | History | Annotate | Download | only in pairing
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chromeos/pairing/fake_controller_pairing_flow.h"
      6 
      7 #include <map>
      8 
      9 #include "base/bind.h"
     10 #include "base/logging.h"
     11 #include "base/message_loop/message_loop.h"
     12 #include "base/rand_util.h"
     13 #include "base/strings/string_number_conversions.h"
     14 #include "base/strings/string_split.h"
     15 #include "base/strings/string_util.h"
     16 
     17 namespace chromeos {
     18 
     19 FakeControllerPairingFlow::FakeControllerPairingFlow(const std::string& config)
     20     : current_stage_(STAGE_NONE),
     21       should_fail_on_connecting_(false),
     22       connection_lost_begin_(STAGE_NONE),
     23       connection_lost_end_(STAGE_NONE),
     24       enrollment_should_fail_(false) {
     25   ApplyConfig(config);
     26   AddObserver(this);
     27 }
     28 
     29 FakeControllerPairingFlow::~FakeControllerPairingFlow() {
     30   RemoveObserver(this);
     31 }
     32 
     33 void FakeControllerPairingFlow::ApplyConfig(const std::string& config) {
     34   typedef std::vector<std::string> Tokens;
     35 
     36   base::StringPairs kv_pairs;
     37   CHECK(base::SplitStringIntoKeyValuePairs(config, ':', ',', &kv_pairs))
     38       << "Wrong config format.";
     39   std::map<std::string, std::string> dict(kv_pairs.begin(), kv_pairs.end());
     40 
     41   if (dict.count("async_duration")) {
     42     int ms = 0;
     43     CHECK(base::StringToInt(dict["async_duration"], &ms))
     44         << "Wrong 'async_duration' format.";
     45     async_duration_ = base::TimeDelta::FromMilliseconds(ms);
     46   } else {
     47     async_duration_ = base::TimeDelta::FromMilliseconds(3000);
     48   }
     49 
     50   should_fail_on_connecting_ =
     51       dict.count("fail_connecting") && (dict["fail_connecting"] == "1");
     52 
     53   enrollment_should_fail_ =
     54       dict.count("fail_enrollment") && (dict["fail_enrollment"] == "1");
     55 
     56   if (dict.count("connection_lost")) {
     57     Tokens lost_begin_end;
     58     CHECK(Tokenize(dict["connection_lost"], "-", &lost_begin_end) == 2)
     59         << "Wrong 'connection_lost' format.";
     60     int begin = 0;
     61     int end = 0;
     62     CHECK(base::StringToInt(lost_begin_end[0], &begin) &&
     63           base::StringToInt(lost_begin_end[1], &end))
     64         << "Wrong 'connection_lost' format.";
     65     CHECK((begin == 0 && end == 0) ||
     66           (STAGE_WAITING_FOR_CODE_CONFIRMATION <= begin && begin <= end &&
     67            end <= STAGE_HOST_ENROLLMENT_ERROR))
     68         << "Wrong 'connection_lost' interval.";
     69     connection_lost_begin_ = static_cast<Stage>(begin);
     70     connection_lost_end_ = static_cast<Stage>(end);
     71   } else {
     72     connection_lost_begin_ = connection_lost_end_ = STAGE_NONE;
     73   }
     74 
     75   if (!dict.count("discovery")) {
     76     dict["discovery"] =
     77         "F-Device_1~F-Device_5~F-Device_3~L-Device_3~L-Device_1~F-Device_1";
     78   }
     79   base::StringPairs events;
     80   CHECK(
     81       base::SplitStringIntoKeyValuePairs(dict["discovery"], '-', '~', &events))
     82       << "Wrong 'discovery' format.";
     83   DiscoveryScenario scenario;
     84   for (base::StringPairs::const_iterator event = events.begin();
     85        event != events.end();
     86        ++event) {
     87     std::string type = event->first;
     88     std::string device_id = event->second;
     89     CHECK(type == "F" || type == "L" || type == "N")
     90         << "Wrong discovery event type.";
     91     CHECK(!device_id.empty() || type == "N") << "Empty device ID.";
     92     scenario.push_back(DiscoveryEvent(
     93         type == "F" ? DEVICE_FOUND : type == "L" ? DEVICE_LOST : NOTHING_FOUND,
     94         device_id));
     95   }
     96   SetDiscoveryScenario(scenario);
     97 
     98   preset_confirmation_code_ = dict["code"];
     99   CHECK(preset_confirmation_code_.empty() ||
    100         (preset_confirmation_code_.length() == 6 &&
    101          preset_confirmation_code_.find_first_not_of("0123456789") ==
    102              std::string::npos))
    103       << "Wrong 'code' format.";
    104 }
    105 
    106 void FakeControllerPairingFlow::SetShouldFailOnConnecting() {
    107   should_fail_on_connecting_ = true;
    108 }
    109 
    110 void FakeControllerPairingFlow::SetShouldLoseConnection(Stage stage_begin,
    111                                                         Stage stage_end) {
    112   connection_lost_begin_ = stage_begin;
    113   connection_lost_end_ = stage_end;
    114 }
    115 
    116 void FakeControllerPairingFlow::SetEnrollmentShouldFail() {
    117   enrollment_should_fail_ = true;
    118 }
    119 
    120 void FakeControllerPairingFlow::SetDiscoveryScenario(
    121     const DiscoveryScenario& discovery_scenario) {
    122   discovery_scenario_ = discovery_scenario;
    123   // Check that scenario is valid.
    124   std::set<std::string> devices;
    125   for (DiscoveryScenario::const_iterator event = discovery_scenario_.begin();
    126        event != discovery_scenario_.end();
    127        ++event) {
    128     switch (event->first) {
    129       case DEVICE_FOUND: {
    130         devices.insert(event->second);
    131         break;
    132       }
    133       case DEVICE_LOST: {
    134         CHECK(devices.count(event->second));
    135         devices.erase(event->second);
    136         break;
    137       }
    138       case NOTHING_FOUND: {
    139         CHECK(++event == discovery_scenario_.end());
    140         return;
    141       }
    142     }
    143   }
    144 }
    145 
    146 void FakeControllerPairingFlow::AddObserver(Observer* observer) {
    147   observers_.AddObserver(observer);
    148 }
    149 
    150 void FakeControllerPairingFlow::RemoveObserver(Observer* observer) {
    151   observers_.RemoveObserver(observer);
    152 }
    153 
    154 ControllerPairingFlow::Stage FakeControllerPairingFlow::GetCurrentStage() {
    155   return current_stage_;
    156 }
    157 
    158 void FakeControllerPairingFlow::StartFlow() {
    159   CHECK(current_stage_ == STAGE_NONE);
    160   ChangeStage(STAGE_DEVICES_DISCOVERY);
    161 }
    162 
    163 ControllerPairingFlow::DeviceIdList
    164 FakeControllerPairingFlow::GetDiscoveredDevices() {
    165   CHECK(current_stage_ == STAGE_DEVICES_DISCOVERY);
    166   return DeviceIdList(discovered_devices_.begin(), discovered_devices_.end());
    167 }
    168 
    169 void FakeControllerPairingFlow::ChooseDeviceForPairing(
    170     const std::string& device_id) {
    171   CHECK(current_stage_ == STAGE_DEVICES_DISCOVERY);
    172   CHECK(discovered_devices_.count(device_id));
    173   choosen_device_ = device_id;
    174   ChangeStage(STAGE_ESTABLISHING_CONNECTION);
    175 }
    176 
    177 void FakeControllerPairingFlow::RepeatDiscovery() {
    178   CHECK(current_stage_ == STAGE_DEVICE_NOT_FOUND ||
    179         current_stage_ == STAGE_ESTABLISHING_CONNECTION_ERROR ||
    180         current_stage_ == STAGE_HOST_ENROLLMENT_ERROR);
    181   ChangeStage(STAGE_DEVICES_DISCOVERY);
    182 }
    183 
    184 std::string FakeControllerPairingFlow::GetConfirmationCode() {
    185   CHECK(current_stage_ == STAGE_WAITING_FOR_CODE_CONFIRMATION);
    186   if (confirmation_code_.empty()) {
    187     if (preset_confirmation_code_.empty()) {
    188       for (int i = 0; i < 6; ++i)
    189         confirmation_code_.push_back(base::RandInt('0', '9'));
    190     } else {
    191       confirmation_code_ = preset_confirmation_code_;
    192     }
    193   }
    194   return confirmation_code_;
    195 }
    196 
    197 void FakeControllerPairingFlow::SetConfirmationCodeIsCorrect(bool correct) {
    198   CHECK(current_stage_ == STAGE_WAITING_FOR_CODE_CONFIRMATION);
    199   if (correct)
    200     ChangeStage(STAGE_HOST_UPDATE_IN_PROGRESS);
    201   else
    202     ChangeStage(STAGE_DEVICES_DISCOVERY);
    203 }
    204 
    205 void FakeControllerPairingFlow::OnAuthenticationDone(
    206     const chromeos::UserContext& user_context,
    207     content::BrowserContext* browser_context) {
    208   CHECK(current_stage_ == STAGE_WAITING_FOR_CREDENTIALS);
    209   ChangeStage(STAGE_HOST_ENROLLMENT_IN_PROGRESS);
    210 }
    211 
    212 void FakeControllerPairingFlow::StartSession() {
    213   CHECK(current_stage_ == STAGE_PAIRING_DONE);
    214   ChangeStage(STAGE_FINISHED);
    215 }
    216 
    217 void FakeControllerPairingFlow::ChangeStage(Stage new_stage) {
    218   if (current_stage_ == new_stage)
    219     return;
    220   current_stage_ = new_stage;
    221   FOR_EACH_OBSERVER(Observer, observers_, PairingStageChanged(new_stage));
    222 }
    223 
    224 void FakeControllerPairingFlow::ChangeStageLater(Stage new_stage) {
    225   base::MessageLoop::current()->PostDelayedTask(
    226       FROM_HERE,
    227       base::Bind(&FakeControllerPairingFlow::ChangeStage,
    228                  base::Unretained(this),
    229                  new_stage),
    230       async_duration_);
    231 }
    232 
    233 void FakeControllerPairingFlow::ExecuteDiscoveryEvent(size_t event_position) {
    234   if (current_stage_ != STAGE_DEVICES_DISCOVERY)
    235     return;
    236   CHECK(event_position < discovery_scenario_.size());
    237   const DiscoveryEvent& event = discovery_scenario_[event_position];
    238   switch (event.first) {
    239     case DEVICE_FOUND: {
    240       DeviceFound(event.second);
    241       break;
    242     }
    243     case DEVICE_LOST: {
    244       DeviceLost(event.second);
    245       break;
    246     }
    247     case NOTHING_FOUND: {
    248       ChangeStage(STAGE_DEVICE_NOT_FOUND);
    249       break;
    250     }
    251   }
    252   if (++event_position == discovery_scenario_.size()) {
    253     return;
    254   }
    255   base::MessageLoop::current()->PostDelayedTask(
    256       FROM_HERE,
    257       base::Bind(&FakeControllerPairingFlow::ExecuteDiscoveryEvent,
    258                  base::Unretained(this),
    259                  event_position),
    260       async_duration_);
    261 }
    262 
    263 void FakeControllerPairingFlow::DeviceFound(const std::string& device_id) {
    264   CHECK(current_stage_ == STAGE_DEVICES_DISCOVERY);
    265   discovered_devices_.insert(device_id);
    266   FOR_EACH_OBSERVER(Observer, observers_, DiscoveredDevicesListChanged());
    267 }
    268 
    269 void FakeControllerPairingFlow::DeviceLost(const std::string& device_id) {
    270   CHECK(current_stage_ == STAGE_DEVICES_DISCOVERY);
    271   discovered_devices_.erase(device_id);
    272   FOR_EACH_OBSERVER(Observer, observers_, DiscoveredDevicesListChanged());
    273 }
    274 
    275 void FakeControllerPairingFlow::PairingStageChanged(Stage new_stage) {
    276   Stage next_stage = STAGE_NONE;
    277   switch (new_stage) {
    278     case STAGE_DEVICES_DISCOVERY: {
    279       discovered_devices_.clear();
    280       base::MessageLoop::current()->PostDelayedTask(
    281           FROM_HERE,
    282           base::Bind(&FakeControllerPairingFlow::ExecuteDiscoveryEvent,
    283                      base::Unretained(this),
    284                      0),
    285           async_duration_);
    286       break;
    287     }
    288     case STAGE_ESTABLISHING_CONNECTION: {
    289       if (should_fail_on_connecting_) {
    290         next_stage = STAGE_ESTABLISHING_CONNECTION_ERROR;
    291         should_fail_on_connecting_ = false;
    292       } else {
    293         confirmation_code_.clear();
    294         next_stage = STAGE_WAITING_FOR_CODE_CONFIRMATION;
    295       }
    296       break;
    297     }
    298     case STAGE_HOST_UPDATE_IN_PROGRESS: {
    299       next_stage = STAGE_WAITING_FOR_CREDENTIALS;
    300       break;
    301     }
    302     case STAGE_HOST_ENROLLMENT_IN_PROGRESS: {
    303       if (enrollment_should_fail_) {
    304         enrollment_should_fail_ = false;
    305         next_stage = STAGE_HOST_ENROLLMENT_ERROR;
    306       } else {
    307         next_stage = STAGE_PAIRING_DONE;
    308       }
    309       break;
    310     }
    311     case STAGE_HOST_CONNECTION_LOST: {
    312       next_stage = connection_lost_end_;
    313       connection_lost_end_ = STAGE_NONE;
    314       break;
    315     }
    316     default:
    317       break;
    318   }
    319   if (new_stage == connection_lost_begin_) {
    320     connection_lost_begin_ = STAGE_NONE;
    321     next_stage = STAGE_HOST_CONNECTION_LOST;
    322   }
    323   if (next_stage != STAGE_NONE)
    324     ChangeStageLater(next_stage);
    325 }
    326 
    327 void FakeControllerPairingFlow::DiscoveredDevicesListChanged() {
    328 }
    329 
    330 }  // namespace chromeos
    331