blob: b9e00802628d5810e37d94413fb24a7c99f3ce14 [file] [log] [blame]
Vitaly Bukacad20f02015-10-16 17:27:15 -07001// Copyright 2015 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
Vitaly Buka58a288b2015-07-31 00:33:31 -070014
15#include "buffet/shill_client.h"
16
17#include <set>
18
19#include <base/message_loop/message_loop.h>
20#include <base/stl_util.h>
Alex Vakulenko41705852015-10-13 10:12:06 -070021#include <brillo/any.h>
22#include <brillo/errors/error.h>
Alex Vakulenko94f8eba2015-10-14 08:52:45 -070023#include <brillo/variant_dictionary.h>
Peter Qiu786a9062015-10-02 11:45:01 -070024#include <dbus/shill/dbus-constants.h>
Vitaly Bukae2713ac2015-08-03 13:50:01 -070025#include <weave/enum_to_string.h>
Vitaly Buka58a288b2015-07-31 00:33:31 -070026
Vitaly Buka3ef4fff2015-07-31 01:12:07 -070027#include "buffet/ap_manager_client.h"
Vitaly Buka493f6042015-08-12 16:17:16 -070028#include "buffet/socket_stream.h"
Vitaly Buka4f771532015-08-14 14:58:39 -070029#include "buffet/weave_error_conversion.h"
Vitaly Buka58a288b2015-07-31 00:33:31 -070030
Alex Vakulenko41705852015-10-13 10:12:06 -070031using brillo::Any;
32using brillo::VariantDictionary;
Vitaly Buka58a288b2015-07-31 00:33:31 -070033using dbus::ObjectPath;
34using org::chromium::flimflam::DeviceProxy;
35using org::chromium::flimflam::ServiceProxy;
36using std::map;
37using std::set;
38using std::string;
39using std::vector;
Alex Vakulenkoe32375b2015-09-28 08:55:40 -070040using weave::EnumToString;
41using weave::provider::Network;
Vitaly Buka58a288b2015-07-31 00:33:31 -070042
43namespace buffet {
44
45namespace {
46
Alex Vakulenkoe32375b2015-09-28 08:55:40 -070047const char kErrorDomain[] = "buffet";
48
Vitaly Buka58a288b2015-07-31 00:33:31 -070049void IgnoreDetachEvent() {}
50
51bool GetStateForService(ServiceProxy* service, string* state) {
52 CHECK(service) << "|service| was nullptr in GetStateForService()";
53 VariantDictionary properties;
54 if (!service->GetProperties(&properties, nullptr)) {
55 LOG(WARNING) << "Failed to read properties from service.";
56 return false;
57 }
58 auto property_it = properties.find(shill::kStateProperty);
59 if (property_it == properties.end()) {
60 LOG(WARNING) << "No state found in service properties.";
61 return false;
62 }
63 string new_state = property_it->second.TryGet<string>();
64 if (new_state.empty()) {
65 LOG(WARNING) << "Invalid state value.";
66 return false;
67 }
68 *state = new_state;
69 return true;
70}
71
Alex Vakulenkoe32375b2015-09-28 08:55:40 -070072Network::State ShillServiceStateToNetworkState(const string& state) {
Vitaly Buka58a288b2015-07-31 00:33:31 -070073 // TODO(wiley) What does "unconfigured" mean in a world with multiple sets
74 // of WiFi credentials?
75 // TODO(wiley) Detect disabled devices, update state appropriately.
76 if ((state.compare(shill::kStateReady) == 0) ||
77 (state.compare(shill::kStatePortal) == 0) ||
78 (state.compare(shill::kStateOnline) == 0)) {
Alex Vakulenkobe39e932015-10-09 08:10:36 -070079 return Network::State::kOnline;
Vitaly Buka58a288b2015-07-31 00:33:31 -070080 }
81 if ((state.compare(shill::kStateAssociation) == 0) ||
82 (state.compare(shill::kStateConfiguration) == 0)) {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -070083 return Network::State::kConnecting;
Vitaly Buka58a288b2015-07-31 00:33:31 -070084 }
85 if ((state.compare(shill::kStateFailure) == 0) ||
86 (state.compare(shill::kStateActivationFailure) == 0)) {
87 // TODO(wiley) Get error information off the service object.
Alex Vakulenkobe39e932015-10-09 08:10:36 -070088 return Network::State::kError;
Vitaly Buka58a288b2015-07-31 00:33:31 -070089 }
90 if ((state.compare(shill::kStateIdle) == 0) ||
91 (state.compare(shill::kStateOffline) == 0) ||
92 (state.compare(shill::kStateDisconnect) == 0)) {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -070093 return Network::State::kOffline;
Vitaly Buka58a288b2015-07-31 00:33:31 -070094 }
95 LOG(WARNING) << "Unknown state found: '" << state << "'";
Alex Vakulenkoe32375b2015-09-28 08:55:40 -070096 return Network::State::kOffline;
Vitaly Buka58a288b2015-07-31 00:33:31 -070097}
98
99} // namespace
100
101ShillClient::ShillClient(const scoped_refptr<dbus::Bus>& bus,
Alex Vakulenko0022b752015-10-02 11:09:59 -0700102 const set<string>& device_whitelist,
103 bool disable_xmpp)
Vitaly Buka58a288b2015-07-31 00:33:31 -0700104 : bus_{bus},
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700105 manager_proxy_{bus_},
Vitaly Buka3ef4fff2015-07-31 01:12:07 -0700106 device_whitelist_{device_whitelist},
Alex Vakulenko0022b752015-10-02 11:09:59 -0700107 disable_xmpp_{disable_xmpp},
Vitaly Buka3ef4fff2015-07-31 01:12:07 -0700108 ap_manager_client_{new ApManagerClient(bus)} {
Vitaly Buka58a288b2015-07-31 00:33:31 -0700109 manager_proxy_.RegisterPropertyChangedSignalHandler(
110 base::Bind(&ShillClient::OnManagerPropertyChange,
111 weak_factory_.GetWeakPtr()),
112 base::Bind(&ShillClient::OnManagerPropertyChangeRegistration,
113 weak_factory_.GetWeakPtr()));
114 auto owner_changed_cb = base::Bind(&ShillClient::OnShillServiceOwnerChange,
115 weak_factory_.GetWeakPtr());
116 bus_->GetObjectProxy(shill::kFlimflamServiceName, ObjectPath{"/"})
117 ->SetNameOwnerChangedCallback(owner_changed_cb);
118}
119
Vitaly Buka3ef4fff2015-07-31 01:12:07 -0700120ShillClient::~ShillClient() {}
121
Vitaly Buka58a288b2015-07-31 00:33:31 -0700122void ShillClient::Init() {
123 VLOG(2) << "ShillClient::Init();";
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700124 CleanupConnectingService();
Vitaly Buka58a288b2015-07-31 00:33:31 -0700125 devices_.clear();
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700126 connectivity_state_ = Network::State::kOffline;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700127 VariantDictionary properties;
128 if (!manager_proxy_.GetProperties(&properties, nullptr)) {
129 LOG(ERROR) << "Unable to get properties from Manager, waiting for "
130 "Manager to come back online.";
131 return;
132 }
133 auto it = properties.find(shill::kDevicesProperty);
134 CHECK(it != properties.end()) << "shill should always publish a device list.";
135 OnManagerPropertyChange(shill::kDevicesProperty, it->second);
136}
137
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700138void ShillClient::Connect(const string& ssid,
139 const string& passphrase,
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700140 const weave::DoneCallback& callback) {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700141 if (connecting_service_) {
142 weave::ErrorPtr error;
143 weave::Error::AddTo(&error, FROM_HERE, kErrorDomain, "busy",
144 "Already connecting to WiFi network");
145 base::MessageLoop::current()->PostTask(
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700146 FROM_HERE, base::Bind(callback, base::Passed(&error)));
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700147 return;
Vitaly Buka4f771532015-08-14 14:58:39 -0700148 }
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700149 CleanupConnectingService();
Vitaly Buka58a288b2015-07-31 00:33:31 -0700150 VariantDictionary service_properties;
151 service_properties[shill::kTypeProperty] = Any{string{shill::kTypeWifi}};
152 service_properties[shill::kSSIDProperty] = Any{ssid};
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700153 if (passphrase.empty()) {
154 service_properties[shill::kSecurityProperty] = Any{shill::kSecurityNone};
155 } else {
156 service_properties[shill::kPassphraseProperty] = Any{passphrase};
157 service_properties[shill::kSecurityProperty] = Any{shill::kSecurityPsk};
158 }
Vitaly Buka58a288b2015-07-31 00:33:31 -0700159 service_properties[shill::kSaveCredentialsProperty] = Any{true};
160 service_properties[shill::kAutoConnectProperty] = Any{true};
161 ObjectPath service_path;
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700162 brillo::ErrorPtr brillo_error;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700163 if (!manager_proxy_.ConfigureService(service_properties, &service_path,
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700164 &brillo_error) ||
165 !manager_proxy_.RequestScan(shill::kTypeWifi, &brillo_error)) {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700166 weave::ErrorPtr weave_error;
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700167 ConvertError(*brillo_error, &weave_error);
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700168 base::MessageLoop::current()->PostTask(
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700169 FROM_HERE, base::Bind(callback, base::Passed(&weave_error)));
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700170 return;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700171 }
172 connecting_service_.reset(new ServiceProxy{bus_, service_path});
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700173 connecting_service_->Connect(nullptr);
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700174 connect_done_callback_ = callback;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700175 connecting_service_->RegisterPropertyChangedSignalHandler(
176 base::Bind(&ShillClient::OnServicePropertyChange,
177 weak_factory_.GetWeakPtr(), service_path),
178 base::Bind(&ShillClient::OnServicePropertyChangeRegistration,
179 weak_factory_.GetWeakPtr(), service_path));
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700180 base::MessageLoop::current()->PostDelayedTask(
181 FROM_HERE, base::Bind(&ShillClient::ConnectToServiceError,
182 weak_factory_.GetWeakPtr(), connecting_service_),
183 base::TimeDelta::FromMinutes(1));
Vitaly Buka58a288b2015-07-31 00:33:31 -0700184}
185
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700186void ShillClient::ConnectToServiceError(
187 std::shared_ptr<org::chromium::flimflam::ServiceProxy> connecting_service) {
188 if (connecting_service != connecting_service_ ||
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700189 connect_done_callback_.is_null()) {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700190 return;
191 }
192 std::string error = have_called_connect_ ? connecting_service_error_
193 : shill::kErrorOutOfRange;
194 if (error.empty())
195 error = shill::kErrorInternal;
196 OnErrorChangeForConnectingService(error);
197}
198
199Network::State ShillClient::GetConnectionState() const {
Vitaly Buka58a288b2015-07-31 00:33:31 -0700200 return connectivity_state_;
201}
202
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700203void ShillClient::StartAccessPoint(const std::string& ssid) {
Vitaly Buka3ef4fff2015-07-31 01:12:07 -0700204 ap_manager_client_->Start(ssid);
205}
206
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700207void ShillClient::StopAccessPoint() {
Vitaly Buka3ef4fff2015-07-31 01:12:07 -0700208 ap_manager_client_->Stop();
209}
210
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700211void ShillClient::AddConnectionChangedCallback(
212 const ConnectionChangedCallback& listener) {
Vitaly Buka58a288b2015-07-31 00:33:31 -0700213 connectivity_listeners_.push_back(listener);
214}
215
216bool ShillClient::IsMonitoredDevice(DeviceProxy* device) {
217 if (device_whitelist_.empty()) {
218 return true;
219 }
220 VariantDictionary device_properties;
221 if (!device->GetProperties(&device_properties, nullptr)) {
222 LOG(ERROR) << "Devices without properties aren't whitelisted.";
223 return false;
224 }
225 auto it = device_properties.find(shill::kInterfaceProperty);
226 if (it == device_properties.end()) {
227 LOG(ERROR) << "Failed to find interface property in device properties.";
228 return false;
229 }
230 return ContainsKey(device_whitelist_, it->second.TryGet<string>());
231}
232
233void ShillClient::OnShillServiceOwnerChange(const string& old_owner,
234 const string& new_owner) {
235 VLOG(1) << "Shill service owner name changed to '" << new_owner << "'";
236 if (new_owner.empty()) {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700237 CleanupConnectingService();
Vitaly Buka58a288b2015-07-31 00:33:31 -0700238 devices_.clear();
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700239 connectivity_state_ = Network::State::kOffline;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700240 } else {
241 Init(); // New service owner means shill reset!
242 }
243}
244
245void ShillClient::OnManagerPropertyChangeRegistration(const string& interface,
246 const string& signal_name,
247 bool success) {
248 VLOG(3) << "Registered ManagerPropertyChange handler.";
249 CHECK(success) << "privetd requires Manager signals.";
250 VariantDictionary properties;
251 if (!manager_proxy_.GetProperties(&properties, nullptr)) {
252 LOG(ERROR) << "Unable to get properties from Manager, waiting for "
253 "Manager to come back online.";
254 return;
255 }
256 auto it = properties.find(shill::kDevicesProperty);
257 CHECK(it != properties.end()) << "Shill should always publish a device list.";
258 OnManagerPropertyChange(shill::kDevicesProperty, it->second);
259}
260
261void ShillClient::OnManagerPropertyChange(const string& property_name,
262 const Any& property_value) {
263 if (property_name != shill::kDevicesProperty) {
264 return;
265 }
266 VLOG(3) << "Manager's device list has changed.";
267 // We're going to remove every device we haven't seen in the update.
268 set<ObjectPath> device_paths_to_remove;
269 for (const auto& kv : devices_) {
270 device_paths_to_remove.insert(kv.first);
271 }
272 for (const auto& device_path : property_value.TryGet<vector<ObjectPath>>()) {
273 if (!device_path.IsValid()) {
274 LOG(ERROR) << "Ignoring invalid device path in Manager's device list.";
275 return;
276 }
277 auto it = devices_.find(device_path);
278 if (it != devices_.end()) {
279 // Found an existing proxy. Since the whitelist never changes,
280 // this still a valid device.
281 device_paths_to_remove.erase(device_path);
282 continue;
283 }
284 std::unique_ptr<DeviceProxy> device{new DeviceProxy{bus_, device_path}};
285 if (!IsMonitoredDevice(device.get())) {
286 continue;
287 }
288 device->RegisterPropertyChangedSignalHandler(
289 base::Bind(&ShillClient::OnDevicePropertyChange,
290 weak_factory_.GetWeakPtr(), device_path),
291 base::Bind(&ShillClient::OnDevicePropertyChangeRegistration,
292 weak_factory_.GetWeakPtr(), device_path));
293 VLOG(3) << "Creating device proxy at " << device_path.value();
294 devices_[device_path].device = std::move(device);
295 }
296 // Clean up devices/services related to removed devices.
297 if (!device_paths_to_remove.empty()) {
298 for (const ObjectPath& device_path : device_paths_to_remove) {
299 devices_.erase(device_path);
300 }
301 UpdateConnectivityState();
302 }
303}
304
305void ShillClient::OnDevicePropertyChangeRegistration(
306 const ObjectPath& device_path,
307 const string& interface,
308 const string& signal_name,
309 bool success) {
310 VLOG(3) << "Registered DevicePropertyChange handler.";
311 auto it = devices_.find(device_path);
312 if (it == devices_.end()) {
313 return;
314 }
315 CHECK(success) << "Failed to subscribe to Device property changes.";
316 DeviceProxy* device = it->second.device.get();
317 VariantDictionary properties;
318 if (!device->GetProperties(&properties, nullptr)) {
319 LOG(WARNING) << "Failed to get device properties?";
320 return;
321 }
322 auto prop_it = properties.find(shill::kSelectedServiceProperty);
323 if (prop_it == properties.end()) {
324 LOG(WARNING) << "Failed to get device's selected service?";
325 return;
326 }
327 OnDevicePropertyChange(device_path, shill::kSelectedServiceProperty,
328 prop_it->second);
329}
330
331void ShillClient::OnDevicePropertyChange(const ObjectPath& device_path,
332 const string& property_name,
333 const Any& property_value) {
334 // We only care about selected services anyway.
335 if (property_name != shill::kSelectedServiceProperty) {
336 return;
337 }
338 // If the device isn't our list of whitelisted devices, ignore it.
339 auto it = devices_.find(device_path);
340 if (it == devices_.end()) {
341 return;
342 }
343 DeviceState& device_state = it->second;
344 ObjectPath service_path{property_value.TryGet<ObjectPath>()};
345 if (!service_path.IsValid()) {
346 LOG(ERROR) << "Device at " << device_path.value()
347 << " selected invalid service path.";
348 return;
349 }
350 VLOG(3) << "Device at " << it->first.value() << " has selected service at "
351 << service_path.value();
352 bool removed_old_service{false};
353 if (device_state.selected_service) {
354 if (device_state.selected_service->GetObjectPath() == service_path) {
355 return; // Spurious update?
356 }
357 device_state.selected_service.reset();
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700358 device_state.service_state = Network::State::kOffline;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700359 removed_old_service = true;
360 }
361 std::shared_ptr<ServiceProxy> new_service;
362 const bool reuse_connecting_service =
363 service_path.value() != "/" && connecting_service_ &&
364 connecting_service_->GetObjectPath() == service_path;
365 if (reuse_connecting_service) {
366 new_service = connecting_service_;
367 // When we reuse the connecting service, we need to make sure that our
368 // cached state is correct. Normally, we do this by relying reading the
369 // state when our signal handlers finish registering, but this may have
370 // happened long in the past for the connecting service.
371 string state;
372 if (GetStateForService(new_service.get(), &state)) {
373 device_state.service_state = ShillServiceStateToNetworkState(state);
374 } else {
375 LOG(WARNING) << "Failed to read properties from existing service "
376 "on selection.";
377 }
378 } else if (service_path.value() != "/") {
379 // The device has selected a new service we haven't see before.
380 new_service.reset(new ServiceProxy{bus_, service_path});
381 new_service->RegisterPropertyChangedSignalHandler(
382 base::Bind(&ShillClient::OnServicePropertyChange,
383 weak_factory_.GetWeakPtr(), service_path),
384 base::Bind(&ShillClient::OnServicePropertyChangeRegistration,
385 weak_factory_.GetWeakPtr(), service_path));
386 }
387 device_state.selected_service = new_service;
388 if (reuse_connecting_service || removed_old_service) {
389 UpdateConnectivityState();
390 }
391}
392
393void ShillClient::OnServicePropertyChangeRegistration(const ObjectPath& path,
394 const string& interface,
395 const string& signal_name,
396 bool success) {
397 VLOG(3) << "OnServicePropertyChangeRegistration(" << path.value() << ");";
398 ServiceProxy* service{nullptr};
399 if (connecting_service_ && connecting_service_->GetObjectPath() == path) {
400 // Note that the connecting service might also be a selected service.
401 service = connecting_service_.get();
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700402 if (!success)
403 CleanupConnectingService();
Vitaly Buka58a288b2015-07-31 00:33:31 -0700404 } else {
405 for (const auto& kv : devices_) {
406 if (kv.second.selected_service &&
407 kv.second.selected_service->GetObjectPath() == path) {
408 service = kv.second.selected_service.get();
409 break;
410 }
411 }
412 }
413 if (service == nullptr || !success) {
414 return; // A failure or success for a proxy we no longer care about.
415 }
416 VariantDictionary properties;
417 if (!service->GetProperties(&properties, nullptr)) {
418 return;
419 }
420 // Give ourselves property changed signals for the initial property
421 // values.
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700422 for (auto name : {shill::kStateProperty, shill::kSignalStrengthProperty,
423 shill::kErrorProperty}) {
424 auto it = properties.find(name);
425 if (it != properties.end())
426 OnServicePropertyChange(path, name, it->second);
Vitaly Buka58a288b2015-07-31 00:33:31 -0700427 }
428}
429
430void ShillClient::OnServicePropertyChange(const ObjectPath& service_path,
431 const string& property_name,
432 const Any& property_value) {
433 VLOG(3) << "ServicePropertyChange(" << service_path.value() << ", "
434 << property_name << ", ...);";
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700435
436 bool is_connecting_service =
437 connecting_service_ &&
438 connecting_service_->GetObjectPath() == service_path;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700439 if (property_name == shill::kStateProperty) {
440 const string state{property_value.TryGet<string>()};
441 if (state.empty()) {
442 VLOG(3) << "Invalid service state update.";
443 return;
444 }
445 VLOG(3) << "New service state=" << state;
446 OnStateChangeForSelectedService(service_path, state);
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700447 if (is_connecting_service)
448 OnStateChangeForConnectingService(state);
Vitaly Buka58a288b2015-07-31 00:33:31 -0700449 } else if (property_name == shill::kSignalStrengthProperty) {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700450 VLOG(3) << "Signal strength=" << property_value.TryGet<uint8_t>();
451 if (is_connecting_service)
452 OnStrengthChangeForConnectingService(property_value.TryGet<uint8_t>());
453 } else if (property_name == shill::kErrorProperty) {
454 VLOG(3) << "Error=" << property_value.TryGet<std::string>();
455 if (is_connecting_service)
456 connecting_service_error_ = property_value.TryGet<std::string>();
Vitaly Buka58a288b2015-07-31 00:33:31 -0700457 }
458}
459
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700460void ShillClient::OnStateChangeForConnectingService(const string& state) {
461 switch (ShillServiceStateToNetworkState(state)) {
Alex Vakulenkobe39e932015-10-09 08:10:36 -0700462 case Network::State::kOnline: {
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700463 auto callback = connect_done_callback_;
464 connect_done_callback_.Reset();
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700465 CleanupConnectingService();
466
467 if (!callback.is_null())
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700468 callback.Run(nullptr);
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700469 break;
470 }
Alex Vakulenkobe39e932015-10-09 08:10:36 -0700471 case Network::State::kError: {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700472 ConnectToServiceError(connecting_service_);
473 break;
474 }
475 case Network::State::kOffline:
476 case Network::State::kConnecting:
477 break;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700478 }
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700479}
480
481void ShillClient::OnErrorChangeForConnectingService(const std::string& error) {
482 if (error.empty())
483 return;
484
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700485 auto callback = connect_done_callback_;
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700486 CleanupConnectingService();
487
488 weave::ErrorPtr weave_error;
489 weave::Error::AddTo(&weave_error, FROM_HERE, kErrorDomain, error,
490 "Failed to connect to WiFi network");
491
492 if (!callback.is_null())
Alex Vakulenko297114c2015-10-12 13:45:43 -0700493 callback.Run(std::move(weave_error));
Vitaly Buka58a288b2015-07-31 00:33:31 -0700494}
495
496void ShillClient::OnStrengthChangeForConnectingService(
Vitaly Buka58a288b2015-07-31 00:33:31 -0700497 uint8_t signal_strength) {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700498 if (signal_strength == 0 || have_called_connect_) {
Vitaly Buka58a288b2015-07-31 00:33:31 -0700499 return;
500 }
501 VLOG(1) << "Connecting service has signal. Calling Connect().";
502 have_called_connect_ = true;
503 // Failures here indicate that we've already connected,
504 // or are connecting, or some other very unexciting thing.
505 // Ignore all that, and rely on state changes to detect
506 // connectivity.
507 connecting_service_->Connect(nullptr);
508}
509
510void ShillClient::OnStateChangeForSelectedService(
511 const ObjectPath& service_path,
512 const string& state) {
513 // Find the device/service pair responsible for this update
514 VLOG(3) << "State for potentially selected service " << service_path.value()
515 << " have changed to " << state;
516 for (auto& kv : devices_) {
517 if (kv.second.selected_service &&
518 kv.second.selected_service->GetObjectPath() == service_path) {
519 VLOG(3) << "Updated cached connection state for selected service.";
520 kv.second.service_state = ShillServiceStateToNetworkState(state);
521 UpdateConnectivityState();
522 return;
523 }
524 }
525}
526
527void ShillClient::UpdateConnectivityState() {
528 // Update the connectivity state of the device by picking the
529 // state of the currently most connected selected service.
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700530 Network::State new_connectivity_state{Network::State::kOffline};
Vitaly Buka58a288b2015-07-31 00:33:31 -0700531 for (const auto& kv : devices_) {
532 if (kv.second.service_state > new_connectivity_state) {
533 new_connectivity_state = kv.second.service_state;
534 }
535 }
536 VLOG(1) << "Connectivity changed: " << EnumToString(connectivity_state_)
537 << " -> " << EnumToString(new_connectivity_state);
538 // Notify listeners even if state changed to the same value. Listeners may
539 // want to handle this event.
540 connectivity_state_ = new_connectivity_state;
541 // We may call UpdateConnectivityState whenever we mutate a data structure
542 // such that our connectivity status could change. However, we don't want
543 // to allow people to call into ShillClient while some other operation is
544 // underway. Therefore, call our callbacks later, when we're in a good
545 // state.
546 base::MessageLoop::current()->PostTask(
Alex Vakulenko297114c2015-10-12 13:45:43 -0700547 FROM_HERE, base::Bind(&ShillClient::NotifyConnectivityListeners,
548 weak_factory_.GetWeakPtr(),
549 GetConnectionState() == Network::State::kOnline));
Vitaly Buka58a288b2015-07-31 00:33:31 -0700550}
551
552void ShillClient::NotifyConnectivityListeners(bool am_online) {
553 VLOG(3) << "Notifying connectivity listeners that online=" << am_online;
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700554 for (const auto& listener : connectivity_listeners_)
555 listener.Run();
Vitaly Buka58a288b2015-07-31 00:33:31 -0700556}
557
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700558void ShillClient::CleanupConnectingService() {
Vitaly Buka58a288b2015-07-31 00:33:31 -0700559 if (connecting_service_) {
560 connecting_service_->ReleaseObjectProxy(base::Bind(&IgnoreDetachEvent));
561 connecting_service_.reset();
562 }
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700563 connect_done_callback_.Reset();
Vitaly Buka58a288b2015-07-31 00:33:31 -0700564 have_called_connect_ = false;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700565}
566
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700567void ShillClient::OpenSslSocket(const std::string& host,
568 uint16_t port,
569 const OpenSslSocketCallback& callback) {
Alex Vakulenko0022b752015-10-02 11:09:59 -0700570 if (disable_xmpp_)
571 return;
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700572 std::unique_ptr<weave::Stream> raw_stream{
573 SocketStream::ConnectBlocking(host, port)};
574 if (!raw_stream) {
Alex Vakulenko41705852015-10-13 10:12:06 -0700575 brillo::ErrorPtr error;
576 brillo::errors::system::AddSystemError(&error, FROM_HERE, errno);
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700577 weave::ErrorPtr weave_error;
578 ConvertError(*error.get(), &weave_error);
579 base::MessageLoop::current()->PostTask(
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700580 FROM_HERE, base::Bind(callback, nullptr, base::Passed(&weave_error)));
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700581 return;
582 }
583
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700584 SocketStream::TlsConnect(std::move(raw_stream), host, callback);
Vitaly Buka493f6042015-08-12 16:17:16 -0700585}
586
Vitaly Buka58a288b2015-07-31 00:33:31 -0700587} // namespace buffet