Home | History | Annotate | Download | only in network
      1 // Copyright (c) 2012 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_state_notifier.h"
      6 
      7 #include "ash/shell.h"
      8 #include "ash/system/chromeos/network/network_connect.h"
      9 #include "ash/system/system_notifier.h"
     10 #include "ash/system/tray/system_tray_delegate.h"
     11 #include "base/strings/string16.h"
     12 #include "base/strings/string_util.h"
     13 #include "base/strings/utf_string_conversions.h"
     14 #include "chromeos/network/network_configuration_handler.h"
     15 #include "chromeos/network/network_connection_handler.h"
     16 #include "chromeos/network/network_event_log.h"
     17 #include "chromeos/network/network_state.h"
     18 #include "chromeos/network/network_state_handler.h"
     19 #include "chromeos/network/shill_property_util.h"
     20 #include "grit/ash_resources.h"
     21 #include "grit/ash_strings.h"
     22 #include "grit/ui_chromeos_resources.h"
     23 #include "third_party/cros_system_api/dbus/service_constants.h"
     24 #include "ui/base/l10n/l10n_util.h"
     25 #include "ui/base/resource/resource_bundle.h"
     26 #include "ui/message_center/message_center.h"
     27 #include "ui/message_center/notification.h"
     28 
     29 using chromeos::NetworkConnectionHandler;
     30 using chromeos::NetworkHandler;
     31 using chromeos::NetworkState;
     32 using chromeos::NetworkStateHandler;
     33 using chromeos::NetworkTypePattern;
     34 
     35 namespace {
     36 
     37 const char kNetworkOutOfCreditsNotificationId[] =
     38     "chrome://settings/internet/out-of-credits";
     39 
     40 const int kMinTimeBetweenOutOfCreditsNotifySeconds = 10 * 60;
     41 
     42 // Ignore in-progress error.
     43 bool ShillErrorIsIgnored(const std::string& shill_error) {
     44   if (shill_error == shill::kErrorResultInProgress)
     45     return true;
     46   return false;
     47 }
     48 
     49 // Error messages based on |error_name|, not network_state->error().
     50 base::string16 GetConnectErrorString(const std::string& error_name) {
     51   if (error_name == NetworkConnectionHandler::kErrorNotFound)
     52     return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_CONNECT_FAILED);
     53   if (error_name == NetworkConnectionHandler::kErrorConfigureFailed) {
     54     return l10n_util::GetStringUTF16(
     55         IDS_CHROMEOS_NETWORK_ERROR_CONFIGURE_FAILED);
     56   }
     57   if (error_name == NetworkConnectionHandler::kErrorCertLoadTimeout) {
     58     return l10n_util::GetStringUTF16(
     59         IDS_CHROMEOS_NETWORK_ERROR_CERTIFICATES_NOT_LOADED);
     60   }
     61   if (error_name == ash::network_connect::kErrorActivateFailed) {
     62     return l10n_util::GetStringUTF16(
     63         IDS_CHROMEOS_NETWORK_ERROR_ACTIVATION_FAILED);
     64   }
     65   return base::string16();
     66 }
     67 
     68 void ShowErrorNotification(const std::string& notification_id,
     69                            const std::string& network_type,
     70                            const base::string16& title,
     71                            const base::string16& message,
     72                            const base::Closure& callback) {
     73   int icon_id = (network_type == shill::kTypeCellular) ?
     74       IDR_AURA_UBER_TRAY_CELLULAR_NETWORK_FAILED :
     75       IDR_AURA_UBER_TRAY_NETWORK_FAILED;
     76   const gfx::Image& icon =
     77       ui::ResourceBundle::GetSharedInstance().GetImageNamed(icon_id);
     78   message_center::MessageCenter::Get()->AddNotification(
     79       message_center::Notification::CreateSystemNotification(
     80           notification_id,
     81           title,
     82           message,
     83           icon,
     84           ash::system_notifier::kNotifierNetworkError,
     85           callback));
     86 }
     87 
     88 }  // namespace
     89 
     90 namespace ash {
     91 
     92 NetworkStateNotifier::NetworkStateNotifier()
     93     : did_show_out_of_credits_(false),
     94       weak_ptr_factory_(this) {
     95   if (!NetworkHandler::IsInitialized())
     96     return;
     97   NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
     98   handler->AddObserver(this, FROM_HERE);
     99   UpdateDefaultNetwork(handler->DefaultNetwork());
    100 }
    101 
    102 NetworkStateNotifier::~NetworkStateNotifier() {
    103   if (!NetworkHandler::IsInitialized())
    104     return;
    105   NetworkHandler::Get()->network_state_handler()->RemoveObserver(
    106       this, FROM_HERE);
    107 }
    108 
    109 void NetworkStateNotifier::DefaultNetworkChanged(const NetworkState* network) {
    110   if (!UpdateDefaultNetwork(network))
    111     return;
    112   // If the default network changes to another network, allow the out of
    113   // credits notification to be shown again. A delay prevents the notification
    114   // from being shown too frequently (see below).
    115   if (network)
    116     did_show_out_of_credits_ = false;
    117 }
    118 
    119 void NetworkStateNotifier::NetworkPropertiesUpdated(
    120     const NetworkState* network) {
    121   if (network->type() != shill::kTypeCellular)
    122     return;
    123   UpdateCellularOutOfCredits(network);
    124   UpdateCellularActivating(network);
    125 }
    126 
    127 bool NetworkStateNotifier::UpdateDefaultNetwork(const NetworkState* network) {
    128   std::string default_network_path;
    129   if (network)
    130     default_network_path = network->path();
    131   if (default_network_path != last_default_network_) {
    132     last_default_network_ = default_network_path;
    133     return true;
    134   }
    135   return false;
    136 }
    137 
    138 void NetworkStateNotifier::UpdateCellularOutOfCredits(
    139     const NetworkState* cellular) {
    140   // Only display a notification if we are out of credits and have not already
    141   // shown a notification (or have since connected to another network type).
    142   if (!cellular->cellular_out_of_credits() || did_show_out_of_credits_)
    143     return;
    144 
    145   // Only display a notification if not connected, connecting, or waiting to
    146   // connect to another network.
    147   NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
    148   const NetworkState* default_network = handler->DefaultNetwork();
    149   if (default_network && default_network != cellular)
    150     return;
    151   if (handler->ConnectingNetworkByType(NetworkTypePattern::NonVirtual()) ||
    152       NetworkHandler::Get()->network_connection_handler()
    153           ->HasPendingConnectRequest())
    154     return;
    155 
    156   did_show_out_of_credits_ = true;
    157   base::TimeDelta dtime = base::Time::Now() - out_of_credits_notify_time_;
    158   if (dtime.InSeconds() > kMinTimeBetweenOutOfCreditsNotifySeconds) {
    159     out_of_credits_notify_time_ = base::Time::Now();
    160     base::string16 error_msg = l10n_util::GetStringFUTF16(
    161         IDS_NETWORK_OUT_OF_CREDITS_BODY,
    162         base::UTF8ToUTF16(cellular->name()));
    163     ShowErrorNotification(
    164         kNetworkOutOfCreditsNotificationId,
    165         cellular->type(),
    166         l10n_util::GetStringUTF16(IDS_NETWORK_OUT_OF_CREDITS_TITLE),
    167         error_msg,
    168         base::Bind(&network_connect::ShowNetworkSettings, cellular->path()));
    169   }
    170 }
    171 
    172 void NetworkStateNotifier::UpdateCellularActivating(
    173     const NetworkState* cellular) {
    174   // Keep track of any activating cellular network.
    175   std::string activation_state = cellular->activation_state();
    176   if (activation_state == shill::kActivationStateActivating) {
    177     cellular_activating_.insert(cellular->path());
    178     return;
    179   }
    180   // Only display a notification if this network was activating and is now
    181   // activated.
    182   if (!cellular_activating_.count(cellular->path()) ||
    183       activation_state != shill::kActivationStateActivated)
    184     return;
    185 
    186   cellular_activating_.erase(cellular->path());
    187   int icon_id;
    188   if (cellular->network_technology() == shill::kNetworkTechnologyLte)
    189     icon_id = IDR_AURA_UBER_TRAY_NOTIFICATION_LTE;
    190   else
    191     icon_id = IDR_AURA_UBER_TRAY_NOTIFICATION_3G;
    192   const gfx::Image& icon =
    193       ui::ResourceBundle::GetSharedInstance().GetImageNamed(icon_id);
    194   message_center::MessageCenter::Get()->AddNotification(
    195       message_center::Notification::CreateSystemNotification(
    196           ash::network_connect::kNetworkActivateNotificationId,
    197           l10n_util::GetStringUTF16(IDS_NETWORK_CELLULAR_ACTIVATED_TITLE),
    198           l10n_util::GetStringFUTF16(IDS_NETWORK_CELLULAR_ACTIVATED,
    199                                      base::UTF8ToUTF16((cellular->name()))),
    200           icon,
    201           system_notifier::kNotifierNetwork,
    202           base::Bind(&ash::network_connect::ShowNetworkSettings,
    203                      cellular->path())));
    204 }
    205 
    206 void NetworkStateNotifier::ShowNetworkConnectError(
    207     const std::string& error_name,
    208     const std::string& service_path) {
    209   if (service_path.empty()) {
    210     base::DictionaryValue shill_properties;
    211     ShowConnectErrorNotification(error_name, service_path, shill_properties);
    212     return;
    213   }
    214   // Get the up-to-date properties for the network and display the error.
    215   NetworkHandler::Get()->network_configuration_handler()->GetProperties(
    216       service_path,
    217       base::Bind(&NetworkStateNotifier::ConnectErrorPropertiesSucceeded,
    218                  weak_ptr_factory_.GetWeakPtr(), error_name),
    219       base::Bind(&NetworkStateNotifier::ConnectErrorPropertiesFailed,
    220                  weak_ptr_factory_.GetWeakPtr(), error_name, service_path));
    221 }
    222 
    223 void NetworkStateNotifier::ConnectErrorPropertiesSucceeded(
    224     const std::string& error_name,
    225     const std::string& service_path,
    226     const base::DictionaryValue& shill_properties) {
    227   std::string state;
    228   shill_properties.GetStringWithoutPathExpansion(shill::kStateProperty, &state);
    229   if (chromeos::NetworkState::StateIsConnected(state) ||
    230       chromeos::NetworkState::StateIsConnecting(state)) {
    231     // Network is no longer in an error state. This can happen if an unexpected
    232     // Idle state transition occurs, see crbug.com/333955.
    233     return;
    234   }
    235   ShowConnectErrorNotification(error_name, service_path, shill_properties);
    236 }
    237 
    238 void NetworkStateNotifier::ConnectErrorPropertiesFailed(
    239     const std::string& error_name,
    240     const std::string& service_path,
    241     const std::string& shill_connect_error,
    242     scoped_ptr<base::DictionaryValue> shill_error_data) {
    243   base::DictionaryValue shill_properties;
    244   ShowConnectErrorNotification(error_name, service_path, shill_properties);
    245 }
    246 
    247 void NetworkStateNotifier::ShowConnectErrorNotification(
    248     const std::string& error_name,
    249     const std::string& service_path,
    250     const base::DictionaryValue& shill_properties) {
    251   base::string16 error = GetConnectErrorString(error_name);
    252   if (error.empty()) {
    253     std::string shill_error;
    254     shill_properties.GetStringWithoutPathExpansion(shill::kErrorProperty,
    255                                                    &shill_error);
    256     if (!chromeos::NetworkState::ErrorIsValid(shill_error)) {
    257       shill_properties.GetStringWithoutPathExpansion(
    258           shill::kPreviousErrorProperty, &shill_error);
    259       NET_LOG_DEBUG("Notify Service.PreviousError: " + shill_error,
    260                     service_path);
    261       if (!chromeos::NetworkState::ErrorIsValid(shill_error))
    262         shill_error.clear();
    263     } else {
    264       NET_LOG_DEBUG("Notify Service.Error: " + shill_error, service_path);
    265     }
    266 
    267     const NetworkState* network =
    268         NetworkHandler::Get()->network_state_handler()->GetNetworkState(
    269             service_path);
    270     if (network) {
    271       // Always log last_error, but only use it if shill_error is empty.
    272       // TODO(stevenjb): This shouldn't ever be necessary, but is kept here as a
    273       // failsafe since more information is better than less when debugging and
    274       // we have encountered some strange edge cases before.
    275       NET_LOG_DEBUG("Notify Network.last_error: " + network->last_error(),
    276                     service_path);
    277       if (shill_error.empty())
    278         shill_error = network->last_error();
    279     }
    280 
    281     if (ShillErrorIsIgnored(shill_error)) {
    282       NET_LOG_DEBUG("Notify Ignoring error: " + error_name, service_path);
    283       return;
    284     }
    285 
    286     error = network_connect::ErrorString(shill_error, service_path);
    287     if (error.empty())
    288       error = l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_UNKNOWN);
    289   }
    290   NET_LOG_ERROR("Notify connect error: " + base::UTF16ToUTF8(error),
    291                 service_path);
    292 
    293   std::string network_name =
    294       chromeos::shill_property_util::GetNameFromProperties(service_path,
    295                                                            shill_properties);
    296   std::string network_error_details;
    297   shill_properties.GetStringWithoutPathExpansion(shill::kErrorDetailsProperty,
    298                                                  &network_error_details);
    299 
    300   base::string16 error_msg;
    301   if (!network_error_details.empty()) {
    302     // network_name should't be empty if network_error_details is set.
    303     error_msg = l10n_util::GetStringFUTF16(
    304         IDS_NETWORK_CONNECTION_ERROR_MESSAGE_WITH_SERVER_MESSAGE,
    305         base::UTF8ToUTF16(network_name),
    306         error,
    307         base::UTF8ToUTF16(network_error_details));
    308   } else if (network_name.empty()) {
    309     error_msg = l10n_util::GetStringFUTF16(
    310         IDS_NETWORK_CONNECTION_ERROR_MESSAGE_NO_NAME, error);
    311   } else {
    312     error_msg = l10n_util::GetStringFUTF16(IDS_NETWORK_CONNECTION_ERROR_MESSAGE,
    313                                            base::UTF8ToUTF16(network_name),
    314                                            error);
    315   }
    316 
    317   std::string network_type;
    318   shill_properties.GetStringWithoutPathExpansion(shill::kTypeProperty,
    319                                                  &network_type);
    320 
    321   ShowErrorNotification(
    322       network_connect::kNetworkConnectNotificationId,
    323       network_type,
    324       l10n_util::GetStringUTF16(IDS_NETWORK_CONNECTION_ERROR_TITLE),
    325       error_msg,
    326       base::Bind(&network_connect::ShowNetworkSettings, service_path));
    327 }
    328 
    329 }  // namespace ash
    330