blob: add3036c94802d25da4dbbd15c583b71eb6326c0 [file] [log] [blame]
Vitaly Buka58a288b2015-07-31 00:33:31 -07001// Copyright 2014 The Chromium OS 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 "buffet/shill_client.h"
6
7#include <set>
8
9#include <base/message_loop/message_loop.h>
10#include <base/stl_util.h>
Alex Vakulenko41705852015-10-13 10:12:06 -070011#include <brillo/any.h>
12#include <brillo/errors/error.h>
Alex Vakulenko94f8eba2015-10-14 08:52:45 -070013#include <brillo/variant_dictionary.h>
Peter Qiu786a9062015-10-02 11:45:01 -070014#include <dbus/shill/dbus-constants.h>
Vitaly Bukae2713ac2015-08-03 13:50:01 -070015#include <weave/enum_to_string.h>
Vitaly Buka58a288b2015-07-31 00:33:31 -070016
Vitaly Buka3ef4fff2015-07-31 01:12:07 -070017#include "buffet/ap_manager_client.h"
Vitaly Buka493f6042015-08-12 16:17:16 -070018#include "buffet/socket_stream.h"
Vitaly Buka4f771532015-08-14 14:58:39 -070019#include "buffet/weave_error_conversion.h"
Vitaly Buka58a288b2015-07-31 00:33:31 -070020
Alex Vakulenko41705852015-10-13 10:12:06 -070021using brillo::Any;
22using brillo::VariantDictionary;
Vitaly Buka58a288b2015-07-31 00:33:31 -070023using dbus::ObjectPath;
24using org::chromium::flimflam::DeviceProxy;
25using org::chromium::flimflam::ServiceProxy;
26using std::map;
27using std::set;
28using std::string;
29using std::vector;
Alex Vakulenkoe32375b2015-09-28 08:55:40 -070030using weave::EnumToString;
31using weave::provider::Network;
Vitaly Buka58a288b2015-07-31 00:33:31 -070032
33namespace buffet {
34
35namespace {
36
Alex Vakulenkoe32375b2015-09-28 08:55:40 -070037const char kErrorDomain[] = "buffet";
38
Vitaly Buka58a288b2015-07-31 00:33:31 -070039void IgnoreDetachEvent() {}
40
41bool GetStateForService(ServiceProxy* service, string* state) {
42 CHECK(service) << "|service| was nullptr in GetStateForService()";
43 VariantDictionary properties;
44 if (!service->GetProperties(&properties, nullptr)) {
45 LOG(WARNING) << "Failed to read properties from service.";
46 return false;
47 }
48 auto property_it = properties.find(shill::kStateProperty);
49 if (property_it == properties.end()) {
50 LOG(WARNING) << "No state found in service properties.";
51 return false;
52 }
53 string new_state = property_it->second.TryGet<string>();
54 if (new_state.empty()) {
55 LOG(WARNING) << "Invalid state value.";
56 return false;
57 }
58 *state = new_state;
59 return true;
60}
61
Alex Vakulenkoe32375b2015-09-28 08:55:40 -070062Network::State ShillServiceStateToNetworkState(const string& state) {
Vitaly Buka58a288b2015-07-31 00:33:31 -070063 // TODO(wiley) What does "unconfigured" mean in a world with multiple sets
64 // of WiFi credentials?
65 // TODO(wiley) Detect disabled devices, update state appropriately.
66 if ((state.compare(shill::kStateReady) == 0) ||
67 (state.compare(shill::kStatePortal) == 0) ||
68 (state.compare(shill::kStateOnline) == 0)) {
Alex Vakulenkobe39e932015-10-09 08:10:36 -070069 return Network::State::kOnline;
Vitaly Buka58a288b2015-07-31 00:33:31 -070070 }
71 if ((state.compare(shill::kStateAssociation) == 0) ||
72 (state.compare(shill::kStateConfiguration) == 0)) {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -070073 return Network::State::kConnecting;
Vitaly Buka58a288b2015-07-31 00:33:31 -070074 }
75 if ((state.compare(shill::kStateFailure) == 0) ||
76 (state.compare(shill::kStateActivationFailure) == 0)) {
77 // TODO(wiley) Get error information off the service object.
Alex Vakulenkobe39e932015-10-09 08:10:36 -070078 return Network::State::kError;
Vitaly Buka58a288b2015-07-31 00:33:31 -070079 }
80 if ((state.compare(shill::kStateIdle) == 0) ||
81 (state.compare(shill::kStateOffline) == 0) ||
82 (state.compare(shill::kStateDisconnect) == 0)) {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -070083 return Network::State::kOffline;
Vitaly Buka58a288b2015-07-31 00:33:31 -070084 }
85 LOG(WARNING) << "Unknown state found: '" << state << "'";
Alex Vakulenkoe32375b2015-09-28 08:55:40 -070086 return Network::State::kOffline;
Vitaly Buka58a288b2015-07-31 00:33:31 -070087}
88
89} // namespace
90
91ShillClient::ShillClient(const scoped_refptr<dbus::Bus>& bus,
Alex Vakulenko0022b752015-10-02 11:09:59 -070092 const set<string>& device_whitelist,
93 bool disable_xmpp)
Vitaly Buka58a288b2015-07-31 00:33:31 -070094 : bus_{bus},
Alex Vakulenkoe32375b2015-09-28 08:55:40 -070095 manager_proxy_{bus_},
Vitaly Buka3ef4fff2015-07-31 01:12:07 -070096 device_whitelist_{device_whitelist},
Alex Vakulenko0022b752015-10-02 11:09:59 -070097 disable_xmpp_{disable_xmpp},
Vitaly Buka3ef4fff2015-07-31 01:12:07 -070098 ap_manager_client_{new ApManagerClient(bus)} {
Vitaly Buka58a288b2015-07-31 00:33:31 -070099 manager_proxy_.RegisterPropertyChangedSignalHandler(
100 base::Bind(&ShillClient::OnManagerPropertyChange,
101 weak_factory_.GetWeakPtr()),
102 base::Bind(&ShillClient::OnManagerPropertyChangeRegistration,
103 weak_factory_.GetWeakPtr()));
104 auto owner_changed_cb = base::Bind(&ShillClient::OnShillServiceOwnerChange,
105 weak_factory_.GetWeakPtr());
106 bus_->GetObjectProxy(shill::kFlimflamServiceName, ObjectPath{"/"})
107 ->SetNameOwnerChangedCallback(owner_changed_cb);
108}
109
Vitaly Buka3ef4fff2015-07-31 01:12:07 -0700110ShillClient::~ShillClient() {}
111
Vitaly Buka58a288b2015-07-31 00:33:31 -0700112void ShillClient::Init() {
113 VLOG(2) << "ShillClient::Init();";
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700114 CleanupConnectingService();
Vitaly Buka58a288b2015-07-31 00:33:31 -0700115 devices_.clear();
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700116 connectivity_state_ = Network::State::kOffline;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700117 VariantDictionary properties;
118 if (!manager_proxy_.GetProperties(&properties, nullptr)) {
119 LOG(ERROR) << "Unable to get properties from Manager, waiting for "
120 "Manager to come back online.";
121 return;
122 }
123 auto it = properties.find(shill::kDevicesProperty);
124 CHECK(it != properties.end()) << "shill should always publish a device list.";
125 OnManagerPropertyChange(shill::kDevicesProperty, it->second);
126}
127
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700128void ShillClient::Connect(const string& ssid,
129 const string& passphrase,
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700130 const weave::DoneCallback& callback) {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700131 if (connecting_service_) {
132 weave::ErrorPtr error;
133 weave::Error::AddTo(&error, FROM_HERE, kErrorDomain, "busy",
134 "Already connecting to WiFi network");
135 base::MessageLoop::current()->PostTask(
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700136 FROM_HERE, base::Bind(callback, base::Passed(&error)));
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700137 return;
Vitaly Buka4f771532015-08-14 14:58:39 -0700138 }
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700139 CleanupConnectingService();
Vitaly Buka58a288b2015-07-31 00:33:31 -0700140 VariantDictionary service_properties;
141 service_properties[shill::kTypeProperty] = Any{string{shill::kTypeWifi}};
142 service_properties[shill::kSSIDProperty] = Any{ssid};
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700143 if (passphrase.empty()) {
144 service_properties[shill::kSecurityProperty] = Any{shill::kSecurityNone};
145 } else {
146 service_properties[shill::kPassphraseProperty] = Any{passphrase};
147 service_properties[shill::kSecurityProperty] = Any{shill::kSecurityPsk};
148 }
Vitaly Buka58a288b2015-07-31 00:33:31 -0700149 service_properties[shill::kSaveCredentialsProperty] = Any{true};
150 service_properties[shill::kAutoConnectProperty] = Any{true};
151 ObjectPath service_path;
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700152 brillo::ErrorPtr brillo_error;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700153 if (!manager_proxy_.ConfigureService(service_properties, &service_path,
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700154 &brillo_error) ||
155 !manager_proxy_.RequestScan(shill::kTypeWifi, &brillo_error)) {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700156 weave::ErrorPtr weave_error;
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700157 ConvertError(*brillo_error, &weave_error);
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700158 base::MessageLoop::current()->PostTask(
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700159 FROM_HERE, base::Bind(callback, base::Passed(&weave_error)));
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700160 return;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700161 }
162 connecting_service_.reset(new ServiceProxy{bus_, service_path});
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700163 connecting_service_->Connect(nullptr);
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700164 connect_done_callback_ = callback;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700165 connecting_service_->RegisterPropertyChangedSignalHandler(
166 base::Bind(&ShillClient::OnServicePropertyChange,
167 weak_factory_.GetWeakPtr(), service_path),
168 base::Bind(&ShillClient::OnServicePropertyChangeRegistration,
169 weak_factory_.GetWeakPtr(), service_path));
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700170 base::MessageLoop::current()->PostDelayedTask(
171 FROM_HERE, base::Bind(&ShillClient::ConnectToServiceError,
172 weak_factory_.GetWeakPtr(), connecting_service_),
173 base::TimeDelta::FromMinutes(1));
Vitaly Buka58a288b2015-07-31 00:33:31 -0700174}
175
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700176void ShillClient::ConnectToServiceError(
177 std::shared_ptr<org::chromium::flimflam::ServiceProxy> connecting_service) {
178 if (connecting_service != connecting_service_ ||
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700179 connect_done_callback_.is_null()) {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700180 return;
181 }
182 std::string error = have_called_connect_ ? connecting_service_error_
183 : shill::kErrorOutOfRange;
184 if (error.empty())
185 error = shill::kErrorInternal;
186 OnErrorChangeForConnectingService(error);
187}
188
189Network::State ShillClient::GetConnectionState() const {
Vitaly Buka58a288b2015-07-31 00:33:31 -0700190 return connectivity_state_;
191}
192
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700193void ShillClient::StartAccessPoint(const std::string& ssid) {
Vitaly Buka3ef4fff2015-07-31 01:12:07 -0700194 ap_manager_client_->Start(ssid);
195}
196
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700197void ShillClient::StopAccessPoint() {
Vitaly Buka3ef4fff2015-07-31 01:12:07 -0700198 ap_manager_client_->Stop();
199}
200
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700201void ShillClient::AddConnectionChangedCallback(
202 const ConnectionChangedCallback& listener) {
Vitaly Buka58a288b2015-07-31 00:33:31 -0700203 connectivity_listeners_.push_back(listener);
204}
205
206bool ShillClient::IsMonitoredDevice(DeviceProxy* device) {
207 if (device_whitelist_.empty()) {
208 return true;
209 }
210 VariantDictionary device_properties;
211 if (!device->GetProperties(&device_properties, nullptr)) {
212 LOG(ERROR) << "Devices without properties aren't whitelisted.";
213 return false;
214 }
215 auto it = device_properties.find(shill::kInterfaceProperty);
216 if (it == device_properties.end()) {
217 LOG(ERROR) << "Failed to find interface property in device properties.";
218 return false;
219 }
220 return ContainsKey(device_whitelist_, it->second.TryGet<string>());
221}
222
223void ShillClient::OnShillServiceOwnerChange(const string& old_owner,
224 const string& new_owner) {
225 VLOG(1) << "Shill service owner name changed to '" << new_owner << "'";
226 if (new_owner.empty()) {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700227 CleanupConnectingService();
Vitaly Buka58a288b2015-07-31 00:33:31 -0700228 devices_.clear();
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700229 connectivity_state_ = Network::State::kOffline;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700230 } else {
231 Init(); // New service owner means shill reset!
232 }
233}
234
235void ShillClient::OnManagerPropertyChangeRegistration(const string& interface,
236 const string& signal_name,
237 bool success) {
238 VLOG(3) << "Registered ManagerPropertyChange handler.";
239 CHECK(success) << "privetd requires Manager signals.";
240 VariantDictionary properties;
241 if (!manager_proxy_.GetProperties(&properties, nullptr)) {
242 LOG(ERROR) << "Unable to get properties from Manager, waiting for "
243 "Manager to come back online.";
244 return;
245 }
246 auto it = properties.find(shill::kDevicesProperty);
247 CHECK(it != properties.end()) << "Shill should always publish a device list.";
248 OnManagerPropertyChange(shill::kDevicesProperty, it->second);
249}
250
251void ShillClient::OnManagerPropertyChange(const string& property_name,
252 const Any& property_value) {
253 if (property_name != shill::kDevicesProperty) {
254 return;
255 }
256 VLOG(3) << "Manager's device list has changed.";
257 // We're going to remove every device we haven't seen in the update.
258 set<ObjectPath> device_paths_to_remove;
259 for (const auto& kv : devices_) {
260 device_paths_to_remove.insert(kv.first);
261 }
262 for (const auto& device_path : property_value.TryGet<vector<ObjectPath>>()) {
263 if (!device_path.IsValid()) {
264 LOG(ERROR) << "Ignoring invalid device path in Manager's device list.";
265 return;
266 }
267 auto it = devices_.find(device_path);
268 if (it != devices_.end()) {
269 // Found an existing proxy. Since the whitelist never changes,
270 // this still a valid device.
271 device_paths_to_remove.erase(device_path);
272 continue;
273 }
274 std::unique_ptr<DeviceProxy> device{new DeviceProxy{bus_, device_path}};
275 if (!IsMonitoredDevice(device.get())) {
276 continue;
277 }
278 device->RegisterPropertyChangedSignalHandler(
279 base::Bind(&ShillClient::OnDevicePropertyChange,
280 weak_factory_.GetWeakPtr(), device_path),
281 base::Bind(&ShillClient::OnDevicePropertyChangeRegistration,
282 weak_factory_.GetWeakPtr(), device_path));
283 VLOG(3) << "Creating device proxy at " << device_path.value();
284 devices_[device_path].device = std::move(device);
285 }
286 // Clean up devices/services related to removed devices.
287 if (!device_paths_to_remove.empty()) {
288 for (const ObjectPath& device_path : device_paths_to_remove) {
289 devices_.erase(device_path);
290 }
291 UpdateConnectivityState();
292 }
293}
294
295void ShillClient::OnDevicePropertyChangeRegistration(
296 const ObjectPath& device_path,
297 const string& interface,
298 const string& signal_name,
299 bool success) {
300 VLOG(3) << "Registered DevicePropertyChange handler.";
301 auto it = devices_.find(device_path);
302 if (it == devices_.end()) {
303 return;
304 }
305 CHECK(success) << "Failed to subscribe to Device property changes.";
306 DeviceProxy* device = it->second.device.get();
307 VariantDictionary properties;
308 if (!device->GetProperties(&properties, nullptr)) {
309 LOG(WARNING) << "Failed to get device properties?";
310 return;
311 }
312 auto prop_it = properties.find(shill::kSelectedServiceProperty);
313 if (prop_it == properties.end()) {
314 LOG(WARNING) << "Failed to get device's selected service?";
315 return;
316 }
317 OnDevicePropertyChange(device_path, shill::kSelectedServiceProperty,
318 prop_it->second);
319}
320
321void ShillClient::OnDevicePropertyChange(const ObjectPath& device_path,
322 const string& property_name,
323 const Any& property_value) {
324 // We only care about selected services anyway.
325 if (property_name != shill::kSelectedServiceProperty) {
326 return;
327 }
328 // If the device isn't our list of whitelisted devices, ignore it.
329 auto it = devices_.find(device_path);
330 if (it == devices_.end()) {
331 return;
332 }
333 DeviceState& device_state = it->second;
334 ObjectPath service_path{property_value.TryGet<ObjectPath>()};
335 if (!service_path.IsValid()) {
336 LOG(ERROR) << "Device at " << device_path.value()
337 << " selected invalid service path.";
338 return;
339 }
340 VLOG(3) << "Device at " << it->first.value() << " has selected service at "
341 << service_path.value();
342 bool removed_old_service{false};
343 if (device_state.selected_service) {
344 if (device_state.selected_service->GetObjectPath() == service_path) {
345 return; // Spurious update?
346 }
347 device_state.selected_service.reset();
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700348 device_state.service_state = Network::State::kOffline;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700349 removed_old_service = true;
350 }
351 std::shared_ptr<ServiceProxy> new_service;
352 const bool reuse_connecting_service =
353 service_path.value() != "/" && connecting_service_ &&
354 connecting_service_->GetObjectPath() == service_path;
355 if (reuse_connecting_service) {
356 new_service = connecting_service_;
357 // When we reuse the connecting service, we need to make sure that our
358 // cached state is correct. Normally, we do this by relying reading the
359 // state when our signal handlers finish registering, but this may have
360 // happened long in the past for the connecting service.
361 string state;
362 if (GetStateForService(new_service.get(), &state)) {
363 device_state.service_state = ShillServiceStateToNetworkState(state);
364 } else {
365 LOG(WARNING) << "Failed to read properties from existing service "
366 "on selection.";
367 }
368 } else if (service_path.value() != "/") {
369 // The device has selected a new service we haven't see before.
370 new_service.reset(new ServiceProxy{bus_, service_path});
371 new_service->RegisterPropertyChangedSignalHandler(
372 base::Bind(&ShillClient::OnServicePropertyChange,
373 weak_factory_.GetWeakPtr(), service_path),
374 base::Bind(&ShillClient::OnServicePropertyChangeRegistration,
375 weak_factory_.GetWeakPtr(), service_path));
376 }
377 device_state.selected_service = new_service;
378 if (reuse_connecting_service || removed_old_service) {
379 UpdateConnectivityState();
380 }
381}
382
383void ShillClient::OnServicePropertyChangeRegistration(const ObjectPath& path,
384 const string& interface,
385 const string& signal_name,
386 bool success) {
387 VLOG(3) << "OnServicePropertyChangeRegistration(" << path.value() << ");";
388 ServiceProxy* service{nullptr};
389 if (connecting_service_ && connecting_service_->GetObjectPath() == path) {
390 // Note that the connecting service might also be a selected service.
391 service = connecting_service_.get();
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700392 if (!success)
393 CleanupConnectingService();
Vitaly Buka58a288b2015-07-31 00:33:31 -0700394 } else {
395 for (const auto& kv : devices_) {
396 if (kv.second.selected_service &&
397 kv.second.selected_service->GetObjectPath() == path) {
398 service = kv.second.selected_service.get();
399 break;
400 }
401 }
402 }
403 if (service == nullptr || !success) {
404 return; // A failure or success for a proxy we no longer care about.
405 }
406 VariantDictionary properties;
407 if (!service->GetProperties(&properties, nullptr)) {
408 return;
409 }
410 // Give ourselves property changed signals for the initial property
411 // values.
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700412 for (auto name : {shill::kStateProperty, shill::kSignalStrengthProperty,
413 shill::kErrorProperty}) {
414 auto it = properties.find(name);
415 if (it != properties.end())
416 OnServicePropertyChange(path, name, it->second);
Vitaly Buka58a288b2015-07-31 00:33:31 -0700417 }
418}
419
420void ShillClient::OnServicePropertyChange(const ObjectPath& service_path,
421 const string& property_name,
422 const Any& property_value) {
423 VLOG(3) << "ServicePropertyChange(" << service_path.value() << ", "
424 << property_name << ", ...);";
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700425
426 bool is_connecting_service =
427 connecting_service_ &&
428 connecting_service_->GetObjectPath() == service_path;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700429 if (property_name == shill::kStateProperty) {
430 const string state{property_value.TryGet<string>()};
431 if (state.empty()) {
432 VLOG(3) << "Invalid service state update.";
433 return;
434 }
435 VLOG(3) << "New service state=" << state;
436 OnStateChangeForSelectedService(service_path, state);
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700437 if (is_connecting_service)
438 OnStateChangeForConnectingService(state);
Vitaly Buka58a288b2015-07-31 00:33:31 -0700439 } else if (property_name == shill::kSignalStrengthProperty) {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700440 VLOG(3) << "Signal strength=" << property_value.TryGet<uint8_t>();
441 if (is_connecting_service)
442 OnStrengthChangeForConnectingService(property_value.TryGet<uint8_t>());
443 } else if (property_name == shill::kErrorProperty) {
444 VLOG(3) << "Error=" << property_value.TryGet<std::string>();
445 if (is_connecting_service)
446 connecting_service_error_ = property_value.TryGet<std::string>();
Vitaly Buka58a288b2015-07-31 00:33:31 -0700447 }
448}
449
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700450void ShillClient::OnStateChangeForConnectingService(const string& state) {
451 switch (ShillServiceStateToNetworkState(state)) {
Alex Vakulenkobe39e932015-10-09 08:10:36 -0700452 case Network::State::kOnline: {
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700453 auto callback = connect_done_callback_;
454 connect_done_callback_.Reset();
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700455 CleanupConnectingService();
456
457 if (!callback.is_null())
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700458 callback.Run(nullptr);
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700459 break;
460 }
Alex Vakulenkobe39e932015-10-09 08:10:36 -0700461 case Network::State::kError: {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700462 ConnectToServiceError(connecting_service_);
463 break;
464 }
465 case Network::State::kOffline:
466 case Network::State::kConnecting:
467 break;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700468 }
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700469}
470
471void ShillClient::OnErrorChangeForConnectingService(const std::string& error) {
472 if (error.empty())
473 return;
474
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700475 auto callback = connect_done_callback_;
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700476 CleanupConnectingService();
477
478 weave::ErrorPtr weave_error;
479 weave::Error::AddTo(&weave_error, FROM_HERE, kErrorDomain, error,
480 "Failed to connect to WiFi network");
481
482 if (!callback.is_null())
Alex Vakulenko297114c2015-10-12 13:45:43 -0700483 callback.Run(std::move(weave_error));
Vitaly Buka58a288b2015-07-31 00:33:31 -0700484}
485
486void ShillClient::OnStrengthChangeForConnectingService(
Vitaly Buka58a288b2015-07-31 00:33:31 -0700487 uint8_t signal_strength) {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700488 if (signal_strength == 0 || have_called_connect_) {
Vitaly Buka58a288b2015-07-31 00:33:31 -0700489 return;
490 }
491 VLOG(1) << "Connecting service has signal. Calling Connect().";
492 have_called_connect_ = true;
493 // Failures here indicate that we've already connected,
494 // or are connecting, or some other very unexciting thing.
495 // Ignore all that, and rely on state changes to detect
496 // connectivity.
497 connecting_service_->Connect(nullptr);
498}
499
500void ShillClient::OnStateChangeForSelectedService(
501 const ObjectPath& service_path,
502 const string& state) {
503 // Find the device/service pair responsible for this update
504 VLOG(3) << "State for potentially selected service " << service_path.value()
505 << " have changed to " << state;
506 for (auto& kv : devices_) {
507 if (kv.second.selected_service &&
508 kv.second.selected_service->GetObjectPath() == service_path) {
509 VLOG(3) << "Updated cached connection state for selected service.";
510 kv.second.service_state = ShillServiceStateToNetworkState(state);
511 UpdateConnectivityState();
512 return;
513 }
514 }
515}
516
517void ShillClient::UpdateConnectivityState() {
518 // Update the connectivity state of the device by picking the
519 // state of the currently most connected selected service.
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700520 Network::State new_connectivity_state{Network::State::kOffline};
Vitaly Buka58a288b2015-07-31 00:33:31 -0700521 for (const auto& kv : devices_) {
522 if (kv.second.service_state > new_connectivity_state) {
523 new_connectivity_state = kv.second.service_state;
524 }
525 }
526 VLOG(1) << "Connectivity changed: " << EnumToString(connectivity_state_)
527 << " -> " << EnumToString(new_connectivity_state);
528 // Notify listeners even if state changed to the same value. Listeners may
529 // want to handle this event.
530 connectivity_state_ = new_connectivity_state;
531 // We may call UpdateConnectivityState whenever we mutate a data structure
532 // such that our connectivity status could change. However, we don't want
533 // to allow people to call into ShillClient while some other operation is
534 // underway. Therefore, call our callbacks later, when we're in a good
535 // state.
536 base::MessageLoop::current()->PostTask(
Alex Vakulenko297114c2015-10-12 13:45:43 -0700537 FROM_HERE, base::Bind(&ShillClient::NotifyConnectivityListeners,
538 weak_factory_.GetWeakPtr(),
539 GetConnectionState() == Network::State::kOnline));
Vitaly Buka58a288b2015-07-31 00:33:31 -0700540}
541
542void ShillClient::NotifyConnectivityListeners(bool am_online) {
543 VLOG(3) << "Notifying connectivity listeners that online=" << am_online;
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700544 for (const auto& listener : connectivity_listeners_)
545 listener.Run();
Vitaly Buka58a288b2015-07-31 00:33:31 -0700546}
547
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700548void ShillClient::CleanupConnectingService() {
Vitaly Buka58a288b2015-07-31 00:33:31 -0700549 if (connecting_service_) {
550 connecting_service_->ReleaseObjectProxy(base::Bind(&IgnoreDetachEvent));
551 connecting_service_.reset();
552 }
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700553 connect_done_callback_.Reset();
Vitaly Buka58a288b2015-07-31 00:33:31 -0700554 have_called_connect_ = false;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700555}
556
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700557void ShillClient::OpenSslSocket(const std::string& host,
558 uint16_t port,
559 const OpenSslSocketCallback& callback) {
Alex Vakulenko0022b752015-10-02 11:09:59 -0700560 if (disable_xmpp_)
561 return;
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700562 std::unique_ptr<weave::Stream> raw_stream{
563 SocketStream::ConnectBlocking(host, port)};
564 if (!raw_stream) {
Alex Vakulenko41705852015-10-13 10:12:06 -0700565 brillo::ErrorPtr error;
566 brillo::errors::system::AddSystemError(&error, FROM_HERE, errno);
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700567 weave::ErrorPtr weave_error;
568 ConvertError(*error.get(), &weave_error);
569 base::MessageLoop::current()->PostTask(
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700570 FROM_HERE, base::Bind(callback, nullptr, base::Passed(&weave_error)));
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700571 return;
572 }
573
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700574 SocketStream::TlsConnect(std::move(raw_stream), host, callback);
Vitaly Buka493f6042015-08-12 16:17:16 -0700575}
576
Vitaly Buka58a288b2015-07-31 00:33:31 -0700577} // namespace buffet