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/pref_names.h"
27 #include "chrome/common/render_messages.h"
28 #include "chrome/common/url_constants.h"
29 #include "chrome/grit/generated_resources.h"
30 #include "chrome/grit/locale_settings.h"
31 #include "chromeos/network/device_state.h"
32 #include "chromeos/network/network_configuration_handler.h"
33 #include "chromeos/network/network_event_log.h"
34 #include "chromeos/network/network_state.h"
35 #include "chromeos/network/network_state_handler.h"
36 #include "chromeos/network/network_state_handler_observer.h"
37 #include "content/public/browser/browser_thread.h"
38 #include "content/public/browser/render_frame_host.h"
39 #include "content/public/browser/url_data_source.h"
40 #include "content/public/browser/web_contents.h"
41 #include "content/public/browser/web_ui.h"
42 #include "content/public/browser/web_ui_message_handler.h"
43 #include "grit/browser_resources.h"
44 #include "third_party/cros_system_api/dbus/service_constants.h"
45 #include "ui/base/l10n/l10n_util.h"
46 #include "ui/base/resource/resource_bundle.h"
47 #include "ui/base/webui/jstemplate_builder.h"
48 #include "ui/base/webui/web_ui_util.h"
51 using chromeos::MobileActivator;
52 using chromeos::NetworkHandler;
53 using chromeos::NetworkState;
54 using content::BrowserThread;
55 using content::RenderViewHost;
56 using content::WebContents;
57 using content::WebUIMessageHandler;
61 // Host page JS API function names.
62 const char kJsApiStartActivation[] = "startActivation";
63 const char kJsApiSetTransactionStatus[] = "setTransactionStatus";
64 const char kJsApiPaymentPortalLoad[] = "paymentPortalLoad";
65 const char kJsGetDeviceInfo[] = "getDeviceInfo";
66 const char kJsApiResultOK[] = "ok";
68 const char kJsDeviceStatusChangedCallback[] =
69 "mobile.MobileSetup.deviceStateChanged";
70 const char kJsPortalFrameLoadFailedCallback[] =
71 "mobile.MobileSetup.portalFrameLoadError";
72 const char kJsPortalFrameLoadCompletedCallback[] =
73 "mobile.MobileSetup.portalFrameLoadCompleted";
74 const char kJsGetDeviceInfoCallback[] =
75 "mobile.MobileSetupPortal.onGotDeviceInfo";
76 const char kJsConnectivityChangedCallback[] =
77 "mobile.MobileSetupPortal.onConnectivityChanged";
79 void DataRequestFailed(
80 const std::string& service_path,
81 const content::URLDataSource::GotDataCallback& callback) {
82 NET_LOG_ERROR("Data Request Failed for Mobile Setup", service_path);
83 scoped_refptr<base::RefCountedBytes> html_bytes(new base::RefCountedBytes);
84 callback.Run(html_bytes.get());
87 // Converts the network properties into a JS object.
88 void GetDeviceInfo(const base::DictionaryValue& properties,
89 base::DictionaryValue* value) {
91 properties.GetStringWithoutPathExpansion(shill::kNameProperty, &name);
92 std::string activation_type;
93 properties.GetStringWithoutPathExpansion(
94 shill::kActivationTypeProperty,
96 const base::DictionaryValue* payment_dict;
97 std::string payment_url, post_method, post_data;
98 if (properties.GetDictionaryWithoutPathExpansion(
99 shill::kPaymentPortalProperty, &payment_dict)) {
100 payment_dict->GetStringWithoutPathExpansion(
101 shill::kPaymentPortalURL, &payment_url);
102 payment_dict->GetStringWithoutPathExpansion(
103 shill::kPaymentPortalMethod, &post_method);
104 payment_dict->GetStringWithoutPathExpansion(
105 shill::kPaymentPortalPostData, &post_data);
108 value->SetString("activation_type", activation_type);
109 value->SetString("carrier", name);
110 value->SetString("payment_url", payment_url);
111 if (LowerCaseEqualsASCII(post_method, "post") && !post_data.empty())
112 value->SetString("post_data", post_data);
114 // Use the cached DeviceState properties.
115 std::string device_path;
116 if (!properties.GetStringWithoutPathExpansion(
117 shill::kDeviceProperty, &device_path) ||
118 device_path.empty()) {
121 const chromeos::DeviceState* device =
122 NetworkHandler::Get()->network_state_handler()->GetDeviceState(
127 value->SetString("MEID", device->meid());
128 value->SetString("IMEI", device->imei());
129 value->SetString("MDN", device->mdn());
132 void SetActivationStateAndError(MobileActivator::PlanActivationState state,
133 const std::string& error_description,
134 base::DictionaryValue* value) {
135 value->SetInteger("state", state);
136 if (!error_description.empty())
137 value->SetString("error", error_description);
142 class MobileSetupUIHTMLSource : public content::URLDataSource {
144 MobileSetupUIHTMLSource();
146 // content::URLDataSource implementation.
147 virtual std::string GetSource() const OVERRIDE;
148 virtual void StartDataRequest(
149 const std::string& path,
150 int render_process_id,
152 const content::URLDataSource::GotDataCallback& callback) OVERRIDE;
153 virtual std::string GetMimeType(const std::string&) const OVERRIDE {
156 virtual bool ShouldAddContentSecurityPolicy() const OVERRIDE {
161 virtual ~MobileSetupUIHTMLSource() {}
163 void GetPropertiesAndStartDataRequest(
164 const content::URLDataSource::GotDataCallback& callback,
165 const std::string& service_path,
166 const base::DictionaryValue& properties);
167 void GetPropertiesFailure(
168 const content::URLDataSource::GotDataCallback& callback,
169 const std::string& service_path,
170 const std::string& error_name,
171 scoped_ptr<base::DictionaryValue> error_data);
173 base::WeakPtrFactory<MobileSetupUIHTMLSource> weak_ptr_factory_;
175 DISALLOW_COPY_AND_ASSIGN(MobileSetupUIHTMLSource);
178 // The handler for Javascript messages related to the "register" view.
179 class MobileSetupHandler
180 : public WebUIMessageHandler,
181 public MobileActivator::Observer,
182 public chromeos::NetworkStateHandlerObserver,
183 public base::SupportsWeakPtr<MobileSetupHandler> {
185 MobileSetupHandler();
186 virtual ~MobileSetupHandler();
188 // WebUIMessageHandler implementation.
189 virtual void RegisterMessages() OVERRIDE;
194 // The network is not yet activated, and the webui is in activation flow.
196 // The network is activated, the webui displays network portal.
198 // Same as TYPE_PORTAL, but the network technology is LTE. The webui is
199 // additionally aware of network manager state and whether the portal can be
204 // MobileActivator::Observer.
205 virtual void OnActivationStateChanged(
206 const NetworkState* network,
207 MobileActivator::PlanActivationState new_state,
208 const std::string& error_description) OVERRIDE;
210 // Callbacks for NetworkConfigurationHandler::GetProperties.
211 void GetPropertiesAndCallStatusChanged(
212 MobileActivator::PlanActivationState state,
213 const std::string& error_description,
214 const std::string& service_path,
215 const base::DictionaryValue& properties);
216 void GetPropertiesAndCallGetDeviceInfo(
217 const std::string& service_path,
218 const base::DictionaryValue& properties);
219 void GetPropertiesFailure(
220 const std::string& service_path,
221 const std::string& callback_name,
222 const std::string& error_name,
223 scoped_ptr<base::DictionaryValue> error_data);
225 // Handlers for JS WebUI messages.
226 void HandleSetTransactionStatus(const base::ListValue* args);
227 void HandleStartActivation(const base::ListValue* args);
228 void HandlePaymentPortalLoad(const base::ListValue* args);
229 void HandleGetDeviceInfo(const base::ListValue* args);
231 // NetworkStateHandlerObserver implementation.
232 virtual void NetworkConnectionStateChanged(
233 const NetworkState* network) OVERRIDE;
234 virtual void DefaultNetworkChanged(
235 const NetworkState* default_network) OVERRIDE;
237 // Updates |lte_portal_reachable_| for lte network |network| and notifies
238 // webui of the new state if the reachability changed or |force_notification|
240 void UpdatePortalReachability(const NetworkState* network,
241 bool force_notification);
243 // Sends message to host registration page with system/user info data.
244 void SendDeviceInfo();
246 // Type of the mobilesetup webui deduced from received messages.
248 // Whether portal page for lte networks can be reached in current network
249 // connection state. This value is reflected in portal webui for lte networks.
250 // Initial value is true.
251 bool lte_portal_reachable_;
252 base::WeakPtrFactory<MobileSetupHandler> weak_ptr_factory_;
254 DISALLOW_COPY_AND_ASSIGN(MobileSetupHandler);
257 ////////////////////////////////////////////////////////////////////////////////
259 // MobileSetupUIHTMLSource
261 ////////////////////////////////////////////////////////////////////////////////
263 MobileSetupUIHTMLSource::MobileSetupUIHTMLSource()
264 : weak_ptr_factory_(this) {
267 std::string MobileSetupUIHTMLSource::GetSource() const {
268 return chrome::kChromeUIMobileSetupHost;
271 void MobileSetupUIHTMLSource::StartDataRequest(
272 const std::string& path,
273 int render_process_id,
275 const content::URLDataSource::GotDataCallback& callback) {
276 NetworkHandler::Get()->network_configuration_handler()->GetProperties(
278 base::Bind(&MobileSetupUIHTMLSource::GetPropertiesAndStartDataRequest,
279 weak_ptr_factory_.GetWeakPtr(),
281 base::Bind(&MobileSetupUIHTMLSource::GetPropertiesFailure,
282 weak_ptr_factory_.GetWeakPtr(),
286 void MobileSetupUIHTMLSource::GetPropertiesAndStartDataRequest(
287 const content::URLDataSource::GotDataCallback& callback,
288 const std::string& service_path,
289 const base::DictionaryValue& properties) {
290 const base::DictionaryValue* payment_dict;
291 std::string name, usage_url, activation_state, payment_url;
292 if (!properties.GetStringWithoutPathExpansion(
293 shill::kNameProperty, &name) ||
294 !properties.GetStringWithoutPathExpansion(
295 shill::kUsageURLProperty, &usage_url) ||
296 !properties.GetStringWithoutPathExpansion(
297 shill::kActivationStateProperty, &activation_state) ||
298 !properties.GetDictionaryWithoutPathExpansion(
299 shill::kPaymentPortalProperty, &payment_dict) ||
300 !payment_dict->GetStringWithoutPathExpansion(
301 shill::kPaymentPortalURL, &payment_url)) {
302 DataRequestFailed(service_path, callback);
306 if (payment_url.empty() && usage_url.empty() &&
307 activation_state != shill::kActivationStateActivated) {
308 DataRequestFailed(service_path, callback);
312 NET_LOG_EVENT("Starting mobile setup", service_path);
313 base::DictionaryValue strings;
315 strings.SetString("connecting_header",
316 l10n_util::GetStringFUTF16(IDS_MOBILE_CONNECTING_HEADER,
317 base::UTF8ToUTF16(name)));
318 strings.SetString("error_header",
319 l10n_util::GetStringUTF16(IDS_MOBILE_ERROR_HEADER));
320 strings.SetString("activating_header",
321 l10n_util::GetStringUTF16(IDS_MOBILE_ACTIVATING_HEADER));
322 strings.SetString("completed_header",
323 l10n_util::GetStringUTF16(IDS_MOBILE_COMPLETED_HEADER));
324 strings.SetString("please_wait",
325 l10n_util::GetStringUTF16(IDS_MOBILE_PLEASE_WAIT));
326 strings.SetString("completed_text",
327 l10n_util::GetStringUTF16(IDS_MOBILE_COMPLETED_TEXT));
328 strings.SetString("portal_unreachable_header",
329 l10n_util::GetStringUTF16(IDS_MOBILE_NO_CONNECTION_HEADER));
330 strings.SetString("invalid_device_info_header",
331 l10n_util::GetStringUTF16(IDS_MOBILE_INVALID_DEVICE_INFO_HEADER));
332 strings.SetString("title", l10n_util::GetStringUTF16(IDS_MOBILE_SETUP_TITLE));
333 strings.SetString("close_button",
334 l10n_util::GetStringUTF16(IDS_CLOSE));
335 strings.SetString("cancel_button",
336 l10n_util::GetStringUTF16(IDS_CANCEL));
337 strings.SetString("ok_button",
338 l10n_util::GetStringUTF16(IDS_OK));
339 webui::SetFontAndTextDirection(&strings);
341 webui::UseVersion2 version_2;
343 // The webui differs based on whether the network is activated or not. If the
344 // network is activated, the webui goes straight to portal. Otherwise the
345 // webui is used for activation flow.
346 std::string full_html;
347 if (activation_state == shill::kActivationStateActivated) {
348 static const base::StringPiece html_for_activated(
349 ResourceBundle::GetSharedInstance().GetRawDataResource(
350 IDR_MOBILE_SETUP_PORTAL_PAGE_HTML));
351 full_html = webui::GetI18nTemplateHtml(html_for_activated, &strings);
353 static const base::StringPiece html_for_non_activated(
354 ResourceBundle::GetSharedInstance().GetRawDataResource(
355 IDR_MOBILE_SETUP_PAGE_HTML));
356 full_html = webui::GetI18nTemplateHtml(html_for_non_activated, &strings);
359 callback.Run(base::RefCountedString::TakeString(&full_html));
362 void MobileSetupUIHTMLSource::GetPropertiesFailure(
363 const content::URLDataSource::GotDataCallback& callback,
364 const std::string& service_path,
365 const std::string& error_name,
366 scoped_ptr<base::DictionaryValue> error_data) {
367 DataRequestFailed(service_path, callback);
370 ////////////////////////////////////////////////////////////////////////////////
372 // MobileSetupHandler
374 ////////////////////////////////////////////////////////////////////////////////
375 MobileSetupHandler::MobileSetupHandler()
376 : type_(TYPE_UNDETERMINED),
377 lte_portal_reachable_(true),
378 weak_ptr_factory_(this) {
381 MobileSetupHandler::~MobileSetupHandler() {
382 if (type_ == TYPE_ACTIVATION) {
383 MobileActivator::GetInstance()->RemoveObserver(this);
384 MobileActivator::GetInstance()->TerminateActivation();
385 } else if (type_ == TYPE_PORTAL_LTE) {
386 NetworkHandler::Get()->network_state_handler()->RemoveObserver(this,
391 void MobileSetupHandler::OnActivationStateChanged(
392 const NetworkState* network,
393 MobileActivator::PlanActivationState state,
394 const std::string& error_description) {
395 DCHECK_EQ(TYPE_ACTIVATION, type_);
400 base::DictionaryValue device_dict;
401 SetActivationStateAndError(state, error_description, &device_dict);
402 web_ui()->CallJavascriptFunction(kJsDeviceStatusChangedCallback,
407 NetworkHandler::Get()->network_configuration_handler()->GetProperties(
409 base::Bind(&MobileSetupHandler::GetPropertiesAndCallStatusChanged,
410 weak_ptr_factory_.GetWeakPtr(),
413 base::Bind(&MobileSetupHandler::GetPropertiesFailure,
414 weak_ptr_factory_.GetWeakPtr(),
416 kJsDeviceStatusChangedCallback));
419 void MobileSetupHandler::GetPropertiesAndCallStatusChanged(
420 MobileActivator::PlanActivationState state,
421 const std::string& error_description,
422 const std::string& service_path,
423 const base::DictionaryValue& properties) {
424 base::DictionaryValue device_dict;
425 GetDeviceInfo(properties, &device_dict);
426 SetActivationStateAndError(state, error_description, &device_dict);
427 web_ui()->CallJavascriptFunction(kJsDeviceStatusChangedCallback, device_dict);
430 void MobileSetupHandler::RegisterMessages() {
431 web_ui()->RegisterMessageCallback(kJsApiStartActivation,
432 base::Bind(&MobileSetupHandler::HandleStartActivation,
433 base::Unretained(this)));
434 web_ui()->RegisterMessageCallback(kJsApiSetTransactionStatus,
435 base::Bind(&MobileSetupHandler::HandleSetTransactionStatus,
436 base::Unretained(this)));
437 web_ui()->RegisterMessageCallback(kJsApiPaymentPortalLoad,
438 base::Bind(&MobileSetupHandler::HandlePaymentPortalLoad,
439 base::Unretained(this)));
440 web_ui()->RegisterMessageCallback(kJsGetDeviceInfo,
441 base::Bind(&MobileSetupHandler::HandleGetDeviceInfo,
442 base::Unretained(this)));
445 void MobileSetupHandler::HandleStartActivation(const base::ListValue* args) {
446 DCHECK_EQ(TYPE_UNDETERMINED, type_);
451 std::string path = web_ui()->GetWebContents()->GetURL().path();
455 LOG(WARNING) << "Starting activation for service " << path;
457 type_ = TYPE_ACTIVATION;
458 MobileActivator::GetInstance()->AddObserver(this);
459 MobileActivator::GetInstance()->InitiateActivation(path.substr(1));
462 void MobileSetupHandler::HandleSetTransactionStatus(
463 const base::ListValue* args) {
464 DCHECK_EQ(TYPE_ACTIVATION, type_);
468 const size_t kSetTransactionStatusParamCount = 1;
469 if (args->GetSize() != kSetTransactionStatusParamCount)
471 // Get change callback function name.
473 if (!args->GetString(0, &status))
476 MobileActivator::GetInstance()->OnSetTransactionStatus(
477 LowerCaseEqualsASCII(status, kJsApiResultOK));
480 void MobileSetupHandler::HandlePaymentPortalLoad(const base::ListValue* args) {
481 // Only activation flow webui is interested in these events.
482 if (type_ != TYPE_ACTIVATION || !web_ui())
485 const size_t kPaymentPortalLoadParamCount = 1;
486 if (args->GetSize() != kPaymentPortalLoadParamCount)
488 // Get change callback function name.
490 if (!args->GetString(0, &result))
493 MobileActivator::GetInstance()->OnPortalLoaded(
494 LowerCaseEqualsASCII(result, kJsApiResultOK));
497 void MobileSetupHandler::HandleGetDeviceInfo(const base::ListValue* args) {
498 DCHECK_NE(TYPE_ACTIVATION, type_);
502 std::string path = web_ui()->GetWebContents()->GetURL().path();
506 chromeos::NetworkStateHandler* nsh =
507 NetworkHandler::Get()->network_state_handler();
508 // TODO: Figure out why the path has an extra '/' in the front. (e.g. It is
509 // '//service/5' instead of '/service/5'.
510 const NetworkState* network = nsh->GetNetworkState(path.substr(1));
512 web_ui()->GetWebContents()->Close();
516 // If this is the initial call, update the network status and start observing
517 // network changes, but only for LTE networks. The other networks should
518 // ignore network status.
519 if (type_ == TYPE_UNDETERMINED) {
520 if (network->network_technology() == shill::kNetworkTechnologyLte ||
521 network->network_technology() == shill::kNetworkTechnologyLteAdvanced) {
522 type_ = TYPE_PORTAL_LTE;
523 nsh->AddObserver(this, FROM_HERE);
524 // Update the network status and notify the webui. This is the initial
525 // network state so the webui should be notified no matter what.
526 UpdatePortalReachability(network,
527 true /* force notification */);
530 // For non-LTE networks network state is ignored, so report the portal is
531 // reachable, so it gets shown.
532 web_ui()->CallJavascriptFunction(kJsConnectivityChangedCallback,
533 base::FundamentalValue(true));
537 NetworkHandler::Get()->network_configuration_handler()->GetProperties(
539 base::Bind(&MobileSetupHandler::GetPropertiesAndCallGetDeviceInfo,
540 weak_ptr_factory_.GetWeakPtr()),
541 base::Bind(&MobileSetupHandler::GetPropertiesFailure,
542 weak_ptr_factory_.GetWeakPtr(),
544 kJsGetDeviceInfoCallback));
547 void MobileSetupHandler::GetPropertiesAndCallGetDeviceInfo(
548 const std::string& service_path,
549 const base::DictionaryValue& properties) {
550 base::DictionaryValue device_info;
551 GetDeviceInfo(properties, &device_info);
552 web_ui()->CallJavascriptFunction(kJsGetDeviceInfoCallback, device_info);
555 void MobileSetupHandler::GetPropertiesFailure(
556 const std::string& service_path,
557 const std::string& callback_name,
558 const std::string& error_name,
559 scoped_ptr<base::DictionaryValue> error_data) {
560 NET_LOG_ERROR("MobileActivator GetProperties Failed: " + error_name,
562 // Invoke |callback_name| with an empty dictionary.
563 base::DictionaryValue device_dict;
564 web_ui()->CallJavascriptFunction(callback_name, device_dict);
567 void MobileSetupHandler::DefaultNetworkChanged(
568 const NetworkState* default_network) {
572 std::string path = web_ui()->GetWebContents()->GetURL().path().substr(1);
576 const NetworkState* network =
577 NetworkHandler::Get()->network_state_handler()->GetNetworkState(path);
579 LOG(ERROR) << "Service path lost";
580 web_ui()->GetWebContents()->Close();
584 UpdatePortalReachability(network, false /* do not force notification */);
587 void MobileSetupHandler::NetworkConnectionStateChanged(
588 const NetworkState* network) {
592 std::string path = web_ui()->GetWebContents()->GetURL().path().substr(1);
593 if (path.empty() || path != network->path())
596 UpdatePortalReachability(network, false /* do not force notification */);
599 void MobileSetupHandler::UpdatePortalReachability(
600 const NetworkState* network,
601 bool force_notification) {
604 DCHECK_EQ(type_, TYPE_PORTAL_LTE);
606 chromeos::NetworkStateHandler* nsh =
607 NetworkHandler::Get()->network_state_handler();
608 bool portal_reachable =
609 (network->IsConnectedState() ||
610 (nsh->DefaultNetwork() &&
611 nsh->DefaultNetwork()->connection_state() == shill::kStateOnline));
613 if (force_notification || portal_reachable != lte_portal_reachable_) {
614 web_ui()->CallJavascriptFunction(kJsConnectivityChangedCallback,
615 base::FundamentalValue(portal_reachable));
618 lte_portal_reachable_ = portal_reachable;
621 ////////////////////////////////////////////////////////////////////////////////
625 ////////////////////////////////////////////////////////////////////////////////
627 MobileSetupUI::MobileSetupUI(content::WebUI* web_ui)
628 : WebUIController(web_ui) {
629 web_ui->AddMessageHandler(new MobileSetupHandler());
630 MobileSetupUIHTMLSource* html_source = new MobileSetupUIHTMLSource();
632 // Set up the chrome://mobilesetup/ source.
633 Profile* profile = Profile::FromWebUI(web_ui);
634 content::URLDataSource::Add(profile, html_source);
636 content::WebContentsObserver::Observe(web_ui->GetWebContents());
639 void MobileSetupUI::DidCommitProvisionalLoadForFrame(
640 content::RenderFrameHost* render_frame_host,
642 ui::PageTransition transition_type) {
643 if (render_frame_host->GetFrameName() != "paymentForm")
646 web_ui()->CallJavascriptFunction(
647 kJsPortalFrameLoadCompletedCallback);
650 void MobileSetupUI::DidFailProvisionalLoad(
651 content::RenderFrameHost* render_frame_host,
652 const GURL& validated_url,
654 const base::string16& error_description) {
655 if (render_frame_host->GetFrameName() != "paymentForm")
658 base::FundamentalValue result_value(-error_code);
659 web_ui()->CallJavascriptFunction(kJsPortalFrameLoadFailedCallback,