1 // Copyright 2014 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/chromeos/net/network_portal_notification_controller.h"
10 #include "ash/system/system_notifier.h"
11 #include "ash/system/tray/system_tray_notifier.h"
12 #include "base/basictypes.h"
13 #include "base/command_line.h"
14 #include "base/compiler_specific.h"
15 #include "base/memory/ref_counted.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/message_loop/message_loop.h"
18 #include "base/metrics/histogram.h"
19 #include "base/prefs/pref_service.h"
20 #include "base/strings/string16.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "chrome/browser/browser_process.h"
24 #include "chrome/browser/browser_process_platform_part.h"
25 #include "chrome/browser/chromeos/mobile/mobile_activator.h"
26 #include "chrome/browser/chromeos/net/network_portal_web_dialog.h"
27 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
28 #include "chrome/browser/chromeos/policy/consumer_management_service.h"
29 #include "chrome/browser/chromeos/profiles/profile_helper.h"
30 #include "chrome/browser/profiles/profile_manager.h"
31 #include "chrome/browser/ui/browser_dialogs.h"
32 #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
33 #include "chrome/browser/ui/singleton_tabs.h"
34 #include "chrome/common/pref_names.h"
35 #include "chrome/grit/generated_resources.h"
36 #include "chrome/grit/theme_resources.h"
37 #include "chromeos/chromeos_switches.h"
38 #include "chromeos/network/network_state.h"
39 #include "chromeos/network/network_type_pattern.h"
40 #include "components/captive_portal/captive_portal_detector.h"
41 #include "components/user_manager/user_manager.h"
42 #include "extensions/browser/api/networking_config/networking_config_service.h"
43 #include "extensions/browser/api/networking_config/networking_config_service_factory.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/message_center/message_center.h"
48 #include "ui/message_center/notification.h"
49 #include "ui/message_center/notification_types.h"
50 #include "ui/message_center/notifier_settings.h"
51 #include "ui/views/widget/widget.h"
53 using message_center::Notification
;
59 bool IsPortalNotificationEnabled() {
60 return !base::CommandLine::ForCurrentProcess()->HasSwitch(
61 switches::kDisableNetworkPortalNotification
);
64 void CloseNotification() {
65 message_center::MessageCenter::Get()->RemoveNotification(
66 NetworkPortalNotificationController::kNotificationId
, false);
69 Profile
* GetProfileForPrimaryUser() {
70 const user_manager::User
* primary_user
=
71 user_manager::UserManager::Get()->GetPrimaryUser();
74 return ProfileHelper::Get()->GetProfileByUser(primary_user
);
77 // Note that NetworkingConfigService may change after login as the profile
78 // switches from the login to the user's profile.
79 extensions::NetworkingConfigService
* GetNetworkingConfigService(
83 return extensions::NetworkingConfigServiceFactory::GetForBrowserContext(
87 const extensions::Extension
* LookupExtensionForRawSsid(
88 extensions::NetworkingConfigService
* networking_config_service
,
89 const std::vector
<uint8_t>& raw_ssid
) {
90 DCHECK(networking_config_service
);
91 Profile
* profile
= GetProfileForPrimaryUser();
92 if (!profile
|| !networking_config_service
)
94 std::string extension_id
;
95 std::string hex_ssid
=
96 base::HexEncode(vector_as_array(&raw_ssid
), raw_ssid
.size());
98 networking_config_service
->LookupExtensionIdForHexSsid(hex_ssid
);
99 if (extension_id
.empty())
101 return extensions::ExtensionRegistry::Get(profile
)
102 ->GetExtensionById(extension_id
, extensions::ExtensionRegistry::ENABLED
);
105 class NetworkPortalNotificationControllerDelegate
106 : public message_center::NotificationDelegate
{
108 explicit NetworkPortalNotificationControllerDelegate(
109 const std::string
& extension_id
,
110 const std::string
& guid
,
111 base::WeakPtr
<NetworkPortalNotificationController
> controller
)
112 : extension_id_(extension_id
),
115 controller_(controller
) {}
117 // Overridden from message_center::NotificationDelegate:
118 void Display() override
;
119 void Close(bool by_user
) override
;
120 void Click() override
;
121 void ButtonClick(int button_click
) override
;
124 ~NetworkPortalNotificationControllerDelegate() override
{}
126 // ID of the extension responsible for network configuration of the network
127 // that this notification is generated for. Empty if none.
128 std::string extension_id_
;
130 // GUID of the network this notification is generated for.
135 base::WeakPtr
<NetworkPortalNotificationController
> controller_
;
137 DISALLOW_COPY_AND_ASSIGN(NetworkPortalNotificationControllerDelegate
);
140 void NetworkPortalNotificationControllerDelegate::Display() {
141 UMA_HISTOGRAM_ENUMERATION(
142 NetworkPortalNotificationController::kNotificationMetric
,
143 NetworkPortalNotificationController::NOTIFICATION_METRIC_DISPLAYED
,
144 NetworkPortalNotificationController::NOTIFICATION_METRIC_COUNT
);
147 void NetworkPortalNotificationControllerDelegate::Close(bool by_user
) {
150 NetworkPortalNotificationController::UserActionMetric metric
=
151 by_user
? NetworkPortalNotificationController::USER_ACTION_METRIC_CLOSED
152 : NetworkPortalNotificationController::USER_ACTION_METRIC_IGNORED
;
153 UMA_HISTOGRAM_ENUMERATION(
154 NetworkPortalNotificationController::kUserActionMetric
, metric
,
155 NetworkPortalNotificationController::USER_ACTION_METRIC_COUNT
);
158 void NetworkPortalNotificationControllerDelegate::Click() {
160 UMA_HISTOGRAM_ENUMERATION(
161 NetworkPortalNotificationController::kUserActionMetric
,
162 NetworkPortalNotificationController::USER_ACTION_METRIC_CLICKED
,
163 NetworkPortalNotificationController::USER_ACTION_METRIC_COUNT
);
165 Profile
* profile
= ProfileManager::GetActiveUserProfile();
167 const bool disable_bypass_proxy_switch_present
=
168 base::CommandLine::ForCurrentProcess()->HasSwitch(
169 chromeos::switches::kDisableCaptivePortalBypassProxy
);
170 const bool use_incognito_profile
=
171 disable_bypass_proxy_switch_present
174 profile
->GetPrefs()->GetBoolean(
175 prefs::kCaptivePortalAuthenticationIgnoresProxy
));
177 if (use_incognito_profile
) {
179 controller_
->ShowDialog();
183 chrome::ScopedTabbedBrowserDisplayer
displayer(
184 profile
, chrome::HOST_DESKTOP_TYPE_ASH
);
185 GURL
url(captive_portal::CaptivePortalDetector::kDefaultURL
);
186 chrome::ShowSingletonTab(displayer
.browser(), url
);
191 void NetworkPortalNotificationControllerDelegate::ButtonClick(
194 NetworkPortalNotificationController::kUseExtensionButtonIndex
) {
195 Profile
* profile
= GetProfileForPrimaryUser();
196 // The user decided to notify the extension to authenticate to the captive
197 // portal. Notify the NetworkingConfigService, which in turn will notify the
198 // extension. OnExtensionFinsihedAuthentication will be called back if the
199 // authentication succeeded.
200 extensions::NetworkingConfigServiceFactory::GetForBrowserContext(profile
)
201 ->DispatchPortalDetectedEvent(
202 extension_id_
, guid_
,
203 base::Bind(&NetworkPortalNotificationController::
204 OnExtensionFinishedAuthentication
,
206 } else if (button_index
==
207 NetworkPortalNotificationController::kOpenPortalButtonIndex
) {
212 gfx::Image
& GetImageForNotification() {
213 ui::ResourceBundle
& bundle
= ui::ResourceBundle::GetSharedInstance();
214 gfx::Image
& icon
= bundle
.GetImageNamed(IDR_PORTAL_DETECTION_ALERT
);
221 const int NetworkPortalNotificationController::kUseExtensionButtonIndex
= 0;
224 const int NetworkPortalNotificationController::kOpenPortalButtonIndex
= 1;
227 const char NetworkPortalNotificationController::kNotificationId
[] =
228 "chrome://net/network_portal_detector";
231 const char NetworkPortalNotificationController::kNotificationMetric
[] =
232 "CaptivePortal.Notification.Status";
235 const char NetworkPortalNotificationController::kUserActionMetric
[] =
236 "CaptivePortal.Notification.UserAction";
238 NetworkPortalNotificationController::NetworkPortalNotificationController()
239 : dialog_(nullptr), ignore_no_network_for_testing_(false) {
242 NetworkPortalNotificationController::~NetworkPortalNotificationController() {
245 void NetworkPortalNotificationController::DefaultNetworkChanged(
246 const NetworkState
* network
) {
249 Profile
* profile
= GetProfileForPrimaryUser();
250 extensions::NetworkingConfigService
* networking_config_service
=
251 GetNetworkingConfigService(profile
);
252 if (!networking_config_service
)
254 networking_config_service
->ResetAuthenticationResult();
257 void NetworkPortalNotificationController::OnPortalDetectionCompleted(
258 const NetworkState
* network
,
259 const NetworkPortalDetector::CaptivePortalState
& state
) {
260 if (!IsPortalNotificationEnabled())
264 state
.status
!= NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PORTAL
) {
265 last_network_path_
.clear();
267 // In browser tests we initiate fake network portal detection, but network
268 // state usually stays connected. This way, after dialog is shown, it is
269 // immediately closed. The testing check below prevents dialog from closing.
271 (!ignore_no_network_for_testing_
||
272 state
.status
== NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE
)) {
280 // Don't do anything if we're currently activating the device.
281 if (MobileActivator::GetInstance()->RunningActivation())
284 // Don't do anything if notification for |network| already was
286 if (network
->path() == last_network_path_
)
288 last_network_path_
= network
->path();
290 if (ash::Shell::HasInstance()) {
291 ash::Shell::GetInstance()
292 ->system_tray_notifier()
293 ->NotifyOnCaptivePortalDetected(network
->path());
296 message_center::MessageCenter::Get()->AddNotification(
297 GetNotification(network
, state
));
300 void NetworkPortalNotificationController::ShowDialog() {
304 Profile
* signin_profile
= ProfileHelper::GetSigninProfile();
305 dialog_
= new NetworkPortalWebDialog(AsWeakPtr());
306 dialog_
->SetWidget(views::Widget::GetWidgetForNativeWindow(
307 chrome::ShowWebDialog(nullptr, signin_profile
, dialog_
)));
310 void NetworkPortalNotificationController::OnDialogDestroyed(
311 const NetworkPortalWebDialog
* dialog
) {
312 if (dialog
== dialog_
) {
314 ProfileHelper::Get()->ClearSigninProfile(base::Closure());
318 scoped_ptr
<message_center::Notification
>
319 NetworkPortalNotificationController::CreateDefaultCaptivePortalNotification(
320 const NetworkState
* network
) {
321 message_center::RichNotificationData data
;
322 scoped_refptr
<NetworkPortalNotificationControllerDelegate
> delegate(
323 new NetworkPortalNotificationControllerDelegate(
324 std::string(), network
->guid(), AsWeakPtr()));
325 gfx::Image
& icon
= GetImageForNotification();
326 message_center::NotifierId
notifier_id(
327 message_center::NotifierId::SYSTEM_COMPONENT
,
328 ash::system_notifier::kNotifierNetworkPortalDetector
);
329 base::string16 notificationText
;
330 bool is_wifi
= NetworkTypePattern::WiFi().MatchesType(network
->type());
331 scoped_ptr
<Notification
> notification(new Notification(
332 message_center::NOTIFICATION_TYPE_SIMPLE
, kNotificationId
,
333 l10n_util::GetStringUTF16(
334 is_wifi
? IDS_PORTAL_DETECTION_NOTIFICATION_TITLE_WIFI
335 : IDS_PORTAL_DETECTION_NOTIFICATION_TITLE_WIRED
),
336 l10n_util::GetStringFUTF16(
337 is_wifi
? IDS_PORTAL_DETECTION_NOTIFICATION_MESSAGE_WIFI
338 : IDS_PORTAL_DETECTION_NOTIFICATION_MESSAGE_WIRED
,
339 base::UTF8ToUTF16(network
->name())),
340 icon
, base::string16(), notifier_id
, data
, delegate
.get()));
341 notification
->SetSystemPriority();
342 return notification
.Pass();
345 scoped_ptr
<message_center::Notification
> NetworkPortalNotificationController::
346 CreateCaptivePortalNotificationForExtension(
347 const NetworkState
* network
,
348 extensions::NetworkingConfigService
* networking_config_service
,
349 const extensions::Extension
* extension
) {
350 message_center::RichNotificationData data
;
351 scoped_refptr
<NetworkPortalNotificationControllerDelegate
> delegate(
352 new NetworkPortalNotificationControllerDelegate(
353 extension
->id(), network
->guid(), AsWeakPtr()));
354 gfx::Image
& icon
= GetImageForNotification();
355 message_center::NotifierId
notifier_id(
356 message_center::NotifierId::SYSTEM_COMPONENT
,
357 ash::system_notifier::kNotifierNetworkPortalDetector
);
359 extensions::NetworkingConfigService::AuthenticationResult
360 authentication_result
=
361 networking_config_service
->GetAuthenticationResult();
362 base::string16 notificationText
;
363 if (authentication_result
.authentication_state
==
364 extensions::NetworkingConfigService::NOTRY
||
365 network
->guid() != authentication_result
.guid
) {
366 notificationText
= l10n_util::GetStringFUTF16(
367 IDS_PORTAL_DETECTION_NOTIFICATION_MESSAGE_ASK_WIFI
,
368 base::UTF8ToUTF16(network
->name()));
369 data
.buttons
.push_back(
370 message_center::ButtonInfo(l10n_util::GetStringFUTF16(
371 IDS_PORTAL_DETECTION_NOTIFICATION_BUTTON_EXTENSION
,
372 base::UTF8ToUTF16(extension
->name()))));
373 data
.buttons
.push_back(message_center::ButtonInfo(l10n_util::GetStringUTF16(
374 IDS_PORTAL_DETECTION_NOTIFICATION_BUTTON_PORTAL
)));
376 notificationText
= l10n_util::GetStringFUTF16(
377 IDS_PORTAL_DETECTION_NOTIFICATION_MESSAGE_FAILED_WIFI
,
378 base::UTF8ToUTF16(network
->name()));
379 data
.buttons
.push_back(
380 message_center::ButtonInfo(l10n_util::GetStringFUTF16(
381 IDS_PORTAL_DETECTION_NOTIFICATION_BUTTON_EXTENSION_RETRY
,
382 base::UTF8ToUTF16(extension
->name()))));
383 data
.buttons
.push_back(message_center::ButtonInfo(l10n_util::GetStringUTF16(
384 IDS_PORTAL_DETECTION_NOTIFICATION_BUTTON_PORTAL
)));
386 scoped_ptr
<Notification
> notification(new Notification(
387 message_center::NOTIFICATION_TYPE_SIMPLE
, kNotificationId
,
388 l10n_util::GetStringUTF16(IDS_PORTAL_DETECTION_NOTIFICATION_TITLE_WIFI
),
389 notificationText
, icon
, base::string16() /* display_source */,
390 notifier_id
, data
, delegate
.get()));
391 notification
->SetSystemPriority();
392 return notification
.Pass();
395 scoped_ptr
<Notification
> NetworkPortalNotificationController::GetNotification(
396 const NetworkState
* network
,
397 const NetworkPortalDetector::CaptivePortalState
& state
) {
398 base::string16 notificationText
;
399 Profile
* profile
= GetProfileForPrimaryUser();
400 extensions::NetworkingConfigService
* networking_config_service
=
401 GetNetworkingConfigService(profile
);
402 std::string extension_id
;
403 const extensions::Extension
* extension
= nullptr;
404 if (networking_config_service
) {
405 extension
= LookupExtensionForRawSsid(networking_config_service
,
406 network
->raw_ssid());
409 return CreateCaptivePortalNotificationForExtension(
410 network
, networking_config_service
, extension
);
412 return CreateDefaultCaptivePortalNotification(network
);
416 void NetworkPortalNotificationController::OnExtensionFinishedAuthentication() {
417 if (!retry_detection_callback_
.is_null())
418 retry_detection_callback_
.Run();
421 void NetworkPortalNotificationController::SetIgnoreNoNetworkForTesting() {
422 ignore_no_network_for_testing_
= true;
425 void NetworkPortalNotificationController::CloseDialog() {
430 const NetworkPortalWebDialog
*
431 NetworkPortalNotificationController::GetDialogForTesting() const {
435 } // namespace chromeos