1 // Copyright (c) 2011 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 "chrome/browser/chromeos/network_message_observer.h"
6
7 #include "base/callback.h"
8 #include "base/stl_util-inl.h"
9 #include "base/string_number_conversions.h"
10 #include "base/utf_string_conversions.h"
11 #include "chrome/browser/chromeos/cros/cros_library.h"
12 #include "chrome/browser/chromeos/cros/network_library.h"
13 #include "chrome/browser/chromeos/notifications/balloon_view_host.h"
14 #include "chrome/browser/prefs/pref_service.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/ui/browser.h"
17 #include "chrome/browser/ui/browser_list.h"
18 #include "chrome/common/pref_names.h"
19 #include "chrome/common/time_format.h"
20 #include "grit/generated_resources.h"
21 #include "grit/theme_resources.h"
22 #include "ui/base/l10n/l10n_util.h"
23
24 namespace {
25
26 // Returns prefs::kShowPlanNotifications in the profile of the last active
27 // browser. If there is no active browser, returns true.
ShouldShowMobilePlanNotifications()28 bool ShouldShowMobilePlanNotifications() {
29 Browser* browser = BrowserList::GetLastActive();
30 if (!browser || !browser->profile())
31 return true;
32
33 PrefService* prefs = browser->profile()->GetPrefs();
34 return prefs->GetBoolean(prefs::kShowPlanNotifications);
35 }
36
37 } // namespace
38
39 namespace chromeos {
40
NetworkMessageObserver(Profile * profile)41 NetworkMessageObserver::NetworkMessageObserver(Profile* profile)
42 : notification_connection_error_(profile, "network_connection.chromeos",
43 IDR_NOTIFICATION_NETWORK_FAILED,
44 l10n_util::GetStringUTF16(IDS_NETWORK_CONNECTION_ERROR_TITLE)),
45 notification_low_data_(profile, "network_low_data.chromeos",
46 IDR_NOTIFICATION_BARS_CRITICAL,
47 l10n_util::GetStringUTF16(IDS_NETWORK_LOW_DATA_TITLE)),
48 notification_no_data_(profile, "network_no_data.chromeos",
49 IDR_NOTIFICATION_BARS_EMPTY,
50 l10n_util::GetStringUTF16(IDS_NETWORK_OUT_OF_DATA_TITLE)) {
51 NetworkLibrary* netlib = CrosLibrary::Get()->GetNetworkLibrary();
52 OnNetworkManagerChanged(netlib);
53 // Note that this gets added as a NetworkManagerObserver,
54 // CellularDataPlanObserver, and UserActionObserver in browser_init.cc
55 }
56
~NetworkMessageObserver()57 NetworkMessageObserver::~NetworkMessageObserver() {
58 NetworkLibrary* netlib = CrosLibrary::Get()->GetNetworkLibrary();
59 netlib->RemoveNetworkManagerObserver(this);
60 netlib->RemoveCellularDataPlanObserver(this);
61 netlib->RemoveUserActionObserver(this);
62 notification_connection_error_.Hide();
63 notification_low_data_.Hide();
64 notification_no_data_.Hide();
65 }
66
67 // static
IsApplicableBackupPlan(const CellularDataPlan * plan,const CellularDataPlan * other_plan)68 bool NetworkMessageObserver::IsApplicableBackupPlan(
69 const CellularDataPlan* plan, const CellularDataPlan* other_plan) {
70 // By applicable plan, we mean that the other plan has data AND the timeframe
71 // will apply: (unlimited OR used bytes < max bytes) AND
72 // ((start time - 1 sec) <= end time of currently active plan).
73 // In other words, there is data available and there is no gap of more than a
74 // second in time between the old plan and the new plan.
75 bool has_data = other_plan->plan_type == CELLULAR_DATA_PLAN_UNLIMITED ||
76 other_plan->remaining_data() > 0;
77 bool will_apply =
78 (other_plan->plan_start_time - plan->plan_end_time).InSeconds() <= 1;
79 return has_data && will_apply;
80 }
81
OpenMobileSetupPage(const ListValue * args)82 void NetworkMessageObserver::OpenMobileSetupPage(const ListValue* args) {
83 Browser* browser = BrowserList::GetLastActive();
84 if (browser)
85 browser->OpenMobilePlanTabAndActivate();
86 }
87
OpenMoreInfoPage(const ListValue * args)88 void NetworkMessageObserver::OpenMoreInfoPage(const ListValue* args) {
89 Browser* browser = BrowserList::GetLastActive();
90 if (!browser)
91 return;
92 chromeos::NetworkLibrary* lib =
93 chromeos::CrosLibrary::Get()->GetNetworkLibrary();
94 const chromeos::CellularNetwork* cellular = lib->cellular_network();
95 if (!cellular)
96 return;
97 browser->ShowSingletonTab(GURL(cellular->payment_url()));
98 }
99
InitNewPlan(const CellularDataPlan * plan)100 void NetworkMessageObserver::InitNewPlan(const CellularDataPlan* plan) {
101 notification_low_data_.Hide();
102 notification_no_data_.Hide();
103 if (plan->plan_type == CELLULAR_DATA_PLAN_UNLIMITED) {
104 notification_no_data_.set_title(
105 l10n_util::GetStringFUTF16(IDS_NETWORK_DATA_EXPIRED_TITLE,
106 ASCIIToUTF16(plan->plan_name)));
107 notification_low_data_.set_title(
108 l10n_util::GetStringFUTF16(IDS_NETWORK_NEARING_EXPIRATION_TITLE,
109 ASCIIToUTF16(plan->plan_name)));
110 } else {
111 notification_no_data_.set_title(
112 l10n_util::GetStringFUTF16(IDS_NETWORK_OUT_OF_DATA_TITLE,
113 ASCIIToUTF16(plan->plan_name)));
114 notification_low_data_.set_title(
115 l10n_util::GetStringFUTF16(IDS_NETWORK_LOW_DATA_TITLE,
116 ASCIIToUTF16(plan->plan_name)));
117 }
118 }
119
ShowNeedsPlanNotification(const CellularNetwork * cellular)120 void NetworkMessageObserver::ShowNeedsPlanNotification(
121 const CellularNetwork* cellular) {
122 notification_no_data_.set_title(
123 l10n_util::GetStringFUTF16(IDS_NETWORK_NO_DATA_PLAN_TITLE,
124 UTF8ToUTF16(cellular->name())));
125 notification_no_data_.Show(
126 l10n_util::GetStringFUTF16(
127 IDS_NETWORK_NO_DATA_PLAN_MESSAGE,
128 UTF8ToUTF16(cellular->name())),
129 l10n_util::GetStringUTF16(IDS_NETWORK_PURCHASE_MORE_MESSAGE),
130 NewCallback(this, &NetworkMessageObserver::OpenMobileSetupPage),
131 false, false);
132 }
133
ShowNoDataNotification(CellularDataPlanType plan_type)134 void NetworkMessageObserver::ShowNoDataNotification(
135 CellularDataPlanType plan_type) {
136 notification_low_data_.Hide(); // Hide previous low data notification.
137 string16 message = plan_type == CELLULAR_DATA_PLAN_UNLIMITED ?
138 TimeFormat::TimeRemaining(base::TimeDelta()) :
139 l10n_util::GetStringFUTF16(IDS_NETWORK_DATA_REMAINING_MESSAGE,
140 ASCIIToUTF16("0"));
141 notification_no_data_.Show(message,
142 l10n_util::GetStringUTF16(IDS_NETWORK_PURCHASE_MORE_MESSAGE),
143 NewCallback(this, &NetworkMessageObserver::OpenMobileSetupPage),
144 false, false);
145 }
146
ShowLowDataNotification(const CellularDataPlan * plan)147 void NetworkMessageObserver::ShowLowDataNotification(
148 const CellularDataPlan* plan) {
149 string16 message;
150 if (plan->plan_type == CELLULAR_DATA_PLAN_UNLIMITED) {
151 message = plan->GetPlanExpiration();
152 } else {
153 int64 remaining_mbytes = plan->remaining_data() / (1024 * 1024);
154 message = l10n_util::GetStringFUTF16(IDS_NETWORK_DATA_REMAINING_MESSAGE,
155 UTF8ToUTF16(base::Int64ToString(remaining_mbytes)));
156 }
157 notification_low_data_.Show(message,
158 l10n_util::GetStringUTF16(IDS_NETWORK_MORE_INFO_MESSAGE),
159 NewCallback(this, &NetworkMessageObserver::OpenMoreInfoPage),
160 false, false);
161 }
162
CheckNetworkFailed(const Network * network)163 bool NetworkMessageObserver::CheckNetworkFailed(const Network* network) {
164 if (network->failed()) {
165 NetworkStateMap::iterator iter =
166 network_states_.find(network->service_path());
167 // If the network did not previously exist, then don't do anything.
168 // For example, if the user travels to a location and finds a service
169 // that has previously failed, we don't want to show a notification.
170 if (iter == network_states_.end())
171 return false;
172 // If network connection failed, display a notification.
173 // We only do this if we were trying to make a new connection.
174 // So if a previously connected network got disconnected for any reason,
175 // we don't display notification.
176 ConnectionState prev_state = iter->second;
177 if (Network::IsConnectingState(prev_state))
178 return true;
179 }
180 return false;
181 }
182
OnNetworkManagerChanged(NetworkLibrary * cros)183 void NetworkMessageObserver::OnNetworkManagerChanged(NetworkLibrary* cros) {
184 const Network* new_failed_network = NULL;
185 // Check to see if we have any newly failed networks.
186 for (WifiNetworkVector::const_iterator it = cros->wifi_networks().begin();
187 it != cros->wifi_networks().end(); it++) {
188 const WifiNetwork* net = *it;
189 if (CheckNetworkFailed(net)) {
190 new_failed_network = net;
191 break; // There should only be one failed network.
192 }
193 }
194
195 if (!new_failed_network) {
196 for (CellularNetworkVector::const_iterator it =
197 cros->cellular_networks().begin();
198 it != cros->cellular_networks().end(); it++) {
199 const CellularNetwork* net = *it;
200 if (CheckNetworkFailed(net)) {
201 new_failed_network = net;
202 break; // There should only be one failed network.
203 }
204 }
205 }
206
207 if (!new_failed_network) {
208 for (VirtualNetworkVector::const_iterator it =
209 cros->virtual_networks().begin();
210 it != cros->virtual_networks().end(); it++) {
211 const VirtualNetwork* net = *it;
212 if (CheckNetworkFailed(net)) {
213 new_failed_network = net;
214 break; // There should only be one failed network.
215 }
216 }
217 }
218
219 network_states_.clear();
220 for (WifiNetworkVector::const_iterator it = cros->wifi_networks().begin();
221 it != cros->wifi_networks().end(); it++)
222 network_states_[(*it)->service_path()] = (*it)->state();
223 for (CellularNetworkVector::const_iterator it =
224 cros->cellular_networks().begin();
225 it != cros->cellular_networks().end(); it++)
226 network_states_[(*it)->service_path()] = (*it)->state();
227 for (VirtualNetworkVector::const_iterator it =
228 cros->virtual_networks().begin();
229 it != cros->virtual_networks().end(); it++)
230 network_states_[(*it)->service_path()] = (*it)->state();
231
232 // Show connection error notification if necessary.
233 if (new_failed_network) {
234 // Hide if already shown to force show it in case user has closed it.
235 if (notification_connection_error_.visible())
236 notification_connection_error_.Hide();
237 notification_connection_error_.Show(l10n_util::GetStringFUTF16(
238 IDS_NETWORK_CONNECTION_ERROR_MESSAGE,
239 UTF8ToUTF16(new_failed_network->name())), false, false);
240 }
241 }
242
OnCellularDataPlanChanged(NetworkLibrary * cros)243 void NetworkMessageObserver::OnCellularDataPlanChanged(NetworkLibrary* cros) {
244 if (!ShouldShowMobilePlanNotifications())
245 return;
246 const CellularNetwork* cellular = cros->cellular_network();
247 if (!cellular || !cellular->SupportsDataPlan())
248 return;
249
250 const CellularDataPlanVector* plans =
251 cros->GetDataPlans(cellular->service_path());
252 // If no plans available, check to see if we need a new plan.
253 if (!plans || plans->empty()) {
254 // If previously, we had low data, we know that a plan was near expiring.
255 // In that case, because the plan disappeared, we assume that it expired.
256 if (cellular_data_left_ == CellularNetwork::DATA_LOW) {
257 ShowNoDataNotification(cellular_data_plan_type_);
258 } else if (cellular->needs_new_plan()) {
259 ShowNeedsPlanNotification(cellular);
260 }
261 SaveLastCellularInfo(cellular, NULL);
262 return;
263 }
264
265 CellularDataPlanVector::const_iterator iter = plans->begin();
266 const CellularDataPlan* current_plan = *iter;
267
268 // If current plan is not the last plan (there is another backup plan),
269 // then we do not show notifications for this plan.
270 // For example, if there is another data plan available when this runs out.
271 for (++iter; iter != plans->end(); ++iter) {
272 if (IsApplicableBackupPlan(current_plan, *iter)) {
273 SaveLastCellularInfo(cellular, current_plan);
274 return;
275 }
276 }
277
278 // If connected cellular network changed, or data plan is different, then
279 // it's a new network. Then hide all previous notifications.
280 bool new_plan = cellular->service_path() != cellular_service_path_ ||
281 current_plan->GetUniqueIdentifier() != cellular_data_plan_unique_id_;
282
283 if (new_plan) {
284 InitNewPlan(current_plan);
285 }
286
287 if (cellular->data_left() == CellularNetwork::DATA_NONE) {
288 ShowNoDataNotification(current_plan->plan_type);
289 } else if (cellular->data_left() == CellularNetwork::DATA_VERY_LOW) {
290 // Only show low data notification if we transition to very low data
291 // and we are on the same plan. This is so that users don't get a
292 // notification whenever they connect to a low data 3g network.
293 if (!new_plan && (cellular_data_left_ != CellularNetwork::DATA_VERY_LOW))
294 ShowLowDataNotification(current_plan);
295 }
296
297 SaveLastCellularInfo(cellular, current_plan);
298 }
299
OnConnectionInitiated(NetworkLibrary * cros,const Network * network)300 void NetworkMessageObserver::OnConnectionInitiated(NetworkLibrary* cros,
301 const Network* network) {
302 // If user initiated any network connection, we hide the error notification.
303 notification_connection_error_.Hide();
304 }
305
SaveLastCellularInfo(const CellularNetwork * cellular,const CellularDataPlan * plan)306 void NetworkMessageObserver::SaveLastCellularInfo(
307 const CellularNetwork* cellular, const CellularDataPlan* plan) {
308 DCHECK(cellular);
309 cellular_service_path_ = cellular->service_path();
310 cellular_data_left_ = cellular->data_left();
311 if (plan) {
312 cellular_data_plan_unique_id_ = plan->GetUniqueIdentifier();
313 cellular_data_plan_type_ = plan->plan_type;
314 } else {
315 cellular_data_plan_unique_id_ = std::string();
316 cellular_data_plan_type_ = CELLULAR_DATA_PLAN_UNKNOWN;
317 }
318 }
319
320 } // namespace chromeos
321