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 37 using chromeos::DeviceState; 38 using chromeos::NetworkConfigurationHandler; 39 using chromeos::NetworkConnectionHandler; 40 using chromeos::NetworkHandler; 41 using chromeos::NetworkProfile; 42 using chromeos::NetworkProfileHandler; 43 using chromeos::NetworkState; 44 using chromeos::NetworkStateHandler; 45 using chromeos::NetworkTypePattern; 46 47 namespace ash { 48 49 namespace { 50 51 // TODO(stevenjb): This should be in service_constants.h 52 const 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. 56 bool IsDirectActivatedCarrier(const std::string& carrier) { 57 if (carrier == shill::kCarrierSprint) 58 return true; 59 return false; 60 } 61 62 void 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 68 void 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|. 114 bool 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 137 void 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 195 void 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). 206 void 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 220 void 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 227 void OnActivateSucceeded(const std::string& service_path) { 228 NET_LOG_USER("Activation Succeeded", service_path); 229 } 230 231 void 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 237 void 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 247 void 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 263 void 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 272 void 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 288 void 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 302 void 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 316 const NetworkState* GetNetworkState(const std::string& service_path) { 317 return NetworkHandler::Get()->network_state_handler()-> 318 GetNetworkState(service_path); 319 } 320 321 } // namespace 322 323 namespace network_connect { 324 325 const char kNetworkConnectNotificationId[] = 326 "chrome://settings/internet/connect"; 327 const char kNetworkActivateNotificationId[] = 328 "chrome://settings/internet/activate"; 329 330 const char kErrorActivateFailed[] = "activate-failed"; 331 332 void 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 351 void 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 402 void 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 434 void 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 461 void 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 482 void CreateConfigurationAndConnect(base::DictionaryValue* properties, 483 bool shared) { 484 NET_LOG_USER("CreateConfigurationAndConnect", ""); 485 CallCreateConfiguration(properties, shared, true /* connect_on_configure */); 486 } 487 488 void CreateConfiguration(base::DictionaryValue* properties, bool shared) { 489 NET_LOG_USER("CreateConfiguration", ""); 490 CallCreateConfiguration(properties, shared, false /* connect_on_configure */); 491 } 492 493 base::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 575 void 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