blob: 292622dca0510703c8ffcfaf5b0ece1ff06ec22b [file] [log] [blame]
Vitaly Buka4615e0d2015-10-14 15:35:12 -07001// Copyright 2015 The Weave Authors. All rights reserved.
Vitaly Buka7ce499f2015-06-09 08:04:11 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Stefan Sauer2d16dfa2015-09-25 17:08:35 +02005#include "src/privet/wifi_bootstrap_manager.h"
Vitaly Buka7ce499f2015-06-09 08:04:11 -07006
7#include <base/logging.h>
8#include <base/memory/weak_ptr.h>
Vitaly Buka7b382ac2015-08-03 13:50:01 -07009#include <weave/enum_to_string.h>
Vitaly Buka1e363672015-09-25 14:01:16 -070010#include <weave/provider/network.h>
11#include <weave/provider/task_runner.h>
12#include <weave/provider/wifi.h>
Vitaly Buka7ce499f2015-06-09 08:04:11 -070013
Stefan Sauer2d16dfa2015-09-25 17:08:35 +020014#include "src/bind_lambda.h"
15#include "src/privet/constants.h"
16#include "src/config.h"
Vitaly Buka7ce499f2015-06-09 08:04:11 -070017
Vitaly Bukab6f015a2015-07-09 14:59:23 -070018namespace weave {
19namespace privet {
Vitaly Buka7ce499f2015-06-09 08:04:11 -070020
Vitaly Bukac8ba2282015-10-01 17:42:40 -070021namespace {
22
Vitaly Buka65e1f212015-11-05 15:54:05 -080023const int kMonitoringWithSsidTimeoutSeconds = 15;
Vitaly Bukaa7c7a6b2015-10-28 13:12:34 -070024const int kMonitoringTimeoutSeconds = 120;
Vitaly Buka65e1f212015-11-05 15:54:05 -080025const int kBootstrapTimeoutSeconds = 600;
26const int kConnectingTimeoutSeconds = 180;
Vitaly Bukaa7c7a6b2015-10-28 13:12:34 -070027
Vitaly Bukac8ba2282015-10-01 17:42:40 -070028const EnumToStringMap<WifiBootstrapManager::State>::Map kWifiSetupStateMap[] = {
29 {WifiBootstrapManager::State::kDisabled, "disabled"},
30 {WifiBootstrapManager::State::kBootstrapping, "waiting"},
31 {WifiBootstrapManager::State::kMonitoring, "monitoring"},
32 {WifiBootstrapManager::State::kConnecting, "connecting"},
33};
34}
35
Vitaly Buka35f317d2015-09-27 22:54:39 -070036using provider::Network;
Vitaly Buka1e363672015-09-25 14:01:16 -070037
Vitaly Buka41a90d62015-09-29 16:58:39 -070038WifiBootstrapManager::WifiBootstrapManager(Config* config,
Vitaly Bukaf86b4d52015-09-28 15:12:03 -070039 provider::TaskRunner* task_runner,
40 provider::Network* network,
41 provider::Wifi* wifi,
42 CloudDelegate* gcd)
43 : config_{config},
44 task_runner_{task_runner},
Vitaly Bukaf9630fb2015-08-12 21:15:40 -070045 network_{network},
Vitaly Bukab1041e72015-09-21 15:26:51 -070046 wifi_{wifi},
Vitaly Buka41a90d62015-09-29 16:58:39 -070047 ssid_generator_{gcd, this} {
48 CHECK(config_);
Vitaly Bukab1041e72015-09-21 15:26:51 -070049 CHECK(network_);
50 CHECK(task_runner_);
51 CHECK(wifi_);
Vitaly Buka7ce499f2015-06-09 08:04:11 -070052}
53
54void WifiBootstrapManager::Init() {
Vitaly Buka7ce499f2015-06-09 08:04:11 -070055 UpdateConnectionState();
Vitaly Buka3ab6f6e2015-09-24 13:16:16 -070056 network_->AddConnectionChangedCallback(
Vitaly Buka7ce499f2015-06-09 08:04:11 -070057 base::Bind(&WifiBootstrapManager::OnConnectivityChange,
58 lifetime_weak_factory_.GetWeakPtr()));
Vitaly Bukaf86b4d52015-09-28 15:12:03 -070059 if (config_->GetSettings().last_configured_ssid.empty()) {
Vitaly Bukaa7c7a6b2015-10-28 13:12:34 -070060 // Give implementation some time to figure out state.
Vitaly Buka65e1f212015-11-05 15:54:05 -080061 StartMonitoring(
62 base::TimeDelta::FromSeconds(kMonitoringWithSsidTimeoutSeconds));
Vitaly Buka7ce499f2015-06-09 08:04:11 -070063 } else {
Vitaly Bukaa7c7a6b2015-10-28 13:12:34 -070064 StartMonitoring(base::TimeDelta::FromSeconds(kMonitoringTimeoutSeconds));
Vitaly Buka7ce499f2015-06-09 08:04:11 -070065 }
Vitaly Buka7ce499f2015-06-09 08:04:11 -070066}
67
Vitaly Buka7ce499f2015-06-09 08:04:11 -070068void WifiBootstrapManager::StartBootstrapping() {
Vitaly Bukaefad5b22015-10-08 10:02:14 -070069 if (network_->GetConnectionState() == Network::State::kOnline) {
Vitaly Buka7ce499f2015-06-09 08:04:11 -070070 // If one of the devices we monitor for connectivity is online, we need not
71 // start an AP. For most devices, this is a situation which happens in
72 // testing when we have an ethernet connection. If you need to always
73 // start an AP to bootstrap WiFi credentials, then add your WiFi interface
74 // to the device whitelist.
Vitaly Bukaa7c7a6b2015-10-28 13:12:34 -070075 StartMonitoring(base::TimeDelta::FromSeconds(kMonitoringTimeoutSeconds));
Vitaly Buka7ce499f2015-06-09 08:04:11 -070076 return;
77 }
78
Vitaly Buka0fa51e52015-07-10 00:12:25 -070079 UpdateState(State::kBootstrapping);
Vitaly Bukaf86b4d52015-09-28 15:12:03 -070080 if (!config_->GetSettings().last_configured_ssid.empty()) {
Vitaly Buka7ce499f2015-06-09 08:04:11 -070081 // If we have been configured before, we'd like to periodically take down
82 // our AP and find out if we can connect again. Many kinds of failures are
83 // transient, and having an AP up prohibits us from connecting as a client.
Vitaly Bukaf9630fb2015-08-12 21:15:40 -070084 task_runner_->PostDelayedTask(
Vitaly Buka7ce499f2015-06-09 08:04:11 -070085 FROM_HERE, base::Bind(&WifiBootstrapManager::OnBootstrapTimeout,
86 tasks_weak_factory_.GetWeakPtr()),
Vitaly Buka65e1f212015-11-05 15:54:05 -080087 base::TimeDelta::FromSeconds(kBootstrapTimeoutSeconds));
Vitaly Buka7ce499f2015-06-09 08:04:11 -070088 }
89 // TODO(vitalybuka): Add SSID probing.
Vitaly Bukab6c8a3e2015-07-31 01:12:07 -070090 privet_ssid_ = GenerateSsid();
91 CHECK(!privet_ssid_.empty());
Vitaly Buka65e1f212015-11-05 15:54:05 -080092
93 VLOG(1) << "Starting AP with SSID: " << privet_ssid_;
Vitaly Buka1fd619a2015-09-24 11:46:05 -070094 wifi_->StartAccessPoint(privet_ssid_);
Vitaly Buka7ce499f2015-06-09 08:04:11 -070095}
96
97void WifiBootstrapManager::EndBootstrapping() {
Vitaly Buka65e1f212015-11-05 15:54:05 -080098 VLOG(1) << "Stopping AP";
Vitaly Buka1fd619a2015-09-24 11:46:05 -070099 wifi_->StopAccessPoint();
Vitaly Bukab6c8a3e2015-07-31 01:12:07 -0700100 privet_ssid_.clear();
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700101}
102
103void WifiBootstrapManager::StartConnecting(const std::string& ssid,
104 const std::string& passphrase) {
Vitaly Buka65e1f212015-11-05 15:54:05 -0800105 VLOG(1) << "Attempting connect to SSID:" << ssid;
Vitaly Buka0fa51e52015-07-10 00:12:25 -0700106 UpdateState(State::kConnecting);
Vitaly Bukaf9630fb2015-08-12 21:15:40 -0700107 task_runner_->PostDelayedTask(
Vitaly Buka74763422015-10-11 00:39:52 -0700108 FROM_HERE, base::Bind(&WifiBootstrapManager::OnConnectTimeout,
109 tasks_weak_factory_.GetWeakPtr()),
Vitaly Buka65e1f212015-11-05 15:54:05 -0800110 base::TimeDelta::FromSeconds(kConnectingTimeoutSeconds));
Vitaly Buka1fd619a2015-09-24 11:46:05 -0700111 wifi_->Connect(ssid, passphrase,
Vitaly Buka74763422015-10-11 00:39:52 -0700112 base::Bind(&WifiBootstrapManager::OnConnectDone,
113 tasks_weak_factory_.GetWeakPtr(), ssid));
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700114}
115
Vitaly Buka4ebd3292015-09-23 18:04:17 -0700116void WifiBootstrapManager::EndConnecting() {}
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700117
Vitaly Bukaa7c7a6b2015-10-28 13:12:34 -0700118void WifiBootstrapManager::StartMonitoring(const base::TimeDelta& timeout) {
Vitaly Buka65e1f212015-11-05 15:54:05 -0800119 monitor_until_ = {};
120 ContinueMonitoring(timeout);
121}
122
123void WifiBootstrapManager::ContinueMonitoring(const base::TimeDelta& timeout) {
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700124 VLOG(1) << "Monitoring connectivity.";
Vitaly Buka387b4f42015-07-30 23:55:13 -0700125 // We already have a callback in place with |network_| to update our
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700126 // connectivity state. See OnConnectivityChange().
Vitaly Buka0fa51e52015-07-10 00:12:25 -0700127 UpdateState(State::kMonitoring);
Vitaly Bukaca365fb2015-09-15 17:38:41 -0700128
Vitaly Bukaefad5b22015-10-08 10:02:14 -0700129 if (network_->GetConnectionState() == Network::State::kOnline) {
Vitaly Bukacaf42bd2015-09-16 11:23:23 -0700130 monitor_until_ = {};
Vitaly Bukaca365fb2015-09-15 17:38:41 -0700131 } else {
Vitaly Bukacaf42bd2015-09-16 11:23:23 -0700132 if (monitor_until_.is_null()) {
Vitaly Bukaa7c7a6b2015-10-28 13:12:34 -0700133 monitor_until_ = base::Time::Now() + timeout;
Vitaly Bukacaf42bd2015-09-16 11:23:23 -0700134 VLOG(2) << "Waiting for connection until: " << monitor_until_;
135 }
Vitaly Bukaca365fb2015-09-15 17:38:41 -0700136
137 // Schedule timeout timer taking into account already offline time.
138 task_runner_->PostDelayedTask(
139 FROM_HERE, base::Bind(&WifiBootstrapManager::OnMonitorTimeout,
140 tasks_weak_factory_.GetWeakPtr()),
141 monitor_until_ - base::Time::Now());
142 }
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700143}
144
Vitaly Buka4ebd3292015-09-23 18:04:17 -0700145void WifiBootstrapManager::EndMonitoring() {}
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700146
147void WifiBootstrapManager::UpdateState(State new_state) {
Vitaly Buka65e1f212015-11-05 15:54:05 -0800148 VLOG(3) << "Switching state from " << EnumToString(state_) << " to "
149 << EnumToString(new_state);
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700150 // Abort irrelevant tasks.
151 tasks_weak_factory_.InvalidateWeakPtrs();
152
153 switch (state_) {
Vitaly Buka0fa51e52015-07-10 00:12:25 -0700154 case State::kDisabled:
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700155 break;
Vitaly Buka0fa51e52015-07-10 00:12:25 -0700156 case State::kBootstrapping:
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700157 EndBootstrapping();
158 break;
Vitaly Buka0fa51e52015-07-10 00:12:25 -0700159 case State::kMonitoring:
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700160 EndMonitoring();
161 break;
Vitaly Buka0fa51e52015-07-10 00:12:25 -0700162 case State::kConnecting:
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700163 EndConnecting();
164 break;
165 }
166
Vitaly Bukaf86b4d52015-09-28 15:12:03 -0700167 state_ = new_state;
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700168}
169
Vitaly Buka557d4522015-07-18 20:43:58 -0700170std::string WifiBootstrapManager::GenerateSsid() const {
Vitaly Buka41a90d62015-09-29 16:58:39 -0700171 const std::string& ssid = config_->GetSettings().test_privet_ssid;
172 return ssid.empty() ? ssid_generator_.GenerateSsid() : ssid;
Vitaly Buka557d4522015-07-18 20:43:58 -0700173}
174
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700175const ConnectionState& WifiBootstrapManager::GetConnectionState() const {
176 return connection_state_;
177}
178
179const SetupState& WifiBootstrapManager::GetSetupState() const {
180 return setup_state_;
181}
182
183bool WifiBootstrapManager::ConfigureCredentials(const std::string& ssid,
184 const std::string& passphrase,
Vitaly Buka0801a1f2015-08-14 10:03:46 -0700185 ErrorPtr* error) {
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700186 setup_state_ = SetupState{SetupState::kInProgress};
Alex Vakulenko464f93e2015-09-11 15:32:39 -0700187 // Since we are changing network, we need to let the web server send out the
188 // response to the HTTP request leading to this action. So, we are waiting
189 // a bit before mocking with network set up.
Vitaly Bukaf9630fb2015-08-12 21:15:40 -0700190 task_runner_->PostDelayedTask(
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700191 FROM_HERE, base::Bind(&WifiBootstrapManager::StartConnecting,
192 tasks_weak_factory_.GetWeakPtr(), ssid, passphrase),
Alex Vakulenko464f93e2015-09-11 15:32:39 -0700193 base::TimeDelta::FromSeconds(1));
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700194 return true;
195}
196
197std::string WifiBootstrapManager::GetCurrentlyConnectedSsid() const {
198 // TODO(vitalybuka): Get from shill, if possible.
Vitaly Bukaf86b4d52015-09-28 15:12:03 -0700199 return config_->GetSettings().last_configured_ssid;
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700200}
201
202std::string WifiBootstrapManager::GetHostedSsid() const {
Vitaly Bukab6c8a3e2015-07-31 01:12:07 -0700203 return privet_ssid_;
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700204}
205
206std::set<WifiType> WifiBootstrapManager::GetTypes() const {
207 // TODO(wiley) This should do some system work to figure this out.
208 return {WifiType::kWifi24};
209}
210
Vitaly Buka74763422015-10-11 00:39:52 -0700211void WifiBootstrapManager::OnConnectDone(const std::string& ssid,
212 ErrorPtr error) {
213 if (error) {
214 Error::AddTo(&error, FROM_HERE, errors::kDomain, errors::kInvalidState,
215 "Failed to connect to provided network");
216 setup_state_ = SetupState{std::move(error)};
217 return StartBootstrapping();
218 }
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700219 VLOG(1) << "Wifi was connected successfully";
Vitaly Bukaf86b4d52015-09-28 15:12:03 -0700220 Config::Transaction change{config_};
221 change.set_last_configured_ssid(ssid);
222 change.Commit();
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700223 setup_state_ = SetupState{SetupState::kSuccess};
Vitaly Bukaa7c7a6b2015-10-28 13:12:34 -0700224 StartMonitoring(base::TimeDelta::FromSeconds(kMonitoringTimeoutSeconds));
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700225}
226
Vitaly Buka74763422015-10-11 00:39:52 -0700227void WifiBootstrapManager::OnConnectTimeout() {
228 ErrorPtr error;
229 Error::AddTo(&error, FROM_HERE, errors::kDomain, errors::kInvalidState,
230 "Timeout connecting to provided network");
231 setup_state_ = SetupState{std::move(error)};
232 return StartBootstrapping();
233}
234
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700235void WifiBootstrapManager::OnBootstrapTimeout() {
236 VLOG(1) << "Bootstrapping has timed out.";
Vitaly Bukaa7c7a6b2015-10-28 13:12:34 -0700237 StartMonitoring(base::TimeDelta::FromSeconds(kMonitoringTimeoutSeconds));
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700238}
239
Vitaly Bukabf6840a2015-09-21 13:38:56 -0700240void WifiBootstrapManager::OnConnectivityChange() {
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700241 UpdateConnectionState();
242
Vitaly Buka65e1f212015-11-05 15:54:05 -0800243 if (state_ == State::kMonitoring ||
Vitaly Buka7e1d8b22015-09-21 15:08:09 -0700244 (state_ != State::kDisabled &&
Vitaly Bukaefad5b22015-10-08 10:02:14 -0700245 network_->GetConnectionState() == Network::State::kOnline)) {
Vitaly Buka65e1f212015-11-05 15:54:05 -0800246 ContinueMonitoring(base::TimeDelta::FromSeconds(kMonitoringTimeoutSeconds));
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700247 }
248}
249
250void WifiBootstrapManager::OnMonitorTimeout() {
Vitaly Buka65e1f212015-11-05 15:54:05 -0800251 VLOG(1) << "Spent too long offline. Entering bootstrap mode.";
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700252 // TODO(wiley) Retrieve relevant errors from shill.
253 StartBootstrapping();
254}
255
256void WifiBootstrapManager::UpdateConnectionState() {
257 connection_state_ = ConnectionState{ConnectionState::kUnconfigured};
Vitaly Buka35f317d2015-09-27 22:54:39 -0700258
259 Network::State service_state{network_->GetConnectionState()};
Vitaly Buka65e1f212015-11-05 15:54:05 -0800260 VLOG(3) << "New network state: " << EnumToString(service_state);
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700261 switch (service_state) {
Vitaly Buka35f317d2015-09-27 22:54:39 -0700262 case Network::State::kOffline:
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700263 connection_state_ = ConnectionState{ConnectionState::kOffline};
264 return;
Vitaly Bukaefad5b22015-10-08 10:02:14 -0700265 case Network::State::kError: {
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700266 // TODO(wiley) Pull error information from somewhere.
Vitaly Buka0801a1f2015-08-14 10:03:46 -0700267 ErrorPtr error;
268 Error::AddTo(&error, FROM_HERE, errors::kDomain, errors::kInvalidState,
269 "Unknown WiFi error");
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700270 connection_state_ = ConnectionState{std::move(error)};
271 return;
272 }
Vitaly Buka35f317d2015-09-27 22:54:39 -0700273 case Network::State::kConnecting:
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700274 connection_state_ = ConnectionState{ConnectionState::kConnecting};
275 return;
Vitaly Bukaefad5b22015-10-08 10:02:14 -0700276 case Network::State::kOnline:
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700277 connection_state_ = ConnectionState{ConnectionState::kOnline};
278 return;
279 }
Vitaly Buka0801a1f2015-08-14 10:03:46 -0700280 ErrorPtr error;
281 Error::AddToPrintf(&error, FROM_HERE, errors::kDomain, errors::kInvalidState,
282 "Unknown network state: %s",
283 EnumToString(service_state).c_str());
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700284 connection_state_ = ConnectionState{std::move(error)};
285}
286
Vitaly Bukab6f015a2015-07-09 14:59:23 -0700287} // namespace privet
Vitaly Bukac8ba2282015-10-01 17:42:40 -0700288
289template <>
290LIBWEAVE_EXPORT
291EnumToStringMap<privet::WifiBootstrapManager::State>::EnumToStringMap()
292 : EnumToStringMap(privet::kWifiSetupStateMap) {}
293
Vitaly Bukab6f015a2015-07-09 14:59:23 -0700294} // namespace weave