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