Home | History | Annotate | Download | only in chromeos
      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