1// Copyright (c) 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 "ash/system/chromeos/network/network_connect.h"
6
7#include "ash/session/session_state_delegate.h"
8#include "ash/shell.h"
9#include "ash/system/chromeos/network/network_state_notifier.h"
10#include "ash/system/system_notifier.h"
11#include "ash/system/tray/system_tray_delegate.h"
12#include "ash/system/tray/system_tray_notifier.h"
13#include "base/bind.h"
14#include "base/memory/scoped_ptr.h"
15#include "base/strings/string_util.h"
16#include "base/strings/utf_string_conversions.h"
17#include "base/values.h"
18#include "chromeos/login/login_state.h"
19#include "chromeos/network/device_state.h"
20#include "chromeos/network/network_activation_handler.h"
21#include "chromeos/network/network_configuration_handler.h"
22#include "chromeos/network/network_connection_handler.h"
23#include "chromeos/network/network_event_log.h"
24#include "chromeos/network/network_handler_callbacks.h"
25#include "chromeos/network/network_profile.h"
26#include "chromeos/network/network_profile_handler.h"
27#include "chromeos/network/network_state.h"
28#include "chromeos/network/network_state_handler.h"
29#include "grit/ash_resources.h"
30#include "grit/ash_strings.h"
31#include "third_party/cros_system_api/dbus/service_constants.h"
32#include "ui/base/l10n/l10n_util.h"
33#include "ui/base/resource/resource_bundle.h"
34#include "ui/message_center/message_center.h"
35#include "ui/message_center/notification.h"
36
37using chromeos::DeviceState;
38using chromeos::NetworkConfigurationHandler;
39using chromeos::NetworkConnectionHandler;
40using chromeos::NetworkHandler;
41using chromeos::NetworkProfile;
42using chromeos::NetworkProfileHandler;
43using chromeos::NetworkState;
44using chromeos::NetworkStateHandler;
45using chromeos::NetworkTypePattern;
46
47namespace ash {
48
49namespace {
50
51// TODO(stevenjb): This should be in service_constants.h
52const char kErrorInProgress[] = "org.chromium.flimflam.Error.InProgress";
53
54// Returns true for carriers that can be activated through Shill instead of
55// through a WebUI dialog.
56bool IsDirectActivatedCarrier(const std::string& carrier) {
57  if (carrier == shill::kCarrierSprint)
58    return true;
59  return false;
60}
61
62void ShowErrorNotification(const std::string& error_name,
63                           const std::string& service_path) {
64  Shell::GetInstance()->system_tray_notifier()->network_state_notifier()->
65      ShowNetworkConnectError(error_name, service_path);
66}
67
68void HandleUnconfiguredNetwork(const std::string& service_path) {
69  const NetworkState* network = NetworkHandler::Get()->network_state_handler()->
70      GetNetworkState(service_path);
71  if (!network) {
72    NET_LOG_ERROR("Configuring unknown network", service_path);
73    return;
74  }
75
76  if (network->type() == shill::kTypeWifi) {
77    // Only show the config view for secure networks, otherwise do nothing.
78    if (network->security() != shill::kSecurityNone) {
79      ash::Shell::GetInstance()->system_tray_delegate()->
80          ShowNetworkConfigure(service_path);
81    }
82    return;
83  }
84
85  if (network->type() == shill::kTypeWimax ||
86      network->type() == shill::kTypeVPN) {
87    ash::Shell::GetInstance()->system_tray_delegate()->
88        ShowNetworkConfigure(service_path);
89    return;
90  }
91
92  if (network->type() == shill::kTypeCellular) {
93    if (network->RequiresActivation()) {
94      ash::network_connect::ActivateCellular(service_path);
95      return;
96    }
97    if (network->cellular_out_of_credits()) {
98      ash::network_connect::ShowMobileSetup(service_path);
99      return;
100    }
101    // No special configure or setup for |network|, show the settings UI.
102    if (chromeos::LoginState::Get()->IsUserLoggedIn()) {
103      ash::Shell::GetInstance()->system_tray_delegate()->
104          ShowNetworkSettings(service_path);
105    }
106    return;
107  }
108  NOTREACHED();
109}
110
111// If |shared| is true, sets |profile_path| to the shared profile path.
112// Otherwise sets |profile_path| to the user profile path if authenticated and
113// available. Returns 'false' if unable to set |profile_path|.
114bool GetNetworkProfilePath(bool shared, std::string* profile_path) {
115  if (shared) {
116    *profile_path = NetworkProfileHandler::GetSharedProfilePath();
117    return true;
118  }
119
120  if (!chromeos::LoginState::Get()->UserHasNetworkProfile()) {
121    NET_LOG_ERROR("User profile specified before login", "");
122    return false;
123  }
124
125  const NetworkProfile* profile  =
126      NetworkHandler::Get()->network_profile_handler()->
127      GetDefaultUserProfile();
128  if (!profile) {
129    NET_LOG_ERROR("No user profile for unshared network configuration", "");
130    return false;
131  }
132
133  *profile_path = profile->path;
134  return true;
135}
136
137void OnConnectFailed(const std::string& service_path,
138                     const std::string& error_name,
139                     scoped_ptr<base::DictionaryValue> error_data) {
140  NET_LOG_ERROR("Connect Failed: " + error_name, service_path);
141
142  if (!ash::Shell::HasInstance())
143    return;
144
145  // If a new connect attempt canceled this connect, no need to notify the user.
146  if (error_name == NetworkConnectionHandler::kErrorConnectCanceled)
147    return;
148
149  if (error_name == shill::kErrorBadPassphrase ||
150      error_name == NetworkConnectionHandler::kErrorPassphraseRequired ||
151      error_name == NetworkConnectionHandler::kErrorConfigurationRequired ||
152      error_name == NetworkConnectionHandler::kErrorAuthenticationRequired) {
153    HandleUnconfiguredNetwork(service_path);
154    return;
155  }
156
157  if (error_name == NetworkConnectionHandler::kErrorCertificateRequired) {
158    if (!ash::Shell::GetInstance()->system_tray_delegate()->EnrollNetwork(
159            service_path)) {
160      HandleUnconfiguredNetwork(service_path);
161    }
162    return;
163  }
164
165  if (error_name == NetworkConnectionHandler::kErrorActivationRequired) {
166    network_connect::ActivateCellular(service_path);
167    return;
168  }
169
170  if (error_name == NetworkConnectionHandler::kErrorConnected ||
171      error_name == NetworkConnectionHandler::kErrorConnecting) {
172    network_connect::ShowNetworkSettings(service_path);
173    return;
174  }
175
176  // ConnectFailed or unknown error; show a notification.
177  ShowErrorNotification(error_name, service_path);
178
179  // Only show a configure dialog if there was a ConnectFailed error and the
180  // screen is not locked.
181  if (error_name != shill::kErrorConnectFailed ||
182      Shell::GetInstance()->session_state_delegate()->IsScreenLocked())
183    return;
184
185  // If Shill reports an InProgress error, don't try to configure the network.
186  std::string dbus_error_name;
187  error_data.get()->GetString(
188      chromeos::network_handler::kDbusErrorName, &dbus_error_name);
189  if (dbus_error_name == kErrorInProgress)
190    return;
191
192  HandleUnconfiguredNetwork(service_path);
193}
194
195void OnConnectSucceeded(const std::string& service_path) {
196  NET_LOG_USER("Connect Succeeded", service_path);
197  if (!ash::Shell::HasInstance())
198    return;
199  message_center::MessageCenter::Get()->RemoveNotification(
200      network_connect::kNetworkConnectNotificationId, false /* not by user */);
201}
202
203// If |check_error_state| is true, error state for the network is checked,
204// otherwise any current error state is ignored (e.g. for recently configured
205// networks or repeat connect attempts).
206void CallConnectToNetwork(const std::string& service_path,
207                          bool check_error_state) {
208  if (!ash::Shell::HasInstance())
209    return;
210  message_center::MessageCenter::Get()->RemoveNotification(
211      network_connect::kNetworkConnectNotificationId, false /* not by user */);
212
213  NetworkHandler::Get()->network_connection_handler()->ConnectToNetwork(
214      service_path,
215      base::Bind(&OnConnectSucceeded, service_path),
216      base::Bind(&OnConnectFailed, service_path),
217      check_error_state);
218}
219
220void OnActivateFailed(const std::string& service_path,
221                      const std::string& error_name,
222                      scoped_ptr<base::DictionaryValue> error_data) {
223  NET_LOG_ERROR("Unable to activate network", service_path);
224  ShowErrorNotification(network_connect::kErrorActivateFailed, service_path);
225}
226
227void OnActivateSucceeded(const std::string& service_path) {
228  NET_LOG_USER("Activation Succeeded", service_path);
229}
230
231void OnConfigureFailed(const std::string& error_name,
232                       scoped_ptr<base::DictionaryValue> error_data) {
233  NET_LOG_ERROR("Unable to configure network", "");
234  ShowErrorNotification(NetworkConnectionHandler::kErrorConfigureFailed, "");
235}
236
237void OnConfigureSucceeded(bool connect_on_configure,
238                          const std::string& service_path) {
239  NET_LOG_USER("Configure Succeeded", service_path);
240  if (!connect_on_configure)
241    return;
242  // After configuring a network, ignore any (possibly stale) error state.
243  const bool check_error_state = false;
244  CallConnectToNetwork(service_path, check_error_state);
245}
246
247void CallCreateConfiguration(base::DictionaryValue* properties,
248                             bool shared,
249                             bool connect_on_configure) {
250  std::string profile_path;
251  if (!GetNetworkProfilePath(shared, &profile_path)) {
252    ShowErrorNotification(NetworkConnectionHandler::kErrorConfigureFailed, "");
253    return;
254  }
255  properties->SetStringWithoutPathExpansion(
256      shill::kProfileProperty, profile_path);
257  NetworkHandler::Get()->network_configuration_handler()->CreateConfiguration(
258      *properties,
259      base::Bind(&OnConfigureSucceeded, connect_on_configure),
260      base::Bind(&OnConfigureFailed));
261}
262
263void SetPropertiesFailed(const std::string& desc,
264                         const std::string& service_path,
265                         const std::string& config_error_name,
266                         scoped_ptr<base::DictionaryValue> error_data) {
267  NET_LOG_ERROR(desc + ": Failed: " + config_error_name, service_path);
268  ShowErrorNotification(
269      NetworkConnectionHandler::kErrorConfigureFailed, service_path);
270}
271
272void SetPropertiesToClear(base::DictionaryValue* properties_to_set,
273                          std::vector<std::string>* properties_to_clear) {
274  // Move empty string properties to properties_to_clear.
275  for (base::DictionaryValue::Iterator iter(*properties_to_set);
276       !iter.IsAtEnd(); iter.Advance()) {
277    std::string value_str;
278    if (iter.value().GetAsString(&value_str) && value_str.empty())
279      properties_to_clear->push_back(iter.key());
280  }
281  // Remove cleared properties from properties_to_set.
282  for (std::vector<std::string>::iterator iter = properties_to_clear->begin();
283       iter != properties_to_clear->end(); ++iter) {
284    properties_to_set->RemoveWithoutPathExpansion(*iter, NULL);
285  }
286}
287
288void ClearPropertiesAndConnect(
289    const std::string& service_path,
290    const std::vector<std::string>& properties_to_clear) {
291  NET_LOG_USER("ClearPropertiesAndConnect", service_path);
292  // After configuring a network, ignore any (possibly stale) error state.
293  const bool check_error_state = false;
294  NetworkHandler::Get()->network_configuration_handler()->ClearProperties(
295      service_path,
296      properties_to_clear,
297      base::Bind(&CallConnectToNetwork,
298                 service_path, check_error_state),
299      base::Bind(&SetPropertiesFailed, "ClearProperties", service_path));
300}
301
302void ConfigureSetProfileSucceeded(
303    const std::string& service_path,
304    scoped_ptr<base::DictionaryValue> properties_to_set) {
305  std::vector<std::string> properties_to_clear;
306  SetPropertiesToClear(properties_to_set.get(), &properties_to_clear);
307  NetworkHandler::Get()->network_configuration_handler()->SetProperties(
308      service_path,
309      *properties_to_set,
310      base::Bind(&ClearPropertiesAndConnect,
311                 service_path,
312                 properties_to_clear),
313      base::Bind(&SetPropertiesFailed, "SetProperties", service_path));
314}
315
316const NetworkState* GetNetworkState(const std::string& service_path) {
317  return NetworkHandler::Get()->network_state_handler()->
318      GetNetworkState(service_path);
319}
320
321}  // namespace
322
323namespace network_connect {
324
325const char kNetworkConnectNotificationId[] =
326    "chrome://settings/internet/connect";
327const char kNetworkActivateNotificationId[] =
328    "chrome://settings/internet/activate";
329
330const char kErrorActivateFailed[] = "activate-failed";
331
332void ConnectToNetwork(const std::string& service_path) {
333  NET_LOG_USER("ConnectToNetwork", service_path);
334  const NetworkState* network = GetNetworkState(service_path);
335  if (network) {
336    if (!network->error().empty() && !network->security().empty()) {
337      NET_LOG_USER("Configure: " + network->error(), service_path);
338      // If the network is in an error state, show the configuration UI directly
339      // to avoid a spurious notification.
340      HandleUnconfiguredNetwork(service_path);
341      return;
342    } else if (network->RequiresActivation()) {
343      ActivateCellular(service_path);
344      return;
345    }
346  }
347  const bool check_error_state = true;
348  CallConnectToNetwork(service_path, check_error_state);
349}
350
351void SetTechnologyEnabled(const NetworkTypePattern& technology,
352                          bool enabled_state) {
353  std::string log_string =
354      base::StringPrintf("technology %s, target state: %s",
355                         technology.ToDebugString().c_str(),
356                         (enabled_state ? "ENABLED" : "DISABLED"));
357  NET_LOG_USER("SetTechnologyEnabled", log_string);
358  NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
359  bool enabled = handler->IsTechnologyEnabled(technology);
360  if (enabled_state == enabled) {
361    NET_LOG_USER("Technology already in target state.", log_string);
362    return;
363  }
364  if (enabled) {
365    // User requested to disable the technology.
366    handler->SetTechnologyEnabled(
367        technology, false, chromeos::network_handler::ErrorCallback());
368    return;
369  }
370  // If we're dealing with a mobile network, then handle SIM lock here.
371  // SIM locking only applies to cellular, so the code below won't execute
372  // if |technology| has been explicitly set to WiMAX.
373  if (technology.MatchesPattern(NetworkTypePattern::Mobile())) {
374    const DeviceState* mobile = handler->GetDeviceStateByType(technology);
375    if (!mobile) {
376      NET_LOG_ERROR("SetTechnologyEnabled with no device", log_string);
377      return;
378    }
379    // The following only applies to cellular.
380    if (mobile->type() == shill::kTypeCellular) {
381      if (mobile->IsSimAbsent()) {
382        // If this is true, then we have a cellular device with no SIM inserted.
383        // TODO(armansito): Chrome should display a notification here, prompting
384        // the user to insert a SIM card and restart the device to enable
385        // cellular. See crbug.com/125171.
386        NET_LOG_USER("Cannot enable cellular device without SIM.", log_string);
387        return;
388      }
389      if (!mobile->sim_lock_type().empty()) {
390        // A SIM has been inserted, but it is locked. Let the user unlock it
391        // via the dialog.
392        ash::Shell::GetInstance()->system_tray_delegate()->
393            ShowMobileSimDialog();
394        return;
395      }
396    }
397  }
398  handler->SetTechnologyEnabled(
399    technology, true, chromeos::network_handler::ErrorCallback());
400}
401
402void ActivateCellular(const std::string& service_path) {
403  NET_LOG_USER("ActivateCellular", service_path);
404  const NetworkState* cellular = GetNetworkState(service_path);
405  if (!cellular || cellular->type() != shill::kTypeCellular) {
406    NET_LOG_ERROR("ActivateCellular with no Service", service_path);
407    return;
408  }
409  const DeviceState* cellular_device =
410      NetworkHandler::Get()->network_state_handler()->
411      GetDeviceState(cellular->device_path());
412  if (!cellular_device) {
413    NET_LOG_ERROR("ActivateCellular with no Device", service_path);
414    return;
415  }
416  if (!IsDirectActivatedCarrier(cellular_device->carrier())) {
417    // For non direct activation, show the mobile setup dialog which can be
418    // used to activate the network.
419    ShowMobileSetup(service_path);
420    return;
421  }
422  if (cellular->activation_state() == shill::kActivationStateActivated) {
423    NET_LOG_ERROR("ActivateCellular for activated service", service_path);
424    return;
425  }
426
427  NetworkHandler::Get()->network_activation_handler()->Activate(
428      service_path,
429      "",  // carrier
430      base::Bind(&OnActivateSucceeded, service_path),
431      base::Bind(&OnActivateFailed, service_path));
432}
433
434void ShowMobileSetup(const std::string& service_path) {
435  NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
436  const NetworkState* cellular = handler->GetNetworkState(service_path);
437  if (!cellular || cellular->type() != shill::kTypeCellular) {
438    NET_LOG_ERROR("ShowMobileSetup without Cellular network", service_path);
439    return;
440  }
441  if (cellular->activation_state() != shill::kActivationStateActivated &&
442      cellular->activation_type() == shill::kActivationTypeNonCellular &&
443      !handler->DefaultNetwork()) {
444    message_center::MessageCenter::Get()->AddNotification(
445        message_center::Notification::CreateSystemNotification(
446            kNetworkActivateNotificationId,
447            l10n_util::GetStringUTF16(IDS_NETWORK_ACTIVATION_ERROR_TITLE),
448            l10n_util::GetStringFUTF16(IDS_NETWORK_ACTIVATION_NEEDS_CONNECTION,
449                                       base::UTF8ToUTF16(cellular->name())),
450            ui::ResourceBundle::GetSharedInstance().GetImageNamed(
451                IDR_AURA_UBER_TRAY_CELLULAR_NETWORK_FAILED),
452            ash::system_notifier::kNotifierNetworkError,
453            base::Bind(&ash::network_connect::ShowNetworkSettings,
454                       service_path)));
455    return;
456  }
457  ash::Shell::GetInstance()->system_tray_delegate()->ShowMobileSetupDialog(
458      service_path);
459}
460
461void ConfigureNetworkAndConnect(const std::string& service_path,
462                                const base::DictionaryValue& properties,
463                                bool shared) {
464  NET_LOG_USER("ConfigureNetworkAndConnect", service_path);
465
466  scoped_ptr<base::DictionaryValue> properties_to_set(properties.DeepCopy());
467
468  std::string profile_path;
469  if (!GetNetworkProfilePath(shared, &profile_path)) {
470    ShowErrorNotification(
471        NetworkConnectionHandler::kErrorConfigureFailed, service_path);
472    return;
473  }
474  NetworkHandler::Get()->network_configuration_handler()->SetNetworkProfile(
475      service_path, profile_path,
476      base::Bind(&ConfigureSetProfileSucceeded,
477                 service_path, base::Passed(&properties_to_set)),
478      base::Bind(&SetPropertiesFailed,
479                 "SetProfile: " + profile_path, service_path));
480}
481
482void CreateConfigurationAndConnect(base::DictionaryValue* properties,
483                                   bool shared) {
484  NET_LOG_USER("CreateConfigurationAndConnect", "");
485  CallCreateConfiguration(properties, shared, true /* connect_on_configure */);
486}
487
488void CreateConfiguration(base::DictionaryValue* properties, bool shared) {
489  NET_LOG_USER("CreateConfiguration", "");
490  CallCreateConfiguration(properties, shared, false /* connect_on_configure */);
491}
492
493base::string16 ErrorString(const std::string& error,
494                     const std::string& service_path) {
495  if (error.empty())
496    return base::string16();
497  if (error == shill::kErrorOutOfRange)
498    return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_OUT_OF_RANGE);
499  if (error == shill::kErrorPinMissing)
500    return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_PIN_MISSING);
501  if (error == shill::kErrorDhcpFailed)
502    return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_DHCP_FAILED);
503  if (error == shill::kErrorConnectFailed)
504    return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_CONNECT_FAILED);
505  if (error == shill::kErrorBadPassphrase)
506    return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_BAD_PASSPHRASE);
507  if (error == shill::kErrorBadWEPKey)
508    return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_BAD_WEPKEY);
509  if (error == shill::kErrorActivationFailed) {
510    return l10n_util::GetStringUTF16(
511        IDS_CHROMEOS_NETWORK_ERROR_ACTIVATION_FAILED);
512  }
513  if (error == shill::kErrorNeedEvdo)
514    return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_NEED_EVDO);
515  if (error == shill::kErrorNeedHomeNetwork) {
516    return l10n_util::GetStringUTF16(
517        IDS_CHROMEOS_NETWORK_ERROR_NEED_HOME_NETWORK);
518  }
519  if (error == shill::kErrorOtaspFailed)
520    return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_OTASP_FAILED);
521  if (error == shill::kErrorAaaFailed)
522    return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_AAA_FAILED);
523  if (error == shill::kErrorInternal)
524    return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_INTERNAL);
525  if (error == shill::kErrorDNSLookupFailed) {
526    return l10n_util::GetStringUTF16(
527        IDS_CHROMEOS_NETWORK_ERROR_DNS_LOOKUP_FAILED);
528  }
529  if (error == shill::kErrorHTTPGetFailed) {
530    return l10n_util::GetStringUTF16(
531        IDS_CHROMEOS_NETWORK_ERROR_HTTP_GET_FAILED);
532  }
533  if (error == shill::kErrorIpsecPskAuthFailed) {
534    return l10n_util::GetStringUTF16(
535        IDS_CHROMEOS_NETWORK_ERROR_IPSEC_PSK_AUTH_FAILED);
536  }
537  if (error == shill::kErrorIpsecCertAuthFailed) {
538    return l10n_util::GetStringUTF16(
539        IDS_CHROMEOS_NETWORK_ERROR_CERT_AUTH_FAILED);
540  }
541  if (error == shill::kErrorEapAuthenticationFailed) {
542    const NetworkState* network = GetNetworkState(service_path);
543    // TLS always requires a client certificate, so show a cert auth
544    // failed message for TLS. Other EAP methods do not generally require
545    // a client certicate.
546    if (network && network->eap_method() == shill::kEapMethodTLS) {
547      return l10n_util::GetStringUTF16(
548          IDS_CHROMEOS_NETWORK_ERROR_CERT_AUTH_FAILED);
549    } else {
550      return l10n_util::GetStringUTF16(
551          IDS_CHROMEOS_NETWORK_ERROR_EAP_AUTH_FAILED);
552    }
553  }
554  if (error == shill::kErrorEapLocalTlsFailed) {
555    return l10n_util::GetStringUTF16(
556        IDS_CHROMEOS_NETWORK_ERROR_EAP_LOCAL_TLS_FAILED);
557  }
558  if (error == shill::kErrorEapRemoteTlsFailed) {
559    return l10n_util::GetStringUTF16(
560        IDS_CHROMEOS_NETWORK_ERROR_EAP_REMOTE_TLS_FAILED);
561  }
562  if (error == shill::kErrorPppAuthFailed) {
563    return l10n_util::GetStringUTF16(
564        IDS_CHROMEOS_NETWORK_ERROR_PPP_AUTH_FAILED);
565  }
566
567  if (base::StringToLowerASCII(error) ==
568      base::StringToLowerASCII(std::string(shill::kUnknownString))) {
569    return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_UNKNOWN);
570  }
571  return l10n_util::GetStringFUTF16(IDS_NETWORK_UNRECOGNIZED_ERROR,
572                                    base::UTF8ToUTF16(error));
573}
574
575void ShowNetworkSettings(const std::string& service_path) {
576  if (!ash::Shell::HasInstance())
577    return;
578  ash::Shell::GetInstance()->system_tray_delegate()->ShowNetworkSettings(
579      service_path);
580}
581
582}  // network_connect
583}  // ash
584