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.
5 #include "chrome/browser/ui/webui/chromeos/mobile_setup_ui.h"
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/memory/weak_ptr.h"
17 #include "base/message_loop/message_loop.h"
18 #include "base/metrics/histogram.h"
19 #include "base/strings/string_piece.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "base/values.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/render_messages.h"
27 #include "chrome/common/url_constants.h"
28 #include "chrome/grit/generated_resources.h"
29 #include "chrome/grit/locale_settings.h"
30 #include "chromeos/network/device_state.h"
31 #include "chromeos/network/network_configuration_handler.h"
32 #include "chromeos/network/network_event_log.h"
33 #include "chromeos/network/network_state.h"
34 #include "chromeos/network/network_state_handler.h"
35 #include "chromeos/network/network_state_handler_observer.h"
36 #include "content/public/browser/browser_thread.h"
37 #include "content/public/browser/render_frame_host.h"
38 #include "content/public/browser/url_data_source.h"
39 #include "content/public/browser/web_contents.h"
40 #include "content/public/browser/web_ui.h"
41 #include "content/public/browser/web_ui_message_handler.h"
42 #include "grit/browser_resources.h"
43 #include "third_party/cros_system_api/dbus/service_constants.h"
44 #include "ui/base/l10n/l10n_util.h"
45 #include "ui/base/resource/resource_bundle.h"
46 #include "ui/base/webui/jstemplate_builder.h"
47 #include "ui/base/webui/web_ui_util.h"
50 using chromeos::MobileActivator;
51 using chromeos::NetworkHandler;
52 using chromeos::NetworkState;
53 using content::BrowserThread;
54 using content::RenderViewHost;
55 using content::WebContents;
56 using content::WebUIMessageHandler;
60 // Host page JS API function names.
61 const char kJsApiStartActivation[] = "startActivation";
62 const char kJsApiSetTransactionStatus[] = "setTransactionStatus";
63 const char kJsApiPaymentPortalLoad[] = "paymentPortalLoad";
64 const char kJsGetDeviceInfo[] = "getDeviceInfo";
65 const char kJsApiResultOK[] = "ok";
67 const char kJsDeviceStatusChangedCallback[] =
68 "mobile.MobileSetup.deviceStateChanged";
69 const char kJsPortalFrameLoadFailedCallback[] =
70 "mobile.MobileSetup.portalFrameLoadError";
71 const char kJsPortalFrameLoadCompletedCallback[] =
72 "mobile.MobileSetup.portalFrameLoadCompleted";
73 const char kJsGetDeviceInfoCallback[] =
74 "mobile.MobileSetupPortal.onGotDeviceInfo";
75 const char kJsConnectivityChangedCallback[] =
76 "mobile.MobileSetupPortal.onConnectivityChanged";
78 void DataRequestFailed(
79 const std::string& service_path,
80 const content::URLDataSource::GotDataCallback& callback) {
81 NET_LOG_ERROR("Data Request Failed for Mobile Setup", service_path);
82 scoped_refptr<base::RefCountedBytes> html_bytes(new base::RefCountedBytes);
83 callback.Run(html_bytes.get());
86 // Converts the network properties into a JS object.
87 void GetDeviceInfo(const base::DictionaryValue& properties,
88 base::DictionaryValue* value) {
90 properties.GetStringWithoutPathExpansion(shill::kNameProperty, &name);
91 std::string activation_type;
92 properties.GetStringWithoutPathExpansion(
93 shill::kActivationTypeProperty,
95 const base::DictionaryValue* payment_dict;
96 std::string payment_url, post_method, post_data;
97 if (properties.GetDictionaryWithoutPathExpansion(
98 shill::kPaymentPortalProperty, &payment_dict)) {
99 payment_dict->GetStringWithoutPathExpansion(
100 shill::kPaymentPortalURL, &payment_url);
101 payment_dict->GetStringWithoutPathExpansion(
102 shill::kPaymentPortalMethod, &post_method);
103 payment_dict->GetStringWithoutPathExpansion(
104 shill::kPaymentPortalPostData, &post_data);
107 value->SetString("activation_type", activation_type);
108 value->SetString("carrier", name);
109 value->SetString("payment_url", payment_url);
110 if (LowerCaseEqualsASCII(post_method, "post") && !post_data.empty())
111 value->SetString("post_data", post_data);
113 // Use the cached DeviceState properties.
114 std::string device_path;
115 if (!properties.GetStringWithoutPathExpansion(
116 shill::kDeviceProperty, &device_path) ||
117 device_path.empty()) {
120 const chromeos::DeviceState* device =
121 NetworkHandler::Get()->network_state_handler()->GetDeviceState(
126 value->SetString("MEID", device->meid());
127 value->SetString("IMEI", device->imei());
128 value->SetString("MDN", device->mdn());
131 void SetActivationStateAndError(MobileActivator::PlanActivationState state,
132 const std::string& error_description,
133 base::DictionaryValue* value) {
134 value->SetInteger("state", state);
135 if (!error_description.empty())
136 value->SetString("error", error_description);
141 class MobileSetupUIHTMLSource : public content::URLDataSource {
143 MobileSetupUIHTMLSource();
145 // content::URLDataSource implementation.
146 virtual std::string GetSource() const override;
147 virtual void StartDataRequest(
148 const std::string& path,
149 int render_process_id,
151 const content::URLDataSource::GotDataCallback& callback) override;
152 virtual std::string GetMimeType(const std::string&) const override {
155 virtual bool ShouldAddContentSecurityPolicy() const override {
160 virtual ~MobileSetupUIHTMLSource() {}
162 void GetPropertiesAndStartDataRequest(
163 const content::URLDataSource::GotDataCallback& callback,
164 const std::string& service_path,
165 const base::DictionaryValue& properties);
166 void GetPropertiesFailure(
167 const content::URLDataSource::GotDataCallback& callback,
168 const std::string& service_path,
169 const std::string& error_name,
170 scoped_ptr<base::DictionaryValue> error_data);
172 base::WeakPtrFactory<MobileSetupUIHTMLSource> weak_ptr_factory_;
174 DISALLOW_COPY_AND_ASSIGN(MobileSetupUIHTMLSource);
177 // The handler for Javascript messages related to the "register" view.
178 class MobileSetupHandler
179 : public WebUIMessageHandler,
180 public MobileActivator::Observer,
181 public chromeos::NetworkStateHandlerObserver,
182 public base::SupportsWeakPtr<MobileSetupHandler> {
184 MobileSetupHandler();
185 virtual ~MobileSetupHandler();
187 // WebUIMessageHandler implementation.
188 virtual void RegisterMessages() override;
193 // The network is not yet activated, and the webui is in activation flow.
195 // The network is activated, the webui displays network portal.
197 // Same as TYPE_PORTAL, but the network technology is LTE. The webui is
198 // additionally aware of network manager state and whether the portal can be
203 // MobileActivator::Observer.
204 virtual void OnActivationStateChanged(
205 const NetworkState* network,
206 MobileActivator::PlanActivationState new_state,
207 const std::string& error_description) override;
209 // Callbacks for NetworkConfigurationHandler::GetProperties.
210 void GetPropertiesAndCallStatusChanged(
211 MobileActivator::PlanActivationState state,
212 const std::string& error_description,
213 const std::string& service_path,
214 const base::DictionaryValue& properties);
215 void GetPropertiesAndCallGetDeviceInfo(
216 const std::string& service_path,
217 const base::DictionaryValue& properties);
218 void GetPropertiesFailure(
219 const std::string& service_path,
220 const std::string& callback_name,
221 const std::string& error_name,
222 scoped_ptr<base::DictionaryValue> error_data);
224 // Handlers for JS WebUI messages.
225 void HandleSetTransactionStatus(const base::ListValue* args);
226 void HandleStartActivation(const base::ListValue* args);
227 void HandlePaymentPortalLoad(const base::ListValue* args);
228 void HandleGetDeviceInfo(const base::ListValue* args);
230 // NetworkStateHandlerObserver implementation.
231 virtual void NetworkConnectionStateChanged(
232 const NetworkState* network) override;
233 virtual void DefaultNetworkChanged(
234 const NetworkState* default_network) override;
236 // Updates |lte_portal_reachable_| for lte network |network| and notifies
237 // webui of the new state if the reachability changed or |force_notification|
239 void UpdatePortalReachability(const NetworkState* network,
240 bool force_notification);
242 // Sends message to host registration page with system/user info data.
243 void SendDeviceInfo();
245 // Type of the mobilesetup webui deduced from received messages.
247 // Whether portal page for lte networks can be reached in current network
248 // connection state. This value is reflected in portal webui for lte networks.
249 // Initial value is true.
250 bool lte_portal_reachable_;
251 base::WeakPtrFactory<MobileSetupHandler> weak_ptr_factory_;
253 DISALLOW_COPY_AND_ASSIGN(MobileSetupHandler);
256 ////////////////////////////////////////////////////////////////////////////////
258 // MobileSetupUIHTMLSource
260 ////////////////////////////////////////////////////////////////////////////////
262 MobileSetupUIHTMLSource::MobileSetupUIHTMLSource()
263 : weak_ptr_factory_(this) {
266 std::string MobileSetupUIHTMLSource::GetSource() const {
267 return chrome::kChromeUIMobileSetupHost;
270 void MobileSetupUIHTMLSource::StartDataRequest(
271 const std::string& path,
272 int render_process_id,
274 const content::URLDataSource::GotDataCallback& callback) {
275 NetworkHandler::Get()->network_configuration_handler()->GetProperties(
277 base::Bind(&MobileSetupUIHTMLSource::GetPropertiesAndStartDataRequest,
278 weak_ptr_factory_.GetWeakPtr(),
280 base::Bind(&MobileSetupUIHTMLSource::GetPropertiesFailure,
281 weak_ptr_factory_.GetWeakPtr(),
285 void MobileSetupUIHTMLSource::GetPropertiesAndStartDataRequest(
286 const content::URLDataSource::GotDataCallback& callback,
287 const std::string& service_path,
288 const base::DictionaryValue& properties) {
289 const base::DictionaryValue* payment_dict;
290 std::string name, usage_url, activation_state, payment_url;
291 if (!properties.GetStringWithoutPathExpansion(
292 shill::kNameProperty, &name) ||
293 !properties.GetStringWithoutPathExpansion(
294 shill::kUsageURLProperty, &usage_url) ||
295 !properties.GetStringWithoutPathExpansion(
296 shill::kActivationStateProperty, &activation_state) ||
297 !properties.GetDictionaryWithoutPathExpansion(
298 shill::kPaymentPortalProperty, &payment_dict) ||
299 !payment_dict->GetStringWithoutPathExpansion(
300 shill::kPaymentPortalURL, &payment_url)) {
301 DataRequestFailed(service_path, callback);
305 if (payment_url.empty() && usage_url.empty() &&
306 activation_state != shill::kActivationStateActivated) {
307 DataRequestFailed(service_path, callback);
311 NET_LOG_EVENT("Starting mobile setup", service_path);
312 base::DictionaryValue strings;
314 strings.SetString("connecting_header",
315 l10n_util::GetStringFUTF16(IDS_MOBILE_CONNECTING_HEADER,
316 base::UTF8ToUTF16(name)));
317 strings.SetString("error_header",
318 l10n_util::GetStringUTF16(IDS_MOBILE_ERROR_HEADER));
319 strings.SetString("activating_header",
320 l10n_util::GetStringUTF16(IDS_MOBILE_ACTIVATING_HEADER));
321 strings.SetString("completed_header",
322 l10n_util::GetStringUTF16(IDS_MOBILE_COMPLETED_HEADER));
323 strings.SetString("please_wait",
324 l10n_util::GetStringUTF16(IDS_MOBILE_PLEASE_WAIT));
325 strings.SetString("completed_text",
326 l10n_util::GetStringUTF16(IDS_MOBILE_COMPLETED_TEXT));
327 strings.SetString("portal_unreachable_header",
328 l10n_util::GetStringUTF16(IDS_MOBILE_NO_CONNECTION_HEADER));
329 strings.SetString("invalid_device_info_header",
330 l10n_util::GetStringUTF16(IDS_MOBILE_INVALID_DEVICE_INFO_HEADER));
331 strings.SetString("title", l10n_util::GetStringUTF16(IDS_MOBILE_SETUP_TITLE));
332 strings.SetString("close_button",
333 l10n_util::GetStringUTF16(IDS_CLOSE));
334 strings.SetString("cancel_button",
335 l10n_util::GetStringUTF16(IDS_CANCEL));
336 strings.SetString("ok_button",
337 l10n_util::GetStringUTF16(IDS_OK));
338 webui::SetFontAndTextDirection(&strings);
340 // The webui differs based on whether the network is activated or not. If the
341 // network is activated, the webui goes straight to portal. Otherwise the
342 // webui is used for activation flow.
343 std::string full_html;
344 if (activation_state == shill::kActivationStateActivated) {
345 static const base::StringPiece html_for_activated(
346 ResourceBundle::GetSharedInstance().GetRawDataResource(
347 IDR_MOBILE_SETUP_PORTAL_PAGE_HTML));
348 full_html = webui::GetI18nTemplateHtml(html_for_activated, &strings);
350 static const base::StringPiece html_for_non_activated(
351 ResourceBundle::GetSharedInstance().GetRawDataResource(
352 IDR_MOBILE_SETUP_PAGE_HTML));
353 full_html = webui::GetI18nTemplateHtml(html_for_non_activated, &strings);
356 callback.Run(base::RefCountedString::TakeString(&full_html));
359 void MobileSetupUIHTMLSource::GetPropertiesFailure(
360 const content::URLDataSource::GotDataCallback& callback,
361 const std::string& service_path,
362 const std::string& error_name,
363 scoped_ptr<base::DictionaryValue> error_data) {
364 DataRequestFailed(service_path, callback);
367 ////////////////////////////////////////////////////////////////////////////////
369 // MobileSetupHandler
371 ////////////////////////////////////////////////////////////////////////////////
372 MobileSetupHandler::MobileSetupHandler()
373 : type_(TYPE_UNDETERMINED),
374 lte_portal_reachable_(true),
375 weak_ptr_factory_(this) {
378 MobileSetupHandler::~MobileSetupHandler() {
379 if (type_ == TYPE_ACTIVATION) {
380 MobileActivator::GetInstance()->RemoveObserver(this);
381 MobileActivator::GetInstance()->TerminateActivation();
382 } else if (type_ == TYPE_PORTAL_LTE) {
383 NetworkHandler::Get()->network_state_handler()->RemoveObserver(this,
388 void MobileSetupHandler::OnActivationStateChanged(
389 const NetworkState* network,
390 MobileActivator::PlanActivationState state,
391 const std::string& error_description) {
392 DCHECK_EQ(TYPE_ACTIVATION, type_);
397 base::DictionaryValue device_dict;
398 SetActivationStateAndError(state, error_description, &device_dict);
399 web_ui()->CallJavascriptFunction(kJsDeviceStatusChangedCallback,
404 NetworkHandler::Get()->network_configuration_handler()->GetProperties(
406 base::Bind(&MobileSetupHandler::GetPropertiesAndCallStatusChanged,
407 weak_ptr_factory_.GetWeakPtr(),
410 base::Bind(&MobileSetupHandler::GetPropertiesFailure,
411 weak_ptr_factory_.GetWeakPtr(),
413 kJsDeviceStatusChangedCallback));
416 void MobileSetupHandler::GetPropertiesAndCallStatusChanged(
417 MobileActivator::PlanActivationState state,
418 const std::string& error_description,
419 const std::string& service_path,
420 const base::DictionaryValue& properties) {
421 base::DictionaryValue device_dict;
422 GetDeviceInfo(properties, &device_dict);
423 SetActivationStateAndError(state, error_description, &device_dict);
424 web_ui()->CallJavascriptFunction(kJsDeviceStatusChangedCallback, device_dict);
427 void MobileSetupHandler::RegisterMessages() {
428 web_ui()->RegisterMessageCallback(kJsApiStartActivation,
429 base::Bind(&MobileSetupHandler::HandleStartActivation,
430 base::Unretained(this)));
431 web_ui()->RegisterMessageCallback(kJsApiSetTransactionStatus,
432 base::Bind(&MobileSetupHandler::HandleSetTransactionStatus,
433 base::Unretained(this)));
434 web_ui()->RegisterMessageCallback(kJsApiPaymentPortalLoad,
435 base::Bind(&MobileSetupHandler::HandlePaymentPortalLoad,
436 base::Unretained(this)));
437 web_ui()->RegisterMessageCallback(kJsGetDeviceInfo,
438 base::Bind(&MobileSetupHandler::HandleGetDeviceInfo,
439 base::Unretained(this)));
442 void MobileSetupHandler::HandleStartActivation(const base::ListValue* args) {
443 DCHECK_EQ(TYPE_UNDETERMINED, type_);
448 std::string path = web_ui()->GetWebContents()->GetURL().path();
452 LOG(WARNING) << "Starting activation for service " << path;
454 type_ = TYPE_ACTIVATION;
455 MobileActivator::GetInstance()->AddObserver(this);
456 MobileActivator::GetInstance()->InitiateActivation(path.substr(1));
459 void MobileSetupHandler::HandleSetTransactionStatus(
460 const base::ListValue* args) {
461 DCHECK_EQ(TYPE_ACTIVATION, type_);
465 const size_t kSetTransactionStatusParamCount = 1;
466 if (args->GetSize() != kSetTransactionStatusParamCount)
468 // Get change callback function name.
470 if (!args->GetString(0, &status))
473 MobileActivator::GetInstance()->OnSetTransactionStatus(
474 LowerCaseEqualsASCII(status, kJsApiResultOK));
477 void MobileSetupHandler::HandlePaymentPortalLoad(const base::ListValue* args) {
478 // Only activation flow webui is interested in these events.
479 if (type_ != TYPE_ACTIVATION || !web_ui())
482 const size_t kPaymentPortalLoadParamCount = 1;
483 if (args->GetSize() != kPaymentPortalLoadParamCount)
485 // Get change callback function name.
487 if (!args->GetString(0, &result))
490 MobileActivator::GetInstance()->OnPortalLoaded(
491 LowerCaseEqualsASCII(result, kJsApiResultOK));
494 void MobileSetupHandler::HandleGetDeviceInfo(const base::ListValue* args) {
495 DCHECK_NE(TYPE_ACTIVATION, type_);
499 std::string path = web_ui()->GetWebContents()->GetURL().path();
503 chromeos::NetworkStateHandler* nsh =
504 NetworkHandler::Get()->network_state_handler();
505 // TODO: Figure out why the path has an extra '/' in the front. (e.g. It is
506 // '//service/5' instead of '/service/5'.
507 const NetworkState* network = nsh->GetNetworkState(path.substr(1));
509 web_ui()->GetWebContents()->Close();
513 // If this is the initial call, update the network status and start observing
514 // network changes, but only for LTE networks. The other networks should
515 // ignore network status.
516 if (type_ == TYPE_UNDETERMINED) {
517 if (network->network_technology() == shill::kNetworkTechnologyLte ||
518 network->network_technology() == shill::kNetworkTechnologyLteAdvanced) {
519 type_ = TYPE_PORTAL_LTE;
520 nsh->AddObserver(this, FROM_HERE);
521 // Update the network status and notify the webui. This is the initial
522 // network state so the webui should be notified no matter what.
523 UpdatePortalReachability(network,
524 true /* force notification */);
527 // For non-LTE networks network state is ignored, so report the portal is
528 // reachable, so it gets shown.
529 web_ui()->CallJavascriptFunction(kJsConnectivityChangedCallback,
530 base::FundamentalValue(true));
534 NetworkHandler::Get()->network_configuration_handler()->GetProperties(
536 base::Bind(&MobileSetupHandler::GetPropertiesAndCallGetDeviceInfo,
537 weak_ptr_factory_.GetWeakPtr()),
538 base::Bind(&MobileSetupHandler::GetPropertiesFailure,
539 weak_ptr_factory_.GetWeakPtr(),
541 kJsGetDeviceInfoCallback));
544 void MobileSetupHandler::GetPropertiesAndCallGetDeviceInfo(
545 const std::string& service_path,
546 const base::DictionaryValue& properties) {
547 base::DictionaryValue device_info;
548 GetDeviceInfo(properties, &device_info);
549 web_ui()->CallJavascriptFunction(kJsGetDeviceInfoCallback, device_info);
552 void MobileSetupHandler::GetPropertiesFailure(
553 const std::string& service_path,
554 const std::string& callback_name,
555 const std::string& error_name,
556 scoped_ptr<base::DictionaryValue> error_data) {
557 NET_LOG_ERROR("MobileActivator GetProperties Failed: " + error_name,
559 // Invoke |callback_name| with an empty dictionary.
560 base::DictionaryValue device_dict;
561 web_ui()->CallJavascriptFunction(callback_name, device_dict);
564 void MobileSetupHandler::DefaultNetworkChanged(
565 const NetworkState* default_network) {
569 std::string path = web_ui()->GetWebContents()->GetURL().path().substr(1);
573 const NetworkState* network =
574 NetworkHandler::Get()->network_state_handler()->GetNetworkState(path);
576 LOG(ERROR) << "Service path lost";
577 web_ui()->GetWebContents()->Close();
581 UpdatePortalReachability(network, false /* do not force notification */);
584 void MobileSetupHandler::NetworkConnectionStateChanged(
585 const NetworkState* network) {
589 std::string path = web_ui()->GetWebContents()->GetURL().path().substr(1);
590 if (path.empty() || path != network->path())
593 UpdatePortalReachability(network, false /* do not force notification */);
596 void MobileSetupHandler::UpdatePortalReachability(
597 const NetworkState* network,
598 bool force_notification) {
601 DCHECK_EQ(type_, TYPE_PORTAL_LTE);
603 chromeos::NetworkStateHandler* nsh =
604 NetworkHandler::Get()->network_state_handler();
605 bool portal_reachable =
606 (network->IsConnectedState() ||
607 (nsh->DefaultNetwork() &&
608 nsh->DefaultNetwork()->connection_state() == shill::kStateOnline));
610 if (force_notification || portal_reachable != lte_portal_reachable_) {
611 web_ui()->CallJavascriptFunction(kJsConnectivityChangedCallback,
612 base::FundamentalValue(portal_reachable));
615 lte_portal_reachable_ = portal_reachable;
618 ////////////////////////////////////////////////////////////////////////////////
622 ////////////////////////////////////////////////////////////////////////////////
624 MobileSetupUI::MobileSetupUI(content::WebUI* web_ui)
625 : WebUIController(web_ui) {
626 web_ui->AddMessageHandler(new MobileSetupHandler());
627 MobileSetupUIHTMLSource* html_source = new MobileSetupUIHTMLSource();
629 // Set up the chrome://mobilesetup/ source.
630 Profile* profile = Profile::FromWebUI(web_ui);
631 content::URLDataSource::Add(profile, html_source);
633 content::WebContentsObserver::Observe(web_ui->GetWebContents());
636 void MobileSetupUI::DidCommitProvisionalLoadForFrame(
637 content::RenderFrameHost* render_frame_host,
639 ui::PageTransition transition_type) {
640 if (render_frame_host->GetFrameName() != "paymentForm")
643 web_ui()->CallJavascriptFunction(
644 kJsPortalFrameLoadCompletedCallback);
647 void MobileSetupUI::DidFailProvisionalLoad(
648 content::RenderFrameHost* render_frame_host,
649 const GURL& validated_url,
651 const base::string16& error_description) {
652 if (render_frame_host->GetFrameName() != "paymentForm")
655 base::FundamentalValue result_value(-error_code);
656 web_ui()->CallJavascriptFunction(kJsPortalFrameLoadFailedCallback,