Home | History | Annotate | Download | only in chromeos
      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 "chrome/browser/ui/webui/chromeos/mobile_setup_ui.h"
      6 
      7 #include <algorithm>
      8 #include <map>
      9 #include <string>
     10 
     11 #include "base/bind.h"
     12 #include "base/bind_helpers.h"
     13 #include "base/json/json_writer.h"
     14 #include "base/logging.h"
     15 #include "base/memory/ref_counted_memory.h"
     16 #include "base/message_loop/message_loop.h"
     17 #include "base/metrics/histogram.h"
     18 #include "base/strings/string_piece.h"
     19 #include "base/strings/string_util.h"
     20 #include "base/strings/utf_string_conversions.h"
     21 #include "base/values.h"
     22 #include "chrome/browser/chromeos/cros/network_library.h"
     23 #include "chrome/browser/chromeos/mobile/mobile_activator.h"
     24 #include "chrome/browser/profiles/profile.h"
     25 #include "chrome/browser/ui/browser_list.h"
     26 #include "chrome/common/pref_names.h"
     27 #include "chrome/common/render_messages.h"
     28 #include "chrome/common/url_constants.h"
     29 #include "content/public/browser/browser_thread.h"
     30 #include "content/public/browser/render_view_host_observer.h"
     31 #include "content/public/browser/url_data_source.h"
     32 #include "content/public/browser/web_contents.h"
     33 #include "content/public/browser/web_ui.h"
     34 #include "content/public/browser/web_ui_message_handler.h"
     35 #include "grit/browser_resources.h"
     36 #include "grit/chromium_strings.h"
     37 #include "grit/generated_resources.h"
     38 #include "grit/locale_settings.h"
     39 #include "ui/base/l10n/l10n_util.h"
     40 #include "ui/base/resource/resource_bundle.h"
     41 #include "ui/webui/jstemplate_builder.h"
     42 #include "ui/webui/web_ui_util.h"
     43 #include "url/gurl.h"
     44 
     45 using chromeos::CellularNetwork;
     46 using chromeos::MobileActivator;
     47 using chromeos::NetworkLibrary;
     48 using content::BrowserThread;
     49 using content::RenderViewHost;
     50 using content::WebContents;
     51 using content::WebUIMessageHandler;
     52 
     53 namespace {
     54 
     55 // Host page JS API function names.
     56 const char kJsApiStartActivation[] = "startActivation";
     57 const char kJsApiSetTransactionStatus[] = "setTransactionStatus";
     58 const char kJsApiPaymentPortalLoad[] = "paymentPortalLoad";
     59 const char kJsGetDeviceInfo[] = "getDeviceInfo";
     60 const char kJsApiResultOK[] = "ok";
     61 
     62 const char kJsDeviceStatusChangedCallback[] =
     63     "mobile.MobileSetup.deviceStateChanged";
     64 const char kJsPortalFrameLoadFailedCallback[] =
     65     "mobile.MobileSetup.portalFrameLoadError";
     66 const char kJsPortalFrameLoadCompletedCallback[] =
     67     "mobile.MobileSetup.portalFrameLoadCompleted";
     68 const char kJsGetDeviceInfoCallback[] =
     69     "mobile.MobileSetupPortal.onGotDeviceInfo";
     70 const char kJsConnectivityChangedCallback[] =
     71     "mobile.MobileSetupPortal.onConnectivityChanged";
     72 
     73 }  // namespace
     74 
     75 // Observes IPC messages from the rederer and notifies JS if frame loading error
     76 // appears.
     77 class PortalFrameLoadObserver : public content::RenderViewHostObserver {
     78  public:
     79   PortalFrameLoadObserver(const base::WeakPtr<MobileSetupUI>& parent,
     80                           RenderViewHost* host)
     81       : content::RenderViewHostObserver(host), parent_(parent) {
     82     Send(new ChromeViewMsg_StartFrameSniffer(routing_id(),
     83                                              UTF8ToUTF16("paymentForm")));
     84   }
     85 
     86   // IPC::Listener implementation.
     87   virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
     88     bool handled = true;
     89     IPC_BEGIN_MESSAGE_MAP(PortalFrameLoadObserver, message)
     90       IPC_MESSAGE_HANDLER(ChromeViewHostMsg_FrameLoadingError, OnFrameLoadError)
     91       IPC_MESSAGE_HANDLER(ChromeViewHostMsg_FrameLoadingCompleted,
     92                           OnFrameLoadCompleted)
     93       IPC_MESSAGE_UNHANDLED(handled = false)
     94     IPC_END_MESSAGE_MAP()
     95     return handled;
     96   }
     97 
     98  private:
     99   void OnFrameLoadError(int error) {
    100     if (!parent_.get())
    101       return;
    102 
    103     base::FundamentalValue result_value(error);
    104     parent_->web_ui()->CallJavascriptFunction(kJsPortalFrameLoadFailedCallback,
    105                                               result_value);
    106   }
    107 
    108   void OnFrameLoadCompleted() {
    109     if (!parent_.get())
    110       return;
    111 
    112     parent_->web_ui()->CallJavascriptFunction(
    113         kJsPortalFrameLoadCompletedCallback);
    114   }
    115 
    116   base::WeakPtr<MobileSetupUI> parent_;
    117   DISALLOW_COPY_AND_ASSIGN(PortalFrameLoadObserver);
    118 };
    119 
    120 class MobileSetupUIHTMLSource : public content::URLDataSource {
    121  public:
    122   MobileSetupUIHTMLSource();
    123 
    124   // content::URLDataSource implementation.
    125   virtual std::string GetSource() const OVERRIDE;
    126   virtual void StartDataRequest(
    127       const std::string& path,
    128       int render_process_id,
    129       int render_view_id,
    130       const content::URLDataSource::GotDataCallback& callback) OVERRIDE;
    131   virtual std::string GetMimeType(const std::string&) const OVERRIDE {
    132     return "text/html";
    133   }
    134   virtual bool ShouldAddContentSecurityPolicy() const OVERRIDE {
    135     return false;
    136   }
    137 
    138  private:
    139   virtual ~MobileSetupUIHTMLSource() {}
    140 
    141   DISALLOW_COPY_AND_ASSIGN(MobileSetupUIHTMLSource);
    142 };
    143 
    144 // The handler for Javascript messages related to the "register" view.
    145 class MobileSetupHandler
    146   : public WebUIMessageHandler,
    147     public MobileActivator::Observer,
    148     public NetworkLibrary::NetworkManagerObserver,
    149     public base::SupportsWeakPtr<MobileSetupHandler> {
    150  public:
    151   MobileSetupHandler();
    152   virtual ~MobileSetupHandler();
    153 
    154   // WebUIMessageHandler implementation.
    155   virtual void RegisterMessages() OVERRIDE;
    156 
    157  private:
    158   enum Type {
    159     TYPE_UNDETERMINED,
    160     // The network is not yet activated, and the webui is in activation flow.
    161     TYPE_ACTIVATION,
    162     // The network is activated, the webui displays network portal.
    163     TYPE_PORTAL,
    164     // Same as TYPE_PORTAL, but the network technology is LTE. The webui is
    165     // additionally aware of network manager state and whether the portal can be
    166     // reached.
    167     TYPE_PORTAL_LTE
    168   };
    169 
    170   // Changes internal state.
    171   virtual void OnActivationStateChanged(
    172       CellularNetwork* network,
    173       MobileActivator::PlanActivationState new_state,
    174       const std::string& error_description) OVERRIDE;
    175 
    176   // Handlers for JS WebUI messages.
    177   void HandleSetTransactionStatus(const ListValue* args);
    178   void HandleStartActivation(const ListValue* args);
    179   void HandlePaymentPortalLoad(const ListValue* args);
    180   void HandleGetDeviceInfo(const ListValue* args);
    181 
    182   // NetworkLibrary::NetworkManagerObserver implementation.
    183   virtual void OnNetworkManagerChanged(NetworkLibrary* network_lib) OVERRIDE;
    184 
    185   // Updates |lte_portal_reachable_| for lte network |network| and notifies
    186   // webui of the new state if the reachability changed or |force_notification|
    187   // is set.
    188   void UpdatePortalReachability(NetworkLibrary* network_lib,
    189                                 CellularNetwork* network,
    190                                 bool force_notification);
    191 
    192   // Sends message to host registration page with system/user info data.
    193   void SendDeviceInfo();
    194 
    195   // Converts the currently active CellularNetwork device into a JS object.
    196   static void GetDeviceInfo(CellularNetwork* network,
    197                             DictionaryValue* value);
    198 
    199   // Type of the mobilesetup webui deduced from received messages.
    200   Type type_;
    201   // Whether portal page for lte networks can be reached in current network
    202   // connection state. This value is reflected in portal webui for lte networks.
    203   // Initial value is true.
    204   bool lte_portal_reachable_;
    205 
    206   DISALLOW_COPY_AND_ASSIGN(MobileSetupHandler);
    207 };
    208 
    209 ////////////////////////////////////////////////////////////////////////////////
    210 //
    211 // MobileSetupUIHTMLSource
    212 //
    213 ////////////////////////////////////////////////////////////////////////////////
    214 
    215 MobileSetupUIHTMLSource::MobileSetupUIHTMLSource() {
    216 }
    217 
    218 std::string MobileSetupUIHTMLSource::GetSource() const {
    219   return chrome::kChromeUIMobileSetupHost;
    220 }
    221 
    222 void MobileSetupUIHTMLSource::StartDataRequest(
    223     const std::string& path,
    224     int render_process_id,
    225     int render_view_id,
    226     const content::URLDataSource::GotDataCallback& callback) {
    227   CellularNetwork* network = NULL;
    228   if (!path.empty()) {
    229     network = NetworkLibrary::Get()-> FindCellularNetworkByPath(path);
    230   }
    231 
    232   if (!network || (!network->SupportsActivation() && !network->activated())) {
    233     LOG(WARNING) << "Can't find device to activate for service path " << path;
    234     scoped_refptr<base::RefCountedBytes> html_bytes(new base::RefCountedBytes);
    235     callback.Run(html_bytes.get());
    236     return;
    237   }
    238 
    239   LOG(WARNING) << "Starting mobile setup for " << path;
    240   DictionaryValue strings;
    241 
    242   strings.SetString("connecting_header",
    243                     l10n_util::GetStringFUTF16(IDS_MOBILE_CONNECTING_HEADER,
    244                         network ? UTF8ToUTF16(network->name()) : string16()));
    245   strings.SetString("error_header",
    246                     l10n_util::GetStringUTF16(IDS_MOBILE_ERROR_HEADER));
    247   strings.SetString("activating_header",
    248                     l10n_util::GetStringUTF16(IDS_MOBILE_ACTIVATING_HEADER));
    249   strings.SetString("completed_header",
    250                     l10n_util::GetStringUTF16(IDS_MOBILE_COMPLETED_HEADER));
    251   strings.SetString("please_wait",
    252                     l10n_util::GetStringUTF16(IDS_MOBILE_PLEASE_WAIT));
    253   strings.SetString("completed_text",
    254                     l10n_util::GetStringUTF16(IDS_MOBILE_COMPLETED_TEXT));
    255   strings.SetString("portal_unreachable_header",
    256                     l10n_util::GetStringUTF16(IDS_MOBILE_NO_CONNECTION_HEADER));
    257   strings.SetString("invalid_device_info_header",
    258       l10n_util::GetStringUTF16(IDS_MOBILE_INVALID_DEVICE_INFO_HEADER));
    259   strings.SetString("title", l10n_util::GetStringUTF16(IDS_MOBILE_SETUP_TITLE));
    260   strings.SetString("close_button",
    261                     l10n_util::GetStringUTF16(IDS_CLOSE));
    262   strings.SetString("cancel_button",
    263                     l10n_util::GetStringUTF16(IDS_CANCEL));
    264   strings.SetString("ok_button",
    265                     l10n_util::GetStringUTF16(IDS_OK));
    266   webui::SetFontAndTextDirection(&strings);
    267 
    268   // The webui differs based on whether the network is activated or not. If the
    269   // network is activated, the webui goes straight to portal. Otherwise the
    270   // webui is used for activation flow.
    271   std::string full_html;
    272   if (network->activated()) {
    273     static const base::StringPiece html_for_activated(
    274         ResourceBundle::GetSharedInstance().GetRawDataResource(
    275             IDR_MOBILE_SETUP_PORTAL_PAGE_HTML));
    276     full_html = webui::GetI18nTemplateHtml(html_for_activated, &strings);
    277   } else {
    278     static const base::StringPiece html_for_non_activated(
    279         ResourceBundle::GetSharedInstance().GetRawDataResource(
    280             IDR_MOBILE_SETUP_PAGE_HTML));
    281     full_html = webui::GetI18nTemplateHtml(html_for_non_activated, &strings);
    282   }
    283 
    284   callback.Run(base::RefCountedString::TakeString(&full_html));
    285 }
    286 
    287 ////////////////////////////////////////////////////////////////////////////////
    288 //
    289 // MobileSetupHandler
    290 //
    291 ////////////////////////////////////////////////////////////////////////////////
    292 MobileSetupHandler::MobileSetupHandler()
    293     : type_(TYPE_UNDETERMINED),
    294       lte_portal_reachable_(true) {
    295 }
    296 
    297 MobileSetupHandler::~MobileSetupHandler() {
    298   if (type_ == TYPE_ACTIVATION) {
    299     MobileActivator::GetInstance()->RemoveObserver(this);
    300     MobileActivator::GetInstance()->TerminateActivation();
    301   } else if (type_ == TYPE_PORTAL_LTE) {
    302     NetworkLibrary::Get()->RemoveNetworkManagerObserver(this);
    303   }
    304 }
    305 
    306 void MobileSetupHandler::OnActivationStateChanged(
    307     CellularNetwork* network,
    308     MobileActivator::PlanActivationState state,
    309     const std::string& error_description) {
    310   DCHECK_EQ(TYPE_ACTIVATION, type_);
    311   if (!web_ui())
    312     return;
    313 
    314   DictionaryValue device_dict;
    315   if (network)
    316     GetDeviceInfo(network, &device_dict);
    317   device_dict.SetInteger("state", state);
    318   if (error_description.length())
    319     device_dict.SetString("error", error_description);
    320   web_ui()->CallJavascriptFunction(
    321       kJsDeviceStatusChangedCallback, device_dict);
    322 }
    323 
    324 void MobileSetupHandler::RegisterMessages() {
    325   web_ui()->RegisterMessageCallback(kJsApiStartActivation,
    326       base::Bind(&MobileSetupHandler::HandleStartActivation,
    327                  base::Unretained(this)));
    328   web_ui()->RegisterMessageCallback(kJsApiSetTransactionStatus,
    329       base::Bind(&MobileSetupHandler::HandleSetTransactionStatus,
    330                  base::Unretained(this)));
    331   web_ui()->RegisterMessageCallback(kJsApiPaymentPortalLoad,
    332       base::Bind(&MobileSetupHandler::HandlePaymentPortalLoad,
    333                  base::Unretained(this)));
    334   web_ui()->RegisterMessageCallback(kJsGetDeviceInfo,
    335       base::Bind(&MobileSetupHandler::HandleGetDeviceInfo,
    336                  base::Unretained(this)));
    337 }
    338 
    339 void MobileSetupHandler::HandleStartActivation(const ListValue* args) {
    340   DCHECK_EQ(TYPE_UNDETERMINED, type_);
    341 
    342   if (!web_ui())
    343     return;
    344 
    345   std::string path = web_ui()->GetWebContents()->GetURL().path();
    346   if (!path.size())
    347     return;
    348 
    349   LOG(WARNING) << "Starting activation for service " << path;
    350 
    351   type_ = TYPE_ACTIVATION;
    352   MobileActivator::GetInstance()->AddObserver(this);
    353   MobileActivator::GetInstance()->InitiateActivation(path.substr(1));
    354 }
    355 
    356 void MobileSetupHandler::HandleSetTransactionStatus(const ListValue* args) {
    357   DCHECK_EQ(TYPE_ACTIVATION, type_);
    358   if (!web_ui())
    359     return;
    360 
    361   const size_t kSetTransactionStatusParamCount = 1;
    362   if (args->GetSize() != kSetTransactionStatusParamCount)
    363     return;
    364   // Get change callback function name.
    365   std::string status;
    366   if (!args->GetString(0, &status))
    367     return;
    368 
    369   MobileActivator::GetInstance()->OnSetTransactionStatus(
    370       LowerCaseEqualsASCII(status, kJsApiResultOK));
    371 }
    372 
    373 void MobileSetupHandler::HandlePaymentPortalLoad(const ListValue* args) {
    374   // Only activation flow webui is interested in these events.
    375   if (type_ != TYPE_ACTIVATION || !web_ui())
    376     return;
    377 
    378   const size_t kPaymentPortalLoadParamCount = 1;
    379   if (args->GetSize() != kPaymentPortalLoadParamCount)
    380     return;
    381   // Get change callback function name.
    382   std::string result;
    383   if (!args->GetString(0, &result))
    384     return;
    385 
    386   MobileActivator::GetInstance()->OnPortalLoaded(
    387       LowerCaseEqualsASCII(result, kJsApiResultOK));
    388 }
    389 
    390 void MobileSetupHandler::HandleGetDeviceInfo(const ListValue* args) {
    391   DCHECK_NE(TYPE_ACTIVATION, type_);
    392   if (!web_ui())
    393     return;
    394 
    395   std::string path = web_ui()->GetWebContents()->GetURL().path();
    396   if (path.empty())
    397     return;
    398 
    399   NetworkLibrary* network_lib = NetworkLibrary::Get();
    400   CellularNetwork* network =
    401       network_lib->FindCellularNetworkByPath(path.substr(1));
    402   if (!network) {
    403     web_ui()->GetWebContents()->Close();
    404     return;
    405   }
    406 
    407   // If this is the initial call, update the network status and start observing
    408   // network changes, but only for LTE networks. The other networks should
    409   // ignore network status.
    410   if (type_ == TYPE_UNDETERMINED) {
    411     if (network->network_technology() == chromeos::NETWORK_TECHNOLOGY_LTE ||
    412         network->network_technology() ==
    413             chromeos::NETWORK_TECHNOLOGY_LTE_ADVANCED) {
    414       type_ = TYPE_PORTAL_LTE;
    415       network_lib->AddNetworkManagerObserver(this);
    416       // Update the network status and notify the webui. This is the initial
    417       // network state so the webui should be notified no matter what.
    418       UpdatePortalReachability(network_lib,
    419                                network,
    420                                true /*force notification*/);
    421     } else {
    422       type_ = TYPE_PORTAL;
    423       // For non-LTE networks network state is ignored, so report the portal is
    424       // reachable, so it gets shown.
    425       web_ui()->CallJavascriptFunction(kJsConnectivityChangedCallback,
    426                                        base::FundamentalValue(true));
    427     }
    428   }
    429 
    430   DictionaryValue device_info;
    431   GetDeviceInfo(network, &device_info);
    432   web_ui()->CallJavascriptFunction(kJsGetDeviceInfoCallback, device_info);
    433 }
    434 
    435 void MobileSetupHandler::OnNetworkManagerChanged(NetworkLibrary* network_lib) {
    436   if (!web_ui())
    437     return;
    438 
    439   std::string path = web_ui()->GetWebContents()->GetURL().path();
    440   if (path.empty())
    441     return;
    442 
    443   CellularNetwork* network =
    444       network_lib->FindCellularNetworkByPath(path.substr(1));
    445   if (!network) {
    446     LOG(ERROR) << "Service path lost";
    447     web_ui()->GetWebContents()->Close();
    448     return;
    449   }
    450 
    451   UpdatePortalReachability(network_lib, network, false /*force notification*/);
    452 }
    453 
    454 void MobileSetupHandler::UpdatePortalReachability(NetworkLibrary* network_lib,
    455                                                   CellularNetwork* network,
    456                                                   bool force_notification) {
    457   DCHECK(web_ui());
    458 
    459   DCHECK_EQ(type_, TYPE_PORTAL_LTE);
    460 
    461   bool portal_reachable = network->connected() ||
    462                           (network_lib->connected_network() &&
    463                            network_lib->connected_network()->online());
    464 
    465   if (force_notification || portal_reachable != lte_portal_reachable_) {
    466     web_ui()->CallJavascriptFunction(kJsConnectivityChangedCallback,
    467                                      base::FundamentalValue(portal_reachable));
    468   }
    469 
    470   lte_portal_reachable_ = portal_reachable;
    471 }
    472 
    473 void MobileSetupHandler::GetDeviceInfo(CellularNetwork* network,
    474                                        DictionaryValue* value) {
    475   DCHECK(network);
    476   chromeos::NetworkLibrary* cros =
    477       chromeos::NetworkLibrary::Get();
    478   if (!cros)
    479     return;
    480   value->SetBoolean("activate_over_non_cellular_network",
    481                     network->activate_over_non_cellular_network());
    482   value->SetString("carrier", network->name());
    483   value->SetString("payment_url", network->payment_url());
    484   if (network->using_post() && network->post_data().length())
    485     value->SetString("post_data", network->post_data());
    486 
    487   const chromeos::NetworkDevice* device =
    488       cros->FindNetworkDeviceByPath(network->device_path());
    489   if (device) {
    490     value->SetString("MEID", device->meid());
    491     value->SetString("IMEI", device->imei());
    492     value->SetString("MDN", device->mdn());
    493   }
    494 }
    495 
    496 ////////////////////////////////////////////////////////////////////////////////
    497 //
    498 // MobileSetupUI
    499 //
    500 ////////////////////////////////////////////////////////////////////////////////
    501 
    502 MobileSetupUI::MobileSetupUI(content::WebUI* web_ui)
    503     : WebUIController(web_ui) {
    504   web_ui->AddMessageHandler(new MobileSetupHandler());
    505   MobileSetupUIHTMLSource* html_source = new MobileSetupUIHTMLSource();
    506 
    507   // Set up the chrome://mobilesetup/ source.
    508   Profile* profile = Profile::FromWebUI(web_ui);
    509   content::URLDataSource::Add(profile, html_source);
    510 }
    511 
    512 void MobileSetupUI::RenderViewCreated(RenderViewHost* host) {
    513   // Destroyed by the corresponding RenderViewHost
    514   new PortalFrameLoadObserver(AsWeakPtr(), host);
    515 }
    516