1cad20f0768bb6f2b5b647c7663e9bfc4e7ac3cb7Vitaly Buka// Copyright 2015 The Android Open Source Project
2cad20f0768bb6f2b5b647c7663e9bfc4e7ac3cb7Vitaly Buka//
3cad20f0768bb6f2b5b647c7663e9bfc4e7ac3cb7Vitaly Buka// Licensed under the Apache License, Version 2.0 (the "License");
4cad20f0768bb6f2b5b647c7663e9bfc4e7ac3cb7Vitaly Buka// you may not use this file except in compliance with the License.
5cad20f0768bb6f2b5b647c7663e9bfc4e7ac3cb7Vitaly Buka// You may obtain a copy of the License at
6cad20f0768bb6f2b5b647c7663e9bfc4e7ac3cb7Vitaly Buka//
7cad20f0768bb6f2b5b647c7663e9bfc4e7ac3cb7Vitaly Buka//      http://www.apache.org/licenses/LICENSE-2.0
8cad20f0768bb6f2b5b647c7663e9bfc4e7ac3cb7Vitaly Buka//
9cad20f0768bb6f2b5b647c7663e9bfc4e7ac3cb7Vitaly Buka// Unless required by applicable law or agreed to in writing, software
10cad20f0768bb6f2b5b647c7663e9bfc4e7ac3cb7Vitaly Buka// distributed under the License is distributed on an "AS IS" BASIS,
11cad20f0768bb6f2b5b647c7663e9bfc4e7ac3cb7Vitaly Buka// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12cad20f0768bb6f2b5b647c7663e9bfc4e7ac3cb7Vitaly Buka// See the License for the specific language governing permissions and
13cad20f0768bb6f2b5b647c7663e9bfc4e7ac3cb7Vitaly Buka// limitations under the License.
1458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka
1558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka#include "buffet/shill_client.h"
1658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka
1758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka#include <set>
1858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka
1958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka#include <base/message_loop/message_loop.h>
2058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka#include <base/stl_util.h>
214170585fe75d99036883229081420f2972dd4ec1Alex Vakulenko#include <brillo/any.h>
224170585fe75d99036883229081420f2972dd4ec1Alex Vakulenko#include <brillo/errors/error.h>
2394f8eba2f71f0d73e15bfd5b1c28bb0ed682706dAlex Vakulenko#include <brillo/variant_dictionary.h>
24786a90630feaa70b81e029edd4f6620e7ab3a211Peter Qiu#include <dbus/shill/dbus-constants.h>
25e2713aceb7b2fb9d2486cfdd983e44a1f8832ecbVitaly Buka#include <weave/enum_to_string.h>
2658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka
273ef4fff987aa1da566262907f993cd3807c37a62Vitaly Buka#include "buffet/ap_manager_client.h"
28493f604eb1cb5bff0a4af86e6cb9e79983d543f6Vitaly Buka#include "buffet/socket_stream.h"
294f77153a124969f9bc48a498d37c943a82a82527Vitaly Buka#include "buffet/weave_error_conversion.h"
3058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka
314170585fe75d99036883229081420f2972dd4ec1Alex Vakulenkousing brillo::Any;
324170585fe75d99036883229081420f2972dd4ec1Alex Vakulenkousing brillo::VariantDictionary;
3358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Bukausing dbus::ObjectPath;
3458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Bukausing org::chromium::flimflam::DeviceProxy;
3558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Bukausing org::chromium::flimflam::ServiceProxy;
3658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Bukausing std::map;
3758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Bukausing std::set;
3858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Bukausing std::string;
3958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Bukausing std::vector;
40e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenkousing weave::EnumToString;
41e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenkousing weave::provider::Network;
4258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka
4358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Bukanamespace buffet {
4458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka
4558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Bukanamespace {
4658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka
4758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Bukavoid IgnoreDetachEvent() {}
4858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka
4958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Bukabool GetStateForService(ServiceProxy* service, string* state) {
5058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  CHECK(service) << "|service| was nullptr in GetStateForService()";
5158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  VariantDictionary properties;
5258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  if (!service->GetProperties(&properties, nullptr)) {
5358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    LOG(WARNING) << "Failed to read properties from service.";
5458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    return false;
5558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
5658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  auto property_it = properties.find(shill::kStateProperty);
5758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  if (property_it == properties.end()) {
5858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    LOG(WARNING) << "No state found in service properties.";
5958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    return false;
6058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
6158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  string new_state = property_it->second.TryGet<string>();
6258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  if (new_state.empty()) {
6358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    LOG(WARNING) << "Invalid state value.";
6458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    return false;
6558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
6658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  *state = new_state;
6758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  return true;
6858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka}
6958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka
70e32375bdf73d6e79a003a194e4c6931f38348520Alex VakulenkoNetwork::State ShillServiceStateToNetworkState(const string& state) {
7158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  // TODO(wiley) What does "unconfigured" mean in a world with multiple sets
7258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  //             of WiFi credentials?
7358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  // TODO(wiley) Detect disabled devices, update state appropriately.
7458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  if ((state.compare(shill::kStateReady) == 0) ||
7558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka      (state.compare(shill::kStatePortal) == 0) ||
7658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka      (state.compare(shill::kStateOnline) == 0)) {
77be39e9339baa85af546344da40b0663a6aea1a97Alex Vakulenko    return Network::State::kOnline;
7858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
7958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  if ((state.compare(shill::kStateAssociation) == 0) ||
8058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka      (state.compare(shill::kStateConfiguration) == 0)) {
81e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    return Network::State::kConnecting;
8258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
8358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  if ((state.compare(shill::kStateFailure) == 0) ||
8458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka      (state.compare(shill::kStateActivationFailure) == 0)) {
8558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    // TODO(wiley) Get error information off the service object.
86be39e9339baa85af546344da40b0663a6aea1a97Alex Vakulenko    return Network::State::kError;
8758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
8858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  if ((state.compare(shill::kStateIdle) == 0) ||
8958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka      (state.compare(shill::kStateOffline) == 0) ||
9058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka      (state.compare(shill::kStateDisconnect) == 0)) {
91e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    return Network::State::kOffline;
9258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
9358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  LOG(WARNING) << "Unknown state found: '" << state << "'";
94e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko  return Network::State::kOffline;
9558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka}
9658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka
9758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka}  // namespace
9858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka
9958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly BukaShillClient::ShillClient(const scoped_refptr<dbus::Bus>& bus,
1000022b7523bca3bde23c0f982384f83a39791e88bAlex Vakulenko                         const set<string>& device_whitelist,
1010022b7523bca3bde23c0f982384f83a39791e88bAlex Vakulenko                         bool disable_xmpp)
10258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    : bus_{bus},
103e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko      manager_proxy_{bus_},
1043ef4fff987aa1da566262907f993cd3807c37a62Vitaly Buka      device_whitelist_{device_whitelist},
1050022b7523bca3bde23c0f982384f83a39791e88bAlex Vakulenko      disable_xmpp_{disable_xmpp},
1063ef4fff987aa1da566262907f993cd3807c37a62Vitaly Buka      ap_manager_client_{new ApManagerClient(bus)} {
10758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  manager_proxy_.RegisterPropertyChangedSignalHandler(
10858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka      base::Bind(&ShillClient::OnManagerPropertyChange,
10958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka                 weak_factory_.GetWeakPtr()),
11058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka      base::Bind(&ShillClient::OnManagerPropertyChangeRegistration,
11158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka                 weak_factory_.GetWeakPtr()));
11258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  auto owner_changed_cb = base::Bind(&ShillClient::OnShillServiceOwnerChange,
11358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka                                     weak_factory_.GetWeakPtr());
11458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  bus_->GetObjectProxy(shill::kFlimflamServiceName, ObjectPath{"/"})
11558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka      ->SetNameOwnerChangedCallback(owner_changed_cb);
1165d14dca2905a70a814a15044fff0f555d7e333c1Vitaly Buka
1175d14dca2905a70a814a15044fff0f555d7e333c1Vitaly Buka  Init();
11858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka}
11958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka
1203ef4fff987aa1da566262907f993cd3807c37a62Vitaly BukaShillClient::~ShillClient() {}
1213ef4fff987aa1da566262907f993cd3807c37a62Vitaly Buka
12258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Bukavoid ShillClient::Init() {
12358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  VLOG(2) << "ShillClient::Init();";
124e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko  CleanupConnectingService();
12558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  devices_.clear();
126e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko  connectivity_state_ = Network::State::kOffline;
12758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  VariantDictionary properties;
12858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  if (!manager_proxy_.GetProperties(&properties, nullptr)) {
12958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    LOG(ERROR) << "Unable to get properties from Manager, waiting for "
13058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka                  "Manager to come back online.";
13158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    return;
13258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
13358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  auto it = properties.find(shill::kDevicesProperty);
13458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  CHECK(it != properties.end()) << "shill should always publish a device list.";
13558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  OnManagerPropertyChange(shill::kDevicesProperty, it->second);
13658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka}
13758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka
138e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenkovoid ShillClient::Connect(const string& ssid,
139e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko                          const string& passphrase,
14094f8eba2f71f0d73e15bfd5b1c28bb0ed682706dAlex Vakulenko                          const weave::DoneCallback& callback) {
141685b2aeed879903b86448428dec1e88c9b6741b1Alex Vakulenko  LOG(INFO) << "Connecting to WiFi network: " << ssid;
142e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko  if (connecting_service_) {
143e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    weave::ErrorPtr error;
14434183c1b5b678a9726a955db9e05a361ed05eeacVitaly Buka    weave::Error::AddTo(&error, FROM_HERE, "busy",
145e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko                        "Already connecting to WiFi network");
146e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    base::MessageLoop::current()->PostTask(
14794f8eba2f71f0d73e15bfd5b1c28bb0ed682706dAlex Vakulenko        FROM_HERE, base::Bind(callback, base::Passed(&error)));
148e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    return;
1494f77153a124969f9bc48a498d37c943a82a82527Vitaly Buka  }
150e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko  CleanupConnectingService();
15158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  VariantDictionary service_properties;
15258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  service_properties[shill::kTypeProperty] = Any{string{shill::kTypeWifi}};
15358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  service_properties[shill::kSSIDProperty] = Any{ssid};
154e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko  if (passphrase.empty()) {
155e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    service_properties[shill::kSecurityProperty] = Any{shill::kSecurityNone};
156e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko  } else {
157e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    service_properties[shill::kPassphraseProperty] = Any{passphrase};
158e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    service_properties[shill::kSecurityProperty] = Any{shill::kSecurityPsk};
159e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko  }
16058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  service_properties[shill::kSaveCredentialsProperty] = Any{true};
16158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  service_properties[shill::kAutoConnectProperty] = Any{true};
16258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  ObjectPath service_path;
16394f8eba2f71f0d73e15bfd5b1c28bb0ed682706dAlex Vakulenko  brillo::ErrorPtr brillo_error;
16458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  if (!manager_proxy_.ConfigureService(service_properties, &service_path,
16594f8eba2f71f0d73e15bfd5b1c28bb0ed682706dAlex Vakulenko                                       &brillo_error) ||
16694f8eba2f71f0d73e15bfd5b1c28bb0ed682706dAlex Vakulenko      !manager_proxy_.RequestScan(shill::kTypeWifi, &brillo_error)) {
167e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    weave::ErrorPtr weave_error;
16894f8eba2f71f0d73e15bfd5b1c28bb0ed682706dAlex Vakulenko    ConvertError(*brillo_error, &weave_error);
169e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    base::MessageLoop::current()->PostTask(
17094f8eba2f71f0d73e15bfd5b1c28bb0ed682706dAlex Vakulenko        FROM_HERE, base::Bind(callback, base::Passed(&weave_error)));
171e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    return;
17258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
17358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  connecting_service_.reset(new ServiceProxy{bus_, service_path});
174e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko  connecting_service_->Connect(nullptr);
17594f8eba2f71f0d73e15bfd5b1c28bb0ed682706dAlex Vakulenko  connect_done_callback_ = callback;
17658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  connecting_service_->RegisterPropertyChangedSignalHandler(
17758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka      base::Bind(&ShillClient::OnServicePropertyChange,
17858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka                 weak_factory_.GetWeakPtr(), service_path),
17958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka      base::Bind(&ShillClient::OnServicePropertyChangeRegistration,
18058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka                 weak_factory_.GetWeakPtr(), service_path));
181e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko  base::MessageLoop::current()->PostDelayedTask(
182e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko      FROM_HERE, base::Bind(&ShillClient::ConnectToServiceError,
183e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko                            weak_factory_.GetWeakPtr(), connecting_service_),
184e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko      base::TimeDelta::FromMinutes(1));
18558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka}
18658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka
187e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenkovoid ShillClient::ConnectToServiceError(
188e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    std::shared_ptr<org::chromium::flimflam::ServiceProxy> connecting_service) {
189e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko  if (connecting_service != connecting_service_ ||
19094f8eba2f71f0d73e15bfd5b1c28bb0ed682706dAlex Vakulenko      connect_done_callback_.is_null()) {
191e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    return;
192e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko  }
193e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko  std::string error = have_called_connect_ ? connecting_service_error_
194e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko                                           : shill::kErrorOutOfRange;
195e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko  if (error.empty())
196e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    error = shill::kErrorInternal;
197e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko  OnErrorChangeForConnectingService(error);
198e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko}
199e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko
200e32375bdf73d6e79a003a194e4c6931f38348520Alex VakulenkoNetwork::State ShillClient::GetConnectionState() const {
20158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  return connectivity_state_;
20258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka}
20358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka
204e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenkovoid ShillClient::StartAccessPoint(const std::string& ssid) {
205685b2aeed879903b86448428dec1e88c9b6741b1Alex Vakulenko  LOG(INFO) << "Starting Soft AP: " << ssid;
2063ef4fff987aa1da566262907f993cd3807c37a62Vitaly Buka  ap_manager_client_->Start(ssid);
2073ef4fff987aa1da566262907f993cd3807c37a62Vitaly Buka}
2083ef4fff987aa1da566262907f993cd3807c37a62Vitaly Buka
209e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenkovoid ShillClient::StopAccessPoint() {
210685b2aeed879903b86448428dec1e88c9b6741b1Alex Vakulenko  LOG(INFO) << "Stopping Soft AP";
2113ef4fff987aa1da566262907f993cd3807c37a62Vitaly Buka  ap_manager_client_->Stop();
2123ef4fff987aa1da566262907f993cd3807c37a62Vitaly Buka}
2133ef4fff987aa1da566262907f993cd3807c37a62Vitaly Buka
214e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenkovoid ShillClient::AddConnectionChangedCallback(
215e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    const ConnectionChangedCallback& listener) {
21658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  connectivity_listeners_.push_back(listener);
21758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka}
21858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka
21958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Bukabool ShillClient::IsMonitoredDevice(DeviceProxy* device) {
22058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  if (device_whitelist_.empty()) {
22158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    return true;
22258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
22358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  VariantDictionary device_properties;
22458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  if (!device->GetProperties(&device_properties, nullptr)) {
22558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    LOG(ERROR) << "Devices without properties aren't whitelisted.";
22658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    return false;
22758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
22858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  auto it = device_properties.find(shill::kInterfaceProperty);
22958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  if (it == device_properties.end()) {
23058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    LOG(ERROR) << "Failed to find interface property in device properties.";
23158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    return false;
23258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
23358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  return ContainsKey(device_whitelist_, it->second.TryGet<string>());
23458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka}
23558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka
23658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Bukavoid ShillClient::OnShillServiceOwnerChange(const string& old_owner,
23758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka                                            const string& new_owner) {
23858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  VLOG(1) << "Shill service owner name changed to '" << new_owner << "'";
23958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  if (new_owner.empty()) {
240e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    CleanupConnectingService();
24158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    devices_.clear();
242e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    connectivity_state_ = Network::State::kOffline;
24358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  } else {
24458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    Init();  // New service owner means shill reset!
24558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
24658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka}
24758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka
24858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Bukavoid ShillClient::OnManagerPropertyChangeRegistration(const string& interface,
24958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka                                                      const string& signal_name,
25058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka                                                      bool success) {
25158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  VLOG(3) << "Registered ManagerPropertyChange handler.";
25258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  CHECK(success) << "privetd requires Manager signals.";
25358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  VariantDictionary properties;
25458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  if (!manager_proxy_.GetProperties(&properties, nullptr)) {
25558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    LOG(ERROR) << "Unable to get properties from Manager, waiting for "
25658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka                  "Manager to come back online.";
25758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    return;
25858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
25958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  auto it = properties.find(shill::kDevicesProperty);
26058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  CHECK(it != properties.end()) << "Shill should always publish a device list.";
26158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  OnManagerPropertyChange(shill::kDevicesProperty, it->second);
26258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka}
26358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka
26458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Bukavoid ShillClient::OnManagerPropertyChange(const string& property_name,
26558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka                                          const Any& property_value) {
26658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  if (property_name != shill::kDevicesProperty) {
26758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    return;
26858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
2695d14dca2905a70a814a15044fff0f555d7e333c1Vitaly Buka  bool update_connectivity = false;
27058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  VLOG(3) << "Manager's device list has changed.";
27158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  // We're going to remove every device we haven't seen in the update.
27258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  set<ObjectPath> device_paths_to_remove;
27358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  for (const auto& kv : devices_) {
27458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    device_paths_to_remove.insert(kv.first);
27558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
27658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  for (const auto& device_path : property_value.TryGet<vector<ObjectPath>>()) {
27758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    if (!device_path.IsValid()) {
27858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka      LOG(ERROR) << "Ignoring invalid device path in Manager's device list.";
27958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka      return;
28058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    }
28158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    auto it = devices_.find(device_path);
28258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    if (it != devices_.end()) {
28358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka      // Found an existing proxy.  Since the whitelist never changes,
28458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka      // this still a valid device.
28558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka      device_paths_to_remove.erase(device_path);
28658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka      continue;
28758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    }
28858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    std::unique_ptr<DeviceProxy> device{new DeviceProxy{bus_, device_path}};
28958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    if (!IsMonitoredDevice(device.get())) {
29058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka      continue;
29158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    }
2925d14dca2905a70a814a15044fff0f555d7e333c1Vitaly Buka    VLOG(3) << "Creating device proxy at " << device_path.value();
2935d14dca2905a70a814a15044fff0f555d7e333c1Vitaly Buka    devices_[device_path].device = std::move(device);
2945d14dca2905a70a814a15044fff0f555d7e333c1Vitaly Buka    update_connectivity = true;
2955d14dca2905a70a814a15044fff0f555d7e333c1Vitaly Buka    devices_[device_path].device->RegisterPropertyChangedSignalHandler(
29658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka        base::Bind(&ShillClient::OnDevicePropertyChange,
29758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka                   weak_factory_.GetWeakPtr(), device_path),
29858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka        base::Bind(&ShillClient::OnDevicePropertyChangeRegistration,
29958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka                   weak_factory_.GetWeakPtr(), device_path));
30058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
30158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  // Clean up devices/services related to removed devices.
3025d14dca2905a70a814a15044fff0f555d7e333c1Vitaly Buka  for (const ObjectPath& device_path : device_paths_to_remove) {
3035d14dca2905a70a814a15044fff0f555d7e333c1Vitaly Buka    devices_.erase(device_path);
3045d14dca2905a70a814a15044fff0f555d7e333c1Vitaly Buka    update_connectivity = true;
30558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
3065d14dca2905a70a814a15044fff0f555d7e333c1Vitaly Buka
3075d14dca2905a70a814a15044fff0f555d7e333c1Vitaly Buka  if (update_connectivity)
3085d14dca2905a70a814a15044fff0f555d7e333c1Vitaly Buka    UpdateConnectivityState();
30958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka}
31058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka
31158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Bukavoid ShillClient::OnDevicePropertyChangeRegistration(
31258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    const ObjectPath& device_path,
31358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    const string& interface,
31458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    const string& signal_name,
31558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    bool success) {
31658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  VLOG(3) << "Registered DevicePropertyChange handler.";
31758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  auto it = devices_.find(device_path);
31858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  if (it == devices_.end()) {
31958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    return;
32058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
32158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  CHECK(success) << "Failed to subscribe to Device property changes.";
32258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  DeviceProxy* device = it->second.device.get();
32358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  VariantDictionary properties;
32458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  if (!device->GetProperties(&properties, nullptr)) {
32558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    LOG(WARNING) << "Failed to get device properties?";
32658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    return;
32758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
32858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  auto prop_it = properties.find(shill::kSelectedServiceProperty);
32958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  if (prop_it == properties.end()) {
33058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    LOG(WARNING) << "Failed to get device's selected service?";
33158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    return;
33258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
33358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  OnDevicePropertyChange(device_path, shill::kSelectedServiceProperty,
33458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka                         prop_it->second);
33558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka}
33658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka
33758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Bukavoid ShillClient::OnDevicePropertyChange(const ObjectPath& device_path,
33858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka                                         const string& property_name,
33958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka                                         const Any& property_value) {
34058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  // We only care about selected services anyway.
34158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  if (property_name != shill::kSelectedServiceProperty) {
34258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    return;
34358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
34458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  // If the device isn't our list of whitelisted devices, ignore it.
34558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  auto it = devices_.find(device_path);
34658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  if (it == devices_.end()) {
34758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    return;
34858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
34958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  DeviceState& device_state = it->second;
35058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  ObjectPath service_path{property_value.TryGet<ObjectPath>()};
35158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  if (!service_path.IsValid()) {
35258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    LOG(ERROR) << "Device at " << device_path.value()
35358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka               << " selected invalid service path.";
35458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    return;
35558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
35658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  VLOG(3) << "Device at " << it->first.value() << " has selected service at "
35758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka          << service_path.value();
35858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  bool removed_old_service{false};
35958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  if (device_state.selected_service) {
36058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    if (device_state.selected_service->GetObjectPath() == service_path) {
36158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka      return;  // Spurious update?
36258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    }
36358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    device_state.selected_service.reset();
364e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    device_state.service_state = Network::State::kOffline;
36558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    removed_old_service = true;
36658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
36758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  const bool reuse_connecting_service =
36858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka      service_path.value() != "/" && connecting_service_ &&
36958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka      connecting_service_->GetObjectPath() == service_path;
37058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  if (reuse_connecting_service) {
3715d14dca2905a70a814a15044fff0f555d7e333c1Vitaly Buka    device_state.selected_service = connecting_service_;
37258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    // When we reuse the connecting service, we need to make sure that our
37358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    // cached state is correct.  Normally, we do this by relying reading the
37458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    // state when our signal handlers finish registering, but this may have
37558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    // happened long in the past for the connecting service.
37658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    string state;
3775d14dca2905a70a814a15044fff0f555d7e333c1Vitaly Buka    if (GetStateForService(connecting_service_.get(), &state)) {
37858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka      device_state.service_state = ShillServiceStateToNetworkState(state);
37958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    } else {
38058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka      LOG(WARNING) << "Failed to read properties from existing service "
38158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka                      "on selection.";
38258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    }
38358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  } else if (service_path.value() != "/") {
38458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    // The device has selected a new service we haven't see before.
3855d14dca2905a70a814a15044fff0f555d7e333c1Vitaly Buka    device_state.selected_service =
3865d14dca2905a70a814a15044fff0f555d7e333c1Vitaly Buka        std::make_shared<ServiceProxy>(bus_, service_path);
3875d14dca2905a70a814a15044fff0f555d7e333c1Vitaly Buka    device_state.selected_service->RegisterPropertyChangedSignalHandler(
38858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka        base::Bind(&ShillClient::OnServicePropertyChange,
38958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka                   weak_factory_.GetWeakPtr(), service_path),
39058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka        base::Bind(&ShillClient::OnServicePropertyChangeRegistration,
39158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka                   weak_factory_.GetWeakPtr(), service_path));
39258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
3935d14dca2905a70a814a15044fff0f555d7e333c1Vitaly Buka
39458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  if (reuse_connecting_service || removed_old_service) {
39558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    UpdateConnectivityState();
39658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
39758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka}
39858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka
39958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Bukavoid ShillClient::OnServicePropertyChangeRegistration(const ObjectPath& path,
40058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka                                                      const string& interface,
40158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka                                                      const string& signal_name,
40258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka                                                      bool success) {
40358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  VLOG(3) << "OnServicePropertyChangeRegistration(" << path.value() << ");";
40458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  ServiceProxy* service{nullptr};
40558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  if (connecting_service_ && connecting_service_->GetObjectPath() == path) {
40658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    // Note that the connecting service might also be a selected service.
40758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    service = connecting_service_.get();
408e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    if (!success)
409e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko      CleanupConnectingService();
41058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  } else {
41158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    for (const auto& kv : devices_) {
41258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka      if (kv.second.selected_service &&
41358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka          kv.second.selected_service->GetObjectPath() == path) {
41458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka        service = kv.second.selected_service.get();
41558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka        break;
41658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka      }
41758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    }
41858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
41958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  if (service == nullptr || !success) {
42058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    return;  // A failure or success for a proxy we no longer care about.
42158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
42258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  VariantDictionary properties;
42358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  if (!service->GetProperties(&properties, nullptr)) {
42458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    return;
42558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
42658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  // Give ourselves property changed signals for the initial property
42758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  // values.
428e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko  for (auto name : {shill::kStateProperty, shill::kSignalStrengthProperty,
429e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko                    shill::kErrorProperty}) {
430e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    auto it = properties.find(name);
431e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    if (it != properties.end())
432e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko      OnServicePropertyChange(path, name, it->second);
43358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
43458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka}
43558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka
43658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Bukavoid ShillClient::OnServicePropertyChange(const ObjectPath& service_path,
43758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka                                          const string& property_name,
43858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka                                          const Any& property_value) {
43958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  VLOG(3) << "ServicePropertyChange(" << service_path.value() << ", "
44058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka          << property_name << ", ...);";
441e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko
442e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko  bool is_connecting_service =
443e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko      connecting_service_ &&
444e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko      connecting_service_->GetObjectPath() == service_path;
44558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  if (property_name == shill::kStateProperty) {
44658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    const string state{property_value.TryGet<string>()};
44758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    if (state.empty()) {
44858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka      VLOG(3) << "Invalid service state update.";
44958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka      return;
45058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    }
45158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    VLOG(3) << "New service state=" << state;
45258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    OnStateChangeForSelectedService(service_path, state);
453e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    if (is_connecting_service)
454e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko      OnStateChangeForConnectingService(state);
45558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  } else if (property_name == shill::kSignalStrengthProperty) {
456e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    VLOG(3) << "Signal strength=" << property_value.TryGet<uint8_t>();
457e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    if (is_connecting_service)
458e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko      OnStrengthChangeForConnectingService(property_value.TryGet<uint8_t>());
459e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko  } else if (property_name == shill::kErrorProperty) {
460e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    VLOG(3) << "Error=" << property_value.TryGet<std::string>();
461e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    if (is_connecting_service)
462e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko      connecting_service_error_ = property_value.TryGet<std::string>();
46358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
46458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka}
46558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka
466e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenkovoid ShillClient::OnStateChangeForConnectingService(const string& state) {
467e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko  switch (ShillServiceStateToNetworkState(state)) {
468be39e9339baa85af546344da40b0663a6aea1a97Alex Vakulenko    case Network::State::kOnline: {
46994f8eba2f71f0d73e15bfd5b1c28bb0ed682706dAlex Vakulenko      auto callback = connect_done_callback_;
47094f8eba2f71f0d73e15bfd5b1c28bb0ed682706dAlex Vakulenko      connect_done_callback_.Reset();
471e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko      CleanupConnectingService();
472e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko
473e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko      if (!callback.is_null())
47494f8eba2f71f0d73e15bfd5b1c28bb0ed682706dAlex Vakulenko        callback.Run(nullptr);
475e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko      break;
476e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    }
477be39e9339baa85af546344da40b0663a6aea1a97Alex Vakulenko    case Network::State::kError: {
478e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko      ConnectToServiceError(connecting_service_);
479e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko      break;
480e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    }
481e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    case Network::State::kOffline:
482e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    case Network::State::kConnecting:
483e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko      break;
48458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
485e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko}
486e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko
487e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenkovoid ShillClient::OnErrorChangeForConnectingService(const std::string& error) {
488e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko  if (error.empty())
489e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    return;
490e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko
49194f8eba2f71f0d73e15bfd5b1c28bb0ed682706dAlex Vakulenko  auto callback = connect_done_callback_;
492e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko  CleanupConnectingService();
493e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko
494e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko  weave::ErrorPtr weave_error;
49534183c1b5b678a9726a955db9e05a361ed05eeacVitaly Buka  weave::Error::AddTo(&weave_error, FROM_HERE, error,
496e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko                      "Failed to connect to WiFi network");
497e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko
498e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko  if (!callback.is_null())
499297114c6c820d71bba206d1b2d38c98abc8540d9Alex Vakulenko    callback.Run(std::move(weave_error));
50058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka}
50158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka
50258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Bukavoid ShillClient::OnStrengthChangeForConnectingService(
50358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    uint8_t signal_strength) {
504e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko  if (signal_strength == 0 || have_called_connect_) {
50558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    return;
50658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
50758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  VLOG(1) << "Connecting service has signal. Calling Connect().";
50858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  have_called_connect_ = true;
50958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  // Failures here indicate that we've already connected,
51058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  // or are connecting, or some other very unexciting thing.
51158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  // Ignore all that, and rely on state changes to detect
51258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  // connectivity.
51358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  connecting_service_->Connect(nullptr);
51458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka}
51558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka
51658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Bukavoid ShillClient::OnStateChangeForSelectedService(
51758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    const ObjectPath& service_path,
51858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    const string& state) {
51958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  // Find the device/service pair responsible for this update
52058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  VLOG(3) << "State for potentially selected service " << service_path.value()
52158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka          << " have changed to " << state;
52258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  for (auto& kv : devices_) {
52358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    if (kv.second.selected_service &&
52458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka        kv.second.selected_service->GetObjectPath() == service_path) {
52558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka      VLOG(3) << "Updated cached connection state for selected service.";
52658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka      kv.second.service_state = ShillServiceStateToNetworkState(state);
52758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka      UpdateConnectivityState();
52858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka      return;
52958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    }
53058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
53158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka}
53258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka
53358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Bukavoid ShillClient::UpdateConnectivityState() {
53458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  // Update the connectivity state of the device by picking the
53558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  // state of the currently most connected selected service.
536e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko  Network::State new_connectivity_state{Network::State::kOffline};
53758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  for (const auto& kv : devices_) {
53858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    if (kv.second.service_state > new_connectivity_state) {
53958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka      new_connectivity_state = kv.second.service_state;
54058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    }
54158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
54258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  VLOG(1) << "Connectivity changed: " << EnumToString(connectivity_state_)
54358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka          << " -> " << EnumToString(new_connectivity_state);
54458a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  // Notify listeners even if state changed to the same value. Listeners may
54558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  // want to handle this event.
54658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  connectivity_state_ = new_connectivity_state;
54758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  // We may call UpdateConnectivityState whenever we mutate a data structure
54858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  // such that our connectivity status could change.  However, we don't want
54958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  // to allow people to call into ShillClient while some other operation is
55058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  // underway.  Therefore, call our callbacks later, when we're in a good
55158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  // state.
55258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  base::MessageLoop::current()->PostTask(
553297114c6c820d71bba206d1b2d38c98abc8540d9Alex Vakulenko      FROM_HERE, base::Bind(&ShillClient::NotifyConnectivityListeners,
554297114c6c820d71bba206d1b2d38c98abc8540d9Alex Vakulenko                            weak_factory_.GetWeakPtr(),
555297114c6c820d71bba206d1b2d38c98abc8540d9Alex Vakulenko                            GetConnectionState() == Network::State::kOnline));
55658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka}
55758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka
55858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Bukavoid ShillClient::NotifyConnectivityListeners(bool am_online) {
55958a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  VLOG(3) << "Notifying connectivity listeners that online=" << am_online;
560e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko  for (const auto& listener : connectivity_listeners_)
561e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    listener.Run();
56258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka}
56358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka
564e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenkovoid ShillClient::CleanupConnectingService() {
56558a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  if (connecting_service_) {
56658a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    connecting_service_->ReleaseObjectProxy(base::Bind(&IgnoreDetachEvent));
56758a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka    connecting_service_.reset();
56858a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  }
56994f8eba2f71f0d73e15bfd5b1c28bb0ed682706dAlex Vakulenko  connect_done_callback_.Reset();
57058a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka  have_called_connect_ = false;
57158a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka}
57258a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka
57394f8eba2f71f0d73e15bfd5b1c28bb0ed682706dAlex Vakulenkovoid ShillClient::OpenSslSocket(const std::string& host,
57494f8eba2f71f0d73e15bfd5b1c28bb0ed682706dAlex Vakulenko                                uint16_t port,
57594f8eba2f71f0d73e15bfd5b1c28bb0ed682706dAlex Vakulenko                                const OpenSslSocketCallback& callback) {
5760022b7523bca3bde23c0f982384f83a39791e88bAlex Vakulenko  if (disable_xmpp_)
5770022b7523bca3bde23c0f982384f83a39791e88bAlex Vakulenko    return;
578e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko  std::unique_ptr<weave::Stream> raw_stream{
579e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko      SocketStream::ConnectBlocking(host, port)};
580e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko  if (!raw_stream) {
5814170585fe75d99036883229081420f2972dd4ec1Alex Vakulenko    brillo::ErrorPtr error;
5824170585fe75d99036883229081420f2972dd4ec1Alex Vakulenko    brillo::errors::system::AddSystemError(&error, FROM_HERE, errno);
583e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    weave::ErrorPtr weave_error;
584e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    ConvertError(*error.get(), &weave_error);
585e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    base::MessageLoop::current()->PostTask(
58694f8eba2f71f0d73e15bfd5b1c28bb0ed682706dAlex Vakulenko        FROM_HERE, base::Bind(callback, nullptr, base::Passed(&weave_error)));
587e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko    return;
588e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko  }
589e32375bdf73d6e79a003a194e4c6931f38348520Alex Vakulenko
59094f8eba2f71f0d73e15bfd5b1c28bb0ed682706dAlex Vakulenko  SocketStream::TlsConnect(std::move(raw_stream), host, callback);
591493f604eb1cb5bff0a4af86e6cb9e79983d543f6Vitaly Buka}
592493f604eb1cb5bff0a4af86e6cb9e79983d543f6Vitaly Buka
59358a288b5b10aec975c7bc4fbfdcbcb80d5899c12Vitaly Buka}  // namespace buffet
594