network_configuration_handler.cc revision a36e5920737c6adbddd3e43b760e5de8431db6e0
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chromeos/network/network_configuration_handler.h"
6
7#include <string>
8#include <vector>
9
10#include "base/bind.h"
11#include "base/format_macros.h"
12#include "base/logging.h"
13#include "base/memory/ref_counted.h"
14#include "base/memory/scoped_ptr.h"
15#include "base/stl_util.h"
16#include "base/strings/stringprintf.h"
17#include "base/values.h"
18#include "chromeos/dbus/dbus_thread_manager.h"
19#include "chromeos/dbus/shill_manager_client.h"
20#include "chromeos/dbus/shill_profile_client.h"
21#include "chromeos/dbus/shill_service_client.h"
22#include "chromeos/network/network_event_log.h"
23#include "chromeos/network/network_state.h"
24#include "chromeos/network/network_state_handler.h"
25#include "dbus/object_path.h"
26#include "third_party/cros_system_api/dbus/service_constants.h"
27
28namespace chromeos {
29
30namespace {
31
32// Strip surrounding "" from keys (if present).
33std::string StripQuotations(const std::string& in_str) {
34  size_t len = in_str.length();
35  if (len >= 2 && in_str[0] == '"' && in_str[len-1] == '"')
36    return in_str.substr(1, len-2);
37  return in_str;
38}
39
40void InvokeErrorCallback(const std::string& error,
41                         const std::string& path,
42                         const network_handler::ErrorCallback& error_callback) {
43  NET_LOG_ERROR(error, path);
44  if (error_callback.is_null())
45    return;
46  scoped_ptr<base::DictionaryValue> error_data(
47      network_handler::CreateErrorData(path, error, ""));
48  error_callback.Run(error, error_data.Pass());
49}
50
51void GetPropertiesCallback(
52    const network_handler::DictionaryResultCallback& callback,
53    const network_handler::ErrorCallback& error_callback,
54    const std::string& service_path,
55    DBusMethodCallStatus call_status,
56    const base::DictionaryValue& properties) {
57  // Get the correct name from WifiHex if necessary.
58  scoped_ptr<base::DictionaryValue> properties_copy(properties.DeepCopy());
59  std::string name = NetworkState::GetNameFromProperties(properties);
60  if (!name.empty()) {
61    properties_copy->SetStringWithoutPathExpansion(
62        flimflam::kNameProperty, name);
63  }
64  network_handler::GetPropertiesCallback(
65      callback, error_callback, service_path, call_status,
66      *properties_copy.get());
67}
68
69}  // namespace
70
71// Helper class to request from Shill the profile entries associated with a
72// Service and delete the service from each profile. Triggers either
73// |callback| on success or |error_callback| on failure, and calls
74// |handler|->ProfileEntryDeleterCompleted() on completion to delete itself.
75class NetworkConfigurationHandler::ProfileEntryDeleter
76    : public base::SupportsWeakPtr<ProfileEntryDeleter> {
77 public:
78  ProfileEntryDeleter(NetworkConfigurationHandler* handler,
79                      const std::string& service_path,
80                      const base::Closure& callback,
81                      const network_handler::ErrorCallback& error_callback)
82      : owner_(handler),
83        service_path_(service_path),
84        callback_(callback),
85        error_callback_(error_callback) {
86  }
87
88  void Run() {
89    DBusThreadManager::Get()->GetShillServiceClient()->
90        GetLoadableProfileEntries(
91            dbus::ObjectPath(service_path_),
92            base::Bind(&ProfileEntryDeleter::GetProfileEntriesToDeleteCallback,
93                       AsWeakPtr()));
94  }
95
96 private:
97  void GetProfileEntriesToDeleteCallback(
98      DBusMethodCallStatus call_status,
99      const base::DictionaryValue& profile_entries) {
100    if (call_status != DBUS_METHOD_CALL_SUCCESS) {
101      InvokeErrorCallback(
102          "GetLoadableProfileEntries Failed", service_path_, error_callback_);
103      owner_->ProfileEntryDeleterCompleted(service_path_);  // Deletes this.
104      return;
105    }
106
107    for (base::DictionaryValue::Iterator iter(profile_entries);
108         !iter.IsAtEnd(); iter.Advance()) {
109      std::string profile_path = StripQuotations(iter.key());
110      std::string entry_path;
111      iter.value().GetAsString(&entry_path);
112      if (profile_path.empty() || entry_path.empty()) {
113        NET_LOG_ERROR("Failed to parse Profile Entry", base::StringPrintf(
114            "%s: %s", profile_path.c_str(), entry_path.c_str()));
115        continue;
116      }
117      if (profile_delete_entries_.count(profile_path) != 0) {
118        NET_LOG_ERROR("Multiple Profile Entries", base::StringPrintf(
119            "%s: %s", profile_path.c_str(), entry_path.c_str()));
120        continue;
121      }
122      NET_LOG_DEBUG("Delete Profile Entry", base::StringPrintf(
123          "%s: %s", profile_path.c_str(), entry_path.c_str()));
124      profile_delete_entries_[profile_path] = entry_path;
125      DBusThreadManager::Get()->GetShillProfileClient()->DeleteEntry(
126          dbus::ObjectPath(profile_path),
127          entry_path,
128          base::Bind(&ProfileEntryDeleter::ProfileEntryDeletedCallback,
129                     AsWeakPtr(), profile_path, entry_path),
130          base::Bind(&ProfileEntryDeleter::ShillErrorCallback,
131                     AsWeakPtr(), profile_path, entry_path));
132    }
133  }
134
135  void ProfileEntryDeletedCallback(const std::string& profile_path,
136                                   const std::string& entry) {
137    NET_LOG_DEBUG("Profile Entry Deleted", base::StringPrintf(
138        "%s: %s", profile_path.c_str(), entry.c_str()));
139    profile_delete_entries_.erase(profile_path);
140    if (!profile_delete_entries_.empty())
141      return;
142    // Run the callback if this is the last pending deletion.
143    if (!callback_.is_null())
144      callback_.Run();
145    // Request NetworkStateHandler manager update to update ServiceCompleteList.
146    owner_->network_state_handler_->UpdateManagerProperties();
147    owner_->ProfileEntryDeleterCompleted(service_path_);  // Deletes this.
148  }
149
150  void ShillErrorCallback(const std::string& profile_path,
151                          const std::string& entry,
152                          const std::string& dbus_error_name,
153                          const std::string& dbus_error_message) {
154    // Any Shill Error triggers a failure / error.
155    network_handler::ShillErrorCallbackFunction(
156        "GetLoadableProfileEntries Failed", profile_path, error_callback_,
157        dbus_error_name, dbus_error_message);
158    // Delete this even if there are pending deletions; any callbacks will
159    // safely become no-ops (by invalidating the WeakPtrs).
160    owner_->ProfileEntryDeleterCompleted(service_path_);  // Deletes this.
161  }
162
163  NetworkConfigurationHandler* owner_;  // Unowned
164  std::string service_path_;
165  base::Closure callback_;
166  network_handler::ErrorCallback error_callback_;
167
168  // Map of pending profile entry deletions, indexed by profile path.
169  std::map<std::string, std::string> profile_delete_entries_;
170
171  DISALLOW_COPY_AND_ASSIGN(ProfileEntryDeleter);
172};
173
174// NetworkConfigurationHandler
175
176void NetworkConfigurationHandler::GetProperties(
177    const std::string& service_path,
178    const network_handler::DictionaryResultCallback& callback,
179    const network_handler::ErrorCallback& error_callback) const {
180  DBusThreadManager::Get()->GetShillServiceClient()->GetProperties(
181      dbus::ObjectPath(service_path),
182      base::Bind(&GetPropertiesCallback,
183                 callback, error_callback, service_path));
184}
185
186void NetworkConfigurationHandler::SetProperties(
187    const std::string& service_path,
188    const base::DictionaryValue& properties,
189    const base::Closure& callback,
190    const network_handler::ErrorCallback& error_callback) {
191  NET_LOG_USER("SetProperties", service_path);
192  DBusThreadManager::Get()->GetShillServiceClient()->SetProperties(
193      dbus::ObjectPath(service_path),
194      properties,
195      base::Bind(&NetworkConfigurationHandler::SetPropertiesSuccessCallback,
196                 AsWeakPtr(), service_path, callback),
197      base::Bind(&NetworkConfigurationHandler::SetPropertiesErrorCallback,
198                 AsWeakPtr(), service_path, error_callback));
199}
200
201void NetworkConfigurationHandler::ClearProperties(
202    const std::string& service_path,
203    const std::vector<std::string>& names,
204    const base::Closure& callback,
205    const network_handler::ErrorCallback& error_callback) {
206  NET_LOG_USER("ClearProperties", service_path);
207  DBusThreadManager::Get()->GetShillServiceClient()->ClearProperties(
208      dbus::ObjectPath(service_path),
209      names,
210      base::Bind(&NetworkConfigurationHandler::ClearPropertiesSuccessCallback,
211                 AsWeakPtr(), service_path, names, callback, error_callback),
212      base::Bind(&NetworkConfigurationHandler::ClearPropertiesErrorCallback,
213                 AsWeakPtr(), service_path, error_callback));
214}
215
216void NetworkConfigurationHandler::CreateConfiguration(
217    const base::DictionaryValue& properties,
218    const network_handler::StringResultCallback& callback,
219    const network_handler::ErrorCallback& error_callback) {
220  ShillManagerClient* manager =
221      DBusThreadManager::Get()->GetShillManagerClient();
222
223  std::string type;
224  properties.GetStringWithoutPathExpansion(flimflam::kTypeProperty, &type);
225  // Shill supports ConfigureServiceForProfile only for network type WiFi. In
226  // all other cases, we have to rely on GetService for now. This is
227  // unproblematic for VPN (user profile only), but will lead to inconsistencies
228  // with WiMax, for example.
229  if (type == flimflam::kTypeWifi) {
230    std::string profile;
231    properties.GetStringWithoutPathExpansion(flimflam::kProfileProperty,
232                                             &profile);
233    manager->ConfigureServiceForProfile(
234        dbus::ObjectPath(profile),
235        properties,
236        base::Bind(&NetworkConfigurationHandler::RunCreateNetworkCallback,
237                   AsWeakPtr(), callback),
238        base::Bind(&network_handler::ShillErrorCallbackFunction,
239                   "Config.CreateConfiguration Failed", "", error_callback));
240  } else {
241    manager->ConfigureService(
242        properties,
243        base::Bind(&NetworkConfigurationHandler::RunCreateNetworkCallback,
244                   AsWeakPtr(), callback),
245        base::Bind(&network_handler::ShillErrorCallbackFunction,
246                   "Config.CreateConfiguration Failed", "", error_callback));
247  }
248}
249
250void NetworkConfigurationHandler::RemoveConfiguration(
251    const std::string& service_path,
252    const base::Closure& callback,
253    const network_handler::ErrorCallback& error_callback) {
254  // Service.Remove is not reliable. Instead, request the profile entries
255  // for the service and remove each entry.
256  if (profile_entry_deleters_.count(service_path)) {
257    InvokeErrorCallback(
258        "RemoveConfiguration In-Progress", service_path, error_callback);
259    return;
260  }
261  NET_LOG_USER("Remove Configuration", service_path);
262  ProfileEntryDeleter* deleter =
263      new ProfileEntryDeleter(this, service_path, callback, error_callback);
264  profile_entry_deleters_[service_path] = deleter;
265  deleter->Run();
266}
267
268// NetworkConfigurationHandler Private methods
269
270NetworkConfigurationHandler::NetworkConfigurationHandler()
271    : network_state_handler_(NULL) {
272}
273
274NetworkConfigurationHandler::~NetworkConfigurationHandler() {
275  STLDeleteContainerPairSecondPointers(
276      profile_entry_deleters_.begin(), profile_entry_deleters_.end());
277}
278
279void NetworkConfigurationHandler::Init(
280    NetworkStateHandler* network_state_handler) {
281  network_state_handler_ = network_state_handler;
282}
283
284void NetworkConfigurationHandler::RunCreateNetworkCallback(
285    const network_handler::StringResultCallback& callback,
286    const dbus::ObjectPath& service_path) {
287  if (!callback.is_null())
288    callback.Run(service_path.value());
289  // This may also get called when CreateConfiguration is used to update an
290  // existing configuration, so request a service update just in case.
291  // TODO(pneubeck): Separate 'Create' and 'Update' calls and only trigger
292  // this on an update.
293  network_state_handler_->RequestUpdateForNetwork(service_path.value());
294}
295
296void NetworkConfigurationHandler::ProfileEntryDeleterCompleted(
297    const std::string& service_path) {
298  std::map<std::string, ProfileEntryDeleter*>::iterator iter =
299      profile_entry_deleters_.find(service_path);
300  DCHECK(iter != profile_entry_deleters_.end());
301  delete iter->second;
302  profile_entry_deleters_.erase(iter);
303}
304
305void NetworkConfigurationHandler::SetPropertiesSuccessCallback(
306    const std::string& service_path,
307    const base::Closure& callback) {
308  if (!callback.is_null())
309    callback.Run();
310  network_state_handler_->RequestUpdateForNetwork(service_path);
311}
312
313void NetworkConfigurationHandler::SetPropertiesErrorCallback(
314    const std::string& service_path,
315    const network_handler::ErrorCallback& error_callback,
316    const std::string& dbus_error_name,
317    const std::string& dbus_error_message) {
318  network_handler::ShillErrorCallbackFunction(
319      "Config.SetProperties Failed",
320      service_path, error_callback,
321      dbus_error_name, dbus_error_message);
322  // Some properties may have changed so request an update regardless.
323  network_state_handler_->RequestUpdateForNetwork(service_path);
324}
325
326void NetworkConfigurationHandler::ClearPropertiesSuccessCallback(
327    const std::string& service_path,
328    const std::vector<std::string>& names,
329    const base::Closure& callback,
330    const network_handler::ErrorCallback& error_callback,
331    const base::ListValue& result) {
332  const std::string kClearPropertiesFailedError("Error.ClearPropertiesFailed");
333  DCHECK(names.size() == result.GetSize())
334      << "Incorrect result size from ClearProperties.";
335
336  bool some_failed = false;
337  for (size_t i = 0; i < result.GetSize(); ++i) {
338    bool success = false;
339    result.GetBoolean(i, &success);
340    if (!success) {
341      NET_LOG_ERROR("ClearProperties Failed: " + names[i], service_path);
342      some_failed = true;
343    }
344  }
345
346  if (some_failed) {
347    if (!error_callback.is_null()) {
348      scoped_ptr<base::DictionaryValue> error_data(
349          network_handler::CreateErrorData(
350              service_path, kClearPropertiesFailedError,
351              base::StringPrintf("Errors: %" PRIuS, result.GetSize())));
352      error_data->Set("errors", result.DeepCopy());
353      scoped_ptr<base::ListValue> name_list(new base::ListValue);
354      name_list->AppendStrings(names);
355      error_data->Set("names", name_list.release());
356      error_callback.Run(kClearPropertiesFailedError, error_data.Pass());
357    }
358  } else if (!callback.is_null()) {
359    callback.Run();
360  }
361  network_state_handler_->RequestUpdateForNetwork(service_path);
362}
363
364void NetworkConfigurationHandler::ClearPropertiesErrorCallback(
365    const std::string& service_path,
366    const network_handler::ErrorCallback& error_callback,
367    const std::string& dbus_error_name,
368    const std::string& dbus_error_message) {
369  network_handler::ShillErrorCallbackFunction(
370      "Config.ClearProperties Failed",
371      service_path, error_callback,
372      dbus_error_name, dbus_error_message);
373  // Some properties may have changed so request an update regardless.
374  network_state_handler_->RequestUpdateForNetwork(service_path);
375}
376
377// static
378NetworkConfigurationHandler* NetworkConfigurationHandler::InitializeForTest(
379    NetworkStateHandler* network_state_handler) {
380  NetworkConfigurationHandler* handler = new NetworkConfigurationHandler();
381  handler->Init(network_state_handler);
382  return handler;
383}
384
385}  // namespace chromeos
386