1 // Copyright 2018 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 "ash/system/network/auto_connect_notifier.h"
6
7 #include "ash/public/cpp/network_icon_image_source.h"
8 #include "ash/public/cpp/notification_utils.h"
9 #include "ash/strings/grit/ash_strings.h"
10 #include "base/callback_helpers.h"
11 #include "base/logging.h"
12 #include "base/strings/string16.h"
13 #include "base/time/time.h"
14 #include "base/timer/timer.h"
15 #include "chromeos/network/network_connection_handler.h"
16 #include "chromeos/network/network_event_log.h"
17 #include "chromeos/network/network_state.h"
18 #include "chromeos/network/network_state_handler.h"
19 #include "chromeos/network/network_type_pattern.h"
20 #include "ui/base/l10n/l10n_util.h"
21 #include "ui/gfx/geometry/size.h"
22 #include "ui/gfx/image/canvas_image_source.h"
23 #include "ui/gfx/image/image.h"
24 #include "ui/message_center/message_center.h"
25 #include "ui/message_center/public/cpp/message_center_constants.h"
26 #include "ui/message_center/public/cpp/notification.h"
27 #include "ui/message_center/public/cpp/notification_types.h"
28 #include "ui/message_center/public/cpp/notifier_id.h"
29 #include "url/gurl.h"
30
31 using chromeos::NetworkHandler;
32
33 namespace ash {
34
35 namespace {
36
37 // Timeout used for connecting to a managed network. When an auto-connection is
38 // initiated, we expect the connection to occur within this amount of time. If
39 // a timeout occurs, we assume that no auto-connection occurred and do not show
40 // a notification.
41 constexpr const base::TimeDelta kNetworkConnectionTimeout =
42 base::TimeDelta::FromSeconds(3);
43
44 const char kNotifierAutoConnect[] = "ash.auto-connect";
45
46 } // namespace
47
48 // static
49 const char AutoConnectNotifier::kAutoConnectNotificationId[] =
50 "cros_auto_connect_notifier_ids.connected_to_network";
51
AutoConnectNotifier()52 AutoConnectNotifier::AutoConnectNotifier()
53 : timer_(std::make_unique<base::OneShotTimer>()) {
54 // NetworkHandler may not be initialized in tests.
55 if (NetworkHandler::IsInitialized()) {
56 auto* network_handler = NetworkHandler::Get();
57 network_handler->network_connection_handler()->AddObserver(this);
58 network_handler->network_state_handler()->AddObserver(this, FROM_HERE);
59 // AutoConnectHandler may not be initialized in tests with NetworkHandler.
60 if (network_handler->auto_connect_handler())
61 network_handler->auto_connect_handler()->AddObserver(this);
62 }
63 }
64
~AutoConnectNotifier()65 AutoConnectNotifier::~AutoConnectNotifier() {
66 // NetworkHandler may not be initialized in tests.
67 if (NetworkHandler::IsInitialized()) {
68 auto* network_handler = NetworkHandler::Get();
69 // AutoConnectHandler may not be initialized in tests with NetworkHandler.
70 if (network_handler->auto_connect_handler())
71 network_handler->auto_connect_handler()->RemoveObserver(this);
72 network_handler->network_state_handler()->RemoveObserver(this, FROM_HERE);
73 network_handler->network_connection_handler()->RemoveObserver(this);
74 }
75 }
76
ConnectToNetworkRequested(const std::string &)77 void AutoConnectNotifier::ConnectToNetworkRequested(
78 const std::string& /* service_path */) {
79 has_user_explicitly_requested_connection_ = true;
80 }
81
NetworkConnectionStateChanged(const chromeos::NetworkState * network)82 void AutoConnectNotifier::NetworkConnectionStateChanged(
83 const chromeos::NetworkState* network) {
84 // Ignore non WiFi networks completely.
85 if (!network->Matches(chromeos::NetworkTypePattern::WiFi()))
86 return;
87
88 // The notification is only shown when a connection has succeeded; if
89 // |network| is not connected, there is nothing to do.
90 if (!network->IsConnectedState()) {
91 // Clear the tracked network if it is no longer connected or connecting.
92 if (!network->IsConnectingState() &&
93 network->guid() == connected_network_guid_) {
94 connected_network_guid_.clear();
95 }
96 return;
97 }
98
99 // No notification should be shown unless an auto-connection is underway.
100 if (!timer_->IsRunning()) {
101 // Track the currently connected network.
102 connected_network_guid_ = network->guid();
103 return;
104 }
105
106 // Ignore NetworkConnectionStateChanged for a previously connected network.
107 if (network->guid() == connected_network_guid_)
108 return;
109
110 // An auto-connected network has connected successfully. Display a
111 // notification alerting the user that this has occurred.
112 DisplayNotification(network);
113 has_user_explicitly_requested_connection_ = false;
114 }
115
OnAutoConnectedInitiated(int auto_connect_reasons)116 void AutoConnectNotifier::OnAutoConnectedInitiated(int auto_connect_reasons) {
117 // If the user has not explicitly requested a connection to another network,
118 // the notification does not need to be shown.
119 if (!has_user_explicitly_requested_connection_)
120 return;
121
122 // The notification should only be shown if a network is joined due to a
123 // policy or certificate. Other reasons (e.g., joining a network due to login)
124 // do not require that a notification be shown.
125 const int kManagedNetworkReasonsBitmask =
126 chromeos::AutoConnectHandler::AUTO_CONNECT_REASON_POLICY_APPLIED |
127 chromeos::AutoConnectHandler::AUTO_CONNECT_REASON_CERTIFICATE_RESOLVED;
128 if (!(auto_connect_reasons & kManagedNetworkReasonsBitmask))
129 return;
130
131 // If a potential connection is already underway, reset the timeout and
132 // continue waiting.
133 if (timer_->IsRunning()) {
134 timer_->Reset();
135 return;
136 }
137
138 // Auto-connection has been requested, so start a timer. If a network connects
139 // successfully before the timer expires, auto-connection has succeeded, so a
140 // notification should be shown. If no connection occurs before the timer
141 // fires, we assume that auto-connect attempted to search for networks to
142 // join but did not succeed in joining one (in that case, no notification
143 // should be shown).
144 timer_->Start(FROM_HERE, kNetworkConnectionTimeout, base::DoNothing());
145 }
146
DisplayNotification(const chromeos::NetworkState * network)147 void AutoConnectNotifier::DisplayNotification(
148 const chromeos::NetworkState* network) {
149 NET_LOG(EVENT) << "Show AutoConnect Notification for: " << NetworkId(network);
150 auto notification = CreateSystemNotification(
151 message_center::NotificationType::NOTIFICATION_TYPE_SIMPLE,
152 kAutoConnectNotificationId,
153 l10n_util::GetStringUTF16(IDS_ASH_NETWORK_AUTOCONNECT_NOTIFICATION_TITLE),
154 l10n_util::GetStringUTF16(
155 IDS_ASH_NETWORK_AUTOCONNECT_NOTIFICATION_MESSAGE),
156 base::string16() /* display_source */, GURL() /* origin_url */,
157 message_center::NotifierId(message_center::NotifierType::SYSTEM_COMPONENT,
158 kNotifierAutoConnect),
159 {} /* optional_fields */,
160 base::MakeRefCounted<message_center::NotificationDelegate>(),
161 gfx::VectorIcon() /* small_image */,
162 message_center::SystemNotificationWarningLevel::NORMAL);
163
164 notification->set_small_image(gfx::Image(network_icon::GetImageForWifiNetwork(
165 notification->accent_color().value_or(
166 ash::kSystemNotificationColorNormal),
167 gfx::Size(message_center::kSmallImageSizeMD,
168 message_center::kSmallImageSizeMD))));
169
170 message_center::MessageCenter* message_center =
171 message_center::MessageCenter::Get();
172 if (message_center->FindVisibleNotificationById(kAutoConnectNotificationId))
173 message_center->RemoveNotification(kAutoConnectNotificationId, false);
174 message_center->AddNotification(std::move(notification));
175 }
176
177 } // namespace ash
178