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