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. 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 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 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 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 82 void NetworkMessageObserver::OpenMobileSetupPage(const ListValue* args) { 83 Browser* browser = BrowserList::GetLastActive(); 84 if (browser) 85 browser->OpenMobilePlanTabAndActivate(); 86 } 87 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 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 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 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 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 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 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 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 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 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