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 
27 using chromeos_update_engine::SystemState;
28 using chromeos_update_engine::connection_utils::ParseConnectionType;
29 using org::chromium::flimflam::ManagerProxyInterface;
30 using org::chromium::flimflam::ServiceProxyInterface;
31 using std::string;
32 
33 namespace chromeos_update_manager {
34 
Init()35 bool RealShillProvider::Init() {
36   ManagerProxyInterface* manager_proxy = shill_proxy_->GetManagerProxy();
37   if (!manager_proxy)
38     return false;
39 
40   // Subscribe to the manager's PropertyChanged signal.
41   manager_proxy->RegisterPropertyChangedSignalHandler(
42       base::Bind(&RealShillProvider::OnManagerPropertyChanged,
43                  base::Unretained(this)),
44       base::Bind(&RealShillProvider::OnSignalConnected,
45                  base::Unretained(this)));
46 
47   // Attempt to read initial connection status. Even if this fails because shill
48   // is not responding (e.g. it is down) we'll be notified via "PropertyChanged"
49   // signal as soon as it comes up, so this is not a critical step.
50   brillo::VariantDictionary properties;
51   brillo::ErrorPtr error;
52   if (!manager_proxy->GetProperties(&properties, &error))
53     return true;
54 
55   const auto& prop_default_service =
56       properties.find(shill::kDefaultServiceProperty);
57   if (prop_default_service != properties.end()) {
58     OnManagerPropertyChanged(prop_default_service->first,
59                              prop_default_service->second);
60   }
61 
62   return true;
63 }
64 
OnManagerPropertyChanged(const string & name,const brillo::Any & value)65 void RealShillProvider::OnManagerPropertyChanged(const string& name,
66                                                  const brillo::Any& value) {
67   if (name == shill::kDefaultServiceProperty) {
68     dbus::ObjectPath service_path = value.TryGet<dbus::ObjectPath>();
69     if (!service_path.IsValid()) {
70       LOG(WARNING) << "Got an invalid DefaultService path. The property value "
71                       "contains a "
72                    << value.GetUndecoratedTypeName()
73                    << ", read as the object path: '" << service_path.value()
74                    << "'";
75     }
76     ProcessDefaultService(service_path);
77   }
78 }
79 
OnSignalConnected(const string & interface_name,const string & signal_name,bool successful)80 void RealShillProvider::OnSignalConnected(const string& interface_name,
81                                           const string& signal_name,
82                                           bool successful) {
83   if (!successful) {
84     LOG(ERROR) << "Couldn't connect to the signal " << interface_name << "."
85                << signal_name;
86   }
87 }
88 
ProcessDefaultService(const dbus::ObjectPath & default_service_path)89 bool RealShillProvider::ProcessDefaultService(
90     const dbus::ObjectPath& default_service_path) {
91   // We assume that if the service path didn't change, then the connection
92   // type and the tethering status of it also didn't change.
93   if (default_service_path_ == default_service_path)
94     return true;
95 
96   // Update the connection status.
97   default_service_path_ = default_service_path;
98   bool is_connected =
99       (default_service_path_.IsValid() && default_service_path_.value() != "/");
100   var_is_connected_.SetValue(is_connected);
101   var_conn_last_changed_.SetValue(
102       SystemState::Get()->clock()->GetWallclockTime());
103 
104   if (!is_connected) {
105     var_conn_type_.UnsetValue();
106     var_conn_tethering_.UnsetValue();
107     return true;
108   }
109 
110   // We create and dispose the ServiceProxyInterface on every request.
111   std::unique_ptr<ServiceProxyInterface> service =
112       shill_proxy_->GetServiceForPath(default_service_path_);
113 
114   // Get the connection properties synchronously.
115   brillo::VariantDictionary properties;
116   brillo::ErrorPtr error;
117   if (!service->GetProperties(&properties, &error)) {
118     var_conn_type_.UnsetValue();
119     var_conn_tethering_.UnsetValue();
120     return false;
121   }
122 
123   // Get the connection tethering mode.
124   const auto& prop_tethering = properties.find(shill::kTetheringProperty);
125   if (prop_tethering == properties.end()) {
126     // Remove the value if not present on the service. This most likely means an
127     // error in shill and the policy will handle it, but we will print a log
128     // message as well for accessing an unused variable.
129     var_conn_tethering_.UnsetValue();
130     LOG(ERROR) << "Could not find connection type (service: "
131                << default_service_path_.value() << ")";
132   } else {
133     // If the property doesn't contain a string value, the empty string will
134     // become kUnknown.
135     var_conn_tethering_.SetValue(
136         chromeos_update_engine::connection_utils::ParseConnectionTethering(
137             prop_tethering->second.TryGet<string>()));
138   }
139 
140   // Get the connection type.
141   const auto& prop_type = properties.find(shill::kTypeProperty);
142   if (prop_type == properties.end()) {
143     var_conn_type_.UnsetValue();
144     LOG(ERROR) << "Could not find connection tethering mode (service: "
145                << default_service_path_.value() << ")";
146   } else {
147     string type_str = prop_type->second.TryGet<string>();
148     if (type_str == shill::kTypeVPN) {
149       const auto& prop_physical =
150           properties.find(shill::kPhysicalTechnologyProperty);
151       if (prop_physical == properties.end()) {
152         LOG(ERROR) << "No PhysicalTechnology property found for a VPN"
153                    << " connection (service: " << default_service_path_.value()
154                    << "). Using default kUnknown value.";
155         var_conn_type_.SetValue(
156             chromeos_update_engine::ConnectionType::kUnknown);
157       } else {
158         var_conn_type_.SetValue(
159             ParseConnectionType(prop_physical->second.TryGet<string>()));
160       }
161     } else {
162       var_conn_type_.SetValue(ParseConnectionType(type_str));
163     }
164   }
165 
166   return true;
167 }
168 
169 }  // namespace chromeos_update_manager
170