blob: 36918ab83a5117f8c2290693ef6253fb42256f4c [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>
Vitaly Buka58a288b2015-07-31 00:33:31 -070012#include <chromeos/errors/error.h>
Peter Qiu786a9062015-10-02 11:45:01 -070013#include <dbus/shill/dbus-constants.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 Vakulenkobe39e932015-10-09 08:10:36 -070068 return Network::State::kOnline;
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 Vakulenkobe39e932015-10-09 08:10:36 -070077 return Network::State::kError;
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,
Alex Vakulenko0022b752015-10-02 11:09:59 -070091 const set<string>& device_whitelist,
92 bool disable_xmpp)
Vitaly Buka58a288b2015-07-31 00:33:31 -070093 : bus_{bus},
Alex Vakulenkoe32375b2015-09-28 08:55:40 -070094 manager_proxy_{bus_},
Vitaly Buka3ef4fff2015-07-31 01:12:07 -070095 device_whitelist_{device_whitelist},
Alex Vakulenko0022b752015-10-02 11:09:59 -070096 disable_xmpp_{disable_xmpp},
Vitaly Buka3ef4fff2015-07-31 01:12:07 -070097 ap_manager_client_{new ApManagerClient(bus)} {
Vitaly Buka58a288b2015-07-31 00:33:31 -070098 manager_proxy_.RegisterPropertyChangedSignalHandler(
99 base::Bind(&ShillClient::OnManagerPropertyChange,
100 weak_factory_.GetWeakPtr()),
101 base::Bind(&ShillClient::OnManagerPropertyChangeRegistration,
102 weak_factory_.GetWeakPtr()));
103 auto owner_changed_cb = base::Bind(&ShillClient::OnShillServiceOwnerChange,
104 weak_factory_.GetWeakPtr());
105 bus_->GetObjectProxy(shill::kFlimflamServiceName, ObjectPath{"/"})
106 ->SetNameOwnerChangedCallback(owner_changed_cb);
107}
108
Vitaly Buka3ef4fff2015-07-31 01:12:07 -0700109ShillClient::~ShillClient() {}
110
Vitaly Buka58a288b2015-07-31 00:33:31 -0700111void ShillClient::Init() {
112 VLOG(2) << "ShillClient::Init();";
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700113 CleanupConnectingService();
Vitaly Buka58a288b2015-07-31 00:33:31 -0700114 devices_.clear();
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700115 connectivity_state_ = Network::State::kOffline;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700116 VariantDictionary properties;
117 if (!manager_proxy_.GetProperties(&properties, nullptr)) {
118 LOG(ERROR) << "Unable to get properties from Manager, waiting for "
119 "Manager to come back online.";
120 return;
121 }
122 auto it = properties.find(shill::kDevicesProperty);
123 CHECK(it != properties.end()) << "shill should always publish a device list.";
124 OnManagerPropertyChange(shill::kDevicesProperty, it->second);
125}
126
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700127void ShillClient::Connect(const string& ssid,
128 const string& passphrase,
129 const weave::SuccessCallback& success_callback,
130 const weave::ErrorCallback& error_callback) {
131 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 Vakulenko297114c2015-10-12 13:45:43 -0700136 FROM_HERE, base::Bind(error_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 Vakulenkoe32375b2015-09-28 08:55:40 -0700152 chromeos::ErrorPtr chromeos_error;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700153 if (!manager_proxy_.ConfigureService(service_properties, &service_path,
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700154 &chromeos_error) ||
155 !manager_proxy_.RequestScan(shill::kTypeWifi, &chromeos_error)) {
156 weave::ErrorPtr weave_error;
157 ConvertError(*chromeos_error, &weave_error);
158 base::MessageLoop::current()->PostTask(
159 FROM_HERE,
Alex Vakulenko297114c2015-10-12 13:45:43 -0700160 base::Bind(error_callback, base::Passed(&weave_error)));
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700161 return;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700162 }
163 connecting_service_.reset(new ServiceProxy{bus_, service_path});
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700164 connecting_service_->Connect(nullptr);
165 connect_success_callback_ = success_callback;
166 connect_error_callback_ = error_callback;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700167 connecting_service_->RegisterPropertyChangedSignalHandler(
168 base::Bind(&ShillClient::OnServicePropertyChange,
169 weak_factory_.GetWeakPtr(), service_path),
170 base::Bind(&ShillClient::OnServicePropertyChangeRegistration,
171 weak_factory_.GetWeakPtr(), service_path));
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700172 base::MessageLoop::current()->PostDelayedTask(
173 FROM_HERE, base::Bind(&ShillClient::ConnectToServiceError,
174 weak_factory_.GetWeakPtr(), connecting_service_),
175 base::TimeDelta::FromMinutes(1));
Vitaly Buka58a288b2015-07-31 00:33:31 -0700176}
177
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700178void ShillClient::ConnectToServiceError(
179 std::shared_ptr<org::chromium::flimflam::ServiceProxy> connecting_service) {
180 if (connecting_service != connecting_service_ ||
181 connect_error_callback_.is_null()) {
182 return;
183 }
184 std::string error = have_called_connect_ ? connecting_service_error_
185 : shill::kErrorOutOfRange;
186 if (error.empty())
187 error = shill::kErrorInternal;
188 OnErrorChangeForConnectingService(error);
189}
190
191Network::State ShillClient::GetConnectionState() const {
Vitaly Buka58a288b2015-07-31 00:33:31 -0700192 return connectivity_state_;
193}
194
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700195void ShillClient::StartAccessPoint(const std::string& ssid) {
Vitaly Buka3ef4fff2015-07-31 01:12:07 -0700196 ap_manager_client_->Start(ssid);
197}
198
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700199void ShillClient::StopAccessPoint() {
Vitaly Buka3ef4fff2015-07-31 01:12:07 -0700200 ap_manager_client_->Stop();
201}
202
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700203void ShillClient::AddConnectionChangedCallback(
204 const ConnectionChangedCallback& listener) {
Vitaly Buka58a288b2015-07-31 00:33:31 -0700205 connectivity_listeners_.push_back(listener);
206}
207
208bool ShillClient::IsMonitoredDevice(DeviceProxy* device) {
209 if (device_whitelist_.empty()) {
210 return true;
211 }
212 VariantDictionary device_properties;
213 if (!device->GetProperties(&device_properties, nullptr)) {
214 LOG(ERROR) << "Devices without properties aren't whitelisted.";
215 return false;
216 }
217 auto it = device_properties.find(shill::kInterfaceProperty);
218 if (it == device_properties.end()) {
219 LOG(ERROR) << "Failed to find interface property in device properties.";
220 return false;
221 }
222 return ContainsKey(device_whitelist_, it->second.TryGet<string>());
223}
224
225void ShillClient::OnShillServiceOwnerChange(const string& old_owner,
226 const string& new_owner) {
227 VLOG(1) << "Shill service owner name changed to '" << new_owner << "'";
228 if (new_owner.empty()) {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700229 CleanupConnectingService();
Vitaly Buka58a288b2015-07-31 00:33:31 -0700230 devices_.clear();
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700231 connectivity_state_ = Network::State::kOffline;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700232 } else {
233 Init(); // New service owner means shill reset!
234 }
235}
236
237void ShillClient::OnManagerPropertyChangeRegistration(const string& interface,
238 const string& signal_name,
239 bool success) {
240 VLOG(3) << "Registered ManagerPropertyChange handler.";
241 CHECK(success) << "privetd requires Manager signals.";
242 VariantDictionary properties;
243 if (!manager_proxy_.GetProperties(&properties, nullptr)) {
244 LOG(ERROR) << "Unable to get properties from Manager, waiting for "
245 "Manager to come back online.";
246 return;
247 }
248 auto it = properties.find(shill::kDevicesProperty);
249 CHECK(it != properties.end()) << "Shill should always publish a device list.";
250 OnManagerPropertyChange(shill::kDevicesProperty, it->second);
251}
252
253void ShillClient::OnManagerPropertyChange(const string& property_name,
254 const Any& property_value) {
255 if (property_name != shill::kDevicesProperty) {
256 return;
257 }
258 VLOG(3) << "Manager's device list has changed.";
259 // We're going to remove every device we haven't seen in the update.
260 set<ObjectPath> device_paths_to_remove;
261 for (const auto& kv : devices_) {
262 device_paths_to_remove.insert(kv.first);
263 }
264 for (const auto& device_path : property_value.TryGet<vector<ObjectPath>>()) {
265 if (!device_path.IsValid()) {
266 LOG(ERROR) << "Ignoring invalid device path in Manager's device list.";
267 return;
268 }
269 auto it = devices_.find(device_path);
270 if (it != devices_.end()) {
271 // Found an existing proxy. Since the whitelist never changes,
272 // this still a valid device.
273 device_paths_to_remove.erase(device_path);
274 continue;
275 }
276 std::unique_ptr<DeviceProxy> device{new DeviceProxy{bus_, device_path}};
277 if (!IsMonitoredDevice(device.get())) {
278 continue;
279 }
280 device->RegisterPropertyChangedSignalHandler(
281 base::Bind(&ShillClient::OnDevicePropertyChange,
282 weak_factory_.GetWeakPtr(), device_path),
283 base::Bind(&ShillClient::OnDevicePropertyChangeRegistration,
284 weak_factory_.GetWeakPtr(), device_path));
285 VLOG(3) << "Creating device proxy at " << device_path.value();
286 devices_[device_path].device = std::move(device);
287 }
288 // Clean up devices/services related to removed devices.
289 if (!device_paths_to_remove.empty()) {
290 for (const ObjectPath& device_path : device_paths_to_remove) {
291 devices_.erase(device_path);
292 }
293 UpdateConnectivityState();
294 }
295}
296
297void ShillClient::OnDevicePropertyChangeRegistration(
298 const ObjectPath& device_path,
299 const string& interface,
300 const string& signal_name,
301 bool success) {
302 VLOG(3) << "Registered DevicePropertyChange handler.";
303 auto it = devices_.find(device_path);
304 if (it == devices_.end()) {
305 return;
306 }
307 CHECK(success) << "Failed to subscribe to Device property changes.";
308 DeviceProxy* device = it->second.device.get();
309 VariantDictionary properties;
310 if (!device->GetProperties(&properties, nullptr)) {
311 LOG(WARNING) << "Failed to get device properties?";
312 return;
313 }
314 auto prop_it = properties.find(shill::kSelectedServiceProperty);
315 if (prop_it == properties.end()) {
316 LOG(WARNING) << "Failed to get device's selected service?";
317 return;
318 }
319 OnDevicePropertyChange(device_path, shill::kSelectedServiceProperty,
320 prop_it->second);
321}
322
323void ShillClient::OnDevicePropertyChange(const ObjectPath& device_path,
324 const string& property_name,
325 const Any& property_value) {
326 // We only care about selected services anyway.
327 if (property_name != shill::kSelectedServiceProperty) {
328 return;
329 }
330 // If the device isn't our list of whitelisted devices, ignore it.
331 auto it = devices_.find(device_path);
332 if (it == devices_.end()) {
333 return;
334 }
335 DeviceState& device_state = it->second;
336 ObjectPath service_path{property_value.TryGet<ObjectPath>()};
337 if (!service_path.IsValid()) {
338 LOG(ERROR) << "Device at " << device_path.value()
339 << " selected invalid service path.";
340 return;
341 }
342 VLOG(3) << "Device at " << it->first.value() << " has selected service at "
343 << service_path.value();
344 bool removed_old_service{false};
345 if (device_state.selected_service) {
346 if (device_state.selected_service->GetObjectPath() == service_path) {
347 return; // Spurious update?
348 }
349 device_state.selected_service.reset();
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700350 device_state.service_state = Network::State::kOffline;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700351 removed_old_service = true;
352 }
353 std::shared_ptr<ServiceProxy> new_service;
354 const bool reuse_connecting_service =
355 service_path.value() != "/" && connecting_service_ &&
356 connecting_service_->GetObjectPath() == service_path;
357 if (reuse_connecting_service) {
358 new_service = connecting_service_;
359 // When we reuse the connecting service, we need to make sure that our
360 // cached state is correct. Normally, we do this by relying reading the
361 // state when our signal handlers finish registering, but this may have
362 // happened long in the past for the connecting service.
363 string state;
364 if (GetStateForService(new_service.get(), &state)) {
365 device_state.service_state = ShillServiceStateToNetworkState(state);
366 } else {
367 LOG(WARNING) << "Failed to read properties from existing service "
368 "on selection.";
369 }
370 } else if (service_path.value() != "/") {
371 // The device has selected a new service we haven't see before.
372 new_service.reset(new ServiceProxy{bus_, service_path});
373 new_service->RegisterPropertyChangedSignalHandler(
374 base::Bind(&ShillClient::OnServicePropertyChange,
375 weak_factory_.GetWeakPtr(), service_path),
376 base::Bind(&ShillClient::OnServicePropertyChangeRegistration,
377 weak_factory_.GetWeakPtr(), service_path));
378 }
379 device_state.selected_service = new_service;
380 if (reuse_connecting_service || removed_old_service) {
381 UpdateConnectivityState();
382 }
383}
384
385void ShillClient::OnServicePropertyChangeRegistration(const ObjectPath& path,
386 const string& interface,
387 const string& signal_name,
388 bool success) {
389 VLOG(3) << "OnServicePropertyChangeRegistration(" << path.value() << ");";
390 ServiceProxy* service{nullptr};
391 if (connecting_service_ && connecting_service_->GetObjectPath() == path) {
392 // Note that the connecting service might also be a selected service.
393 service = connecting_service_.get();
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700394 if (!success)
395 CleanupConnectingService();
Vitaly Buka58a288b2015-07-31 00:33:31 -0700396 } else {
397 for (const auto& kv : devices_) {
398 if (kv.second.selected_service &&
399 kv.second.selected_service->GetObjectPath() == path) {
400 service = kv.second.selected_service.get();
401 break;
402 }
403 }
404 }
405 if (service == nullptr || !success) {
406 return; // A failure or success for a proxy we no longer care about.
407 }
408 VariantDictionary properties;
409 if (!service->GetProperties(&properties, nullptr)) {
410 return;
411 }
412 // Give ourselves property changed signals for the initial property
413 // values.
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700414 for (auto name : {shill::kStateProperty, shill::kSignalStrengthProperty,
415 shill::kErrorProperty}) {
416 auto it = properties.find(name);
417 if (it != properties.end())
418 OnServicePropertyChange(path, name, it->second);
Vitaly Buka58a288b2015-07-31 00:33:31 -0700419 }
420}
421
422void ShillClient::OnServicePropertyChange(const ObjectPath& service_path,
423 const string& property_name,
424 const Any& property_value) {
425 VLOG(3) << "ServicePropertyChange(" << service_path.value() << ", "
426 << property_name << ", ...);";
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700427
428 bool is_connecting_service =
429 connecting_service_ &&
430 connecting_service_->GetObjectPath() == service_path;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700431 if (property_name == shill::kStateProperty) {
432 const string state{property_value.TryGet<string>()};
433 if (state.empty()) {
434 VLOG(3) << "Invalid service state update.";
435 return;
436 }
437 VLOG(3) << "New service state=" << state;
438 OnStateChangeForSelectedService(service_path, state);
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700439 if (is_connecting_service)
440 OnStateChangeForConnectingService(state);
Vitaly Buka58a288b2015-07-31 00:33:31 -0700441 } else if (property_name == shill::kSignalStrengthProperty) {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700442 VLOG(3) << "Signal strength=" << property_value.TryGet<uint8_t>();
443 if (is_connecting_service)
444 OnStrengthChangeForConnectingService(property_value.TryGet<uint8_t>());
445 } else if (property_name == shill::kErrorProperty) {
446 VLOG(3) << "Error=" << property_value.TryGet<std::string>();
447 if (is_connecting_service)
448 connecting_service_error_ = property_value.TryGet<std::string>();
Vitaly Buka58a288b2015-07-31 00:33:31 -0700449 }
450}
451
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700452void ShillClient::OnStateChangeForConnectingService(const string& state) {
453 switch (ShillServiceStateToNetworkState(state)) {
Alex Vakulenkobe39e932015-10-09 08:10:36 -0700454 case Network::State::kOnline: {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700455 auto callback = connect_success_callback_;
456 CleanupConnectingService();
457
458 if (!callback.is_null())
459 callback.Run();
460 break;
461 }
Alex Vakulenkobe39e932015-10-09 08:10:36 -0700462 case Network::State::kError: {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700463 ConnectToServiceError(connecting_service_);
464 break;
465 }
466 case Network::State::kOffline:
467 case Network::State::kConnecting:
468 break;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700469 }
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700470}
471
472void ShillClient::OnErrorChangeForConnectingService(const std::string& error) {
473 if (error.empty())
474 return;
475
476 auto callback = connect_error_callback_;
477 CleanupConnectingService();
478
479 weave::ErrorPtr weave_error;
480 weave::Error::AddTo(&weave_error, FROM_HERE, kErrorDomain, error,
481 "Failed to connect to WiFi network");
482
483 if (!callback.is_null())
Alex Vakulenko297114c2015-10-12 13:45:43 -0700484 callback.Run(std::move(weave_error));
Vitaly Buka58a288b2015-07-31 00:33:31 -0700485}
486
487void ShillClient::OnStrengthChangeForConnectingService(
Vitaly Buka58a288b2015-07-31 00:33:31 -0700488 uint8_t signal_strength) {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700489 if (signal_strength == 0 || have_called_connect_) {
Vitaly Buka58a288b2015-07-31 00:33:31 -0700490 return;
491 }
492 VLOG(1) << "Connecting service has signal. Calling Connect().";
493 have_called_connect_ = true;
494 // Failures here indicate that we've already connected,
495 // or are connecting, or some other very unexciting thing.
496 // Ignore all that, and rely on state changes to detect
497 // connectivity.
498 connecting_service_->Connect(nullptr);
499}
500
501void ShillClient::OnStateChangeForSelectedService(
502 const ObjectPath& service_path,
503 const string& state) {
504 // Find the device/service pair responsible for this update
505 VLOG(3) << "State for potentially selected service " << service_path.value()
506 << " have changed to " << state;
507 for (auto& kv : devices_) {
508 if (kv.second.selected_service &&
509 kv.second.selected_service->GetObjectPath() == service_path) {
510 VLOG(3) << "Updated cached connection state for selected service.";
511 kv.second.service_state = ShillServiceStateToNetworkState(state);
512 UpdateConnectivityState();
513 return;
514 }
515 }
516}
517
518void ShillClient::UpdateConnectivityState() {
519 // Update the connectivity state of the device by picking the
520 // state of the currently most connected selected service.
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700521 Network::State new_connectivity_state{Network::State::kOffline};
Vitaly Buka58a288b2015-07-31 00:33:31 -0700522 for (const auto& kv : devices_) {
523 if (kv.second.service_state > new_connectivity_state) {
524 new_connectivity_state = kv.second.service_state;
525 }
526 }
527 VLOG(1) << "Connectivity changed: " << EnumToString(connectivity_state_)
528 << " -> " << EnumToString(new_connectivity_state);
529 // Notify listeners even if state changed to the same value. Listeners may
530 // want to handle this event.
531 connectivity_state_ = new_connectivity_state;
532 // We may call UpdateConnectivityState whenever we mutate a data structure
533 // such that our connectivity status could change. However, we don't want
534 // to allow people to call into ShillClient while some other operation is
535 // underway. Therefore, call our callbacks later, when we're in a good
536 // state.
537 base::MessageLoop::current()->PostTask(
Alex Vakulenko297114c2015-10-12 13:45:43 -0700538 FROM_HERE, base::Bind(&ShillClient::NotifyConnectivityListeners,
539 weak_factory_.GetWeakPtr(),
540 GetConnectionState() == Network::State::kOnline));
Vitaly Buka58a288b2015-07-31 00:33:31 -0700541}
542
543void ShillClient::NotifyConnectivityListeners(bool am_online) {
544 VLOG(3) << "Notifying connectivity listeners that online=" << am_online;
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700545 for (const auto& listener : connectivity_listeners_)
546 listener.Run();
Vitaly Buka58a288b2015-07-31 00:33:31 -0700547}
548
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700549void ShillClient::CleanupConnectingService() {
Vitaly Buka58a288b2015-07-31 00:33:31 -0700550 if (connecting_service_) {
551 connecting_service_->ReleaseObjectProxy(base::Bind(&IgnoreDetachEvent));
552 connecting_service_.reset();
553 }
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700554 connect_success_callback_.Reset();
555 connect_error_callback_.Reset();
Vitaly Buka58a288b2015-07-31 00:33:31 -0700556 have_called_connect_ = false;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700557}
558
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700559void ShillClient::OpenSslSocket(
Vitaly Buka493f6042015-08-12 16:17:16 -0700560 const std::string& host,
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700561 uint16_t port,
Alex Vakulenko297114c2015-10-12 13:45:43 -0700562 const OpenSslSocketSuccessCallback& success_callback,
563 const weave::ErrorCallback& error_callback) {
Alex Vakulenko0022b752015-10-02 11:09:59 -0700564 if (disable_xmpp_)
565 return;
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700566 std::unique_ptr<weave::Stream> raw_stream{
567 SocketStream::ConnectBlocking(host, port)};
568 if (!raw_stream) {
569 chromeos::ErrorPtr error;
570 chromeos::errors::system::AddSystemError(&error, FROM_HERE, errno);
571 weave::ErrorPtr weave_error;
572 ConvertError(*error.get(), &weave_error);
573 base::MessageLoop::current()->PostTask(
574 FROM_HERE,
Alex Vakulenko297114c2015-10-12 13:45:43 -0700575 base::Bind(error_callback, base::Passed(&weave_error)));
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700576 return;
577 }
578
579 SocketStream::TlsConnect(std::move(raw_stream), host, success_callback,
Vitaly Buka493f6042015-08-12 16:17:16 -0700580 error_callback);
581}
582
Vitaly Buka58a288b2015-07-31 00:33:31 -0700583} // namespace buffet