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