Supervised user import: Listen for profile creation/deletion
[chromium-blink-merge.git] / extensions / browser / api / networking_private / networking_private_linux.cc
blobf28eccdd73c99301d2379e27151f4cb1ec7141fc
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"
7 #include "base/bind.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"
18 #include "dbus/bus.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 {
30 namespace {
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,
49 // and the ssid.
50 bool ParseNetworkGuid(const std::string& guid,
51 std::string* device_path,
52 std::string* access_point_path,
53 std::string* ssid) {
54 std::vector<std::string> guid_parts;
56 base::SplitString(guid, '|', &guid_parts);
58 if (guid_parts.size() != 3) {
59 return false;
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()) {
67 return false;
70 return true;
73 // Simplified helper to parse the SSID from the GUID.
74 bool GuidToSsid(const std::string& guid, std::string* ssid) {
75 std::string unused_1;
76 std::string unused_2;
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
110 // or fails.
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);
117 return;
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);
131 return;
133 success_callback.Run(properties.Pass());
136 } // namespace
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(
149 FROM_HERE,
150 base::Bind(&NetworkingPrivateLinux::Initialize, base::Unretained(this)));
153 NetworkingPrivateLinux::~NetworkingPrivateLinux() {
154 dbus_thread_.Stop();
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);
187 return false;
190 return true;
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))
212 return;
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,
226 failure_callback));
229 void NetworkingPrivateLinux::GetCachedNetworkProperties(
230 const std::string& guid,
231 base::DictionaryValue* properties,
232 std::string* error) {
233 AssertOnDBusThread();
234 std::string ssid;
236 if (!GuidToSsid(guid, &ssid)) {
237 *error = "Invalid Network GUID format";
238 return;
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";
245 return;
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(
265 bool shared,
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,
283 bool visible_only,
284 int limit,
285 const NetworkListCallback& success_callback,
286 const FailureCallback& failure_callback) {
287 if (!CheckNetworkManagerSupported(failure_callback)) {
288 return;
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,
298 failure_callback);
299 return;
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(
305 FROM_HERE,
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_) {
316 return false;
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)));
332 return true;
335 // Constructs the network configuration message and connects to the network.
336 // The message is of the form:
337 // {
338 // '802-11-wireless': {
339 // 'ssid': 'FooNetwork'
340 // }
341 // }
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;
347 std::string ssid;
348 DVLOG(1) << "Connecting to network GUID " << guid;
350 if (!ParseNetworkGuid(guid, &device_path_str, &access_point_path_str,
351 &ssid)) {
352 *error = "Invalid Network GUID format";
353 return;
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";
360 return;
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
378 // supported.
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));
408 if (!response) {
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);
415 return;
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);
430 return;
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);
441 return;
444 // Set the connection state to Connected in the map.
445 SetConnectionStateAndPostEvent(guid, ssid,
446 ::onc::connection_state::kConnected);
447 return;
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;
455 std::string ssid;
456 DVLOG(1) << "Disconnecting from network GUID " << guid;
458 if (!ParseNetworkGuid(guid, &device_path_str, &access_point_path_str,
459 &ssid)) {
460 *error = "Invalid Network GUID format";
461 return;
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.
472 return;
475 std::string connection_state;
476 network_iter->second->GetString(kAccessPointInfoConnectionState,
477 &connection_state);
478 if (connection_state == ::onc::connection_state::kNotConnected) {
479 // Already disconnected so nothing to do.
480 return;
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));
493 if (!response) {
494 LOG(WARNING) << "Failed to disconnect network on device "
495 << device_path_str;
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))
505 return;
507 scoped_ptr<std::string> error(new std::string);
509 // Runs ConnectToNetwork on |dbus_thread|.
510 dbus_thread_.task_runner()->PostTaskAndReply(
511 FROM_HERE,
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))
523 return;
525 scoped_ptr<std::string> error(new std::string);
527 // Runs DisconnectFromNetwork on |dbus_thread|.
528 dbus_thread_.task_runner()->PostTaskAndReply(
529 FROM_HERE,
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,
538 bool enabled,
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 scoped_ptr<base::ListValue> NetworkingPrivateLinux::GetEnabledNetworkTypes() {
559 scoped_ptr<base::ListValue> network_list(new base::ListValue);
560 network_list->AppendString(::onc::network_type::kWiFi);
561 return network_list.Pass();
564 scoped_ptr<NetworkingPrivateDelegate::DeviceStateList>
565 NetworkingPrivateLinux::GetDeviceStateList() {
566 scoped_ptr<DeviceStateList> device_state_list(new DeviceStateList);
567 scoped_ptr<core_api::networking_private::DeviceStateProperties> properties(
568 new core_api::networking_private::DeviceStateProperties);
569 properties->type = core_api::networking_private::NETWORK_TYPE_WIFI;
570 properties->state = core_api::networking_private::DEVICE_STATE_TYPE_ENABLED;
571 device_state_list->push_back(properties.Pass());
572 return device_state_list.Pass();
575 bool NetworkingPrivateLinux::EnableNetworkType(const std::string& type) {
576 return false;
579 bool NetworkingPrivateLinux::DisableNetworkType(const std::string& type) {
580 return false;
583 bool NetworkingPrivateLinux::RequestScan() {
584 return GetNetworksForScanRequest();
587 void NetworkingPrivateLinux::AddObserver(
588 NetworkingPrivateDelegateObserver* observer) {
589 network_events_observers_.AddObserver(observer);
592 void NetworkingPrivateLinux::RemoveObserver(
593 NetworkingPrivateDelegateObserver* observer) {
594 network_events_observers_.RemoveObserver(observer);
597 void NetworkingPrivateLinux::OnAccessPointsFound(
598 scoped_ptr<NetworkMap> network_map,
599 const NetworkListCallback& success_callback,
600 const FailureCallback& failure_callback) {
601 scoped_ptr<base::ListValue> network_list = CopyNetworkMapToList(*network_map);
602 // Give ownership to the member variable.
603 network_map_.swap(network_map);
604 SendNetworkListChangedEvent(*network_list);
605 success_callback.Run(network_list.Pass());
608 void NetworkingPrivateLinux::OnAccessPointsFoundViaScan(
609 scoped_ptr<NetworkMap> network_map) {
610 scoped_ptr<base::ListValue> network_list = CopyNetworkMapToList(*network_map);
611 // Give ownership to the member variable.
612 network_map_.swap(network_map);
613 SendNetworkListChangedEvent(*network_list);
616 void NetworkingPrivateLinux::SendNetworkListChangedEvent(
617 const base::ListValue& network_list) {
618 GuidList guidsForEventCallback;
620 for (const auto& network : network_list) {
621 std::string guid;
622 base::DictionaryValue* dict;
623 if (network->GetAsDictionary(&dict)) {
624 if (dict->GetString(kAccessPointInfoGuid, &guid)) {
625 guidsForEventCallback.push_back(guid);
630 OnNetworkListChangedEventOnUIThread(guidsForEventCallback);
633 bool NetworkingPrivateLinux::GetNetworkDevices(
634 std::vector<dbus::ObjectPath>* device_paths) {
635 AssertOnDBusThread();
636 dbus::MethodCall method_call(
637 networking_private::kNetworkManagerNamespace,
638 networking_private::kNetworkManagerGetDevicesMethod);
640 scoped_ptr<dbus::Response> device_response(
641 network_manager_proxy_->CallMethodAndBlock(
642 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
644 if (!device_response) {
645 return false;
648 dbus::MessageReader reader(device_response.get());
649 if (!reader.PopArrayOfObjectPaths(device_paths)) {
650 LOG(WARNING) << "Unexpected response: " << device_response->ToString();
651 return false;
654 return true;
657 NetworkingPrivateLinux::DeviceType NetworkingPrivateLinux::GetDeviceType(
658 const dbus::ObjectPath& device_path) {
659 AssertOnDBusThread();
660 dbus::ObjectProxy* device_proxy = dbus_->GetObjectProxy(
661 networking_private::kNetworkManagerNamespace, device_path);
662 dbus::MethodCall method_call(DBUS_INTERFACE_PROPERTIES,
663 networking_private::kNetworkManagerGetMethod);
664 dbus::MessageWriter builder(&method_call);
665 builder.AppendString(networking_private::kNetworkManagerDeviceNamespace);
666 builder.AppendString(networking_private::kNetworkManagerDeviceType);
668 scoped_ptr<dbus::Response> response(device_proxy->CallMethodAndBlock(
669 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
671 if (!response) {
672 LOG(ERROR) << "Failed to get the device type for device "
673 << device_path.value();
674 return NetworkingPrivateLinux::NM_DEVICE_TYPE_UNKNOWN;
677 dbus::MessageReader reader(response.get());
678 uint32 device_type = 0;
679 if (!reader.PopVariantOfUint32(&device_type)) {
680 LOG(ERROR) << "Unexpected response for device " << device_type << ": "
681 << response->ToString();
682 return NM_DEVICE_TYPE_UNKNOWN;
685 return static_cast<NetworkingPrivateLinux::DeviceType>(device_type);
688 void NetworkingPrivateLinux::GetAllWiFiAccessPoints(bool configured_only,
689 bool visible_only,
690 int limit,
691 NetworkMap* network_map) {
692 AssertOnDBusThread();
693 // TODO(zentaro): The filters are not implemented and are ignored.
694 std::vector<dbus::ObjectPath> device_paths;
695 if (!GetNetworkDevices(&device_paths)) {
696 LOG(ERROR) << "Failed to enumerate network devices";
697 return;
700 for (const auto& device_path : device_paths) {
701 NetworkingPrivateLinux::DeviceType device_type = GetDeviceType(device_path);
703 // Get the access points for each WiFi adapter. Other network types are
704 // ignored.
705 if (device_type != NetworkingPrivateLinux::NM_DEVICE_TYPE_WIFI)
706 continue;
708 // Found a wlan adapter
709 if (!AddAccessPointsFromDevice(device_path, network_map)) {
710 // Ignore devices we can't enumerate.
711 LOG(WARNING) << "Failed to add access points from device "
712 << device_path.value();
717 scoped_ptr<dbus::Response> NetworkingPrivateLinux::GetAccessPointProperty(
718 dbus::ObjectProxy* access_point_proxy,
719 const std::string& property_name) {
720 AssertOnDBusThread();
721 dbus::MethodCall method_call(DBUS_INTERFACE_PROPERTIES,
722 networking_private::kNetworkManagerGetMethod);
723 dbus::MessageWriter builder(&method_call);
724 builder.AppendString(networking_private::kNetworkManagerAccessPointNamespace);
725 builder.AppendString(property_name);
726 scoped_ptr<dbus::Response> response = access_point_proxy->CallMethodAndBlock(
727 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
728 if (!response) {
729 LOG(ERROR) << "Failed to get property for " << property_name;
731 return response.Pass();
734 bool NetworkingPrivateLinux::GetAccessPointInfo(
735 const dbus::ObjectPath& access_point_path,
736 const scoped_ptr<base::DictionaryValue>& access_point_info) {
737 AssertOnDBusThread();
738 dbus::ObjectProxy* access_point_proxy = dbus_->GetObjectProxy(
739 networking_private::kNetworkManagerNamespace, access_point_path);
741 // Read the SSID. The GUID is derived from the Ssid.
743 scoped_ptr<dbus::Response> response(GetAccessPointProperty(
744 access_point_proxy, networking_private::kNetworkManagerSsidProperty));
746 if (!response) {
747 return false;
750 // The response should contain a variant that contains an array of bytes.
751 dbus::MessageReader reader(response.get());
752 dbus::MessageReader variant_reader(response.get());
753 if (!reader.PopVariant(&variant_reader)) {
754 LOG(ERROR) << "Unexpected response for " << access_point_path.value()
755 << ": " << response->ToString();
756 return false;
759 const uint8* ssid_bytes = NULL;
760 size_t ssid_length = 0;
761 if (!variant_reader.PopArrayOfBytes(&ssid_bytes, &ssid_length)) {
762 LOG(ERROR) << "Unexpected response for " << access_point_path.value()
763 << ": " << response->ToString();
764 return false;
767 std::string ssidUTF8(ssid_bytes, ssid_bytes + ssid_length);
768 base::string16 ssid = base::UTF8ToUTF16(ssidUTF8);
770 access_point_info->SetString(kAccessPointInfoName, ssid);
773 // Read signal strength.
775 scoped_ptr<dbus::Response> response(GetAccessPointProperty(
776 access_point_proxy,
777 networking_private::kNetworkManagerStrengthProperty));
778 if (!response) {
779 return false;
782 dbus::MessageReader reader(response.get());
783 uint8 strength = 0;
784 if (!reader.PopVariantOfByte(&strength)) {
785 LOG(ERROR) << "Unexpected response for " << access_point_path.value()
786 << ": " << response->ToString();
787 return false;
790 access_point_info->SetInteger(kAccessPointInfoWifiSignalStrengthDotted,
791 strength);
794 // Read the security type. This is from the WpaFlags and RsnFlags property
795 // which are of the same type and can be OR'd together to find all supported
796 // security modes.
798 uint32 wpa_security_flags = 0;
800 scoped_ptr<dbus::Response> response(GetAccessPointProperty(
801 access_point_proxy,
802 networking_private::kNetworkManagerWpaFlagsProperty));
803 if (!response) {
804 return false;
807 dbus::MessageReader reader(response.get());
809 if (!reader.PopVariantOfUint32(&wpa_security_flags)) {
810 LOG(ERROR) << "Unexpected response for " << access_point_path.value()
811 << ": " << response->ToString();
812 return false;
816 uint32 rsn_security_flags = 0;
818 scoped_ptr<dbus::Response> response(GetAccessPointProperty(
819 access_point_proxy,
820 networking_private::kNetworkManagerRsnFlagsProperty));
821 if (!response) {
822 return false;
825 dbus::MessageReader reader(response.get());
827 if (!reader.PopVariantOfUint32(&rsn_security_flags)) {
828 LOG(ERROR) << "Unexpected response for " << access_point_path.value()
829 << ": " << response->ToString();
830 return false;
834 std::string security;
835 MapSecurityFlagsToString(rsn_security_flags | wpa_security_flags, &security);
836 access_point_info->SetString(kAccessPointInfoWifiSecurityDotted, security);
837 access_point_info->SetString(kAccessPointInfoType, kAccessPointInfoTypeWifi);
838 access_point_info->SetBoolean(kAccessPointInfoConnectable, true);
839 return true;
842 bool NetworkingPrivateLinux::AddAccessPointsFromDevice(
843 const dbus::ObjectPath& device_path,
844 NetworkMap* network_map) {
845 AssertOnDBusThread();
846 dbus::ObjectPath connected_access_point;
847 if (!GetConnectedAccessPoint(device_path, &connected_access_point)) {
848 return false;
851 dbus::ObjectProxy* device_proxy = dbus_->GetObjectProxy(
852 networking_private::kNetworkManagerNamespace, device_path);
853 dbus::MethodCall method_call(
854 networking_private::kNetworkManagerWirelessDeviceNamespace,
855 networking_private::kNetworkManagerGetAccessPointsMethod);
856 scoped_ptr<dbus::Response> response(device_proxy->CallMethodAndBlock(
857 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
859 if (!response) {
860 LOG(WARNING) << "Failed to get access points data for "
861 << device_path.value();
862 return false;
865 dbus::MessageReader reader(response.get());
866 std::vector<dbus::ObjectPath> access_point_paths;
867 if (!reader.PopArrayOfObjectPaths(&access_point_paths)) {
868 LOG(ERROR) << "Unexpected response for " << device_path.value() << ": "
869 << response->ToString();
870 return false;
873 for (const auto& access_point_path : access_point_paths) {
874 scoped_ptr<base::DictionaryValue> access_point(new base::DictionaryValue);
876 if (GetAccessPointInfo(access_point_path, access_point)) {
877 std::string connection_state =
878 (access_point_path == connected_access_point)
879 ? ::onc::connection_state::kConnected
880 : ::onc::connection_state::kNotConnected;
882 access_point->SetString(kAccessPointInfoConnectionState,
883 connection_state);
884 std::string ssid;
885 access_point->GetString(kAccessPointInfoName, &ssid);
887 std::string network_guid =
888 ConstructNetworkGuid(device_path, access_point_path, ssid);
890 // Adds the network to the map. Since each SSID can actually have multiple
891 // access point paths, this consolidates them. If it is already
892 // in the map it updates the signal strength and GUID paths if this
893 // network is stronger or the one that is connected.
894 AddOrUpdateAccessPoint(network_map, network_guid, access_point);
898 return true;
901 void NetworkingPrivateLinux::AddOrUpdateAccessPoint(
902 NetworkMap* network_map,
903 const std::string& network_guid,
904 scoped_ptr<base::DictionaryValue>& access_point) {
905 base::string16 ssid;
906 std::string connection_state;
907 int signal_strength;
909 access_point->GetString(kAccessPointInfoConnectionState, &connection_state);
910 access_point->GetInteger(kAccessPointInfoWifiSignalStrengthDotted,
911 &signal_strength);
912 access_point->GetString(kAccessPointInfoName, &ssid);
913 access_point->SetString(kAccessPointInfoGuid, network_guid);
915 NetworkMap::iterator existing_access_point_iter = network_map->find(ssid);
917 if (existing_access_point_iter == network_map->end()) {
918 // Unseen access point. Add it to the map.
919 network_map->insert(NetworkMap::value_type(
920 ssid, linked_ptr<base::DictionaryValue>(access_point.release())));
921 } else {
922 // Already seen access point. Update the record if this is the connected
923 // record or if the signal strength is higher. But don't override a weaker
924 // access point if that is the one that is connected.
925 int existing_signal_strength;
926 linked_ptr<base::DictionaryValue>& existing_access_point =
927 existing_access_point_iter->second;
928 existing_access_point->GetInteger(kAccessPointInfoWifiSignalStrengthDotted,
929 &existing_signal_strength);
931 std::string existing_connection_state;
932 existing_access_point->GetString(kAccessPointInfoConnectionState,
933 &existing_connection_state);
935 if ((connection_state == ::onc::connection_state::kConnected) ||
936 (!(existing_connection_state == ::onc::connection_state::kConnected) &&
937 signal_strength > existing_signal_strength)) {
938 existing_access_point->SetString(kAccessPointInfoConnectionState,
939 connection_state);
940 existing_access_point->SetInteger(
941 kAccessPointInfoWifiSignalStrengthDotted, signal_strength);
942 existing_access_point->SetString(kAccessPointInfoGuid, network_guid);
947 void NetworkingPrivateLinux::MapSecurityFlagsToString(uint32 security_flags,
948 std::string* security) {
949 // Valid values are None, WEP-PSK, WEP-8021X, WPA-PSK, WPA-EAP
950 if (security_flags == NetworkingPrivateLinux::NM_802_11_AP_SEC_NONE) {
951 *security = kAccessPointSecurityNone;
952 } else if (security_flags &
953 NetworkingPrivateLinux::NM_802_11_AP_SEC_KEY_MGMT_PSK) {
954 *security = kAccessPointSecurityWpaPsk;
955 } else if (security_flags &
956 NetworkingPrivateLinux::NM_802_11_AP_SEC_KEY_MGMT_802_1X) {
957 *security = kAccessPointSecurity9021X;
958 } else {
959 DVLOG(1) << "Security flag mapping is missing. Found " << security_flags;
960 *security = kAccessPointSecurityUnknown;
963 DVLOG(1) << "Network security setting " << *security;
966 bool NetworkingPrivateLinux::GetConnectedAccessPoint(
967 dbus::ObjectPath device_path,
968 dbus::ObjectPath* access_point_path) {
969 AssertOnDBusThread();
970 dbus::MethodCall method_call(DBUS_INTERFACE_PROPERTIES,
971 networking_private::kNetworkManagerGetMethod);
972 dbus::MessageWriter builder(&method_call);
973 builder.AppendString(networking_private::kNetworkManagerNamespace);
974 builder.AppendString(networking_private::kNetworkManagerActiveConnections);
976 scoped_ptr<dbus::Response> response(
977 network_manager_proxy_->CallMethodAndBlock(
978 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
980 if (!response) {
981 LOG(WARNING) << "Failed to get a list of active connections";
982 return false;
985 dbus::MessageReader reader(response.get());
986 dbus::MessageReader variant_reader(response.get());
987 if (!reader.PopVariant(&variant_reader)) {
988 LOG(ERROR) << "Unexpected response: " << response->ToString();
989 return false;
992 std::vector<dbus::ObjectPath> connection_paths;
993 if (!variant_reader.PopArrayOfObjectPaths(&connection_paths)) {
994 LOG(ERROR) << "Unexpected response: " << response->ToString();
995 return false;
998 for (const auto& connection_path : connection_paths) {
999 dbus::ObjectPath connections_device_path;
1000 if (!GetDeviceOfConnection(connection_path, &connections_device_path)) {
1001 return false;
1004 if (connections_device_path == device_path) {
1005 if (!GetAccessPointForConnection(connection_path, access_point_path)) {
1006 return false;
1009 break;
1013 return true;
1016 bool NetworkingPrivateLinux::GetDeviceOfConnection(
1017 dbus::ObjectPath connection_path,
1018 dbus::ObjectPath* device_path) {
1019 AssertOnDBusThread();
1020 dbus::ObjectProxy* connection_proxy = dbus_->GetObjectProxy(
1021 networking_private::kNetworkManagerNamespace, connection_path);
1023 if (!connection_proxy) {
1024 return false;
1027 dbus::MethodCall method_call(DBUS_INTERFACE_PROPERTIES,
1028 networking_private::kNetworkManagerGetMethod);
1029 dbus::MessageWriter builder(&method_call);
1030 builder.AppendString(
1031 networking_private::kNetworkManagerActiveConnectionNamespace);
1032 builder.AppendString("Devices");
1034 scoped_ptr<dbus::Response> response(connection_proxy->CallMethodAndBlock(
1035 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
1037 if (!response) {
1038 LOG(ERROR) << "Failed to get devices";
1039 return false;
1042 dbus::MessageReader reader(response.get());
1043 dbus::MessageReader variant_reader(response.get());
1044 if (!reader.PopVariant(&variant_reader)) {
1045 LOG(ERROR) << "Unexpected response: " << response->ToString();
1046 return false;
1049 std::vector<dbus::ObjectPath> device_paths;
1050 if (!variant_reader.PopArrayOfObjectPaths(&device_paths)) {
1051 LOG(ERROR) << "Unexpected response: " << response->ToString();
1052 return false;
1055 if (device_paths.size() == 1) {
1056 *device_path = device_paths[0];
1058 return true;
1061 return false;
1064 bool NetworkingPrivateLinux::GetAccessPointForConnection(
1065 dbus::ObjectPath connection_path,
1066 dbus::ObjectPath* access_point_path) {
1067 AssertOnDBusThread();
1068 dbus::ObjectProxy* connection_proxy = dbus_->GetObjectProxy(
1069 networking_private::kNetworkManagerNamespace, connection_path);
1071 if (!connection_proxy) {
1072 return false;
1075 dbus::MethodCall method_call(DBUS_INTERFACE_PROPERTIES,
1076 networking_private::kNetworkManagerGetMethod);
1077 dbus::MessageWriter builder(&method_call);
1078 builder.AppendString(
1079 networking_private::kNetworkManagerActiveConnectionNamespace);
1080 builder.AppendString(networking_private::kNetworkManagerSpecificObject);
1082 scoped_ptr<dbus::Response> response(connection_proxy->CallMethodAndBlock(
1083 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
1085 if (!response) {
1086 LOG(WARNING) << "Failed to get access point from active connection";
1087 return false;
1090 dbus::MessageReader reader(response.get());
1091 dbus::MessageReader variant_reader(response.get());
1092 if (!reader.PopVariant(&variant_reader)) {
1093 LOG(ERROR) << "Unexpected response: " << response->ToString();
1094 return false;
1097 if (!variant_reader.PopObjectPath(access_point_path)) {
1098 LOG(ERROR) << "Unexpected response: " << response->ToString();
1099 return false;
1102 return true;
1105 bool NetworkingPrivateLinux::SetConnectionStateAndPostEvent(
1106 const std::string& guid,
1107 const std::string& ssid,
1108 const std::string& connection_state) {
1109 AssertOnDBusThread();
1111 NetworkMap::iterator network_iter =
1112 network_map_->find(base::UTF8ToUTF16(ssid));
1113 if (network_iter == network_map_->end()) {
1114 return false;
1117 DVLOG(1) << "Setting connection state of " << ssid << " to "
1118 << connection_state;
1120 // If setting this network to connected, find the previously connected network
1121 // and disconnect that one. Also retain the guid of that network to fire a
1122 // changed event.
1123 std::string connected_network_guid;
1124 if (connection_state == ::onc::connection_state::kConnected) {
1125 for (auto& network : *network_map_) {
1126 std::string other_connection_state;
1127 if (network.second->GetString(kAccessPointInfoConnectionState,
1128 &other_connection_state)) {
1129 if (other_connection_state == ::onc::connection_state::kConnected) {
1130 network.second->GetString(kAccessPointInfoGuid,
1131 &connected_network_guid);
1132 network.second->SetString(kAccessPointInfoConnectionState,
1133 ::onc::connection_state::kNotConnected);
1139 // Set the status.
1140 network_iter->second->SetString(kAccessPointInfoConnectionState,
1141 connection_state);
1143 scoped_ptr<GuidList> changed_networks(new GuidList());
1144 changed_networks->push_back(guid);
1146 // Only add a second network if it exists and it is not the same as the
1147 // network already being added to the list.
1148 if (!connected_network_guid.empty() && connected_network_guid != guid) {
1149 changed_networks->push_back(connected_network_guid);
1152 PostOnNetworksChangedToUIThread(changed_networks.Pass());
1153 return true;
1156 void NetworkingPrivateLinux::OnNetworksChangedEventOnUIThread(
1157 const GuidList& network_guids) {
1158 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
1159 FOR_EACH_OBSERVER(NetworkingPrivateDelegateObserver,
1160 network_events_observers_,
1161 OnNetworksChangedEvent(network_guids));
1164 void NetworkingPrivateLinux::OnNetworkListChangedEventOnUIThread(
1165 const GuidList& network_guids) {
1166 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
1167 FOR_EACH_OBSERVER(NetworkingPrivateDelegateObserver,
1168 network_events_observers_,
1169 OnNetworkListChangedEvent(network_guids));
1172 void NetworkingPrivateLinux::PostOnNetworksChangedToUIThread(
1173 scoped_ptr<GuidList> guid_list) {
1174 AssertOnDBusThread();
1176 content::BrowserThread::PostTask(
1177 content::BrowserThread::UI, FROM_HERE,
1178 base::Bind(&NetworkingPrivateLinux::OnNetworksChangedEventTask,
1179 base::Unretained(this), base::Passed(&guid_list)));
1182 void NetworkingPrivateLinux::OnNetworksChangedEventTask(
1183 scoped_ptr<GuidList> guid_list) {
1184 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
1185 OnNetworksChangedEventOnUIThread(*guid_list);
1188 } // namespace extensions