real_shill_provider.cc revision 758dd53cf503adbcb049909f25f54603d411be09
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 <chromeos/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  chromeos::VariantDictionary properties;
76  chromeos::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 chromeos::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                   << chromeos::UndecorateTypeName(value.GetType().name())
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  chromeos::VariantDictionary properties;
140  chromeos::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