1// Copyright 2013 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_device_handler_impl.h"
6
7#include "base/bind.h"
8#include "base/location.h"
9#include "base/message_loop/message_loop_proxy.h"
10#include "base/time/time.h"
11#include "base/values.h"
12#include "chromeos/dbus/dbus_thread_manager.h"
13#include "chromeos/dbus/shill_device_client.h"
14#include "chromeos/dbus/shill_ipconfig_client.h"
15#include "chromeos/network/device_state.h"
16#include "chromeos/network/network_event_log.h"
17#include "chromeos/network/network_handler_callbacks.h"
18#include "chromeos/network/network_state_handler.h"
19#include "dbus/object_path.h"
20#include "third_party/cros_system_api/dbus/service_constants.h"
21
22namespace chromeos {
23
24namespace {
25
26std::string GetErrorNameForShillError(const std::string& shill_error_name) {
27  if (shill_error_name == shill::kErrorResultFailure)
28    return NetworkDeviceHandler::kErrorFailure;
29  if (shill_error_name == shill::kErrorResultNotSupported)
30    return NetworkDeviceHandler::kErrorNotSupported;
31  if (shill_error_name == shill::kErrorResultIncorrectPin)
32    return NetworkDeviceHandler::kErrorIncorrectPin;
33  if (shill_error_name == shill::kErrorResultPinBlocked)
34    return NetworkDeviceHandler::kErrorPinBlocked;
35  if (shill_error_name == shill::kErrorResultPinRequired)
36    return NetworkDeviceHandler::kErrorPinRequired;
37  if (shill_error_name == shill::kErrorResultNotFound)
38    return NetworkDeviceHandler::kErrorDeviceMissing;
39  return NetworkDeviceHandler::kErrorUnknown;
40}
41
42void InvokeErrorCallback(const std::string& service_path,
43                         const network_handler::ErrorCallback& error_callback,
44                         const std::string& error_name) {
45  std::string error_msg = "Device Error: " + error_name;
46  NET_LOG_ERROR(error_msg, service_path);
47  network_handler::RunErrorCallback(
48      error_callback, service_path, error_name, error_msg);
49}
50
51void HandleShillCallFailure(
52    const std::string& device_path,
53    const network_handler::ErrorCallback& error_callback,
54    const std::string& shill_error_name,
55    const std::string& shill_error_message) {
56  network_handler::ShillErrorCallbackFunction(
57      GetErrorNameForShillError(shill_error_name),
58      device_path,
59      error_callback,
60      shill_error_name,
61      shill_error_message);
62}
63
64void IPConfigRefreshCallback(const std::string& ipconfig_path,
65                             DBusMethodCallStatus call_status) {
66  if (call_status != DBUS_METHOD_CALL_SUCCESS) {
67    NET_LOG_ERROR(
68        base::StringPrintf("IPConfigs.Refresh Failed: %d", call_status),
69        ipconfig_path);
70  } else {
71    NET_LOG_EVENT("IPConfigs.Refresh Succeeded", ipconfig_path);
72  }
73}
74
75void RefreshIPConfigsCallback(
76    const base::Closure& callback,
77    const network_handler::ErrorCallback& error_callback,
78    const std::string& device_path,
79    const base::DictionaryValue& properties) {
80  const base::ListValue* ip_configs;
81  if (!properties.GetListWithoutPathExpansion(
82          shill::kIPConfigsProperty, &ip_configs)) {
83    NET_LOG_ERROR("RequestRefreshIPConfigs Failed", device_path);
84    network_handler::ShillErrorCallbackFunction(
85        "RequestRefreshIPConfigs Failed",
86        device_path,
87        error_callback,
88        std::string("Missing ") + shill::kIPConfigsProperty, "");
89    return;
90  }
91
92  for (size_t i = 0; i < ip_configs->GetSize(); i++) {
93    std::string ipconfig_path;
94    if (!ip_configs->GetString(i, &ipconfig_path))
95      continue;
96    DBusThreadManager::Get()->GetShillIPConfigClient()->Refresh(
97        dbus::ObjectPath(ipconfig_path),
98        base::Bind(&IPConfigRefreshCallback, ipconfig_path));
99  }
100  // It is safe to invoke |callback| here instead of waiting for the
101  // IPConfig.Refresh callbacks to complete because the Refresh DBus calls will
102  // be executed in order and thus before any further DBus requests that
103  // |callback| may issue.
104  if (!callback.is_null())
105    callback.Run();
106}
107
108void ProposeScanCallback(
109    const std::string& device_path,
110    const base::Closure& callback,
111    const network_handler::ErrorCallback& error_callback,
112    DBusMethodCallStatus call_status) {
113  if (call_status != DBUS_METHOD_CALL_SUCCESS) {
114    network_handler::ShillErrorCallbackFunction(
115        "Device.ProposeScan Failed",
116        device_path,
117        error_callback,
118        base::StringPrintf("DBus call failed: %d", call_status), "");
119    return;
120  }
121  NET_LOG_EVENT("Device.ProposeScan succeeded.", device_path);
122  if (!callback.is_null())
123    callback.Run();
124}
125
126void SetDevicePropertyInternal(
127    const std::string& device_path,
128    const std::string& property_name,
129    const base::Value& value,
130    const base::Closure& callback,
131    const network_handler::ErrorCallback& error_callback) {
132  DBusThreadManager::Get()->GetShillDeviceClient()->SetProperty(
133      dbus::ObjectPath(device_path),
134      property_name,
135      value,
136      callback,
137      base::Bind(&HandleShillCallFailure, device_path, error_callback));
138}
139
140// Struct containing TDLS Operation parameters.
141struct TDLSOperationParams {
142  TDLSOperationParams() : retry_count(0) {}
143  std::string operation;
144  std::string ip_or_mac_address;
145  int retry_count;
146};
147
148// Forward declare for PostDelayedTask.
149void CallPerformTDLSOperation(
150    const std::string& device_path,
151    const TDLSOperationParams& params,
152    const network_handler::StringResultCallback& callback,
153    const network_handler::ErrorCallback& error_callback);
154
155void TDLSSuccessCallback(
156    const std::string& device_path,
157    const TDLSOperationParams& params,
158    const network_handler::StringResultCallback& callback,
159    const network_handler::ErrorCallback& error_callback,
160    const std::string& result) {
161  std::string event_desc = "TDLSSuccessCallback: " + params.operation;
162  if (!result.empty())
163    event_desc += ": " + result;
164  NET_LOG_EVENT(event_desc, device_path);
165  if (params.operation != shill::kTDLSSetupOperation) {
166    if (!callback.is_null())
167      callback.Run(result);
168    return;
169  }
170
171  if (!result.empty())
172    NET_LOG_ERROR("Unexpected TDLS result: " + result, device_path);
173
174  // Send a delayed Status request after a successful Setup call.
175  TDLSOperationParams status_params;
176  status_params.operation = shill::kTDLSStatusOperation;
177  status_params.ip_or_mac_address = params.ip_or_mac_address;
178
179  const int64 kRequestStatusDelayMs = 500;
180  base::TimeDelta request_delay;
181  if (!DBusThreadManager::Get()->GetShillDeviceClient()->GetTestInterface())
182    request_delay = base::TimeDelta::FromMilliseconds(kRequestStatusDelayMs);
183
184  base::MessageLoopProxy::current()->PostDelayedTask(
185      FROM_HERE,
186      base::Bind(&CallPerformTDLSOperation,
187                 device_path, status_params, callback, error_callback),
188      request_delay);
189}
190
191void TDLSErrorCallback(
192    const std::string& device_path,
193    const TDLSOperationParams& params,
194    const network_handler::StringResultCallback& callback,
195    const network_handler::ErrorCallback& error_callback,
196    const std::string& dbus_error_name,
197    const std::string& dbus_error_message) {
198  // If a Setup operation receives an InProgress error, retry.
199  const int kMaxRetries = 5;
200  if (params.operation == shill::kTDLSSetupOperation &&
201      dbus_error_name == shill::kErrorResultInProgress &&
202      params.retry_count < kMaxRetries) {
203    TDLSOperationParams retry_params = params;
204    ++retry_params.retry_count;
205    NET_LOG_EVENT(base::StringPrintf("TDLS Retry: %d", params.retry_count),
206                  device_path);
207    const int64 kReRequestDelayMs = 1000;
208    base::TimeDelta request_delay;
209    if (!DBusThreadManager::Get()->GetShillDeviceClient()->GetTestInterface())
210      request_delay = base::TimeDelta::FromMilliseconds(kReRequestDelayMs);
211
212    base::MessageLoopProxy::current()->PostDelayedTask(
213        FROM_HERE,
214        base::Bind(&CallPerformTDLSOperation,
215                   device_path, retry_params, callback, error_callback),
216        request_delay);
217    return;
218  }
219
220  NET_LOG_ERROR("TDLS Error:" + dbus_error_name + ":" + dbus_error_message,
221                device_path);
222  if (error_callback.is_null())
223    return;
224
225  const std::string error_name =
226      dbus_error_name == shill::kErrorResultInProgress ?
227      NetworkDeviceHandler::kErrorTimeout : NetworkDeviceHandler::kErrorUnknown;
228  const std::string& error_detail = params.ip_or_mac_address;
229  scoped_ptr<base::DictionaryValue> error_data(
230      network_handler::CreateDBusErrorData(
231          device_path, error_name, error_detail,
232          dbus_error_name, dbus_error_message));
233  error_callback.Run(error_name, error_data.Pass());
234}
235
236void CallPerformTDLSOperation(
237    const std::string& device_path,
238    const TDLSOperationParams& params,
239    const network_handler::StringResultCallback& callback,
240    const network_handler::ErrorCallback& error_callback) {
241  NET_LOG_EVENT("CallPerformTDLSOperation: " + params.operation, device_path);
242  DBusThreadManager::Get()->GetShillDeviceClient()->PerformTDLSOperation(
243      dbus::ObjectPath(device_path),
244      params.operation,
245      params.ip_or_mac_address,
246      base::Bind(&TDLSSuccessCallback,
247                 device_path, params, callback, error_callback),
248      base::Bind(&TDLSErrorCallback,
249                 device_path, params, callback, error_callback));
250}
251
252}  // namespace
253
254NetworkDeviceHandlerImpl::~NetworkDeviceHandlerImpl() {
255  network_state_handler_->RemoveObserver(this, FROM_HERE);
256}
257
258void NetworkDeviceHandlerImpl::GetDeviceProperties(
259    const std::string& device_path,
260    const network_handler::DictionaryResultCallback& callback,
261    const network_handler::ErrorCallback& error_callback) const {
262  DBusThreadManager::Get()->GetShillDeviceClient()->GetProperties(
263      dbus::ObjectPath(device_path),
264      base::Bind(&network_handler::GetPropertiesCallback,
265                 callback, error_callback, device_path));
266}
267
268void NetworkDeviceHandlerImpl::SetDeviceProperty(
269    const std::string& device_path,
270    const std::string& property_name,
271    const base::Value& value,
272    const base::Closure& callback,
273    const network_handler::ErrorCallback& error_callback) {
274  const char* const property_blacklist[] = {
275      // Must only be changed by policy/owner through.
276      shill::kCellularAllowRoamingProperty
277  };
278
279  for (size_t i = 0; i < arraysize(property_blacklist); ++i) {
280    if (property_name == property_blacklist[i]) {
281      InvokeErrorCallback(
282          device_path,
283          error_callback,
284          "SetDeviceProperty called on blacklisted property " + property_name);
285      return;
286    }
287  }
288
289  SetDevicePropertyInternal(
290      device_path, property_name, value, callback, error_callback);
291}
292
293void NetworkDeviceHandlerImpl::RequestRefreshIPConfigs(
294    const std::string& device_path,
295    const base::Closure& callback,
296    const network_handler::ErrorCallback& error_callback) {
297  GetDeviceProperties(device_path,
298                      base::Bind(&RefreshIPConfigsCallback,
299                                 callback, error_callback),
300                      error_callback);
301}
302
303void NetworkDeviceHandlerImpl::ProposeScan(
304    const std::string& device_path,
305    const base::Closure& callback,
306    const network_handler::ErrorCallback& error_callback) {
307  DBusThreadManager::Get()->GetShillDeviceClient()->ProposeScan(
308      dbus::ObjectPath(device_path),
309      base::Bind(&ProposeScanCallback, device_path, callback, error_callback));
310}
311
312void NetworkDeviceHandlerImpl::RegisterCellularNetwork(
313    const std::string& device_path,
314    const std::string& network_id,
315    const base::Closure& callback,
316    const network_handler::ErrorCallback& error_callback) {
317  DBusThreadManager::Get()->GetShillDeviceClient()->Register(
318      dbus::ObjectPath(device_path),
319      network_id,
320      callback,
321      base::Bind(&HandleShillCallFailure, device_path, error_callback));
322}
323
324void NetworkDeviceHandlerImpl::SetCarrier(
325    const std::string& device_path,
326    const std::string& carrier,
327    const base::Closure& callback,
328    const network_handler::ErrorCallback& error_callback) {
329  DBusThreadManager::Get()->GetShillDeviceClient()->SetCarrier(
330      dbus::ObjectPath(device_path),
331      carrier,
332      callback,
333      base::Bind(&HandleShillCallFailure, device_path, error_callback));
334}
335
336void NetworkDeviceHandlerImpl::RequirePin(
337    const std::string& device_path,
338    bool require_pin,
339    const std::string& pin,
340    const base::Closure& callback,
341    const network_handler::ErrorCallback& error_callback) {
342  DBusThreadManager::Get()->GetShillDeviceClient()->RequirePin(
343      dbus::ObjectPath(device_path),
344      pin,
345      require_pin,
346      callback,
347      base::Bind(&HandleShillCallFailure, device_path, error_callback));
348}
349
350void NetworkDeviceHandlerImpl::EnterPin(
351    const std::string& device_path,
352    const std::string& pin,
353    const base::Closure& callback,
354    const network_handler::ErrorCallback& error_callback) {
355  DBusThreadManager::Get()->GetShillDeviceClient()->EnterPin(
356      dbus::ObjectPath(device_path),
357      pin,
358      callback,
359      base::Bind(&HandleShillCallFailure, device_path, error_callback));
360}
361
362void NetworkDeviceHandlerImpl::UnblockPin(
363    const std::string& device_path,
364    const std::string& puk,
365    const std::string& new_pin,
366    const base::Closure& callback,
367    const network_handler::ErrorCallback& error_callback) {
368  DBusThreadManager::Get()->GetShillDeviceClient()->UnblockPin(
369      dbus::ObjectPath(device_path),
370      puk,
371      new_pin,
372      callback,
373      base::Bind(&HandleShillCallFailure, device_path, error_callback));
374}
375
376void NetworkDeviceHandlerImpl::ChangePin(
377    const std::string& device_path,
378    const std::string& old_pin,
379    const std::string& new_pin,
380    const base::Closure& callback,
381    const network_handler::ErrorCallback& error_callback) {
382  DBusThreadManager::Get()->GetShillDeviceClient()->ChangePin(
383      dbus::ObjectPath(device_path),
384      old_pin,
385      new_pin,
386      callback,
387      base::Bind(&HandleShillCallFailure, device_path, error_callback));
388}
389
390void NetworkDeviceHandlerImpl::SetCellularAllowRoaming(
391    const bool allow_roaming) {
392  cellular_allow_roaming_ = allow_roaming;
393  ApplyCellularAllowRoamingToShill();
394}
395
396void NetworkDeviceHandlerImpl::SetWifiTDLSEnabled(
397    const std::string& ip_or_mac_address,
398    bool enabled,
399    const network_handler::StringResultCallback& callback,
400    const network_handler::ErrorCallback& error_callback) {
401  const DeviceState* device_state =
402      network_state_handler_->GetDeviceStateByType(NetworkTypePattern::WiFi());
403  if (!device_state) {
404    if (error_callback.is_null())
405      return;
406    scoped_ptr<base::DictionaryValue> error_data(new base::DictionaryValue);
407    error_data->SetString(network_handler::kErrorName, kErrorDeviceMissing);
408    error_callback.Run(kErrorDeviceMissing, error_data.Pass());
409    return;
410  }
411  TDLSOperationParams params;
412  params.operation = enabled ? shill::kTDLSSetupOperation
413      : shill::kTDLSTeardownOperation;
414  params.ip_or_mac_address = ip_or_mac_address;
415  CallPerformTDLSOperation(
416      device_state->path(), params, callback, error_callback);
417}
418
419void NetworkDeviceHandlerImpl::GetWifiTDLSStatus(
420    const std::string& ip_or_mac_address,
421    const network_handler::StringResultCallback& callback,
422    const network_handler::ErrorCallback& error_callback) {
423  const DeviceState* device_state =
424      network_state_handler_->GetDeviceStateByType(NetworkTypePattern::WiFi());
425  if (!device_state) {
426    if (error_callback.is_null())
427      return;
428    scoped_ptr<base::DictionaryValue> error_data(new base::DictionaryValue);
429    error_data->SetString(network_handler::kErrorName, kErrorDeviceMissing);
430    error_callback.Run(kErrorDeviceMissing, error_data.Pass());
431    return;
432  }
433  TDLSOperationParams params;
434  params.operation = shill::kTDLSStatusOperation;
435  params.ip_or_mac_address = ip_or_mac_address;
436  CallPerformTDLSOperation(
437      device_state->path(), params, callback, error_callback);
438}
439
440void NetworkDeviceHandlerImpl::DeviceListChanged() {
441  ApplyCellularAllowRoamingToShill();
442}
443
444NetworkDeviceHandlerImpl::NetworkDeviceHandlerImpl()
445    : network_state_handler_(NULL),
446      cellular_allow_roaming_(false) {}
447
448void NetworkDeviceHandlerImpl::Init(
449    NetworkStateHandler* network_state_handler) {
450  DCHECK(network_state_handler);
451  network_state_handler_ = network_state_handler;
452  network_state_handler_->AddObserver(this, FROM_HERE);
453}
454
455void NetworkDeviceHandlerImpl::ApplyCellularAllowRoamingToShill() {
456  NetworkStateHandler::DeviceStateList list;
457  network_state_handler_->GetDeviceListByType(NetworkTypePattern::Cellular(),
458                                              &list);
459  if (list.empty()) {
460    NET_LOG_DEBUG("No cellular device is available",
461                  "Roaming is only supported by cellular devices.");
462    return;
463  }
464  for (NetworkStateHandler::DeviceStateList::const_iterator it = list.begin();
465      it != list.end(); ++it) {
466    const DeviceState* device_state = *it;
467    bool current_allow_roaming = device_state->allow_roaming();
468
469    // If roaming is required by the provider, always try to set to true.
470    bool new_device_value =
471        device_state->provider_requires_roaming() || cellular_allow_roaming_;
472
473    // Only set the value if the current value is different from
474    // |new_device_value|.
475    if (new_device_value == current_allow_roaming)
476      continue;
477
478    SetDevicePropertyInternal(device_state->path(),
479                              shill::kCellularAllowRoamingProperty,
480                              base::FundamentalValue(new_device_value),
481                              base::Bind(&base::DoNothing),
482                              network_handler::ErrorCallback());
483  }
484}
485
486}  // namespace chromeos
487