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