| // | |
| // Copyright (C) 2014 The Android Open Source Project | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| // | |
| #include "update_engine/update_manager/real_shill_provider.h" | |
| #include <string> | |
| #include <base/logging.h> | |
| #include <base/strings/stringprintf.h> | |
| #include <brillo/type_name_undecorate.h> | |
| #include <shill/dbus-constants.h> | |
| #include <shill/dbus-proxies.h> | |
| using org::chromium::flimflam::ManagerProxyInterface; | |
| using org::chromium::flimflam::ServiceProxyInterface; | |
| using std::string; | |
| namespace chromeos_update_manager { | |
| ConnectionType RealShillProvider::ParseConnectionType(const string& type_str) { | |
| if (type_str == shill::kTypeEthernet) { | |
| return ConnectionType::kEthernet; | |
| } else if (type_str == shill::kTypeWifi) { | |
| return ConnectionType::kWifi; | |
| } else if (type_str == shill::kTypeWimax) { | |
| return ConnectionType::kWimax; | |
| } else if (type_str == shill::kTypeBluetooth) { | |
| return ConnectionType::kBluetooth; | |
| } else if (type_str == shill::kTypeCellular) { | |
| return ConnectionType::kCellular; | |
| } | |
| return ConnectionType::kUnknown; | |
| } | |
| ConnectionTethering RealShillProvider::ParseConnectionTethering( | |
| const string& tethering_str) { | |
| if (tethering_str == shill::kTetheringNotDetectedState) { | |
| return ConnectionTethering::kNotDetected; | |
| } else if (tethering_str == shill::kTetheringSuspectedState) { | |
| return ConnectionTethering::kSuspected; | |
| } else if (tethering_str == shill::kTetheringConfirmedState) { | |
| return ConnectionTethering::kConfirmed; | |
| } | |
| return ConnectionTethering::kUnknown; | |
| } | |
| bool RealShillProvider::Init() { | |
| ManagerProxyInterface* manager_proxy = shill_proxy_->GetManagerProxy(); | |
| if (!manager_proxy) | |
| return false; | |
| // Subscribe to the manager's PropertyChanged signal. | |
| manager_proxy->RegisterPropertyChangedSignalHandler( | |
| base::Bind(&RealShillProvider::OnManagerPropertyChanged, | |
| base::Unretained(this)), | |
| base::Bind(&RealShillProvider::OnSignalConnected, | |
| base::Unretained(this))); | |
| // Attempt to read initial connection status. Even if this fails because shill | |
| // is not responding (e.g. it is down) we'll be notified via "PropertyChanged" | |
| // signal as soon as it comes up, so this is not a critical step. | |
| brillo::VariantDictionary properties; | |
| brillo::ErrorPtr error; | |
| if (!manager_proxy->GetProperties(&properties, &error)) | |
| return true; | |
| const auto& prop_default_service = | |
| properties.find(shill::kDefaultServiceProperty); | |
| if (prop_default_service != properties.end()) { | |
| OnManagerPropertyChanged(prop_default_service->first, | |
| prop_default_service->second); | |
| } | |
| return true; | |
| } | |
| void RealShillProvider::OnManagerPropertyChanged(const string& name, | |
| const brillo::Any& value) { | |
| if (name == shill::kDefaultServiceProperty) { | |
| dbus::ObjectPath service_path = value.TryGet<dbus::ObjectPath>(); | |
| if (!service_path.IsValid()) { | |
| LOG(WARNING) << "Got an invalid DefaultService path. The property value " | |
| "contains a " | |
| << value.GetUndecoratedTypeName() | |
| << ", read as the object path: '" << service_path.value() | |
| << "'"; | |
| } | |
| ProcessDefaultService(service_path); | |
| } | |
| } | |
| void RealShillProvider::OnSignalConnected(const string& interface_name, | |
| const string& signal_name, | |
| bool successful) { | |
| if (!successful) { | |
| LOG(ERROR) << "Couldn't connect to the signal " << interface_name << "." | |
| << signal_name; | |
| } | |
| } | |
| bool RealShillProvider::ProcessDefaultService( | |
| const dbus::ObjectPath& default_service_path) { | |
| // We assume that if the service path didn't change, then the connection | |
| // type and the tethering status of it also didn't change. | |
| if (default_service_path_ == default_service_path) | |
| return true; | |
| // Update the connection status. | |
| default_service_path_ = default_service_path; | |
| bool is_connected = (default_service_path_.IsValid() && | |
| default_service_path_.value() != "/"); | |
| var_is_connected_.SetValue(is_connected); | |
| var_conn_last_changed_.SetValue(clock_->GetWallclockTime()); | |
| if (!is_connected) { | |
| var_conn_type_.UnsetValue(); | |
| var_conn_tethering_.UnsetValue(); | |
| return true; | |
| } | |
| // We create and dispose the ServiceProxyInterface on every request. | |
| std::unique_ptr<ServiceProxyInterface> service = | |
| shill_proxy_->GetServiceForPath(default_service_path_); | |
| // Get the connection properties synchronously. | |
| brillo::VariantDictionary properties; | |
| brillo::ErrorPtr error; | |
| if (!service->GetProperties(&properties, &error)) { | |
| var_conn_type_.UnsetValue(); | |
| var_conn_tethering_.UnsetValue(); | |
| return false; | |
| } | |
| // Get the connection tethering mode. | |
| const auto& prop_tethering = properties.find(shill::kTetheringProperty); | |
| if (prop_tethering == properties.end()) { | |
| // Remove the value if not present on the service. This most likely means an | |
| // error in shill and the policy will handle it, but we will print a log | |
| // message as well for accessing an unused variable. | |
| var_conn_tethering_.UnsetValue(); | |
| LOG(ERROR) << "Could not find connection type (service: " | |
| << default_service_path_.value() << ")"; | |
| } else { | |
| // If the property doesn't contain a string value, the empty string will | |
| // become kUnknown. | |
| var_conn_tethering_.SetValue( | |
| ParseConnectionTethering(prop_tethering->second.TryGet<string>())); | |
| } | |
| // Get the connection type. | |
| const auto& prop_type = properties.find(shill::kTypeProperty); | |
| if (prop_type == properties.end()) { | |
| var_conn_type_.UnsetValue(); | |
| LOG(ERROR) << "Could not find connection tethering mode (service: " | |
| << default_service_path_.value() << ")"; | |
| } else { | |
| string type_str = prop_type->second.TryGet<string>(); | |
| if (type_str == shill::kTypeVPN) { | |
| const auto& prop_physical = | |
| properties.find(shill::kPhysicalTechnologyProperty); | |
| if (prop_physical == properties.end()) { | |
| LOG(ERROR) << "No PhysicalTechnology property found for a VPN" | |
| << " connection (service: " << default_service_path_.value() | |
| << "). Using default kUnknown value."; | |
| var_conn_type_.SetValue(ConnectionType::kUnknown); | |
| } else { | |
| var_conn_type_.SetValue( | |
| ParseConnectionType(prop_physical->second.TryGet<string>())); | |
| } | |
| } else { | |
| var_conn_type_.SetValue(ParseConnectionType(type_str)); | |
| } | |
| } | |
| return true; | |
| } | |
| } // namespace chromeos_update_manager |