buffet: Move privetd sources into buffet
No functional changes, only renaming, fixed include paths and include
guards to avoid resubmit warnings.
BUG=brillo:1161
CQ-DEPEND=CL:276521
TEST=none
Change-Id: Icacff92aef47fdd46542bc96eba3ffbb4df6241a
Reviewed-on: https://chromium-review.googlesource.com/276319
Reviewed-by: Vitaly Buka <vitalybuka@chromium.org>
Commit-Queue: Vitaly Buka <vitalybuka@chromium.org>
Tested-by: Vitaly Buka <vitalybuka@chromium.org>
diff --git a/buffet/privet/shill_client.cc b/buffet/privet/shill_client.cc
new file mode 100644
index 0000000..335d568
--- /dev/null
+++ b/buffet/privet/shill_client.cc
@@ -0,0 +1,515 @@
+// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "buffet/privet/shill_client.h"
+
+#include <set>
+
+#include <base/message_loop/message_loop.h>
+#include <base/stl_util.h>
+#include <chromeos/any.h>
+#include <chromeos/dbus/service_constants.h>
+#include <chromeos/errors/error.h>
+#include <chromeos/errors/error_codes.h>
+
+using chromeos::Any;
+using chromeos::VariantDictionary;
+using dbus::ObjectPath;
+using org::chromium::flimflam::DeviceProxy;
+using org::chromium::flimflam::ServiceProxy;
+using std::map;
+using std::set;
+using std::string;
+using std::vector;
+
+namespace privetd {
+
+namespace {
+
+void IgnoreDetachEvent() { }
+
+bool GetStateForService(ServiceProxy* service, string* state) {
+ CHECK(service) << "|service| was nullptr in GetStateForService()";
+ VariantDictionary properties;
+ if (!service->GetProperties(&properties, nullptr)) {
+ LOG(WARNING) << "Failed to read properties from service.";
+ return false;
+ }
+ auto property_it = properties.find(shill::kStateProperty);
+ if (property_it == properties.end()) {
+ LOG(WARNING) << "No state found in service properties.";
+ return false;
+ }
+ string new_state = property_it->second.TryGet<string>();
+ if (new_state.empty()) {
+ LOG(WARNING) << "Invalid state value.";
+ return false;
+ }
+ *state = new_state;
+ return true;
+}
+
+ServiceState ShillServiceStateToServiceState(const string& state) {
+ // TODO(wiley) What does "unconfigured" mean in a world with multiple sets
+ // of WiFi credentials?
+ // TODO(wiley) Detect disabled devices, update state appropriately.
+ if ((state.compare(shill::kStateReady) == 0) ||
+ (state.compare(shill::kStatePortal) == 0) ||
+ (state.compare(shill::kStateOnline) == 0)) {
+ return ServiceState::kConnected;
+ }
+ if ((state.compare(shill::kStateAssociation) == 0) ||
+ (state.compare(shill::kStateConfiguration) == 0)) {
+ return ServiceState::kConnecting;
+ }
+ if ((state.compare(shill::kStateFailure) == 0) ||
+ (state.compare(shill::kStateActivationFailure) == 0)) {
+ // TODO(wiley) Get error information off the service object.
+ return ServiceState::kFailure;
+ }
+ if ((state.compare(shill::kStateIdle) == 0) ||
+ (state.compare(shill::kStateOffline) == 0) ||
+ (state.compare(shill::kStateDisconnect) == 0)) {
+ return ServiceState::kOffline;
+ }
+ LOG(WARNING) << "Unknown state found: '" << state << "'";
+ return ServiceState::kOffline;
+}
+
+} // namespace
+
+std::string ServiceStateToString(ServiceState state) {
+ switch (state) {
+ case ServiceState::kOffline:
+ return "offline";
+ case ServiceState::kFailure:
+ return "failure";
+ case ServiceState::kConnecting:
+ return "connecting";
+ case ServiceState::kConnected:
+ return "connected";
+ }
+ LOG(ERROR) << "Unknown ServiceState value!";
+ return "unknown";
+}
+
+ShillClient::ShillClient(const scoped_refptr<dbus::Bus>& bus,
+ const set<string>& device_whitelist)
+ : bus_{bus},
+ manager_proxy_{bus_, ObjectPath{"/"}},
+ device_whitelist_{device_whitelist} {
+ manager_proxy_.RegisterPropertyChangedSignalHandler(
+ base::Bind(&ShillClient::OnManagerPropertyChange,
+ weak_factory_.GetWeakPtr()),
+ base::Bind(&ShillClient::OnManagerPropertyChangeRegistration,
+ weak_factory_.GetWeakPtr()));
+ auto owner_changed_cb = base::Bind(&ShillClient::OnShillServiceOwnerChange,
+ weak_factory_.GetWeakPtr());
+ bus_->GetObjectProxy(
+ shill::kFlimflamServiceName,
+ ObjectPath{"/"})->SetNameOwnerChangedCallback(owner_changed_cb);
+}
+
+void ShillClient::Init() {
+ VLOG(2) << "ShillClient::Init();";
+ CleanupConnectingService(false);
+ devices_.clear();
+ connectivity_state_ = ServiceState::kOffline;
+ VariantDictionary properties;
+ if (!manager_proxy_.GetProperties(&properties, nullptr)) {
+ LOG(ERROR) << "Unable to get properties from Manager, waiting for "
+ "Manager to come back online.";
+ return;
+ }
+ auto it = properties.find(shill::kDevicesProperty);
+ CHECK(it != properties.end()) << "shill should always publish a device list.";
+ OnManagerPropertyChange(shill::kDevicesProperty, it->second);
+}
+
+bool ShillClient::ConnectToService(const string& ssid,
+ const string& passphrase,
+ const base::Closure& on_success,
+ chromeos::ErrorPtr* error) {
+ CleanupConnectingService(false);
+ VariantDictionary service_properties;
+ service_properties[shill::kTypeProperty] = Any{string{shill::kTypeWifi}};
+ service_properties[shill::kSSIDProperty] = Any{ssid};
+ service_properties[shill::kPassphraseProperty] = Any{passphrase};
+ service_properties[shill::kSecurityProperty] = Any{
+ string{passphrase.empty() ? shill::kSecurityNone : shill::kSecurityPsk}};
+ service_properties[shill::kSaveCredentialsProperty] = Any{true};
+ service_properties[shill::kAutoConnectProperty] = Any{true};
+ ObjectPath service_path;
+ if (!manager_proxy_.ConfigureService(service_properties,
+ &service_path,
+ error)) {
+ return false;
+ }
+ if (!manager_proxy_.RequestScan(shill::kTypeWifi, error)) {
+ return false;
+ }
+ connecting_service_.reset(new ServiceProxy{bus_, service_path});
+ on_connect_success_.Reset(on_success);
+ connecting_service_->RegisterPropertyChangedSignalHandler(
+ base::Bind(&ShillClient::OnServicePropertyChange,
+ weak_factory_.GetWeakPtr(),
+ service_path),
+ base::Bind(&ShillClient::OnServicePropertyChangeRegistration,
+ weak_factory_.GetWeakPtr(),
+ service_path));
+ return true;
+}
+
+ServiceState ShillClient::GetConnectionState() const {
+ return connectivity_state_;
+}
+
+bool ShillClient::AmOnline() const {
+ return connectivity_state_ == ServiceState::kConnected;
+}
+
+void ShillClient::RegisterConnectivityListener(
+ const ConnectivityListener& listener) {
+ connectivity_listeners_.push_back(listener);
+}
+
+bool ShillClient::IsMonitoredDevice(DeviceProxy* device) {
+ if (device_whitelist_.empty()) {
+ return true;
+ }
+ VariantDictionary device_properties;
+ if (!device->GetProperties(&device_properties, nullptr)) {
+ LOG(ERROR) << "Devices without properties aren't whitelisted.";
+ return false;
+ }
+ auto it = device_properties.find(shill::kInterfaceProperty);
+ if (it == device_properties.end()) {
+ LOG(ERROR) << "Failed to find interface property in device properties.";
+ return false;
+ }
+ return ContainsKey(device_whitelist_, it->second.TryGet<string>());
+}
+
+void ShillClient::OnShillServiceOwnerChange(const string& old_owner,
+ const string& new_owner) {
+ VLOG(1) << "Shill service owner name changed to '" << new_owner << "'";
+ if (new_owner.empty()) {
+ CleanupConnectingService(false);
+ devices_.clear();
+ connectivity_state_ = ServiceState::kOffline;
+ } else {
+ Init(); // New service owner means shill reset!
+ }
+}
+
+void ShillClient::OnManagerPropertyChangeRegistration(const string& interface,
+ const string& signal_name,
+ bool success) {
+ VLOG(3) << "Registered ManagerPropertyChange handler.";
+ CHECK(success) << "privetd requires Manager signals.";
+ VariantDictionary properties;
+ if (!manager_proxy_.GetProperties(&properties, nullptr)) {
+ LOG(ERROR) << "Unable to get properties from Manager, waiting for "
+ "Manager to come back online.";
+ return;
+ }
+ auto it = properties.find(shill::kDevicesProperty);
+ CHECK(it != properties.end()) << "Shill should always publish a device list.";
+ OnManagerPropertyChange(shill::kDevicesProperty, it->second);
+}
+
+void ShillClient::OnManagerPropertyChange(const string& property_name,
+ const Any& property_value) {
+ if (property_name != shill::kDevicesProperty) {
+ return;
+ }
+ VLOG(3) << "Manager's device list has changed.";
+ // We're going to remove every device we haven't seen in the update.
+ set<ObjectPath> device_paths_to_remove;
+ for (const auto& kv : devices_) {
+ device_paths_to_remove.insert(kv.first);
+ }
+ for (const auto& device_path : property_value.TryGet<vector<ObjectPath>>()) {
+ if (!device_path.IsValid()) {
+ LOG(ERROR) << "Ignoring invalid device path in Manager's device list.";
+ return;
+ }
+ auto it = devices_.find(device_path);
+ if (it != devices_.end()) {
+ // Found an existing proxy. Since the whitelist never changes,
+ // this still a valid device.
+ device_paths_to_remove.erase(device_path);
+ continue;
+ }
+ std::unique_ptr<DeviceProxy> device{new DeviceProxy{bus_, device_path}};
+ if (!IsMonitoredDevice(device.get())) {
+ continue;
+ }
+ device->RegisterPropertyChangedSignalHandler(
+ base::Bind(&ShillClient::OnDevicePropertyChange,
+ weak_factory_.GetWeakPtr(),
+ device_path),
+ base::Bind(&ShillClient::OnDevicePropertyChangeRegistration,
+ weak_factory_.GetWeakPtr(),
+ device_path));
+ VLOG(3) << "Creating device proxy at " << device_path.value();
+ devices_[device_path].device = std::move(device);
+ }
+ // Clean up devices/services related to removed devices.
+ if (!device_paths_to_remove.empty()) {
+ for (const ObjectPath& device_path : device_paths_to_remove) {
+ devices_.erase(device_path);
+ }
+ UpdateConnectivityState();
+ }
+}
+
+void ShillClient::OnDevicePropertyChangeRegistration(
+ const ObjectPath& device_path,
+ const string& interface,
+ const string& signal_name,
+ bool success) {
+ VLOG(3) << "Registered DevicePropertyChange handler.";
+ auto it = devices_.find(device_path);
+ if (it == devices_.end()) {
+ return;
+ }
+ CHECK(success) << "Failed to subscribe to Device property changes.";
+ DeviceProxy* device = it->second.device.get();
+ VariantDictionary properties;
+ if (!device->GetProperties(&properties, nullptr)) {
+ LOG(WARNING) << "Failed to get device properties?";
+ return;
+ }
+ auto prop_it = properties.find(shill::kSelectedServiceProperty);
+ if (prop_it == properties.end()) {
+ LOG(WARNING) << "Failed to get device's selected service?";
+ return;
+ }
+ OnDevicePropertyChange(device_path,
+ shill::kSelectedServiceProperty,
+ prop_it->second);
+}
+
+void ShillClient::OnDevicePropertyChange(const ObjectPath& device_path,
+ const string& property_name,
+ const Any& property_value) {
+ // We only care about selected services anyway.
+ if (property_name != shill::kSelectedServiceProperty) {
+ return;
+ }
+ // If the device isn't our list of whitelisted devices, ignore it.
+ auto it = devices_.find(device_path);
+ if (it == devices_.end()) {
+ return;
+ }
+ DeviceState& device_state = it->second;
+ ObjectPath service_path{property_value.TryGet<ObjectPath>()};
+ if (!service_path.IsValid()) {
+ LOG(ERROR) << "Device at " << device_path.value()
+ << " selected invalid service path.";
+ return;
+ }
+ VLOG(3) << "Device at " << it->first.value()
+ << " has selected service at " << service_path.value();
+ bool removed_old_service{false};
+ if (device_state.selected_service) {
+ if (device_state.selected_service->GetObjectPath() == service_path) {
+ return; // Spurious update?
+ }
+ device_state.selected_service.reset();
+ device_state.service_state = ServiceState::kOffline;
+ removed_old_service = true;
+ }
+ std::shared_ptr<ServiceProxy> new_service;
+ const bool reuse_connecting_service =
+ service_path.value() != "/" &&
+ connecting_service_ &&
+ connecting_service_->GetObjectPath() == service_path;
+ if (reuse_connecting_service) {
+ new_service = connecting_service_;
+ // When we reuse the connecting service, we need to make sure that our
+ // cached state is correct. Normally, we do this by relying reading the
+ // state when our signal handlers finish registering, but this may have
+ // happened long in the past for the connecting service.
+ string state;
+ if (GetStateForService(new_service.get(), &state)) {
+ device_state.service_state = ShillServiceStateToServiceState(state);
+ } else {
+ LOG(WARNING) << "Failed to read properties from existing service "
+ "on selection.";
+ }
+ } else if (service_path.value() != "/") {
+ // The device has selected a new service we haven't see before.
+ new_service.reset(new ServiceProxy{bus_, service_path});
+ new_service->RegisterPropertyChangedSignalHandler(
+ base::Bind(&ShillClient::OnServicePropertyChange,
+ weak_factory_.GetWeakPtr(),
+ service_path),
+ base::Bind(&ShillClient::OnServicePropertyChangeRegistration,
+ weak_factory_.GetWeakPtr(),
+ service_path));
+ }
+ device_state.selected_service = new_service;
+ if (reuse_connecting_service || removed_old_service) {
+ UpdateConnectivityState();
+ }
+}
+
+void ShillClient::OnServicePropertyChangeRegistration(const ObjectPath& path,
+ const string& interface,
+ const string& signal_name,
+ bool success) {
+ VLOG(3) << "OnServicePropertyChangeRegistration(" << path.value() << ");";
+ ServiceProxy* service{nullptr};
+ if (connecting_service_ && connecting_service_->GetObjectPath() == path) {
+ // Note that the connecting service might also be a selected service.
+ service = connecting_service_.get();
+ if (!success) {
+ CleanupConnectingService(false);
+ }
+ } else {
+ for (const auto& kv : devices_) {
+ if (kv.second.selected_service &&
+ kv.second.selected_service->GetObjectPath() == path) {
+ service = kv.second.selected_service.get();
+ break;
+ }
+ }
+ }
+ if (service == nullptr || !success) {
+ return; // A failure or success for a proxy we no longer care about.
+ }
+ VariantDictionary properties;
+ if (!service->GetProperties(&properties, nullptr)) {
+ return;
+ }
+ // Give ourselves property changed signals for the initial property
+ // values.
+ auto it = properties.find(shill::kStateProperty);
+ if (it != properties.end()) {
+ OnServicePropertyChange(path, shill::kStateProperty,
+ it->second);
+ }
+ it = properties.find(shill::kSignalStrengthProperty);
+ if (it != properties.end()) {
+ OnServicePropertyChange(path, shill::kSignalStrengthProperty,
+ it->second);
+ }
+}
+
+void ShillClient::OnServicePropertyChange(const ObjectPath& service_path,
+ const string& property_name,
+ const Any& property_value) {
+ VLOG(3) << "ServicePropertyChange(" << service_path.value() << ", "
+ << property_name << ", ...);";
+ if (property_name == shill::kStateProperty) {
+ const string state{property_value.TryGet<string>()};
+ if (state.empty()) {
+ VLOG(3) << "Invalid service state update.";
+ return;
+ }
+ VLOG(3) << "New service state=" << state;
+ OnStateChangeForSelectedService(service_path, state);
+ OnStateChangeForConnectingService(service_path, state);
+ } else if (property_name == shill::kSignalStrengthProperty) {
+ OnStrengthChangeForConnectingService(
+ service_path, property_value.TryGet<uint8_t>());
+ }
+}
+
+void ShillClient::OnStateChangeForConnectingService(
+ const ObjectPath& service_path,
+ const string& state) {
+ if (!connecting_service_ ||
+ connecting_service_->GetObjectPath() != service_path ||
+ ShillServiceStateToServiceState(state) != ServiceState::kConnected) {
+ return;
+ }
+ connecting_service_reset_pending_ = true;
+ on_connect_success_.callback().Run();
+ CleanupConnectingService(true);
+}
+
+void ShillClient::OnStrengthChangeForConnectingService(
+ const ObjectPath& service_path,
+ uint8_t signal_strength) {
+ if (!connecting_service_ ||
+ connecting_service_->GetObjectPath() != service_path ||
+ signal_strength <= 0 ||
+ have_called_connect_) {
+ return;
+ }
+ VLOG(1) << "Connecting service has signal. Calling Connect().";
+ have_called_connect_ = true;
+ // Failures here indicate that we've already connected,
+ // or are connecting, or some other very unexciting thing.
+ // Ignore all that, and rely on state changes to detect
+ // connectivity.
+ connecting_service_->Connect(nullptr);
+}
+
+void ShillClient::OnStateChangeForSelectedService(
+ const ObjectPath& service_path,
+ const string& state) {
+ // Find the device/service pair responsible for this update
+ VLOG(3) << "State for potentially selected service " << service_path.value()
+ << " have changed to " << state;
+ for (auto& kv : devices_) {
+ if (kv.second.selected_service &&
+ kv.second.selected_service->GetObjectPath() == service_path) {
+ VLOG(3) << "Updated cached connection state for selected service.";
+ kv.second.service_state = ShillServiceStateToServiceState(state);
+ UpdateConnectivityState();
+ return;
+ }
+ }
+}
+
+void ShillClient::UpdateConnectivityState() {
+ // Update the connectivity state of the device by picking the
+ // state of the currently most connected selected service.
+ ServiceState new_connectivity_state{ServiceState::kOffline};
+ for (const auto& kv : devices_) {
+ if (kv.second.service_state > new_connectivity_state) {
+ new_connectivity_state = kv.second.service_state;
+ }
+ }
+ VLOG(3) << "New connectivity state is "
+ << ServiceStateToString(new_connectivity_state);
+ if (new_connectivity_state != connectivity_state_) {
+ connectivity_state_ = new_connectivity_state;
+ // We may call UpdateConnectivityState whenever we mutate a data structure
+ // such that our connectivity status could change. However, we don't want
+ // to allow people to call into ShillClient while some other operation is
+ // underway. Therefore, call our callbacks later, when we're in a good
+ // state.
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&ShillClient::NotifyConnectivityListeners,
+ weak_factory_.GetWeakPtr(), AmOnline()));
+ }
+}
+
+void ShillClient::NotifyConnectivityListeners(bool am_online) {
+ VLOG(3) << "Notifying connectivity listeners that online=" << am_online;
+ for (const auto& listener : connectivity_listeners_) {
+ listener.Run(am_online);
+ }
+}
+
+void ShillClient::CleanupConnectingService(bool check_for_reset_pending) {
+ if (check_for_reset_pending && !connecting_service_reset_pending_) {
+ return; // Must have called connect before we got here.
+ }
+ if (connecting_service_) {
+ connecting_service_->ReleaseObjectProxy(base::Bind(&IgnoreDetachEvent));
+ connecting_service_.reset();
+ }
+ on_connect_success_.Cancel();
+ have_called_connect_ = false;
+ connecting_service_reset_pending_ = false;
+}
+
+} // namespace privetd