1// 2// Copyright (C) 2014 The Android Open Source Project 3// 4// Licensed under the Apache License, Version 2.0 (the "License"); 5// you may not use this file except in compliance with the License. 6// You may obtain a copy of the License at 7// 8// http://www.apache.org/licenses/LICENSE-2.0 9// 10// Unless required by applicable law or agreed to in writing, software 11// distributed under the License is distributed on an "AS IS" BASIS, 12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13// See the License for the specific language governing permissions and 14// limitations under the License. 15// 16 17#include "update_engine/update_manager/real_shill_provider.h" 18 19#include <string> 20 21#include <base/logging.h> 22#include <base/strings/stringprintf.h> 23#include <brillo/type_name_undecorate.h> 24#include <shill/dbus-constants.h> 25#include <shill/dbus-proxies.h> 26 27using org::chromium::flimflam::ManagerProxyInterface; 28using org::chromium::flimflam::ServiceProxyInterface; 29using std::string; 30 31namespace chromeos_update_manager { 32 33ConnectionType RealShillProvider::ParseConnectionType(const string& type_str) { 34 if (type_str == shill::kTypeEthernet) { 35 return ConnectionType::kEthernet; 36 } else if (type_str == shill::kTypeWifi) { 37 return ConnectionType::kWifi; 38 } else if (type_str == shill::kTypeWimax) { 39 return ConnectionType::kWimax; 40 } else if (type_str == shill::kTypeBluetooth) { 41 return ConnectionType::kBluetooth; 42 } else if (type_str == shill::kTypeCellular) { 43 return ConnectionType::kCellular; 44 } 45 return ConnectionType::kUnknown; 46} 47 48ConnectionTethering RealShillProvider::ParseConnectionTethering( 49 const string& tethering_str) { 50 if (tethering_str == shill::kTetheringNotDetectedState) { 51 return ConnectionTethering::kNotDetected; 52 } else if (tethering_str == shill::kTetheringSuspectedState) { 53 return ConnectionTethering::kSuspected; 54 } else if (tethering_str == shill::kTetheringConfirmedState) { 55 return ConnectionTethering::kConfirmed; 56 } 57 return ConnectionTethering::kUnknown; 58} 59 60bool RealShillProvider::Init() { 61 ManagerProxyInterface* manager_proxy = shill_proxy_->GetManagerProxy(); 62 if (!manager_proxy) 63 return false; 64 65 // Subscribe to the manager's PropertyChanged signal. 66 manager_proxy->RegisterPropertyChangedSignalHandler( 67 base::Bind(&RealShillProvider::OnManagerPropertyChanged, 68 base::Unretained(this)), 69 base::Bind(&RealShillProvider::OnSignalConnected, 70 base::Unretained(this))); 71 72 // Attempt to read initial connection status. Even if this fails because shill 73 // is not responding (e.g. it is down) we'll be notified via "PropertyChanged" 74 // signal as soon as it comes up, so this is not a critical step. 75 brillo::VariantDictionary properties; 76 brillo::ErrorPtr error; 77 if (!manager_proxy->GetProperties(&properties, &error)) 78 return true; 79 80 const auto& prop_default_service = 81 properties.find(shill::kDefaultServiceProperty); 82 if (prop_default_service != properties.end()) { 83 OnManagerPropertyChanged(prop_default_service->first, 84 prop_default_service->second); 85 } 86 87 return true; 88} 89 90void RealShillProvider::OnManagerPropertyChanged(const string& name, 91 const brillo::Any& value) { 92 if (name == shill::kDefaultServiceProperty) { 93 dbus::ObjectPath service_path = value.TryGet<dbus::ObjectPath>(); 94 if (!service_path.IsValid()) { 95 LOG(WARNING) << "Got an invalid DefaultService path. The property value " 96 "contains a " 97 << value.GetUndecoratedTypeName() 98 << ", read as the object path: '" << service_path.value() 99 << "'"; 100 } 101 ProcessDefaultService(service_path); 102 } 103} 104 105void RealShillProvider::OnSignalConnected(const string& interface_name, 106 const string& signal_name, 107 bool successful) { 108 if (!successful) { 109 LOG(ERROR) << "Couldn't connect to the signal " << interface_name << "." 110 << signal_name; 111 } 112} 113 114bool RealShillProvider::ProcessDefaultService( 115 const dbus::ObjectPath& default_service_path) { 116 // We assume that if the service path didn't change, then the connection 117 // type and the tethering status of it also didn't change. 118 if (default_service_path_ == default_service_path) 119 return true; 120 121 // Update the connection status. 122 default_service_path_ = default_service_path; 123 bool is_connected = (default_service_path_.IsValid() && 124 default_service_path_.value() != "/"); 125 var_is_connected_.SetValue(is_connected); 126 var_conn_last_changed_.SetValue(clock_->GetWallclockTime()); 127 128 if (!is_connected) { 129 var_conn_type_.UnsetValue(); 130 var_conn_tethering_.UnsetValue(); 131 return true; 132 } 133 134 // We create and dispose the ServiceProxyInterface on every request. 135 std::unique_ptr<ServiceProxyInterface> service = 136 shill_proxy_->GetServiceForPath(default_service_path_); 137 138 // Get the connection properties synchronously. 139 brillo::VariantDictionary properties; 140 brillo::ErrorPtr error; 141 if (!service->GetProperties(&properties, &error)) { 142 var_conn_type_.UnsetValue(); 143 var_conn_tethering_.UnsetValue(); 144 return false; 145 } 146 147 // Get the connection tethering mode. 148 const auto& prop_tethering = properties.find(shill::kTetheringProperty); 149 if (prop_tethering == properties.end()) { 150 // Remove the value if not present on the service. This most likely means an 151 // error in shill and the policy will handle it, but we will print a log 152 // message as well for accessing an unused variable. 153 var_conn_tethering_.UnsetValue(); 154 LOG(ERROR) << "Could not find connection type (service: " 155 << default_service_path_.value() << ")"; 156 } else { 157 // If the property doesn't contain a string value, the empty string will 158 // become kUnknown. 159 var_conn_tethering_.SetValue( 160 ParseConnectionTethering(prop_tethering->second.TryGet<string>())); 161 } 162 163 // Get the connection type. 164 const auto& prop_type = properties.find(shill::kTypeProperty); 165 if (prop_type == properties.end()) { 166 var_conn_type_.UnsetValue(); 167 LOG(ERROR) << "Could not find connection tethering mode (service: " 168 << default_service_path_.value() << ")"; 169 } else { 170 string type_str = prop_type->second.TryGet<string>(); 171 if (type_str == shill::kTypeVPN) { 172 const auto& prop_physical = 173 properties.find(shill::kPhysicalTechnologyProperty); 174 if (prop_physical == properties.end()) { 175 LOG(ERROR) << "No PhysicalTechnology property found for a VPN" 176 << " connection (service: " << default_service_path_.value() 177 << "). Using default kUnknown value."; 178 var_conn_type_.SetValue(ConnectionType::kUnknown); 179 } else { 180 var_conn_type_.SetValue( 181 ParseConnectionType(prop_physical->second.TryGet<string>())); 182 } 183 } else { 184 var_conn_type_.SetValue(ParseConnectionType(type_str)); 185 } 186 } 187 188 return true; 189} 190 191} // namespace chromeos_update_manager 192