blob: c68766ad192e1a6f810e56080628f1a1dc31fc2d [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
47void IgnoreDetachEvent() {}
48
49bool GetStateForService(ServiceProxy* service, string* state) {
50 CHECK(service) << "|service| was nullptr in GetStateForService()";
51 VariantDictionary properties;
52 if (!service->GetProperties(&properties, nullptr)) {
53 LOG(WARNING) << "Failed to read properties from service.";
54 return false;
55 }
56 auto property_it = properties.find(shill::kStateProperty);
57 if (property_it == properties.end()) {
58 LOG(WARNING) << "No state found in service properties.";
59 return false;
60 }
61 string new_state = property_it->second.TryGet<string>();
62 if (new_state.empty()) {
63 LOG(WARNING) << "Invalid state value.";
64 return false;
65 }
66 *state = new_state;
67 return true;
68}
69
Alex Vakulenkoe32375b2015-09-28 08:55:40 -070070Network::State ShillServiceStateToNetworkState(const string& state) {
Vitaly Buka58a288b2015-07-31 00:33:31 -070071 // TODO(wiley) What does "unconfigured" mean in a world with multiple sets
72 // of WiFi credentials?
73 // TODO(wiley) Detect disabled devices, update state appropriately.
74 if ((state.compare(shill::kStateReady) == 0) ||
75 (state.compare(shill::kStatePortal) == 0) ||
76 (state.compare(shill::kStateOnline) == 0)) {
Alex Vakulenkobe39e932015-10-09 08:10:36 -070077 return Network::State::kOnline;
Vitaly Buka58a288b2015-07-31 00:33:31 -070078 }
79 if ((state.compare(shill::kStateAssociation) == 0) ||
80 (state.compare(shill::kStateConfiguration) == 0)) {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -070081 return Network::State::kConnecting;
Vitaly Buka58a288b2015-07-31 00:33:31 -070082 }
83 if ((state.compare(shill::kStateFailure) == 0) ||
84 (state.compare(shill::kStateActivationFailure) == 0)) {
85 // TODO(wiley) Get error information off the service object.
Alex Vakulenkobe39e932015-10-09 08:10:36 -070086 return Network::State::kError;
Vitaly Buka58a288b2015-07-31 00:33:31 -070087 }
88 if ((state.compare(shill::kStateIdle) == 0) ||
89 (state.compare(shill::kStateOffline) == 0) ||
90 (state.compare(shill::kStateDisconnect) == 0)) {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -070091 return Network::State::kOffline;
Vitaly Buka58a288b2015-07-31 00:33:31 -070092 }
93 LOG(WARNING) << "Unknown state found: '" << state << "'";
Alex Vakulenkoe32375b2015-09-28 08:55:40 -070094 return Network::State::kOffline;
Vitaly Buka58a288b2015-07-31 00:33:31 -070095}
96
97} // namespace
98
99ShillClient::ShillClient(const scoped_refptr<dbus::Bus>& bus,
Alex Vakulenko0022b752015-10-02 11:09:59 -0700100 const set<string>& device_whitelist,
101 bool disable_xmpp)
Vitaly Buka58a288b2015-07-31 00:33:31 -0700102 : bus_{bus},
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700103 manager_proxy_{bus_},
Vitaly Buka3ef4fff2015-07-31 01:12:07 -0700104 device_whitelist_{device_whitelist},
Alex Vakulenko0022b752015-10-02 11:09:59 -0700105 disable_xmpp_{disable_xmpp},
Vitaly Buka3ef4fff2015-07-31 01:12:07 -0700106 ap_manager_client_{new ApManagerClient(bus)} {
Vitaly Buka58a288b2015-07-31 00:33:31 -0700107 manager_proxy_.RegisterPropertyChangedSignalHandler(
108 base::Bind(&ShillClient::OnManagerPropertyChange,
109 weak_factory_.GetWeakPtr()),
110 base::Bind(&ShillClient::OnManagerPropertyChangeRegistration,
111 weak_factory_.GetWeakPtr()));
112 auto owner_changed_cb = base::Bind(&ShillClient::OnShillServiceOwnerChange,
113 weak_factory_.GetWeakPtr());
114 bus_->GetObjectProxy(shill::kFlimflamServiceName, ObjectPath{"/"})
115 ->SetNameOwnerChangedCallback(owner_changed_cb);
Vitaly Buka5d14dca2015-11-05 22:26:19 -0800116
117 Init();
Vitaly Buka58a288b2015-07-31 00:33:31 -0700118}
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 Vakulenko685b2ae2015-10-21 07:59:44 -0700141 LOG(INFO) << "Connecting to WiFi network: " << ssid;
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700142 if (connecting_service_) {
143 weave::ErrorPtr error;
Vitaly Buka34183c12016-01-22 18:30:49 -0800144 weave::Error::AddTo(&error, FROM_HERE, "busy",
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700145 "Already connecting to WiFi network");
146 base::MessageLoop::current()->PostTask(
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700147 FROM_HERE, base::Bind(callback, base::Passed(&error)));
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700148 return;
Vitaly Buka4f771532015-08-14 14:58:39 -0700149 }
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700150 CleanupConnectingService();
Vitaly Buka58a288b2015-07-31 00:33:31 -0700151 VariantDictionary service_properties;
152 service_properties[shill::kTypeProperty] = Any{string{shill::kTypeWifi}};
153 service_properties[shill::kSSIDProperty] = Any{ssid};
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700154 if (passphrase.empty()) {
155 service_properties[shill::kSecurityProperty] = Any{shill::kSecurityNone};
156 } else {
157 service_properties[shill::kPassphraseProperty] = Any{passphrase};
158 service_properties[shill::kSecurityProperty] = Any{shill::kSecurityPsk};
159 }
Vitaly Buka58a288b2015-07-31 00:33:31 -0700160 service_properties[shill::kSaveCredentialsProperty] = Any{true};
161 service_properties[shill::kAutoConnectProperty] = Any{true};
162 ObjectPath service_path;
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700163 brillo::ErrorPtr brillo_error;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700164 if (!manager_proxy_.ConfigureService(service_properties, &service_path,
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700165 &brillo_error) ||
166 !manager_proxy_.RequestScan(shill::kTypeWifi, &brillo_error)) {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700167 weave::ErrorPtr weave_error;
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700168 ConvertError(*brillo_error, &weave_error);
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700169 base::MessageLoop::current()->PostTask(
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700170 FROM_HERE, base::Bind(callback, base::Passed(&weave_error)));
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700171 return;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700172 }
173 connecting_service_.reset(new ServiceProxy{bus_, service_path});
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700174 connecting_service_->Connect(nullptr);
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700175 connect_done_callback_ = callback;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700176 connecting_service_->RegisterPropertyChangedSignalHandler(
177 base::Bind(&ShillClient::OnServicePropertyChange,
178 weak_factory_.GetWeakPtr(), service_path),
179 base::Bind(&ShillClient::OnServicePropertyChangeRegistration,
180 weak_factory_.GetWeakPtr(), service_path));
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700181 base::MessageLoop::current()->PostDelayedTask(
182 FROM_HERE, base::Bind(&ShillClient::ConnectToServiceError,
183 weak_factory_.GetWeakPtr(), connecting_service_),
184 base::TimeDelta::FromMinutes(1));
Vitaly Buka58a288b2015-07-31 00:33:31 -0700185}
186
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700187void ShillClient::ConnectToServiceError(
188 std::shared_ptr<org::chromium::flimflam::ServiceProxy> connecting_service) {
189 if (connecting_service != connecting_service_ ||
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700190 connect_done_callback_.is_null()) {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700191 return;
192 }
193 std::string error = have_called_connect_ ? connecting_service_error_
194 : shill::kErrorOutOfRange;
195 if (error.empty())
196 error = shill::kErrorInternal;
197 OnErrorChangeForConnectingService(error);
198}
199
200Network::State ShillClient::GetConnectionState() const {
Vitaly Buka58a288b2015-07-31 00:33:31 -0700201 return connectivity_state_;
202}
203
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700204void ShillClient::StartAccessPoint(const std::string& ssid) {
Alex Vakulenko685b2ae2015-10-21 07:59:44 -0700205 LOG(INFO) << "Starting Soft AP: " << ssid;
Vitaly Buka3ef4fff2015-07-31 01:12:07 -0700206 ap_manager_client_->Start(ssid);
207}
208
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700209void ShillClient::StopAccessPoint() {
Alex Vakulenko685b2ae2015-10-21 07:59:44 -0700210 LOG(INFO) << "Stopping Soft AP";
Vitaly Buka3ef4fff2015-07-31 01:12:07 -0700211 ap_manager_client_->Stop();
212}
213
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700214void ShillClient::AddConnectionChangedCallback(
215 const ConnectionChangedCallback& listener) {
Vitaly Buka58a288b2015-07-31 00:33:31 -0700216 connectivity_listeners_.push_back(listener);
217}
218
219bool ShillClient::IsMonitoredDevice(DeviceProxy* device) {
220 if (device_whitelist_.empty()) {
221 return true;
222 }
223 VariantDictionary device_properties;
224 if (!device->GetProperties(&device_properties, nullptr)) {
225 LOG(ERROR) << "Devices without properties aren't whitelisted.";
226 return false;
227 }
228 auto it = device_properties.find(shill::kInterfaceProperty);
229 if (it == device_properties.end()) {
230 LOG(ERROR) << "Failed to find interface property in device properties.";
231 return false;
232 }
233 return ContainsKey(device_whitelist_, it->second.TryGet<string>());
234}
235
236void ShillClient::OnShillServiceOwnerChange(const string& old_owner,
237 const string& new_owner) {
238 VLOG(1) << "Shill service owner name changed to '" << new_owner << "'";
239 if (new_owner.empty()) {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700240 CleanupConnectingService();
Vitaly Buka58a288b2015-07-31 00:33:31 -0700241 devices_.clear();
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700242 connectivity_state_ = Network::State::kOffline;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700243 } else {
244 Init(); // New service owner means shill reset!
245 }
246}
247
248void ShillClient::OnManagerPropertyChangeRegistration(const string& interface,
249 const string& signal_name,
250 bool success) {
251 VLOG(3) << "Registered ManagerPropertyChange handler.";
252 CHECK(success) << "privetd requires Manager signals.";
253 VariantDictionary properties;
254 if (!manager_proxy_.GetProperties(&properties, nullptr)) {
255 LOG(ERROR) << "Unable to get properties from Manager, waiting for "
256 "Manager to come back online.";
257 return;
258 }
259 auto it = properties.find(shill::kDevicesProperty);
260 CHECK(it != properties.end()) << "Shill should always publish a device list.";
261 OnManagerPropertyChange(shill::kDevicesProperty, it->second);
262}
263
264void ShillClient::OnManagerPropertyChange(const string& property_name,
265 const Any& property_value) {
266 if (property_name != shill::kDevicesProperty) {
267 return;
268 }
Vitaly Buka5d14dca2015-11-05 22:26:19 -0800269 bool update_connectivity = false;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700270 VLOG(3) << "Manager's device list has changed.";
271 // We're going to remove every device we haven't seen in the update.
272 set<ObjectPath> device_paths_to_remove;
273 for (const auto& kv : devices_) {
274 device_paths_to_remove.insert(kv.first);
275 }
276 for (const auto& device_path : property_value.TryGet<vector<ObjectPath>>()) {
277 if (!device_path.IsValid()) {
278 LOG(ERROR) << "Ignoring invalid device path in Manager's device list.";
279 return;
280 }
281 auto it = devices_.find(device_path);
282 if (it != devices_.end()) {
283 // Found an existing proxy. Since the whitelist never changes,
284 // this still a valid device.
285 device_paths_to_remove.erase(device_path);
286 continue;
287 }
288 std::unique_ptr<DeviceProxy> device{new DeviceProxy{bus_, device_path}};
289 if (!IsMonitoredDevice(device.get())) {
290 continue;
291 }
Vitaly Buka5d14dca2015-11-05 22:26:19 -0800292 VLOG(3) << "Creating device proxy at " << device_path.value();
293 devices_[device_path].device = std::move(device);
294 update_connectivity = true;
295 devices_[device_path].device->RegisterPropertyChangedSignalHandler(
Vitaly Buka58a288b2015-07-31 00:33:31 -0700296 base::Bind(&ShillClient::OnDevicePropertyChange,
297 weak_factory_.GetWeakPtr(), device_path),
298 base::Bind(&ShillClient::OnDevicePropertyChangeRegistration,
299 weak_factory_.GetWeakPtr(), device_path));
Vitaly Buka58a288b2015-07-31 00:33:31 -0700300 }
301 // Clean up devices/services related to removed devices.
Vitaly Buka5d14dca2015-11-05 22:26:19 -0800302 for (const ObjectPath& device_path : device_paths_to_remove) {
303 devices_.erase(device_path);
304 update_connectivity = true;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700305 }
Vitaly Buka5d14dca2015-11-05 22:26:19 -0800306
307 if (update_connectivity)
308 UpdateConnectivityState();
Vitaly Buka58a288b2015-07-31 00:33:31 -0700309}
310
311void ShillClient::OnDevicePropertyChangeRegistration(
312 const ObjectPath& device_path,
313 const string& interface,
314 const string& signal_name,
315 bool success) {
316 VLOG(3) << "Registered DevicePropertyChange handler.";
317 auto it = devices_.find(device_path);
318 if (it == devices_.end()) {
319 return;
320 }
321 CHECK(success) << "Failed to subscribe to Device property changes.";
322 DeviceProxy* device = it->second.device.get();
323 VariantDictionary properties;
324 if (!device->GetProperties(&properties, nullptr)) {
325 LOG(WARNING) << "Failed to get device properties?";
326 return;
327 }
328 auto prop_it = properties.find(shill::kSelectedServiceProperty);
329 if (prop_it == properties.end()) {
330 LOG(WARNING) << "Failed to get device's selected service?";
331 return;
332 }
333 OnDevicePropertyChange(device_path, shill::kSelectedServiceProperty,
334 prop_it->second);
335}
336
337void ShillClient::OnDevicePropertyChange(const ObjectPath& device_path,
338 const string& property_name,
339 const Any& property_value) {
340 // We only care about selected services anyway.
341 if (property_name != shill::kSelectedServiceProperty) {
342 return;
343 }
344 // If the device isn't our list of whitelisted devices, ignore it.
345 auto it = devices_.find(device_path);
346 if (it == devices_.end()) {
347 return;
348 }
349 DeviceState& device_state = it->second;
350 ObjectPath service_path{property_value.TryGet<ObjectPath>()};
351 if (!service_path.IsValid()) {
352 LOG(ERROR) << "Device at " << device_path.value()
353 << " selected invalid service path.";
354 return;
355 }
356 VLOG(3) << "Device at " << it->first.value() << " has selected service at "
357 << service_path.value();
358 bool removed_old_service{false};
359 if (device_state.selected_service) {
360 if (device_state.selected_service->GetObjectPath() == service_path) {
361 return; // Spurious update?
362 }
363 device_state.selected_service.reset();
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700364 device_state.service_state = Network::State::kOffline;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700365 removed_old_service = true;
366 }
Vitaly Buka58a288b2015-07-31 00:33:31 -0700367 const bool reuse_connecting_service =
368 service_path.value() != "/" && connecting_service_ &&
369 connecting_service_->GetObjectPath() == service_path;
370 if (reuse_connecting_service) {
Vitaly Buka5d14dca2015-11-05 22:26:19 -0800371 device_state.selected_service = connecting_service_;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700372 // When we reuse the connecting service, we need to make sure that our
373 // cached state is correct. Normally, we do this by relying reading the
374 // state when our signal handlers finish registering, but this may have
375 // happened long in the past for the connecting service.
376 string state;
Vitaly Buka5d14dca2015-11-05 22:26:19 -0800377 if (GetStateForService(connecting_service_.get(), &state)) {
Vitaly Buka58a288b2015-07-31 00:33:31 -0700378 device_state.service_state = ShillServiceStateToNetworkState(state);
379 } else {
380 LOG(WARNING) << "Failed to read properties from existing service "
381 "on selection.";
382 }
383 } else if (service_path.value() != "/") {
384 // The device has selected a new service we haven't see before.
Vitaly Buka5d14dca2015-11-05 22:26:19 -0800385 device_state.selected_service =
386 std::make_shared<ServiceProxy>(bus_, service_path);
387 device_state.selected_service->RegisterPropertyChangedSignalHandler(
Vitaly Buka58a288b2015-07-31 00:33:31 -0700388 base::Bind(&ShillClient::OnServicePropertyChange,
389 weak_factory_.GetWeakPtr(), service_path),
390 base::Bind(&ShillClient::OnServicePropertyChangeRegistration,
391 weak_factory_.GetWeakPtr(), service_path));
392 }
Vitaly Buka5d14dca2015-11-05 22:26:19 -0800393
Vitaly Buka58a288b2015-07-31 00:33:31 -0700394 if (reuse_connecting_service || removed_old_service) {
395 UpdateConnectivityState();
396 }
397}
398
399void ShillClient::OnServicePropertyChangeRegistration(const ObjectPath& path,
400 const string& interface,
401 const string& signal_name,
402 bool success) {
403 VLOG(3) << "OnServicePropertyChangeRegistration(" << path.value() << ");";
404 ServiceProxy* service{nullptr};
405 if (connecting_service_ && connecting_service_->GetObjectPath() == path) {
406 // Note that the connecting service might also be a selected service.
407 service = connecting_service_.get();
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700408 if (!success)
409 CleanupConnectingService();
Vitaly Buka58a288b2015-07-31 00:33:31 -0700410 } else {
411 for (const auto& kv : devices_) {
412 if (kv.second.selected_service &&
413 kv.second.selected_service->GetObjectPath() == path) {
414 service = kv.second.selected_service.get();
415 break;
416 }
417 }
418 }
419 if (service == nullptr || !success) {
420 return; // A failure or success for a proxy we no longer care about.
421 }
422 VariantDictionary properties;
423 if (!service->GetProperties(&properties, nullptr)) {
424 return;
425 }
426 // Give ourselves property changed signals for the initial property
427 // values.
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700428 for (auto name : {shill::kStateProperty, shill::kSignalStrengthProperty,
429 shill::kErrorProperty}) {
430 auto it = properties.find(name);
431 if (it != properties.end())
432 OnServicePropertyChange(path, name, it->second);
Vitaly Buka58a288b2015-07-31 00:33:31 -0700433 }
434}
435
436void ShillClient::OnServicePropertyChange(const ObjectPath& service_path,
437 const string& property_name,
438 const Any& property_value) {
439 VLOG(3) << "ServicePropertyChange(" << service_path.value() << ", "
440 << property_name << ", ...);";
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700441
442 bool is_connecting_service =
443 connecting_service_ &&
444 connecting_service_->GetObjectPath() == service_path;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700445 if (property_name == shill::kStateProperty) {
446 const string state{property_value.TryGet<string>()};
447 if (state.empty()) {
448 VLOG(3) << "Invalid service state update.";
449 return;
450 }
451 VLOG(3) << "New service state=" << state;
452 OnStateChangeForSelectedService(service_path, state);
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700453 if (is_connecting_service)
454 OnStateChangeForConnectingService(state);
Vitaly Buka58a288b2015-07-31 00:33:31 -0700455 } else if (property_name == shill::kSignalStrengthProperty) {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700456 VLOG(3) << "Signal strength=" << property_value.TryGet<uint8_t>();
457 if (is_connecting_service)
458 OnStrengthChangeForConnectingService(property_value.TryGet<uint8_t>());
459 } else if (property_name == shill::kErrorProperty) {
460 VLOG(3) << "Error=" << property_value.TryGet<std::string>();
461 if (is_connecting_service)
462 connecting_service_error_ = property_value.TryGet<std::string>();
Vitaly Buka58a288b2015-07-31 00:33:31 -0700463 }
464}
465
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700466void ShillClient::OnStateChangeForConnectingService(const string& state) {
467 switch (ShillServiceStateToNetworkState(state)) {
Alex Vakulenkobe39e932015-10-09 08:10:36 -0700468 case Network::State::kOnline: {
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700469 auto callback = connect_done_callback_;
470 connect_done_callback_.Reset();
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700471 CleanupConnectingService();
472
473 if (!callback.is_null())
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700474 callback.Run(nullptr);
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700475 break;
476 }
Alex Vakulenkobe39e932015-10-09 08:10:36 -0700477 case Network::State::kError: {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700478 ConnectToServiceError(connecting_service_);
479 break;
480 }
481 case Network::State::kOffline:
482 case Network::State::kConnecting:
483 break;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700484 }
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700485}
486
487void ShillClient::OnErrorChangeForConnectingService(const std::string& error) {
488 if (error.empty())
489 return;
490
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700491 auto callback = connect_done_callback_;
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700492 CleanupConnectingService();
493
494 weave::ErrorPtr weave_error;
Vitaly Buka34183c12016-01-22 18:30:49 -0800495 weave::Error::AddTo(&weave_error, FROM_HERE, error,
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700496 "Failed to connect to WiFi network");
497
498 if (!callback.is_null())
Alex Vakulenko297114c2015-10-12 13:45:43 -0700499 callback.Run(std::move(weave_error));
Vitaly Buka58a288b2015-07-31 00:33:31 -0700500}
501
502void ShillClient::OnStrengthChangeForConnectingService(
Vitaly Buka58a288b2015-07-31 00:33:31 -0700503 uint8_t signal_strength) {
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700504 if (signal_strength == 0 || have_called_connect_) {
Vitaly Buka58a288b2015-07-31 00:33:31 -0700505 return;
506 }
507 VLOG(1) << "Connecting service has signal. Calling Connect().";
508 have_called_connect_ = true;
509 // Failures here indicate that we've already connected,
510 // or are connecting, or some other very unexciting thing.
511 // Ignore all that, and rely on state changes to detect
512 // connectivity.
513 connecting_service_->Connect(nullptr);
514}
515
516void ShillClient::OnStateChangeForSelectedService(
517 const ObjectPath& service_path,
518 const string& state) {
519 // Find the device/service pair responsible for this update
520 VLOG(3) << "State for potentially selected service " << service_path.value()
521 << " have changed to " << state;
522 for (auto& kv : devices_) {
523 if (kv.second.selected_service &&
524 kv.second.selected_service->GetObjectPath() == service_path) {
525 VLOG(3) << "Updated cached connection state for selected service.";
526 kv.second.service_state = ShillServiceStateToNetworkState(state);
527 UpdateConnectivityState();
528 return;
529 }
530 }
531}
532
533void ShillClient::UpdateConnectivityState() {
534 // Update the connectivity state of the device by picking the
535 // state of the currently most connected selected service.
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700536 Network::State new_connectivity_state{Network::State::kOffline};
Vitaly Buka58a288b2015-07-31 00:33:31 -0700537 for (const auto& kv : devices_) {
538 if (kv.second.service_state > new_connectivity_state) {
539 new_connectivity_state = kv.second.service_state;
540 }
541 }
542 VLOG(1) << "Connectivity changed: " << EnumToString(connectivity_state_)
543 << " -> " << EnumToString(new_connectivity_state);
544 // Notify listeners even if state changed to the same value. Listeners may
545 // want to handle this event.
546 connectivity_state_ = new_connectivity_state;
547 // We may call UpdateConnectivityState whenever we mutate a data structure
548 // such that our connectivity status could change. However, we don't want
549 // to allow people to call into ShillClient while some other operation is
550 // underway. Therefore, call our callbacks later, when we're in a good
551 // state.
552 base::MessageLoop::current()->PostTask(
Alex Vakulenko297114c2015-10-12 13:45:43 -0700553 FROM_HERE, base::Bind(&ShillClient::NotifyConnectivityListeners,
554 weak_factory_.GetWeakPtr(),
555 GetConnectionState() == Network::State::kOnline));
Vitaly Buka58a288b2015-07-31 00:33:31 -0700556}
557
558void ShillClient::NotifyConnectivityListeners(bool am_online) {
559 VLOG(3) << "Notifying connectivity listeners that online=" << am_online;
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700560 for (const auto& listener : connectivity_listeners_)
561 listener.Run();
Vitaly Buka58a288b2015-07-31 00:33:31 -0700562}
563
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700564void ShillClient::CleanupConnectingService() {
Vitaly Buka58a288b2015-07-31 00:33:31 -0700565 if (connecting_service_) {
566 connecting_service_->ReleaseObjectProxy(base::Bind(&IgnoreDetachEvent));
567 connecting_service_.reset();
568 }
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700569 connect_done_callback_.Reset();
Vitaly Buka58a288b2015-07-31 00:33:31 -0700570 have_called_connect_ = false;
Vitaly Buka58a288b2015-07-31 00:33:31 -0700571}
572
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700573void ShillClient::OpenSslSocket(const std::string& host,
574 uint16_t port,
575 const OpenSslSocketCallback& callback) {
Alex Vakulenko0022b752015-10-02 11:09:59 -0700576 if (disable_xmpp_)
577 return;
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700578 std::unique_ptr<weave::Stream> raw_stream{
579 SocketStream::ConnectBlocking(host, port)};
580 if (!raw_stream) {
Alex Vakulenko41705852015-10-13 10:12:06 -0700581 brillo::ErrorPtr error;
582 brillo::errors::system::AddSystemError(&error, FROM_HERE, errno);
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700583 weave::ErrorPtr weave_error;
584 ConvertError(*error.get(), &weave_error);
585 base::MessageLoop::current()->PostTask(
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700586 FROM_HERE, base::Bind(callback, nullptr, base::Passed(&weave_error)));
Alex Vakulenkoe32375b2015-09-28 08:55:40 -0700587 return;
588 }
589
Alex Vakulenko94f8eba2015-10-14 08:52:45 -0700590 SocketStream::TlsConnect(std::move(raw_stream), host, callback);
Vitaly Buka493f6042015-08-12 16:17:16 -0700591}
592
Vitaly Buka58a288b2015-07-31 00:33:31 -0700593} // namespace buffet