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