1 // Copyright 2014 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.
5 #include "extensions/browser/api/networking_private/networking_private_linux.h"
8 #include "base/bind_helpers.h"
9 #include "base/callback.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/strings/string16.h"
12 #include "base/strings/string_split.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/threading/sequenced_worker_pool.h"
15 #include "components/onc/onc_constants.h"
16 #include "content/public/browser/browser_context.h"
17 #include "content/public/browser/browser_thread.h"
19 #include "dbus/message.h"
20 #include "dbus/object_path.h"
21 #include "dbus/object_proxy.h"
22 #include "extensions/browser/api/networking_private/network_config_dbus_constants_linux.h"
23 #include "extensions/browser/api/networking_private/networking_private_api.h"
24 #include "extensions/browser/api/networking_private/networking_private_delegate_observer.h"
26 ////////////////////////////////////////////////////////////////////////////////
28 namespace extensions
{
31 // Access Point info strings.
32 const char kAccessPointInfoName
[] = "Name";
33 const char kAccessPointInfoGuid
[] = "GUID";
34 const char kAccessPointInfoConnectable
[] = "Connectable";
35 const char kAccessPointInfoConnectionState
[] = "ConnectionState";
36 const char kAccessPointInfoType
[] = "Type";
37 const char kAccessPointInfoTypeWifi
[] = "WiFi";
38 const char kAccessPointInfoWifiSignalStrengthDotted
[] = "WiFi.SignalStrength";
39 const char kAccessPointInfoWifiSecurityDotted
[] = "WiFi.Security";
41 // Access point security type strings.
42 const char kAccessPointSecurityNone
[] = "None";
43 const char kAccessPointSecurityUnknown
[] = "Unknown";
44 const char kAccessPointSecurityWpaPsk
[] = "WPA-PSK";
45 const char kAccessPointSecurity9021X
[] = "WEP-8021X";
47 // Parses the GUID which contains 3 pieces of relevant information. The
48 // object path to the network device, the object path of the access point,
50 bool ParseNetworkGuid(const std::string
& guid
,
51 std::string
* device_path
,
52 std::string
* access_point_path
,
54 std::vector
<std::string
> guid_parts
;
56 base::SplitString(guid
, '|', &guid_parts
);
58 if (guid_parts
.size() != 3) {
62 *device_path
= guid_parts
[0];
63 *access_point_path
= guid_parts
[1];
64 *ssid
= guid_parts
[2];
66 if (device_path
->empty() || access_point_path
->empty() || ssid
->empty()) {
73 // Simplified helper to parse the SSID from the GUID.
74 bool GuidToSsid(const std::string
& guid
, std::string
* ssid
) {
77 return ParseNetworkGuid(guid
, &unused_1
, &unused_2
, ssid
);
80 // Iterates over the map cloning the contained networks to a
81 // list then returns the list.
82 scoped_ptr
<base::ListValue
> CopyNetworkMapToList(
83 const NetworkingPrivateLinux::NetworkMap
& network_map
) {
84 scoped_ptr
<base::ListValue
> network_list(new base::ListValue
);
86 for (const auto& network
: network_map
) {
87 network_list
->Append(network
.second
->DeepCopy());
90 return network_list
.Pass();
93 // Constructs a network guid from its constituent parts.
94 std::string
ConstructNetworkGuid(const dbus::ObjectPath
& device_path
,
95 const dbus::ObjectPath
& access_point_path
,
96 const std::string
& ssid
) {
97 return device_path
.value() + "|" + access_point_path
.value() + "|" + ssid
;
100 // Logs that the method is not implemented and reports |kErrorNotSupported|
101 // to the failure callback.
102 void ReportNotSupported(
103 const std::string
& method_name
,
104 const NetworkingPrivateDelegate::FailureCallback
& failure_callback
) {
105 LOG(WARNING
) << method_name
<< " is not supported";
106 failure_callback
.Run(extensions::networking_private::kErrorNotSupported
);
109 // Fires the appropriate callback when the network connect operation succeeds
111 void OnNetworkConnectOperationCompleted(
112 scoped_ptr
<std::string
> error
,
113 const NetworkingPrivateDelegate::VoidCallback
& success_callback
,
114 const NetworkingPrivateDelegate::FailureCallback
& failure_callback
) {
115 if (!error
->empty()) {
116 failure_callback
.Run(*error
);
119 success_callback
.Run();
122 // Fires the appropriate callback when the network properties are returned
123 // from the |dbus_thread_|.
124 void GetCachedNetworkPropertiesCallback(
125 scoped_ptr
<std::string
> error
,
126 scoped_ptr
<base::DictionaryValue
> properties
,
127 const NetworkingPrivateDelegate::DictionaryCallback
& success_callback
,
128 const NetworkingPrivateDelegate::FailureCallback
& failure_callback
) {
129 if (!error
->empty()) {
130 failure_callback
.Run(*error
);
133 success_callback
.Run(properties
.Pass());
138 NetworkingPrivateLinux::NetworkingPrivateLinux(
139 content::BrowserContext
* browser_context
,
140 scoped_ptr
<VerifyDelegate
> verify_delegate
)
141 : NetworkingPrivateDelegate(verify_delegate
.Pass()),
142 browser_context_(browser_context
),
143 dbus_thread_("Networking Private DBus"),
144 network_manager_proxy_(NULL
) {
145 base::Thread::Options
thread_options(base::MessageLoop::Type::TYPE_IO
, 0);
147 dbus_thread_
.StartWithOptions(thread_options
);
148 dbus_thread_
.task_runner()->PostTask(
150 base::Bind(&NetworkingPrivateLinux::Initialize
, base::Unretained(this)));
153 NetworkingPrivateLinux::~NetworkingPrivateLinux() {
157 void NetworkingPrivateLinux::AssertOnDBusThread() {
158 DCHECK(dbus_task_runner_
->RunsTasksOnCurrentThread());
161 void NetworkingPrivateLinux::Initialize() {
162 dbus_task_runner_
= dbus_thread_
.task_runner();
163 // This has to be called after the task runner is initialized.
164 AssertOnDBusThread();
166 dbus::Bus::Options dbus_options
;
167 dbus_options
.bus_type
= dbus::Bus::SYSTEM
;
168 dbus_options
.connection_type
= dbus::Bus::PRIVATE
;
169 dbus_options
.dbus_task_runner
= dbus_task_runner_
;
171 dbus_
= new dbus::Bus(dbus_options
);
172 network_manager_proxy_
= dbus_
->GetObjectProxy(
173 networking_private::kNetworkManagerNamespace
,
174 dbus::ObjectPath(networking_private::kNetworkManagerPath
));
176 if (!network_manager_proxy_
) {
177 LOG(ERROR
) << "Platform does not support NetworkManager over DBUS";
180 network_map_
.reset(new NetworkMap());
183 bool NetworkingPrivateLinux::CheckNetworkManagerSupported(
184 const FailureCallback
& failure_callback
) {
185 if (!network_manager_proxy_
) {
186 ReportNotSupported("NetworkManager over DBus", failure_callback
);
193 void NetworkingPrivateLinux::GetProperties(
194 const std::string
& guid
,
195 const DictionaryCallback
& success_callback
,
196 const FailureCallback
& failure_callback
) {
197 GetState(guid
, success_callback
, failure_callback
);
200 void NetworkingPrivateLinux::GetManagedProperties(
201 const std::string
& guid
,
202 const DictionaryCallback
& success_callback
,
203 const FailureCallback
& failure_callback
) {
204 ReportNotSupported("GetManagedProperties", failure_callback
);
207 void NetworkingPrivateLinux::GetState(
208 const std::string
& guid
,
209 const DictionaryCallback
& success_callback
,
210 const FailureCallback
& failure_callback
) {
211 if (!CheckNetworkManagerSupported(failure_callback
))
214 scoped_ptr
<std::string
> error(new std::string
);
215 scoped_ptr
<base::DictionaryValue
> network_properties(
216 new base::DictionaryValue
);
218 // Runs GetCachedNetworkProperties on |dbus_thread|.
219 dbus_thread_
.task_runner()->PostTaskAndReply(
220 FROM_HERE
, base::Bind(&NetworkingPrivateLinux::GetCachedNetworkProperties
,
221 base::Unretained(this), guid
,
222 base::Unretained(network_properties
.get()),
223 base::Unretained(error
.get())),
224 base::Bind(&GetCachedNetworkPropertiesCallback
, base::Passed(&error
),
225 base::Passed(&network_properties
), success_callback
,
229 void NetworkingPrivateLinux::GetCachedNetworkProperties(
230 const std::string
& guid
,
231 base::DictionaryValue
* properties
,
232 std::string
* error
) {
233 AssertOnDBusThread();
236 if (!GuidToSsid(guid
, &ssid
)) {
237 *error
= "Invalid Network GUID format";
241 NetworkMap::const_iterator network_iter
=
242 network_map_
->find(base::UTF8ToUTF16(ssid
));
243 if (network_iter
== network_map_
->end()) {
244 *error
= "Unknown network GUID";
248 // Make a copy of the properties out of the cached map.
249 scoped_ptr
<base::DictionaryValue
> temp_properties(
250 network_iter
->second
->DeepCopy());
252 // Swap the new copy into the dictionary that is shared with the reply.
253 properties
->Swap(temp_properties
.get());
256 void NetworkingPrivateLinux::SetProperties(
257 const std::string
& guid
,
258 scoped_ptr
<base::DictionaryValue
> properties
,
259 const VoidCallback
& success_callback
,
260 const FailureCallback
& failure_callback
) {
261 ReportNotSupported("SetProperties", failure_callback
);
264 void NetworkingPrivateLinux::CreateNetwork(
266 scoped_ptr
<base::DictionaryValue
> properties
,
267 const StringCallback
& success_callback
,
268 const FailureCallback
& failure_callback
) {
269 ReportNotSupported("CreateNetwork", failure_callback
);
272 void NetworkingPrivateLinux::ForgetNetwork(
273 const std::string
& guid
,
274 const VoidCallback
& success_callback
,
275 const FailureCallback
& failure_callback
) {
276 // TODO(zentaro): Implement for Linux.
277 ReportNotSupported("ForgetNetwork", failure_callback
);
280 void NetworkingPrivateLinux::GetNetworks(
281 const std::string
& network_type
,
282 bool configured_only
,
285 const NetworkListCallback
& success_callback
,
286 const FailureCallback
& failure_callback
) {
287 if (!CheckNetworkManagerSupported(failure_callback
)) {
291 scoped_ptr
<NetworkMap
> network_map(new NetworkMap
);
293 if (!(network_type
== ::onc::network_type::kWiFi
||
294 network_type
== ::onc::network_type::kWireless
||
295 network_type
== ::onc::network_type::kAllTypes
)) {
296 // Only enumerating WiFi networks is supported on linux.
297 ReportNotSupported("GetNetworks with network_type=" + network_type
,
302 // Runs GetAllWiFiAccessPoints on the dbus_thread and returns the
303 // results back to OnAccessPointsFound where the callback is fired.
304 dbus_thread_
.task_runner()->PostTaskAndReply(
306 base::Bind(&NetworkingPrivateLinux::GetAllWiFiAccessPoints
,
307 base::Unretained(this), configured_only
, visible_only
, limit
,
308 base::Unretained(network_map
.get())),
309 base::Bind(&NetworkingPrivateLinux::OnAccessPointsFound
,
310 base::Unretained(this), base::Passed(&network_map
),
311 success_callback
, failure_callback
));
314 bool NetworkingPrivateLinux::GetNetworksForScanRequest() {
315 if (!network_manager_proxy_
) {
319 scoped_ptr
<NetworkMap
> network_map(new NetworkMap
);
321 // Runs GetAllWiFiAccessPoints on the dbus_thread and returns the
322 // results back to SendNetworkListChangedEvent to fire the event. No
323 // callbacks are used in this case.
324 dbus_thread_
.task_runner()->PostTaskAndReply(
325 FROM_HERE
, base::Bind(&NetworkingPrivateLinux::GetAllWiFiAccessPoints
,
326 base::Unretained(this), false /* configured_only */,
327 false /* visible_only */, 0 /* limit */,
328 base::Unretained(network_map
.get())),
329 base::Bind(&NetworkingPrivateLinux::OnAccessPointsFoundViaScan
,
330 base::Unretained(this), base::Passed(&network_map
)));
335 // Constructs the network configuration message and connects to the network.
336 // The message is of the form:
338 // '802-11-wireless': {
339 // 'ssid': 'FooNetwork'
342 void NetworkingPrivateLinux::ConnectToNetwork(const std::string
& guid
,
343 std::string
* error
) {
344 AssertOnDBusThread();
345 std::string device_path_str
;
346 std::string access_point_path_str
;
348 DVLOG(1) << "Connecting to network GUID " << guid
;
350 if (!ParseNetworkGuid(guid
, &device_path_str
, &access_point_path_str
,
352 *error
= "Invalid Network GUID format";
356 // Set the connection state to connecting in the map.
357 if (!SetConnectionStateAndPostEvent(guid
, ssid
,
358 ::onc::connection_state::kConnecting
)) {
359 *error
= "Unknown network GUID";
363 dbus::ObjectPath
device_path(device_path_str
);
364 dbus::ObjectPath
access_point_path(access_point_path_str
);
366 dbus::MethodCall
method_call(
367 networking_private::kNetworkManagerNamespace
,
368 networking_private::kNetworkManagerAddAndActivateConnectionMethod
);
369 dbus::MessageWriter
builder(&method_call
);
371 // Build up the settings nested dictionary.
372 dbus::MessageWriter
array_writer(&method_call
);
373 builder
.OpenArray("{sa{sv}}", &array_writer
);
375 dbus::MessageWriter
dict_writer(&method_call
);
376 array_writer
.OpenDictEntry(&dict_writer
);
377 // TODO(zentaro): Support other network types. Currently only WiFi is
379 dict_writer
.AppendString(
380 networking_private::kNetworkManagerConnectionConfig80211Wireless
);
382 dbus::MessageWriter
wifi_array(&method_call
);
383 dict_writer
.OpenArray("{sv}", &wifi_array
);
385 dbus::MessageWriter
wifi_dict_writer(&method_call
);
386 wifi_array
.OpenDictEntry(&wifi_dict_writer
);
387 wifi_dict_writer
.AppendString(
388 networking_private::kNetworkManagerConnectionConfigSsid
);
390 dbus::MessageWriter
variant_writer(&method_call
);
391 wifi_dict_writer
.OpenVariant("ay", &variant_writer
);
392 variant_writer
.AppendArrayOfBytes(
393 reinterpret_cast<const uint8
*>(ssid
.c_str()), ssid
.size());
395 // Close all the arrays and dicts.
396 wifi_dict_writer
.CloseContainer(&variant_writer
);
397 wifi_array
.CloseContainer(&wifi_dict_writer
);
398 dict_writer
.CloseContainer(&wifi_array
);
399 array_writer
.CloseContainer(&dict_writer
);
400 builder
.CloseContainer(&array_writer
);
402 builder
.AppendObjectPath(device_path
);
403 builder
.AppendObjectPath(access_point_path
);
405 scoped_ptr
<dbus::Response
> response(
406 network_manager_proxy_
->CallMethodAndBlock(
407 &method_call
, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
));
409 LOG(ERROR
) << "Failed to add a new connection";
410 *error
= "Failed to connect.";
412 // Set the connection state to NotConnected in the map.
413 SetConnectionStateAndPostEvent(guid
, ssid
,
414 ::onc::connection_state::kNotConnected
);
418 dbus::MessageReader
reader(response
.get());
419 dbus::ObjectPath connection_settings_path
;
420 dbus::ObjectPath active_connection_path
;
422 if (!reader
.PopObjectPath(&connection_settings_path
)) {
423 LOG(ERROR
) << "Unexpected response for add connection path "
424 << ": " << response
->ToString();
425 *error
= "Failed to connect.";
427 // Set the connection state to NotConnected in the map.
428 SetConnectionStateAndPostEvent(guid
, ssid
,
429 ::onc::connection_state::kNotConnected
);
433 if (!reader
.PopObjectPath(&active_connection_path
)) {
434 LOG(ERROR
) << "Unexpected response for connection path "
435 << ": " << response
->ToString();
436 *error
= "Failed to connect.";
438 // Set the connection state to NotConnected in the map.
439 SetConnectionStateAndPostEvent(guid
, ssid
,
440 ::onc::connection_state::kNotConnected
);
444 // Set the connection state to Connected in the map.
445 SetConnectionStateAndPostEvent(guid
, ssid
,
446 ::onc::connection_state::kConnected
);
450 void NetworkingPrivateLinux::DisconnectFromNetwork(const std::string
& guid
,
451 std::string
* error
) {
452 AssertOnDBusThread();
453 std::string device_path_str
;
454 std::string access_point_path_str
;
456 DVLOG(1) << "Disconnecting from network GUID " << guid
;
458 if (!ParseNetworkGuid(guid
, &device_path_str
, &access_point_path_str
,
460 *error
= "Invalid Network GUID format";
464 scoped_ptr
<NetworkMap
> network_map(new NetworkMap
);
465 GetAllWiFiAccessPoints(false /* configured_only */, false /* visible_only */,
466 0 /* limit */, network_map
.get());
468 NetworkMap::const_iterator network_iter
=
469 network_map
->find(base::UTF8ToUTF16(ssid
));
470 if (network_iter
== network_map
->end()) {
471 // This network doesn't exist so there's nothing to do.
475 std::string connection_state
;
476 network_iter
->second
->GetString(kAccessPointInfoConnectionState
,
478 if (connection_state
== ::onc::connection_state::kNotConnected
) {
479 // Already disconnected so nothing to do.
483 // It's not disconnected so disconnect it.
484 dbus::ObjectProxy
* device_proxy
=
485 dbus_
->GetObjectProxy(networking_private::kNetworkManagerNamespace
,
486 dbus::ObjectPath(device_path_str
));
487 dbus::MethodCall
method_call(
488 networking_private::kNetworkManagerDeviceNamespace
,
489 networking_private::kNetworkManagerDisconnectMethod
);
490 scoped_ptr
<dbus::Response
> response(device_proxy
->CallMethodAndBlock(
491 &method_call
, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
));
494 LOG(WARNING
) << "Failed to disconnect network on device "
496 *error
= "Failed to disconnect network";
500 void NetworkingPrivateLinux::StartConnect(
501 const std::string
& guid
,
502 const VoidCallback
& success_callback
,
503 const FailureCallback
& failure_callback
) {
504 if (!CheckNetworkManagerSupported(failure_callback
))
507 scoped_ptr
<std::string
> error(new std::string
);
509 // Runs ConnectToNetwork on |dbus_thread|.
510 dbus_thread_
.task_runner()->PostTaskAndReply(
512 base::Bind(&NetworkingPrivateLinux::ConnectToNetwork
,
513 base::Unretained(this), guid
, base::Unretained(error
.get())),
514 base::Bind(&OnNetworkConnectOperationCompleted
, base::Passed(&error
),
515 success_callback
, failure_callback
));
518 void NetworkingPrivateLinux::StartDisconnect(
519 const std::string
& guid
,
520 const VoidCallback
& success_callback
,
521 const FailureCallback
& failure_callback
) {
522 if (!CheckNetworkManagerSupported(failure_callback
))
525 scoped_ptr
<std::string
> error(new std::string
);
527 // Runs DisconnectFromNetwork on |dbus_thread|.
528 dbus_thread_
.task_runner()->PostTaskAndReply(
530 base::Bind(&NetworkingPrivateLinux::DisconnectFromNetwork
,
531 base::Unretained(this), guid
, base::Unretained(error
.get())),
532 base::Bind(&OnNetworkConnectOperationCompleted
, base::Passed(&error
),
533 success_callback
, failure_callback
));
536 void NetworkingPrivateLinux::SetWifiTDLSEnabledState(
537 const std::string
& ip_or_mac_address
,
539 const StringCallback
& success_callback
,
540 const FailureCallback
& failure_callback
) {
541 ReportNotSupported("SetWifiTDLSEnabledState", failure_callback
);
544 void NetworkingPrivateLinux::GetWifiTDLSStatus(
545 const std::string
& ip_or_mac_address
,
546 const StringCallback
& success_callback
,
547 const FailureCallback
& failure_callback
) {
548 ReportNotSupported("GetWifiTDLSStatus", failure_callback
);
551 void NetworkingPrivateLinux::GetCaptivePortalStatus(
552 const std::string
& guid
,
553 const StringCallback
& success_callback
,
554 const FailureCallback
& failure_callback
) {
555 ReportNotSupported("GetCaptivePortalStatus", failure_callback
);
558 void NetworkingPrivateLinux::UnlockCellularSim(
559 const std::string
& guid
,
560 const std::string
& pin
,
561 const std::string
& puk
,
562 const VoidCallback
& success_callback
,
563 const FailureCallback
& failure_callback
) {
564 ReportNotSupported("UnlockCellularSim", failure_callback
);
567 void NetworkingPrivateLinux::SetCellularSimState(
568 const std::string
& guid
,
570 const std::string
& current_pin
,
571 const std::string
& new_pin
,
572 const VoidCallback
& success_callback
,
573 const FailureCallback
& failure_callback
) {
574 ReportNotSupported("SetCellularSimState", failure_callback
);
577 scoped_ptr
<base::ListValue
> NetworkingPrivateLinux::GetEnabledNetworkTypes() {
578 scoped_ptr
<base::ListValue
> network_list(new base::ListValue
);
579 network_list
->AppendString(::onc::network_type::kWiFi
);
580 return network_list
.Pass();
583 scoped_ptr
<NetworkingPrivateDelegate::DeviceStateList
>
584 NetworkingPrivateLinux::GetDeviceStateList() {
585 scoped_ptr
<DeviceStateList
> device_state_list(new DeviceStateList
);
586 scoped_ptr
<api::networking_private::DeviceStateProperties
> properties(
587 new api::networking_private::DeviceStateProperties
);
588 properties
->type
= api::networking_private::NETWORK_TYPE_WIFI
;
589 properties
->state
= api::networking_private::DEVICE_STATE_TYPE_ENABLED
;
590 device_state_list
->push_back(properties
.Pass());
591 return device_state_list
.Pass();
594 bool NetworkingPrivateLinux::EnableNetworkType(const std::string
& type
) {
598 bool NetworkingPrivateLinux::DisableNetworkType(const std::string
& type
) {
602 bool NetworkingPrivateLinux::RequestScan() {
603 return GetNetworksForScanRequest();
606 void NetworkingPrivateLinux::AddObserver(
607 NetworkingPrivateDelegateObserver
* observer
) {
608 network_events_observers_
.AddObserver(observer
);
611 void NetworkingPrivateLinux::RemoveObserver(
612 NetworkingPrivateDelegateObserver
* observer
) {
613 network_events_observers_
.RemoveObserver(observer
);
616 void NetworkingPrivateLinux::OnAccessPointsFound(
617 scoped_ptr
<NetworkMap
> network_map
,
618 const NetworkListCallback
& success_callback
,
619 const FailureCallback
& failure_callback
) {
620 scoped_ptr
<base::ListValue
> network_list
= CopyNetworkMapToList(*network_map
);
621 // Give ownership to the member variable.
622 network_map_
.swap(network_map
);
623 SendNetworkListChangedEvent(*network_list
);
624 success_callback
.Run(network_list
.Pass());
627 void NetworkingPrivateLinux::OnAccessPointsFoundViaScan(
628 scoped_ptr
<NetworkMap
> network_map
) {
629 scoped_ptr
<base::ListValue
> network_list
= CopyNetworkMapToList(*network_map
);
630 // Give ownership to the member variable.
631 network_map_
.swap(network_map
);
632 SendNetworkListChangedEvent(*network_list
);
635 void NetworkingPrivateLinux::SendNetworkListChangedEvent(
636 const base::ListValue
& network_list
) {
637 GuidList guidsForEventCallback
;
639 for (const auto& network
: network_list
) {
641 base::DictionaryValue
* dict
;
642 if (network
->GetAsDictionary(&dict
)) {
643 if (dict
->GetString(kAccessPointInfoGuid
, &guid
)) {
644 guidsForEventCallback
.push_back(guid
);
649 OnNetworkListChangedEventOnUIThread(guidsForEventCallback
);
652 bool NetworkingPrivateLinux::GetNetworkDevices(
653 std::vector
<dbus::ObjectPath
>* device_paths
) {
654 AssertOnDBusThread();
655 dbus::MethodCall
method_call(
656 networking_private::kNetworkManagerNamespace
,
657 networking_private::kNetworkManagerGetDevicesMethod
);
659 scoped_ptr
<dbus::Response
> device_response(
660 network_manager_proxy_
->CallMethodAndBlock(
661 &method_call
, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
));
663 if (!device_response
) {
667 dbus::MessageReader
reader(device_response
.get());
668 if (!reader
.PopArrayOfObjectPaths(device_paths
)) {
669 LOG(WARNING
) << "Unexpected response: " << device_response
->ToString();
676 NetworkingPrivateLinux::DeviceType
NetworkingPrivateLinux::GetDeviceType(
677 const dbus::ObjectPath
& device_path
) {
678 AssertOnDBusThread();
679 dbus::ObjectProxy
* device_proxy
= dbus_
->GetObjectProxy(
680 networking_private::kNetworkManagerNamespace
, device_path
);
681 dbus::MethodCall
method_call(DBUS_INTERFACE_PROPERTIES
,
682 networking_private::kNetworkManagerGetMethod
);
683 dbus::MessageWriter
builder(&method_call
);
684 builder
.AppendString(networking_private::kNetworkManagerDeviceNamespace
);
685 builder
.AppendString(networking_private::kNetworkManagerDeviceType
);
687 scoped_ptr
<dbus::Response
> response(device_proxy
->CallMethodAndBlock(
688 &method_call
, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
));
691 LOG(ERROR
) << "Failed to get the device type for device "
692 << device_path
.value();
693 return NetworkingPrivateLinux::NM_DEVICE_TYPE_UNKNOWN
;
696 dbus::MessageReader
reader(response
.get());
697 uint32 device_type
= 0;
698 if (!reader
.PopVariantOfUint32(&device_type
)) {
699 LOG(ERROR
) << "Unexpected response for device " << device_type
<< ": "
700 << response
->ToString();
701 return NM_DEVICE_TYPE_UNKNOWN
;
704 return static_cast<NetworkingPrivateLinux::DeviceType
>(device_type
);
707 void NetworkingPrivateLinux::GetAllWiFiAccessPoints(bool configured_only
,
710 NetworkMap
* network_map
) {
711 AssertOnDBusThread();
712 // TODO(zentaro): The filters are not implemented and are ignored.
713 std::vector
<dbus::ObjectPath
> device_paths
;
714 if (!GetNetworkDevices(&device_paths
)) {
715 LOG(ERROR
) << "Failed to enumerate network devices";
719 for (const auto& device_path
: device_paths
) {
720 NetworkingPrivateLinux::DeviceType device_type
= GetDeviceType(device_path
);
722 // Get the access points for each WiFi adapter. Other network types are
724 if (device_type
!= NetworkingPrivateLinux::NM_DEVICE_TYPE_WIFI
)
727 // Found a wlan adapter
728 if (!AddAccessPointsFromDevice(device_path
, network_map
)) {
729 // Ignore devices we can't enumerate.
730 LOG(WARNING
) << "Failed to add access points from device "
731 << device_path
.value();
736 scoped_ptr
<dbus::Response
> NetworkingPrivateLinux::GetAccessPointProperty(
737 dbus::ObjectProxy
* access_point_proxy
,
738 const std::string
& property_name
) {
739 AssertOnDBusThread();
740 dbus::MethodCall
method_call(DBUS_INTERFACE_PROPERTIES
,
741 networking_private::kNetworkManagerGetMethod
);
742 dbus::MessageWriter
builder(&method_call
);
743 builder
.AppendString(networking_private::kNetworkManagerAccessPointNamespace
);
744 builder
.AppendString(property_name
);
745 scoped_ptr
<dbus::Response
> response
= access_point_proxy
->CallMethodAndBlock(
746 &method_call
, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
);
748 LOG(ERROR
) << "Failed to get property for " << property_name
;
750 return response
.Pass();
753 bool NetworkingPrivateLinux::GetAccessPointInfo(
754 const dbus::ObjectPath
& access_point_path
,
755 const scoped_ptr
<base::DictionaryValue
>& access_point_info
) {
756 AssertOnDBusThread();
757 dbus::ObjectProxy
* access_point_proxy
= dbus_
->GetObjectProxy(
758 networking_private::kNetworkManagerNamespace
, access_point_path
);
760 // Read the SSID. The GUID is derived from the Ssid.
762 scoped_ptr
<dbus::Response
> response(GetAccessPointProperty(
763 access_point_proxy
, networking_private::kNetworkManagerSsidProperty
));
769 // The response should contain a variant that contains an array of bytes.
770 dbus::MessageReader
reader(response
.get());
771 dbus::MessageReader
variant_reader(response
.get());
772 if (!reader
.PopVariant(&variant_reader
)) {
773 LOG(ERROR
) << "Unexpected response for " << access_point_path
.value()
774 << ": " << response
->ToString();
778 const uint8
* ssid_bytes
= NULL
;
779 size_t ssid_length
= 0;
780 if (!variant_reader
.PopArrayOfBytes(&ssid_bytes
, &ssid_length
)) {
781 LOG(ERROR
) << "Unexpected response for " << access_point_path
.value()
782 << ": " << response
->ToString();
786 std::string
ssidUTF8(ssid_bytes
, ssid_bytes
+ ssid_length
);
787 base::string16 ssid
= base::UTF8ToUTF16(ssidUTF8
);
789 access_point_info
->SetString(kAccessPointInfoName
, ssid
);
792 // Read signal strength.
794 scoped_ptr
<dbus::Response
> response(GetAccessPointProperty(
796 networking_private::kNetworkManagerStrengthProperty
));
801 dbus::MessageReader
reader(response
.get());
803 if (!reader
.PopVariantOfByte(&strength
)) {
804 LOG(ERROR
) << "Unexpected response for " << access_point_path
.value()
805 << ": " << response
->ToString();
809 access_point_info
->SetInteger(kAccessPointInfoWifiSignalStrengthDotted
,
813 // Read the security type. This is from the WpaFlags and RsnFlags property
814 // which are of the same type and can be OR'd together to find all supported
817 uint32 wpa_security_flags
= 0;
819 scoped_ptr
<dbus::Response
> response(GetAccessPointProperty(
821 networking_private::kNetworkManagerWpaFlagsProperty
));
826 dbus::MessageReader
reader(response
.get());
828 if (!reader
.PopVariantOfUint32(&wpa_security_flags
)) {
829 LOG(ERROR
) << "Unexpected response for " << access_point_path
.value()
830 << ": " << response
->ToString();
835 uint32 rsn_security_flags
= 0;
837 scoped_ptr
<dbus::Response
> response(GetAccessPointProperty(
839 networking_private::kNetworkManagerRsnFlagsProperty
));
844 dbus::MessageReader
reader(response
.get());
846 if (!reader
.PopVariantOfUint32(&rsn_security_flags
)) {
847 LOG(ERROR
) << "Unexpected response for " << access_point_path
.value()
848 << ": " << response
->ToString();
853 std::string security
;
854 MapSecurityFlagsToString(rsn_security_flags
| wpa_security_flags
, &security
);
855 access_point_info
->SetString(kAccessPointInfoWifiSecurityDotted
, security
);
856 access_point_info
->SetString(kAccessPointInfoType
, kAccessPointInfoTypeWifi
);
857 access_point_info
->SetBoolean(kAccessPointInfoConnectable
, true);
861 bool NetworkingPrivateLinux::AddAccessPointsFromDevice(
862 const dbus::ObjectPath
& device_path
,
863 NetworkMap
* network_map
) {
864 AssertOnDBusThread();
865 dbus::ObjectPath connected_access_point
;
866 if (!GetConnectedAccessPoint(device_path
, &connected_access_point
)) {
870 dbus::ObjectProxy
* device_proxy
= dbus_
->GetObjectProxy(
871 networking_private::kNetworkManagerNamespace
, device_path
);
872 dbus::MethodCall
method_call(
873 networking_private::kNetworkManagerWirelessDeviceNamespace
,
874 networking_private::kNetworkManagerGetAccessPointsMethod
);
875 scoped_ptr
<dbus::Response
> response(device_proxy
->CallMethodAndBlock(
876 &method_call
, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
));
879 LOG(WARNING
) << "Failed to get access points data for "
880 << device_path
.value();
884 dbus::MessageReader
reader(response
.get());
885 std::vector
<dbus::ObjectPath
> access_point_paths
;
886 if (!reader
.PopArrayOfObjectPaths(&access_point_paths
)) {
887 LOG(ERROR
) << "Unexpected response for " << device_path
.value() << ": "
888 << response
->ToString();
892 for (const auto& access_point_path
: access_point_paths
) {
893 scoped_ptr
<base::DictionaryValue
> access_point(new base::DictionaryValue
);
895 if (GetAccessPointInfo(access_point_path
, access_point
)) {
896 std::string connection_state
=
897 (access_point_path
== connected_access_point
)
898 ? ::onc::connection_state::kConnected
899 : ::onc::connection_state::kNotConnected
;
901 access_point
->SetString(kAccessPointInfoConnectionState
,
904 access_point
->GetString(kAccessPointInfoName
, &ssid
);
906 std::string network_guid
=
907 ConstructNetworkGuid(device_path
, access_point_path
, ssid
);
909 // Adds the network to the map. Since each SSID can actually have multiple
910 // access point paths, this consolidates them. If it is already
911 // in the map it updates the signal strength and GUID paths if this
912 // network is stronger or the one that is connected.
913 AddOrUpdateAccessPoint(network_map
, network_guid
, access_point
);
920 void NetworkingPrivateLinux::AddOrUpdateAccessPoint(
921 NetworkMap
* network_map
,
922 const std::string
& network_guid
,
923 scoped_ptr
<base::DictionaryValue
>& access_point
) {
925 std::string connection_state
;
928 access_point
->GetString(kAccessPointInfoConnectionState
, &connection_state
);
929 access_point
->GetInteger(kAccessPointInfoWifiSignalStrengthDotted
,
931 access_point
->GetString(kAccessPointInfoName
, &ssid
);
932 access_point
->SetString(kAccessPointInfoGuid
, network_guid
);
934 NetworkMap::iterator existing_access_point_iter
= network_map
->find(ssid
);
936 if (existing_access_point_iter
== network_map
->end()) {
937 // Unseen access point. Add it to the map.
938 network_map
->insert(NetworkMap::value_type(
939 ssid
, linked_ptr
<base::DictionaryValue
>(access_point
.release())));
941 // Already seen access point. Update the record if this is the connected
942 // record or if the signal strength is higher. But don't override a weaker
943 // access point if that is the one that is connected.
944 int existing_signal_strength
;
945 linked_ptr
<base::DictionaryValue
>& existing_access_point
=
946 existing_access_point_iter
->second
;
947 existing_access_point
->GetInteger(kAccessPointInfoWifiSignalStrengthDotted
,
948 &existing_signal_strength
);
950 std::string existing_connection_state
;
951 existing_access_point
->GetString(kAccessPointInfoConnectionState
,
952 &existing_connection_state
);
954 if ((connection_state
== ::onc::connection_state::kConnected
) ||
955 (!(existing_connection_state
== ::onc::connection_state::kConnected
) &&
956 signal_strength
> existing_signal_strength
)) {
957 existing_access_point
->SetString(kAccessPointInfoConnectionState
,
959 existing_access_point
->SetInteger(
960 kAccessPointInfoWifiSignalStrengthDotted
, signal_strength
);
961 existing_access_point
->SetString(kAccessPointInfoGuid
, network_guid
);
966 void NetworkingPrivateLinux::MapSecurityFlagsToString(uint32 security_flags
,
967 std::string
* security
) {
968 // Valid values are None, WEP-PSK, WEP-8021X, WPA-PSK, WPA-EAP
969 if (security_flags
== NetworkingPrivateLinux::NM_802_11_AP_SEC_NONE
) {
970 *security
= kAccessPointSecurityNone
;
971 } else if (security_flags
&
972 NetworkingPrivateLinux::NM_802_11_AP_SEC_KEY_MGMT_PSK
) {
973 *security
= kAccessPointSecurityWpaPsk
;
974 } else if (security_flags
&
975 NetworkingPrivateLinux::NM_802_11_AP_SEC_KEY_MGMT_802_1X
) {
976 *security
= kAccessPointSecurity9021X
;
978 DVLOG(1) << "Security flag mapping is missing. Found " << security_flags
;
979 *security
= kAccessPointSecurityUnknown
;
982 DVLOG(1) << "Network security setting " << *security
;
985 bool NetworkingPrivateLinux::GetConnectedAccessPoint(
986 dbus::ObjectPath device_path
,
987 dbus::ObjectPath
* access_point_path
) {
988 AssertOnDBusThread();
989 dbus::MethodCall
method_call(DBUS_INTERFACE_PROPERTIES
,
990 networking_private::kNetworkManagerGetMethod
);
991 dbus::MessageWriter
builder(&method_call
);
992 builder
.AppendString(networking_private::kNetworkManagerNamespace
);
993 builder
.AppendString(networking_private::kNetworkManagerActiveConnections
);
995 scoped_ptr
<dbus::Response
> response(
996 network_manager_proxy_
->CallMethodAndBlock(
997 &method_call
, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
));
1000 LOG(WARNING
) << "Failed to get a list of active connections";
1004 dbus::MessageReader
reader(response
.get());
1005 dbus::MessageReader
variant_reader(response
.get());
1006 if (!reader
.PopVariant(&variant_reader
)) {
1007 LOG(ERROR
) << "Unexpected response: " << response
->ToString();
1011 std::vector
<dbus::ObjectPath
> connection_paths
;
1012 if (!variant_reader
.PopArrayOfObjectPaths(&connection_paths
)) {
1013 LOG(ERROR
) << "Unexpected response: " << response
->ToString();
1017 for (const auto& connection_path
: connection_paths
) {
1018 dbus::ObjectPath connections_device_path
;
1019 if (!GetDeviceOfConnection(connection_path
, &connections_device_path
)) {
1023 if (connections_device_path
== device_path
) {
1024 if (!GetAccessPointForConnection(connection_path
, access_point_path
)) {
1035 bool NetworkingPrivateLinux::GetDeviceOfConnection(
1036 dbus::ObjectPath connection_path
,
1037 dbus::ObjectPath
* device_path
) {
1038 AssertOnDBusThread();
1039 dbus::ObjectProxy
* connection_proxy
= dbus_
->GetObjectProxy(
1040 networking_private::kNetworkManagerNamespace
, connection_path
);
1042 if (!connection_proxy
) {
1046 dbus::MethodCall
method_call(DBUS_INTERFACE_PROPERTIES
,
1047 networking_private::kNetworkManagerGetMethod
);
1048 dbus::MessageWriter
builder(&method_call
);
1049 builder
.AppendString(
1050 networking_private::kNetworkManagerActiveConnectionNamespace
);
1051 builder
.AppendString("Devices");
1053 scoped_ptr
<dbus::Response
> response(connection_proxy
->CallMethodAndBlock(
1054 &method_call
, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
));
1057 LOG(ERROR
) << "Failed to get devices";
1061 dbus::MessageReader
reader(response
.get());
1062 dbus::MessageReader
variant_reader(response
.get());
1063 if (!reader
.PopVariant(&variant_reader
)) {
1064 LOG(ERROR
) << "Unexpected response: " << response
->ToString();
1068 std::vector
<dbus::ObjectPath
> device_paths
;
1069 if (!variant_reader
.PopArrayOfObjectPaths(&device_paths
)) {
1070 LOG(ERROR
) << "Unexpected response: " << response
->ToString();
1074 if (device_paths
.size() == 1) {
1075 *device_path
= device_paths
[0];
1083 bool NetworkingPrivateLinux::GetAccessPointForConnection(
1084 dbus::ObjectPath connection_path
,
1085 dbus::ObjectPath
* access_point_path
) {
1086 AssertOnDBusThread();
1087 dbus::ObjectProxy
* connection_proxy
= dbus_
->GetObjectProxy(
1088 networking_private::kNetworkManagerNamespace
, connection_path
);
1090 if (!connection_proxy
) {
1094 dbus::MethodCall
method_call(DBUS_INTERFACE_PROPERTIES
,
1095 networking_private::kNetworkManagerGetMethod
);
1096 dbus::MessageWriter
builder(&method_call
);
1097 builder
.AppendString(
1098 networking_private::kNetworkManagerActiveConnectionNamespace
);
1099 builder
.AppendString(networking_private::kNetworkManagerSpecificObject
);
1101 scoped_ptr
<dbus::Response
> response(connection_proxy
->CallMethodAndBlock(
1102 &method_call
, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
));
1105 LOG(WARNING
) << "Failed to get access point from active connection";
1109 dbus::MessageReader
reader(response
.get());
1110 dbus::MessageReader
variant_reader(response
.get());
1111 if (!reader
.PopVariant(&variant_reader
)) {
1112 LOG(ERROR
) << "Unexpected response: " << response
->ToString();
1116 if (!variant_reader
.PopObjectPath(access_point_path
)) {
1117 LOG(ERROR
) << "Unexpected response: " << response
->ToString();
1124 bool NetworkingPrivateLinux::SetConnectionStateAndPostEvent(
1125 const std::string
& guid
,
1126 const std::string
& ssid
,
1127 const std::string
& connection_state
) {
1128 AssertOnDBusThread();
1130 NetworkMap::iterator network_iter
=
1131 network_map_
->find(base::UTF8ToUTF16(ssid
));
1132 if (network_iter
== network_map_
->end()) {
1136 DVLOG(1) << "Setting connection state of " << ssid
<< " to "
1137 << connection_state
;
1139 // If setting this network to connected, find the previously connected network
1140 // and disconnect that one. Also retain the guid of that network to fire a
1142 std::string connected_network_guid
;
1143 if (connection_state
== ::onc::connection_state::kConnected
) {
1144 for (auto& network
: *network_map_
) {
1145 std::string other_connection_state
;
1146 if (network
.second
->GetString(kAccessPointInfoConnectionState
,
1147 &other_connection_state
)) {
1148 if (other_connection_state
== ::onc::connection_state::kConnected
) {
1149 network
.second
->GetString(kAccessPointInfoGuid
,
1150 &connected_network_guid
);
1151 network
.second
->SetString(kAccessPointInfoConnectionState
,
1152 ::onc::connection_state::kNotConnected
);
1159 network_iter
->second
->SetString(kAccessPointInfoConnectionState
,
1162 scoped_ptr
<GuidList
> changed_networks(new GuidList());
1163 changed_networks
->push_back(guid
);
1165 // Only add a second network if it exists and it is not the same as the
1166 // network already being added to the list.
1167 if (!connected_network_guid
.empty() && connected_network_guid
!= guid
) {
1168 changed_networks
->push_back(connected_network_guid
);
1171 PostOnNetworksChangedToUIThread(changed_networks
.Pass());
1175 void NetworkingPrivateLinux::OnNetworksChangedEventOnUIThread(
1176 const GuidList
& network_guids
) {
1177 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
1178 FOR_EACH_OBSERVER(NetworkingPrivateDelegateObserver
,
1179 network_events_observers_
,
1180 OnNetworksChangedEvent(network_guids
));
1183 void NetworkingPrivateLinux::OnNetworkListChangedEventOnUIThread(
1184 const GuidList
& network_guids
) {
1185 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
1186 FOR_EACH_OBSERVER(NetworkingPrivateDelegateObserver
,
1187 network_events_observers_
,
1188 OnNetworkListChangedEvent(network_guids
));
1191 void NetworkingPrivateLinux::PostOnNetworksChangedToUIThread(
1192 scoped_ptr
<GuidList
> guid_list
) {
1193 AssertOnDBusThread();
1195 content::BrowserThread::PostTask(
1196 content::BrowserThread::UI
, FROM_HERE
,
1197 base::Bind(&NetworkingPrivateLinux::OnNetworksChangedEventTask
,
1198 base::Unretained(this), base::Passed(&guid_list
)));
1201 void NetworkingPrivateLinux::OnNetworksChangedEventTask(
1202 scoped_ptr
<GuidList
> guid_list
) {
1203 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
1204 OnNetworksChangedEventOnUIThread(*guid_list
);
1207 } // namespace extensions