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()
240 ignore_no_network_for_testing_(false),
241 weak_factory_(this) {}
243 NetworkPortalNotificationController::~NetworkPortalNotificationController() {
246 void NetworkPortalNotificationController::DefaultNetworkChanged(
247 const NetworkState
* network
) {
250 Profile
* profile
= GetProfileForPrimaryUser();
251 extensions::NetworkingConfigService
* networking_config_service
=
252 GetNetworkingConfigService(profile
);
253 if (!networking_config_service
)
255 networking_config_service
->ResetAuthenticationResult();
258 void NetworkPortalNotificationController::OnPortalDetectionCompleted(
259 const NetworkState
* network
,
260 const NetworkPortalDetector::CaptivePortalState
& state
) {
261 if (!IsPortalNotificationEnabled())
265 state
.status
!= NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PORTAL
) {
266 last_network_path_
.clear();
268 // In browser tests we initiate fake network portal detection, but network
269 // state usually stays connected. This way, after dialog is shown, it is
270 // immediately closed. The testing check below prevents dialog from closing.
272 (!ignore_no_network_for_testing_
||
273 state
.status
== NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE
)) {
281 // Don't do anything if we're currently activating the device.
282 if (MobileActivator::GetInstance()->RunningActivation())
285 // Don't do anything if notification for |network| already was
287 if (network
->path() == last_network_path_
)
289 last_network_path_
= network
->path();
291 if (ash::Shell::HasInstance()) {
292 ash::Shell::GetInstance()
293 ->system_tray_notifier()
294 ->NotifyOnCaptivePortalDetected(network
->path());
297 message_center::MessageCenter::Get()->AddNotification(
298 GetNotification(network
, state
));
301 void NetworkPortalNotificationController::ShowDialog() {
305 Profile
* signin_profile
= ProfileHelper::GetSigninProfile();
306 dialog_
= new NetworkPortalWebDialog(weak_factory_
.GetWeakPtr());
307 dialog_
->SetWidget(views::Widget::GetWidgetForNativeWindow(
308 chrome::ShowWebDialog(nullptr, signin_profile
, dialog_
)));
311 void NetworkPortalNotificationController::OnDialogDestroyed(
312 const NetworkPortalWebDialog
* dialog
) {
313 if (dialog
== dialog_
) {
315 ProfileHelper::Get()->ClearSigninProfile(base::Closure());
319 scoped_ptr
<message_center::Notification
>
320 NetworkPortalNotificationController::CreateDefaultCaptivePortalNotification(
321 const NetworkState
* network
) {
322 message_center::RichNotificationData data
;
323 scoped_refptr
<NetworkPortalNotificationControllerDelegate
> delegate(
324 new NetworkPortalNotificationControllerDelegate(
325 std::string(), network
->guid(), weak_factory_
.GetWeakPtr()));
326 gfx::Image
& icon
= GetImageForNotification();
327 message_center::NotifierId
notifier_id(
328 message_center::NotifierId::SYSTEM_COMPONENT
,
329 ash::system_notifier::kNotifierNetworkPortalDetector
);
330 base::string16 notificationText
;
331 bool is_wifi
= NetworkTypePattern::WiFi().MatchesType(network
->type());
332 scoped_ptr
<Notification
> notification(new Notification(
333 message_center::NOTIFICATION_TYPE_SIMPLE
, kNotificationId
,
334 l10n_util::GetStringUTF16(
335 is_wifi
? IDS_PORTAL_DETECTION_NOTIFICATION_TITLE_WIFI
336 : IDS_PORTAL_DETECTION_NOTIFICATION_TITLE_WIRED
),
337 l10n_util::GetStringFUTF16(
338 is_wifi
? IDS_PORTAL_DETECTION_NOTIFICATION_MESSAGE_WIFI
339 : IDS_PORTAL_DETECTION_NOTIFICATION_MESSAGE_WIRED
,
340 base::UTF8ToUTF16(network
->name())),
341 icon
, base::string16(), GURL(), notifier_id
, data
, delegate
.get()));
342 notification
->SetSystemPriority();
343 return notification
.Pass();
346 scoped_ptr
<message_center::Notification
> NetworkPortalNotificationController::
347 CreateCaptivePortalNotificationForExtension(
348 const NetworkState
* network
,
349 extensions::NetworkingConfigService
* networking_config_service
,
350 const extensions::Extension
* extension
) {
351 message_center::RichNotificationData data
;
352 scoped_refptr
<NetworkPortalNotificationControllerDelegate
> delegate(
353 new NetworkPortalNotificationControllerDelegate(
354 extension
->id(), network
->guid(), weak_factory_
.GetWeakPtr()));
355 gfx::Image
& icon
= GetImageForNotification();
356 message_center::NotifierId
notifier_id(
357 message_center::NotifierId::SYSTEM_COMPONENT
,
358 ash::system_notifier::kNotifierNetworkPortalDetector
);
360 extensions::NetworkingConfigService::AuthenticationResult
361 authentication_result
=
362 networking_config_service
->GetAuthenticationResult();
363 base::string16 notificationText
;
364 if (authentication_result
.authentication_state
==
365 extensions::NetworkingConfigService::NOTRY
||
366 network
->guid() != authentication_result
.guid
) {
367 notificationText
= l10n_util::GetStringFUTF16(
368 IDS_PORTAL_DETECTION_NOTIFICATION_MESSAGE_ASK_WIFI
,
369 base::UTF8ToUTF16(network
->name()));
370 data
.buttons
.push_back(
371 message_center::ButtonInfo(l10n_util::GetStringFUTF16(
372 IDS_PORTAL_DETECTION_NOTIFICATION_BUTTON_EXTENSION
,
373 base::UTF8ToUTF16(extension
->name()))));
374 data
.buttons
.push_back(message_center::ButtonInfo(l10n_util::GetStringUTF16(
375 IDS_PORTAL_DETECTION_NOTIFICATION_BUTTON_PORTAL
)));
377 notificationText
= l10n_util::GetStringFUTF16(
378 IDS_PORTAL_DETECTION_NOTIFICATION_MESSAGE_FAILED_WIFI
,
379 base::UTF8ToUTF16(network
->name()));
380 data
.buttons
.push_back(
381 message_center::ButtonInfo(l10n_util::GetStringFUTF16(
382 IDS_PORTAL_DETECTION_NOTIFICATION_BUTTON_EXTENSION_RETRY
,
383 base::UTF8ToUTF16(extension
->name()))));
384 data
.buttons
.push_back(message_center::ButtonInfo(l10n_util::GetStringUTF16(
385 IDS_PORTAL_DETECTION_NOTIFICATION_BUTTON_PORTAL
)));
387 scoped_ptr
<Notification
> notification(new Notification(
388 message_center::NOTIFICATION_TYPE_SIMPLE
, kNotificationId
,
389 l10n_util::GetStringUTF16(IDS_PORTAL_DETECTION_NOTIFICATION_TITLE_WIFI
),
390 notificationText
, icon
, base::string16() /* display_source */, GURL(),
391 notifier_id
, data
, delegate
.get()));
392 notification
->SetSystemPriority();
393 return notification
.Pass();
396 scoped_ptr
<Notification
> NetworkPortalNotificationController::GetNotification(
397 const NetworkState
* network
,
398 const NetworkPortalDetector::CaptivePortalState
& state
) {
399 base::string16 notificationText
;
400 Profile
* profile
= GetProfileForPrimaryUser();
401 extensions::NetworkingConfigService
* networking_config_service
=
402 GetNetworkingConfigService(profile
);
403 std::string extension_id
;
404 const extensions::Extension
* extension
= nullptr;
405 if (networking_config_service
) {
406 extension
= LookupExtensionForRawSsid(networking_config_service
,
407 network
->raw_ssid());
410 return CreateCaptivePortalNotificationForExtension(
411 network
, networking_config_service
, extension
);
413 return CreateDefaultCaptivePortalNotification(network
);
417 void NetworkPortalNotificationController::OnExtensionFinishedAuthentication() {
418 if (!retry_detection_callback_
.is_null())
419 retry_detection_callback_
.Run();
422 void NetworkPortalNotificationController::SetIgnoreNoNetworkForTesting() {
423 ignore_no_network_for_testing_
= true;
426 void NetworkPortalNotificationController::CloseDialog() {
431 const NetworkPortalWebDialog
*
432 NetworkPortalNotificationController::GetDialogForTesting() const {
436 } // namespace chromeos