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/wake_on_wifi_manager.h"
10 #include "base/containers/scoped_ptr_hash_map.h"
11 #include "base/logging.h"
12 #include "base/macros.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/sys_info.h"
15 #include "base/values.h"
16 #include "chrome/browser/chrome_notification_types.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/services/gcm/gcm_profile_service.h"
19 #include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
20 #include "chromeos/chromeos_switches.h"
21 #include "chromeos/login/login_state.h"
22 #include "chromeos/network/device_state.h"
23 #include "chromeos/network/network_device_handler.h"
24 #include "chromeos/network/network_handler.h"
25 #include "chromeos/network/network_state_handler.h"
26 #include "chromeos/network/network_type_pattern.h"
27 #include "components/gcm_driver/gcm_connection_observer.h"
28 #include "components/gcm_driver/gcm_driver.h"
29 #include "content/public/browser/browser_thread.h"
30 #include "content/public/browser/notification_service.h"
31 #include "content/public/browser/notification_source.h"
32 #include "net/base/ip_endpoint.h"
33 #include "third_party/cros_system_api/dbus/service_constants.h"
39 const char kWakeOnNone
[] = "none";
40 const char kWakeOnPacket
[] = "packet";
41 const char kWakeOnSsid
[] = "ssid";
42 const char kWakeOnPacketAndSsid
[] = "packet_and_ssid";
44 std::string
WakeOnWifiFeatureToString(
45 WakeOnWifiManager::WakeOnWifiFeature feature
) {
47 case WakeOnWifiManager::WAKE_ON_NONE
:
49 case WakeOnWifiManager::WAKE_ON_PACKET
:
51 case WakeOnWifiManager::WAKE_ON_SSID
:
53 case WakeOnWifiManager::WAKE_ON_PACKET_AND_SSID
:
54 return kWakeOnPacketAndSsid
;
55 case WakeOnWifiManager::INVALID
:
57 case WakeOnWifiManager::NOT_SUPPORTED
:
62 NOTREACHED() << "Unknown wake on wifi feature: " << feature
;
66 bool IsWakeOnPacketEnabled(WakeOnWifiManager::WakeOnWifiFeature feature
) {
67 return feature
& WakeOnWifiManager::WAKE_ON_PACKET
;
70 // Weak pointer. This class is owned by ChromeBrowserMainPartsChromeos.
71 WakeOnWifiManager
* g_wake_on_wifi_manager
= NULL
;
75 // Simple class that listens for a connection to the GCM server and passes the
76 // connection information down to shill. Each profile gets its own instance of
78 class WakeOnWifiManager::WakeOnPacketConnectionObserver
79 : public gcm::GCMConnectionObserver
{
81 WakeOnPacketConnectionObserver(Profile
* profile
,
82 bool wifi_properties_received
)
84 ip_endpoint_(net::IPEndPoint()),
85 wifi_properties_received_(wifi_properties_received
) {
86 gcm::GCMProfileServiceFactory::GetForProfile(profile_
)
88 ->AddConnectionObserver(this);
91 ~WakeOnPacketConnectionObserver() override
{
92 if (!(ip_endpoint_
== net::IPEndPoint()))
95 gcm::GCMProfileServiceFactory::GetForProfile(profile_
)
97 ->RemoveConnectionObserver(this);
100 void HandleWifiDevicePropertiesReady() {
101 wifi_properties_received_
= true;
103 // IPEndPoint doesn't implement operator!=
104 if (ip_endpoint_
== net::IPEndPoint())
107 AddWakeOnPacketConnection();
110 // gcm::GCMConnectionObserver overrides.
112 void OnConnected(const net::IPEndPoint
& ip_endpoint
) override
{
113 ip_endpoint_
= ip_endpoint
;
115 if (wifi_properties_received_
)
116 AddWakeOnPacketConnection();
119 void OnDisconnected() override
{
120 if (ip_endpoint_
== net::IPEndPoint()) {
121 VLOG(1) << "Received GCMConnectionObserver::OnDisconnected without a "
122 << "valid IPEndPoint.";
126 if (wifi_properties_received_
)
127 RemoveWakeOnPacketConnection();
129 ip_endpoint_
= net::IPEndPoint();
133 void AddWakeOnPacketConnection() {
134 NetworkHandler::Get()
135 ->network_device_handler()
136 ->AddWifiWakeOnPacketConnection(ip_endpoint_
,
137 base::Bind(&base::DoNothing
),
138 network_handler::ErrorCallback());
141 void RemoveWakeOnPacketConnection() {
142 NetworkHandler::Get()
143 ->network_device_handler()
144 ->RemoveWifiWakeOnPacketConnection(ip_endpoint_
,
145 base::Bind(&base::DoNothing
),
146 network_handler::ErrorCallback());
150 net::IPEndPoint ip_endpoint_
;
151 bool wifi_properties_received_
;
153 DISALLOW_COPY_AND_ASSIGN(WakeOnPacketConnectionObserver
);
157 WakeOnWifiManager
* WakeOnWifiManager::Get() {
158 DCHECK(g_wake_on_wifi_manager
);
159 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
160 return g_wake_on_wifi_manager
;
163 WakeOnWifiManager::WakeOnWifiManager()
164 : current_feature_(WakeOnWifiManager::INVALID
),
165 wifi_properties_received_(false),
166 extension_event_observer_(new ExtensionEventObserver()),
167 weak_ptr_factory_(this) {
168 // This class must be constructed before any users are logged in, i.e., before
169 // any profiles are created or added to the ProfileManager. Additionally,
170 // IsUserLoggedIn always returns true when we are not running on a Chrome OS
171 // device so this check should only run on real devices.
172 CHECK(!base::SysInfo::IsRunningOnChromeOS() ||
173 !LoginState::Get()->IsUserLoggedIn());
174 DCHECK(!g_wake_on_wifi_manager
);
175 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
177 g_wake_on_wifi_manager
= this;
180 chrome::NOTIFICATION_PROFILE_ADDED
,
181 content::NotificationService::AllBrowserContextsAndSources());
183 chrome::NOTIFICATION_PROFILE_DESTROYED
,
184 content::NotificationService::AllBrowserContextsAndSources());
186 NetworkHandler::Get()->network_state_handler()->AddObserver(this, FROM_HERE
);
188 GetWifiDeviceProperties();
191 WakeOnWifiManager::~WakeOnWifiManager() {
192 DCHECK(g_wake_on_wifi_manager
);
193 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
194 if (current_feature_
!= NOT_SUPPORTED
) {
195 NetworkHandler::Get()->network_state_handler()->RemoveObserver(this,
198 g_wake_on_wifi_manager
= NULL
;
201 void WakeOnWifiManager::OnPreferenceChanged(
202 WakeOnWifiManager::WakeOnWifiFeature feature
) {
203 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
204 if (current_feature_
== NOT_SUPPORTED
)
206 if (!switches::WakeOnWifiEnabled())
207 feature
= WAKE_ON_NONE
;
208 if (feature
== current_feature_
)
211 current_feature_
= feature
;
213 if (wifi_properties_received_
)
214 HandleWakeOnWifiFeatureUpdated();
217 bool WakeOnWifiManager::WakeOnWifiSupported() {
218 return current_feature_
!= NOT_SUPPORTED
&& current_feature_
!= INVALID
;
221 void WakeOnWifiManager::Observe(int type
,
222 const content::NotificationSource
& source
,
223 const content::NotificationDetails
& details
) {
225 case chrome::NOTIFICATION_PROFILE_ADDED
: {
226 OnProfileAdded(content::Source
<Profile
>(source
).ptr());
229 case chrome::NOTIFICATION_PROFILE_DESTROYED
: {
230 OnProfileDestroyed(content::Source
<Profile
>(source
).ptr());
238 void WakeOnWifiManager::DeviceListChanged() {
239 if (current_feature_
!= NOT_SUPPORTED
)
240 GetWifiDeviceProperties();
243 void WakeOnWifiManager::DevicePropertiesUpdated(const DeviceState
* device
) {
244 if (device
->Matches(NetworkTypePattern::WiFi()) &&
245 current_feature_
!= NOT_SUPPORTED
) {
246 GetWifiDeviceProperties();
250 void WakeOnWifiManager::HandleWakeOnWifiFeatureUpdated() {
251 const DeviceState
* device
=
252 NetworkHandler::Get()->network_state_handler()->GetDeviceStateByType(
253 NetworkTypePattern::WiFi());
257 std::string
feature_string(WakeOnWifiFeatureToString(current_feature_
));
258 DCHECK(!feature_string
.empty());
260 NetworkHandler::Get()->network_device_handler()->SetDeviceProperty(
262 shill::kWakeOnWiFiFeaturesEnabledProperty
,
263 base::StringValue(feature_string
),
264 base::Bind(&base::DoNothing
),
265 network_handler::ErrorCallback());
267 bool wake_on_packet_enabled
= IsWakeOnPacketEnabled(current_feature_
);
268 for (const auto& kv_pair
: connection_observers_
) {
269 Profile
* profile
= kv_pair
.first
;
270 gcm::GCMProfileServiceFactory::GetForProfile(profile
)
272 ->WakeFromSuspendForHeartbeat(wake_on_packet_enabled
);
275 extension_event_observer_
->SetShouldDelaySuspend(wake_on_packet_enabled
);
278 void WakeOnWifiManager::GetWifiDeviceProperties() {
279 const DeviceState
* device
=
280 NetworkHandler::Get()->network_state_handler()->GetDeviceStateByType(
281 NetworkTypePattern::WiFi());
285 NetworkHandler::Get()->network_device_handler()->GetDeviceProperties(
287 base::Bind(&WakeOnWifiManager::GetDevicePropertiesCallback
,
288 weak_ptr_factory_
.GetWeakPtr()),
289 network_handler::ErrorCallback());
292 void WakeOnWifiManager::GetDevicePropertiesCallback(
293 const std::string
& device_path
,
294 const base::DictionaryValue
& properties
) {
296 if (!properties
.HasKey(shill::kWakeOnWiFiFeaturesEnabledProperty
) ||
297 !properties
.GetString(shill::kWakeOnWiFiFeaturesEnabledProperty
,
299 enabled
== shill::kWakeOnWiFiFeaturesEnabledNotSupported
) {
300 current_feature_
= NOT_SUPPORTED
;
301 connection_observers_
.clear();
302 NetworkHandler::Get()->network_state_handler()->RemoveObserver(this,
304 registrar_
.RemoveAll();
305 extension_event_observer_
.reset();
310 // We always resend the wake on wifi setting unless it hasn't been set yet.
311 // This covers situations where shill restarts or ends up recreating the wifi
312 // device (crbug.com/475199).
313 if (current_feature_
!= INVALID
)
314 HandleWakeOnWifiFeatureUpdated();
316 if (wifi_properties_received_
)
319 wifi_properties_received_
= true;
321 NetworkHandler::Get()
322 ->network_device_handler()
323 ->RemoveAllWifiWakeOnPacketConnections(base::Bind(&base::DoNothing
),
324 network_handler::ErrorCallback());
326 for (const auto& kv_pair
: connection_observers_
) {
327 WakeOnPacketConnectionObserver
* observer
= kv_pair
.second
;
328 observer
->HandleWifiDevicePropertiesReady();
332 void WakeOnWifiManager::OnProfileAdded(Profile
* profile
) {
333 // add will do nothing if |profile| already exists in |connection_observers_|.
334 auto result
= connection_observers_
.add(
336 make_scoped_ptr(new WakeOnWifiManager::WakeOnPacketConnectionObserver(
337 profile
, wifi_properties_received_
)));
340 // This is a profile we haven't seen before.
341 gcm::GCMProfileServiceFactory::GetForProfile(profile
)
343 ->WakeFromSuspendForHeartbeat(
344 IsWakeOnPacketEnabled(current_feature_
));
348 void WakeOnWifiManager::OnProfileDestroyed(Profile
* profile
) {
349 connection_observers_
.erase(profile
);
352 } // namespace chromeos