blob: d3a53f5faaae0bb531a3bb067201948fbe98870a [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>
11#include <chromeos/any.h>
12#include <chromeos/dbus/service_constants.h>
13#include <chromeos/errors/error.h>
Vitaly Bukae2713ac2015-08-03 13:50:01 -070014#include <weave/enum_to_string.h>
Vitaly Buka58a288b2015-07-31 00:33:31 -070015
Vitaly Buka3ef4fff2015-07-31 01:12:07 -070016#include "buffet/ap_manager_client.h"
Vitaly Buka493f6042015-08-12 16:17:16 -070017#include "buffet/socket_stream.h"
Vitaly Buka4f771532015-08-14 14:58:39 -070018#include "buffet/weave_error_conversion.h"
Vitaly Buka58a288b2015-07-31 00:33:31 -070019
20using chromeos::Any;
21using chromeos::VariantDictionary;
22using dbus::ObjectPath;
23using org::chromium::flimflam::DeviceProxy;
24using org::chromium::flimflam::ServiceProxy;
25using std::map;
26using std::set;
27using std::string;
28using std::vector;
Alex Vakulenkoe32375b2015-09-28 08:55:40 -070029using weave::EnumToString;
30using weave::provider::Network;
Vitaly Buka58a288b2015-07-31 00:33:31 -070031
32namespace buffet {
33
34namespace {
35
Alex Vakulenkoe32375b2015-09-28 08:55:40 -070036const char kErrorDomain[] = "buffet";
37
Vitaly Buka58a288b2015-07-31 00:33:31 -070038void IgnoreDetachEvent() {}
39
40bool GetStateForService(ServiceProxy* service, string* state) {
41 CHECK(service) << "|service| was nullptr in GetStateForService()";
42 VariantDictionary properties;
43 if (!service->GetProperties(&properties, nullptr)) {
44 LOG(WARNING) << "Failed to read properties from service.";
45 return false;
46 }
47 auto property_it = properties.find(shill::kStateProperty);
48 if (property_it == properties.end()) {
49 LOG(WARNING) << "No state found in service properties.";
50 return false;
51 }
52 string new_state = property_it->second.TryGet<string>();
53 if (new_state.empty()) {
54 LOG(WARNING) << "Invalid state value.";
55 return false;
56 }
57 *state = new_state;
58 return true;
59}
60
Alex Vakulenkoe32375b2015-09-28 08:55:40 -070061Network::State ShillServiceStateToNetworkState(const string& state) {
Vitaly Buka58a288b2015-07-31 00:33:31 -070062 // TODO(wiley) What does "unconfigured" mean in a world with multiple sets
63 // of WiFi credentials?
64 // TODO(wiley) Detect disabled devices, update state appropriately.
65 if ((state.compare(shill::kStateReady) == 0) ||
66 (state.compare(shill::kStatePortal) == 0) ||
67 (state.compare(shill::kStateOnline) == 0)) {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -070068 return Network::State::kConnected;
Vitaly Buka58a288b2015-07-31 00:33:31 -070069 }
70 if ((state.compare(shill::kStateAssociation) == 0) ||
71 (state.compare(shill::kStateConfiguration) == 0)) {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -070072 return Network::State::kConnecting;
Vitaly Buka58a288b2015-07-31 00:33:31 -070073 }
74 if ((state.compare(shill::kStateFailure) == 0) ||
75 (state.compare(shill::kStateActivationFailure) == 0)) {
76 // TODO(wiley) Get error information off the service object.
Alex Vakulenkoe32375b2015-09-28 08:55:40 -070077 return Network::State::kFailure;
Vitaly Buka58a288b2015-07-31 00:33:31 -070078 }
79 if ((state.compare(shill::kStateIdle) == 0) ||
80 (state.compare(shill::kStateOffline) == 0) ||
81 (state.compare(shill::kStateDisconnect) == 0)) {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -070082 return Network::State::kOffline;
Vitaly Buka58a288b2015-07-31 00:33:31 -070083 }
84 LOG(WARNING) << "Unknown state found: '" << state << "'";
Alex Vakulenkoe32375b2015-09-28 08:55:40 -070085 return Network::State::kOffline;
Vitaly Buka58a288b2015-07-31 00:33:31 -070086}
87
88} // namespace
89
90ShillClient::ShillClient(const scoped_refptr<dbus::Bus>& bus,
91 const set<string>& device_whitelist)
92 : bus_{bus},
Alex Vakulenkoe32375b2015-09-28 08:55:40 -070093 manager_proxy_{bus_},
Vitaly Buka3ef4fff2015-07-31 01:12:07 -070094 device_whitelist_{device_whitelist},
95 ap_manager_client_{new ApManagerClient(bus)} {
Vitaly Buka58a288b2015-07-31 00:33:31 -070096 manager_proxy_.RegisterPropertyChangedSignalHandler(
97 base::Bind(&ShillClient::OnManagerPropertyChange,
98 weak_factory_.GetWeakPtr()),
99 base::Bind(&ShillClient::OnManagerPropertyChangeRegistration,
100 weak_factory_.GetWeakPtr()));
101 auto owner_changed_cb = base::Bind(&ShillClient::OnShillServiceOwnerChange,
102 weak_factory_.GetWeakPtr());
103 bus_->GetObjectProxy(shill::kFlimflamServiceName, ObjectPath{"/"})
104 ->SetNameOwnerChangedCallback(owner_changed_cb);
105}
106
Vitaly Buka3ef4fff2015-07-31 01:12:07 -0700107ShillClient::~ShillClient() {}
108
Vitaly Buka58a288b2015-07-31 00:33:31 -0700109void ShillClient::Init() {
110 VLOG(2) << "ShillClient::Init();";
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700111 CleanupConnectingService();
Vitaly Buka58a288b2015-07-31 00:33:31 -0700112 devices_.clear();
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700113 connectivity_state_ = Network::State::kOffline;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700114 VariantDictionary properties;
115 if (!manager_proxy_.GetProperties(&properties, nullptr)) {
116 LOG(ERROR) << "Unable to get properties from Manager, waiting for "
117 "Manager to come back online.";
118 return;
119 }
120 auto it = properties.find(shill::kDevicesProperty);
121 CHECK(it != properties.end()) << "shill should always publish a device list.";
122 OnManagerPropertyChange(shill::kDevicesProperty, it->second);
123}
124
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700125void ShillClient::Connect(const string& ssid,
126 const string& passphrase,
127 const weave::SuccessCallback& success_callback,
128 const weave::ErrorCallback& error_callback) {
129 if (connecting_service_) {
130 weave::ErrorPtr error;
131 weave::Error::AddTo(&error, FROM_HERE, kErrorDomain, "busy",
132 "Already connecting to WiFi network");
133 base::MessageLoop::current()->PostTask(
134 FROM_HERE, base::Bind(error_callback, base::Owned(error.release())));
135 return;
Vitaly Buka4f771532015-08-14 14:58:39 -0700136 }
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700137 CleanupConnectingService();
Vitaly Buka58a288b2015-07-31 00:33:31 -0700138 VariantDictionary service_properties;
139 service_properties[shill::kTypeProperty] = Any{string{shill::kTypeWifi}};
140 service_properties[shill::kSSIDProperty] = Any{ssid};
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700141 if (passphrase.empty()) {
142 service_properties[shill::kSecurityProperty] = Any{shill::kSecurityNone};
143 } else {
144 service_properties[shill::kPassphraseProperty] = Any{passphrase};
145 service_properties[shill::kSecurityProperty] = Any{shill::kSecurityPsk};
146 }
Vitaly Buka58a288b2015-07-31 00:33:31 -0700147 service_properties[shill::kSaveCredentialsProperty] = Any{true};
148 service_properties[shill::kAutoConnectProperty] = Any{true};
149 ObjectPath service_path;
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700150 chromeos::ErrorPtr chromeos_error;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700151 if (!manager_proxy_.ConfigureService(service_properties, &service_path,
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700152 &chromeos_error) ||
153 !manager_proxy_.RequestScan(shill::kTypeWifi, &chromeos_error)) {
154 weave::ErrorPtr weave_error;
155 ConvertError(*chromeos_error, &weave_error);
156 base::MessageLoop::current()->PostTask(
157 FROM_HERE,
158 base::Bind(error_callback, base::Owned(weave_error.release())));
159 return;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700160 }
161 connecting_service_.reset(new ServiceProxy{bus_, service_path});
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700162 connecting_service_->Connect(nullptr);
163 connect_success_callback_ = success_callback;
164 connect_error_callback_ = error_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_ ||
179 connect_error_callback_.is_null()) {
180 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)) {
452 case Network::State::kConnected: {
453 auto callback = connect_success_callback_;
454 CleanupConnectingService();
455
456 if (!callback.is_null())
457 callback.Run();
458 break;
459 }
460 case Network::State::kFailure: {
461 ConnectToServiceError(connecting_service_);
462 break;
463 }
464 case Network::State::kOffline:
465 case Network::State::kConnecting:
466 break;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700467 }
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700468}
469
470void ShillClient::OnErrorChangeForConnectingService(const std::string& error) {
471 if (error.empty())
472 return;
473
474 auto callback = connect_error_callback_;
475 CleanupConnectingService();
476
477 weave::ErrorPtr weave_error;
478 weave::Error::AddTo(&weave_error, FROM_HERE, kErrorDomain, error,
479 "Failed to connect to WiFi network");
480
481 if (!callback.is_null())
482 callback.Run(weave_error.get());
Vitaly Buka58a288b2015-07-31 00:33:31 -0700483}
484
485void ShillClient::OnStrengthChangeForConnectingService(
Vitaly Buka58a288b2015-07-31 00:33:31 -0700486 uint8_t signal_strength) {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700487 if (signal_strength == 0 || have_called_connect_) {
Vitaly Buka58a288b2015-07-31 00:33:31 -0700488 return;
489 }
490 VLOG(1) << "Connecting service has signal. Calling Connect().";
491 have_called_connect_ = true;
492 // Failures here indicate that we've already connected,
493 // or are connecting, or some other very unexciting thing.
494 // Ignore all that, and rely on state changes to detect
495 // connectivity.
496 connecting_service_->Connect(nullptr);
497}
498
499void ShillClient::OnStateChangeForSelectedService(
500 const ObjectPath& service_path,
501 const string& state) {
502 // Find the device/service pair responsible for this update
503 VLOG(3) << "State for potentially selected service " << service_path.value()
504 << " have changed to " << state;
505 for (auto& kv : devices_) {
506 if (kv.second.selected_service &&
507 kv.second.selected_service->GetObjectPath() == service_path) {
508 VLOG(3) << "Updated cached connection state for selected service.";
509 kv.second.service_state = ShillServiceStateToNetworkState(state);
510 UpdateConnectivityState();
511 return;
512 }
513 }
514}
515
516void ShillClient::UpdateConnectivityState() {
517 // Update the connectivity state of the device by picking the
518 // state of the currently most connected selected service.
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700519 Network::State new_connectivity_state{Network::State::kOffline};
Vitaly Buka58a288b2015-07-31 00:33:31 -0700520 for (const auto& kv : devices_) {
521 if (kv.second.service_state > new_connectivity_state) {
522 new_connectivity_state = kv.second.service_state;
523 }
524 }
525 VLOG(1) << "Connectivity changed: " << EnumToString(connectivity_state_)
526 << " -> " << EnumToString(new_connectivity_state);
527 // Notify listeners even if state changed to the same value. Listeners may
528 // want to handle this event.
529 connectivity_state_ = new_connectivity_state;
530 // We may call UpdateConnectivityState whenever we mutate a data structure
531 // such that our connectivity status could change. However, we don't want
532 // to allow people to call into ShillClient while some other operation is
533 // underway. Therefore, call our callbacks later, when we're in a good
534 // state.
535 base::MessageLoop::current()->PostTask(
536 FROM_HERE,
537 base::Bind(&ShillClient::NotifyConnectivityListeners,
538 weak_factory_.GetWeakPtr(),
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700539 GetConnectionState() == Network::State::kConnected));
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 Vakulenkoe32375b2015-09-28 08:55:40 -0700553 connect_success_callback_.Reset();
554 connect_error_callback_.Reset();
Vitaly Buka58a288b2015-07-31 00:33:31 -0700555 have_called_connect_ = false;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700556}
557
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700558void ShillClient::OpenSslSocket(
Vitaly Buka493f6042015-08-12 16:17:16 -0700559 const std::string& host,
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700560 uint16_t port,
Vitaly Buka493f6042015-08-12 16:17:16 -0700561 const base::Callback<void(std::unique_ptr<weave::Stream>)>&
562 success_callback,
Vitaly Buka4f771532015-08-14 14:58:39 -0700563 const base::Callback<void(const weave::Error*)>& error_callback) {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700564 std::unique_ptr<weave::Stream> raw_stream{
565 SocketStream::ConnectBlocking(host, port)};
566 if (!raw_stream) {
567 chromeos::ErrorPtr error;
568 chromeos::errors::system::AddSystemError(&error, FROM_HERE, errno);
569 weave::ErrorPtr weave_error;
570 ConvertError(*error.get(), &weave_error);
571 base::MessageLoop::current()->PostTask(
572 FROM_HERE,
573 base::Bind(error_callback, base::Owned(weave_error.release())));
574 return;
575 }
576
577 SocketStream::TlsConnect(std::move(raw_stream), host, success_callback,
Vitaly Buka493f6042015-08-12 16:17:16 -0700578 error_callback);
579}
580
Vitaly Buka58a288b2015-07-31 00:33:31 -0700581} // namespace buffet