wake_on_wifi.cc revision ff59a1896ea250b4450b54db697692e0b1949528
1// Copyright 2014 The Chromium OS 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 "shill/wifi/wake_on_wifi.h"
6
7#include <errno.h>
8#include <linux/nl80211.h>
9#include <stdio.h>
10
11#include <algorithm>
12#include <set>
13#include <string>
14#include <utility>
15#include <vector>
16
17#include <base/cancelable_callback.h>
18#include <chromeos/dbus/service_constants.h>
19#include <components/timers/alarm_timer.h>
20
21#include "shill/error.h"
22#include "shill/event_dispatcher.h"
23#include "shill/ip_address_store.h"
24#include "shill/logging.h"
25#include "shill/metrics.h"
26#include "shill/net/event_history.h"
27#include "shill/net/netlink_manager.h"
28#include "shill/net/nl80211_message.h"
29#include "shill/property_accessor.h"
30#include "shill/wifi/wifi.h"
31
32using base::Bind;
33using base::Closure;
34using std::pair;
35using std::set;
36using std::string;
37using std::vector;
38using timers::AlarmTimer;
39
40namespace shill {
41
42namespace Logging {
43static auto kModuleLogScope = ScopeLogger::kWiFi;
44static std::string ObjectID(WakeOnWiFi *w) { return "(wake_on_wifi)"; }
45}
46
47const char WakeOnWiFi::kWakeOnIPAddressPatternsNotSupported[] =
48    "Wake on IP address patterns not supported by this WiFi device";
49const char WakeOnWiFi::kWakeOnPacketDisabled[] =
50    "Wake on Packet feature disabled, so do nothing";
51const char WakeOnWiFi::kWakeOnWiFiDisabled[] = "Wake on WiFi is disabled";
52const uint32_t WakeOnWiFi::kDefaultWiphyIndex = 999;
53const int WakeOnWiFi::kVerifyWakeOnWiFiSettingsDelayMilliseconds = 300;
54const int WakeOnWiFi::kMaxSetWakeOnPacketRetries = 2;
55const int WakeOnWiFi::kMetricsReportingFrequencySeconds = 600;
56const uint32_t WakeOnWiFi::kDefaultWakeToScanPeriodSeconds = 900;
57const uint32_t WakeOnWiFi::kDefaultNetDetectScanPeriodSeconds = 120;
58const uint32_t WakeOnWiFi::kImmediateDHCPLeaseRenewalThresholdSeconds = 60;
59// We tolerate no more than 5 dark resumes where we wake up disconnected per
60// minute before throttling the feature (i.e. disabling wake on WiFi on the
61// NIC).
62const int WakeOnWiFi::kDarkResumeFrequencySamplingPeriodMinutes = 1;
63const int WakeOnWiFi::kMaxDarkResumesPerPeriod = 5;
64// If a connection is not established during dark resume, give up and prepare
65// the system to wake on SSID 1 second before suspending again.
66// TODO(samueltan): link this to
67// Manager::kTerminationActionsTimeoutMilliseconds rather than hard-coding
68// this value.
69int64_t WakeOnWiFi::DarkResumeActionsTimeoutMilliseconds = 18500;
70
71WakeOnWiFi::WakeOnWiFi(NetlinkManager *netlink_manager,
72                       EventDispatcher *dispatcher, Metrics *metrics)
73    : dispatcher_(dispatcher),
74      netlink_manager_(netlink_manager),
75      metrics_(metrics),
76      report_metrics_callback_(
77          Bind(&WakeOnWiFi::ReportMetrics, base::Unretained(this))),
78      num_set_wake_on_packet_retries_(0),
79      wake_on_wifi_max_patterns_(0),
80      wiphy_index_(kDefaultWiphyIndex),
81      wiphy_index_received_(false),
82#if defined(DISABLE_WAKE_ON_WIFI)
83      wake_on_wifi_features_enabled_(kWakeOnWiFiFeaturesEnabledNotSupported),
84#else
85      // Wake on WiFi features temporarily disabled at run-time for boards that
86      // support wake on WiFi.
87      // TODO(samueltan): re-enable once pending issues have been resolved.
88      wake_on_wifi_features_enabled_(kWakeOnWiFiFeaturesEnabledNone),
89#endif  // DISABLE_WAKE_ON_WIFI
90      dhcp_lease_renewal_timer_(true, false),
91      wake_to_scan_timer_(true, false),
92      in_dark_resume_(false),
93      wake_to_scan_period_seconds_(kDefaultWakeToScanPeriodSeconds),
94      net_detect_scan_period_seconds_(kDefaultNetDetectScanPeriodSeconds),
95      dark_resumes_since_last_suspend_(kMaxDarkResumesPerPeriod),
96      weak_ptr_factory_(this) {
97}
98
99WakeOnWiFi::~WakeOnWiFi() {}
100
101void WakeOnWiFi::InitPropertyStore(PropertyStore *store) {
102  store->RegisterDerivedString(
103      kWakeOnWiFiFeaturesEnabledProperty,
104      StringAccessor(new CustomAccessor<WakeOnWiFi, string>(
105          this, &WakeOnWiFi::GetWakeOnWiFiFeaturesEnabled,
106          &WakeOnWiFi::SetWakeOnWiFiFeaturesEnabled)));
107  store->RegisterUint32(kWakeToScanPeriodSecondsProperty,
108                        &wake_to_scan_period_seconds_);
109  store->RegisterUint32(kNetDetectScanPeriodSecondsProperty,
110                        &net_detect_scan_period_seconds_);
111}
112
113void WakeOnWiFi::StartMetricsTimer() {
114#if !defined(DISABLE_WAKE_ON_WIFI)
115  dispatcher_->PostDelayedTask(report_metrics_callback_.callback(),
116                               kMetricsReportingFrequencySeconds * 1000);
117#endif  // DISABLE_WAKE_ON_WIFI
118}
119
120string WakeOnWiFi::GetWakeOnWiFiFeaturesEnabled(Error *error) {
121  return wake_on_wifi_features_enabled_;
122}
123
124bool WakeOnWiFi::SetWakeOnWiFiFeaturesEnabled(const std::string &enabled,
125                                              Error *error) {
126#if defined(DISABLE_WAKE_ON_WIFI)
127  Error::PopulateAndLog(FROM_HERE, error, Error::kNotSupported,
128                        "Wake on WiFi is not supported");
129  return false;
130#else
131  if (wake_on_wifi_features_enabled_ == enabled) {
132    return false;
133  }
134  if (enabled != kWakeOnWiFiFeaturesEnabledPacket &&
135      enabled != kWakeOnWiFiFeaturesEnabledSSID &&
136      enabled != kWakeOnWiFiFeaturesEnabledPacketSSID &&
137      enabled != kWakeOnWiFiFeaturesEnabledNone) {
138    Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments,
139                          "Invalid Wake on WiFi feature");
140    return false;
141  }
142  wake_on_wifi_features_enabled_ = enabled;
143  return true;
144#endif  // DISABLE_WAKE_ON_WIFI
145}
146
147void WakeOnWiFi::RunAndResetSuspendActionsDoneCallback(const Error &error) {
148  if (!suspend_actions_done_callback_.is_null()) {
149    suspend_actions_done_callback_.Run(error);
150    suspend_actions_done_callback_.Reset();
151  }
152}
153
154bool WakeOnWiFi::ByteStringPairIsLessThan(
155    const std::pair<ByteString, ByteString> &lhs,
156    const std::pair<ByteString, ByteString> &rhs) {
157  // Treat the first value of the pair as the key.
158  return ByteString::IsLessThan(lhs.first, rhs.first);
159}
160
161// static
162void WakeOnWiFi::SetMask(ByteString *mask, uint32_t pattern_len,
163                         uint32_t offset) {
164  // Round up number of bytes required for the mask.
165  int result_mask_len = (pattern_len + 8 - 1) / 8;
166  vector<unsigned char> result_mask(result_mask_len, 0);
167  // Set mask bits from offset to (pattern_len - 1)
168  int mask_index;
169  for (uint32_t curr_mask_bit = offset; curr_mask_bit < pattern_len;
170       ++curr_mask_bit) {
171    mask_index = curr_mask_bit / 8;
172    result_mask[mask_index] |= 1 << (curr_mask_bit % 8);
173  }
174  mask->Clear();
175  mask->Append(ByteString(result_mask));
176}
177
178// static
179bool WakeOnWiFi::CreateIPAddressPatternAndMask(const IPAddress &ip_addr,
180                                               ByteString *pattern,
181                                               ByteString *mask) {
182  if (ip_addr.family() == IPAddress::kFamilyIPv4) {
183    WakeOnWiFi::CreateIPV4PatternAndMask(ip_addr, pattern, mask);
184    return true;
185  } else if (ip_addr.family() == IPAddress::kFamilyIPv6) {
186    WakeOnWiFi::CreateIPV6PatternAndMask(ip_addr, pattern, mask);
187    return true;
188  } else {
189    LOG(ERROR) << "Unrecognized IP Address type.";
190    return false;
191  }
192}
193
194// static
195void WakeOnWiFi::CreateIPV4PatternAndMask(const IPAddress &ip_addr,
196                                          ByteString *pattern,
197                                          ByteString *mask) {
198  struct {
199    struct ethhdr eth_hdr;
200    struct iphdr ipv4_hdr;
201  } __attribute__((__packed__)) pattern_bytes;
202  memset(&pattern_bytes, 0, sizeof(pattern_bytes));
203  CHECK_EQ(sizeof(pattern_bytes.ipv4_hdr.saddr), ip_addr.GetLength());
204  memcpy(&pattern_bytes.ipv4_hdr.saddr, ip_addr.GetConstData(),
205         ip_addr.GetLength());
206  int src_ip_offset =
207      reinterpret_cast<unsigned char *>(&pattern_bytes.ipv4_hdr.saddr) -
208      reinterpret_cast<unsigned char *>(&pattern_bytes);
209  int pattern_len = src_ip_offset + ip_addr.GetLength();
210  pattern->Clear();
211  pattern->Append(ByteString(
212      reinterpret_cast<const unsigned char *>(&pattern_bytes), pattern_len));
213  WakeOnWiFi::SetMask(mask, pattern_len, src_ip_offset);
214}
215
216// static
217void WakeOnWiFi::CreateIPV6PatternAndMask(const IPAddress &ip_addr,
218                                          ByteString *pattern,
219                                          ByteString *mask) {
220  struct {
221    struct ethhdr eth_hdr;
222    struct ip6_hdr ipv6_hdr;
223  } __attribute__((__packed__)) pattern_bytes;
224  memset(&pattern_bytes, 0, sizeof(pattern_bytes));
225  CHECK_EQ(sizeof(pattern_bytes.ipv6_hdr.ip6_src), ip_addr.GetLength());
226  memcpy(&pattern_bytes.ipv6_hdr.ip6_src, ip_addr.GetConstData(),
227         ip_addr.GetLength());
228  int src_ip_offset =
229      reinterpret_cast<unsigned char *>(&pattern_bytes.ipv6_hdr.ip6_src) -
230      reinterpret_cast<unsigned char *>(&pattern_bytes);
231  int pattern_len = src_ip_offset + ip_addr.GetLength();
232  pattern->Clear();
233  pattern->Append(ByteString(
234      reinterpret_cast<const unsigned char *>(&pattern_bytes), pattern_len));
235  WakeOnWiFi::SetMask(mask, pattern_len, src_ip_offset);
236}
237
238// static
239bool WakeOnWiFi::ConfigureWiphyIndex(Nl80211Message *msg, int32_t index) {
240  if (!msg->attributes()->CreateU32Attribute(NL80211_ATTR_WIPHY,
241                                             "WIPHY index")) {
242    return false;
243  }
244  if (!msg->attributes()->SetU32AttributeValue(NL80211_ATTR_WIPHY, index)) {
245    return false;
246  }
247  return true;
248}
249
250// static
251bool WakeOnWiFi::ConfigureDisableWakeOnWiFiMessage(
252    SetWakeOnPacketConnMessage *msg, uint32_t wiphy_index, Error *error) {
253  if (!ConfigureWiphyIndex(msg, wiphy_index)) {
254    Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
255                          "Failed to configure Wiphy index.");
256    return false;
257  }
258  return true;
259}
260
261// static
262bool WakeOnWiFi::ConfigureSetWakeOnWiFiSettingsMessage(
263    SetWakeOnPacketConnMessage *msg, const set<WakeOnWiFiTrigger> &trigs,
264    const IPAddressStore &addrs, uint32_t wiphy_index, Error *error) {
265  if (trigs.empty()) {
266    Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments,
267                          "No triggers to configure.");
268    return false;
269  }
270  if (trigs.find(kPattern) != trigs.end() && addrs.Empty()) {
271    Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments,
272                          "No IP addresses to configure.");
273    return false;
274  }
275  if (!ConfigureWiphyIndex(msg, wiphy_index)) {
276    Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
277                          "Failed to configure Wiphy index.");
278    return false;
279  }
280  if (!msg->attributes()->CreateNestedAttribute(NL80211_ATTR_WOWLAN_TRIGGERS,
281                                                "WoWLAN Triggers")) {
282    Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
283                          "Could not create nested attribute "
284                          "NL80211_ATTR_WOWLAN_TRIGGERS for "
285                          "SetWakeOnPacketConnMessage.");
286    return false;
287  }
288  if (!msg->attributes()->SetNestedAttributeHasAValue(
289          NL80211_ATTR_WOWLAN_TRIGGERS)) {
290    Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
291                          "Could not set nested attribute "
292                          "NL80211_ATTR_WOWLAN_TRIGGERS for "
293                          "SetWakeOnPacketConnMessage.");
294    return false;
295  }
296
297  AttributeListRefPtr triggers;
298  if (!msg->attributes()->GetNestedAttributeList(NL80211_ATTR_WOWLAN_TRIGGERS,
299                                                 &triggers)) {
300    Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
301                          "Could not get nested attribute list "
302                          "NL80211_ATTR_WOWLAN_TRIGGERS for "
303                          "SetWakeOnPacketConnMessage.");
304    return false;
305  }
306  // Add triggers.
307  for (WakeOnWiFiTrigger t : trigs) {
308    switch (t) {
309      case kDisconnect: {
310        if (!triggers->CreateFlagAttribute(NL80211_WOWLAN_TRIG_DISCONNECT,
311                                           "Wake on Disconnect")) {
312          LOG(ERROR) << __func__ << "Could not create flag attribute "
313                                    "NL80211_WOWLAN_TRIG_DISCONNECT";
314          return false;
315        }
316        if (!triggers->SetFlagAttributeValue(NL80211_WOWLAN_TRIG_DISCONNECT,
317                                             true)) {
318          LOG(ERROR) << __func__ << "Could not set flag attribute "
319                                    "NL80211_WOWLAN_TRIG_DISCONNECT";
320          return false;
321        }
322        break;
323      }
324      case kPattern: {
325        if (!triggers->CreateNestedAttribute(NL80211_WOWLAN_TRIG_PKT_PATTERN,
326                                             "Pattern trigger")) {
327          Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
328                                "Could not create nested attribute "
329                                "NL80211_WOWLAN_TRIG_PKT_PATTERN for "
330                                "SetWakeOnPacketConnMessage.");
331          return false;
332        }
333        if (!triggers->SetNestedAttributeHasAValue(
334                NL80211_WOWLAN_TRIG_PKT_PATTERN)) {
335          Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
336                                "Could not set nested attribute "
337                                "NL80211_WOWLAN_TRIG_PKT_PATTERN for "
338                                "SetWakeOnPacketConnMessage.");
339          return false;
340        }
341        AttributeListRefPtr patterns;
342        if (!triggers->GetNestedAttributeList(NL80211_WOWLAN_TRIG_PKT_PATTERN,
343                                              &patterns)) {
344          Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
345                                "Could not get nested attribute list "
346                                "NL80211_WOWLAN_TRIG_PKT_PATTERN for "
347                                "SetWakeOnPacketConnMessage.");
348          return false;
349        }
350        uint8_t patnum = 1;
351        for (const IPAddress &addr : addrs.GetIPAddresses()) {
352          if (!CreateSinglePattern(addr, patterns, patnum++, error)) {
353            return false;
354          }
355        }
356        break;
357      }
358      case kSSID: {
359        // TODO(samueltan): construct wake on SSID trigger when available.
360        break;
361      }
362      default: {
363        LOG(ERROR) << __func__ << ": Unrecognized trigger";
364        return false;
365      }
366    }
367  }
368  return true;
369}
370
371// static
372bool WakeOnWiFi::CreateSinglePattern(const IPAddress &ip_addr,
373                                     AttributeListRefPtr patterns,
374                                     uint8_t patnum, Error *error) {
375  ByteString pattern;
376  ByteString mask;
377  WakeOnWiFi::CreateIPAddressPatternAndMask(ip_addr, &pattern, &mask);
378  if (!patterns->CreateNestedAttribute(patnum, "Pattern info")) {
379    Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
380                          "Could not create nested attribute "
381                          "patnum for SetWakeOnPacketConnMessage.");
382    return false;
383  }
384  if (!patterns->SetNestedAttributeHasAValue(patnum)) {
385    Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
386                          "Could not set nested attribute "
387                          "patnum for SetWakeOnPacketConnMessage.");
388    return false;
389  }
390
391  AttributeListRefPtr pattern_info;
392  if (!patterns->GetNestedAttributeList(patnum, &pattern_info)) {
393    Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
394                          "Could not get nested attribute list "
395                          "patnum for SetWakeOnPacketConnMessage.");
396    return false;
397  }
398  // Add mask.
399  if (!pattern_info->CreateRawAttribute(NL80211_PKTPAT_MASK, "Mask")) {
400    Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
401                          "Could not add attribute NL80211_PKTPAT_MASK to "
402                          "pattern_info.");
403    return false;
404  }
405  if (!pattern_info->SetRawAttributeValue(NL80211_PKTPAT_MASK, mask)) {
406    Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
407                          "Could not set attribute NL80211_PKTPAT_MASK in "
408                          "pattern_info.");
409    return false;
410  }
411
412  // Add pattern.
413  if (!pattern_info->CreateRawAttribute(NL80211_PKTPAT_PATTERN, "Pattern")) {
414    Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
415                          "Could not add attribute NL80211_PKTPAT_PATTERN to "
416                          "pattern_info.");
417    return false;
418  }
419  if (!pattern_info->SetRawAttributeValue(NL80211_PKTPAT_PATTERN, pattern)) {
420    Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
421                          "Could not set attribute NL80211_PKTPAT_PATTERN in "
422                          "pattern_info.");
423    return false;
424  }
425
426  // Add offset.
427  if (!pattern_info->CreateU32Attribute(NL80211_PKTPAT_OFFSET, "Offset")) {
428    Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
429                          "Could not add attribute NL80211_PKTPAT_OFFSET to "
430                          "pattern_info.");
431    return false;
432  }
433  if (!pattern_info->SetU32AttributeValue(NL80211_PKTPAT_OFFSET, 0)) {
434    Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
435                          "Could not set attribute NL80211_PKTPAT_OFFSET in "
436                          "pattern_info.");
437    return false;
438  }
439  return true;
440}
441
442// static
443bool WakeOnWiFi::ConfigureGetWakeOnWiFiSettingsMessage(
444    GetWakeOnPacketConnMessage *msg, uint32_t wiphy_index, Error *error) {
445  if (!ConfigureWiphyIndex(msg, wiphy_index)) {
446    Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
447                          "Failed to configure Wiphy index.");
448    return false;
449  }
450  return true;
451}
452
453// static
454bool WakeOnWiFi::WakeOnWiFiSettingsMatch(const Nl80211Message &msg,
455                                         const set<WakeOnWiFiTrigger> &trigs,
456                                         const IPAddressStore &addrs) {
457  if (msg.command() != NL80211_CMD_GET_WOWLAN &&
458      msg.command() != NL80211_CMD_SET_WOWLAN) {
459    LOG(ERROR) << "Invalid message command";
460    return false;
461  }
462  AttributeListConstRefPtr triggers;
463  if (!msg.const_attributes()->ConstGetNestedAttributeList(
464          NL80211_ATTR_WOWLAN_TRIGGERS, &triggers)) {
465    // No triggers in the returned message, which is valid iff we expect there
466    // to be no triggers programmed into the NIC.
467    return trigs.empty();
468  }
469  // If the disconnect trigger is found and set, but we did not expect this
470  // trigger, we have a mismatch.
471  bool wake_on_disconnect = false;
472  triggers->GetFlagAttributeValue(NL80211_WOWLAN_TRIG_DISCONNECT,
473                                  &wake_on_disconnect);
474  if (trigs.find(kDisconnect) == trigs.end() && wake_on_disconnect) {
475    return false;
476  }
477  // Check each trigger.
478  for (WakeOnWiFiTrigger t : trigs) {
479    switch (t) {
480      case kDisconnect: {
481        if (!wake_on_disconnect) {
482          return false;
483        }
484        break;
485      }
486      case kPattern: {
487        // Create pattern and masks that we expect to find in |msg|.
488        set<pair<ByteString, ByteString>,
489            bool (*)(const pair<ByteString, ByteString> &,
490                     const pair<ByteString, ByteString> &)>
491            expected_patt_mask_pairs(ByteStringPairIsLessThan);
492        ByteString temp_pattern;
493        ByteString temp_mask;
494        for (const IPAddress &addr : addrs.GetIPAddresses()) {
495          temp_pattern.Clear();
496          temp_mask.Clear();
497          CreateIPAddressPatternAndMask(addr, &temp_pattern, &temp_mask);
498          expected_patt_mask_pairs.emplace(temp_pattern, temp_mask);
499        }
500        // Check these expected pattern and masks against those actually
501        // contained in |msg|.
502        AttributeListConstRefPtr patterns;
503        if (!triggers->ConstGetNestedAttributeList(
504                NL80211_WOWLAN_TRIG_PKT_PATTERN, &patterns)) {
505          LOG(ERROR) << "Could not get nested attribute list "
506                        "NL80211_WOWLAN_TRIG_PKT_PATTERN.";
507          return false;
508        }
509        bool mismatch_found = false;
510        size_t num_mismatch = expected_patt_mask_pairs.size();
511        int pattern_index;
512        AttributeIdIterator pattern_iter(*patterns);
513        AttributeListConstRefPtr pattern_info;
514        ByteString returned_mask;
515        ByteString returned_pattern;
516        while (!pattern_iter.AtEnd()) {
517          returned_mask.Clear();
518          returned_pattern.Clear();
519          pattern_index = pattern_iter.GetId();
520          if (!patterns->ConstGetNestedAttributeList(pattern_index,
521                                                     &pattern_info)) {
522            LOG(ERROR) << "Could not get nested attribute list index "
523                       << pattern_index << " in patterns.";
524            return false;
525          }
526          if (!pattern_info->GetRawAttributeValue(NL80211_PKTPAT_MASK,
527                                                  &returned_mask)) {
528            LOG(ERROR) << "Could not get attribute NL80211_PKTPAT_MASK in "
529                          "pattern_info.";
530            return false;
531          }
532          if (!pattern_info->GetRawAttributeValue(NL80211_PKTPAT_PATTERN,
533                                                  &returned_pattern)) {
534            LOG(ERROR) << "Could not get attribute NL80211_PKTPAT_PATTERN in "
535                          "pattern_info.";
536            return false;
537          }
538          if (expected_patt_mask_pairs.find(pair<ByteString, ByteString>(
539                  returned_pattern, returned_mask)) ==
540              expected_patt_mask_pairs.end()) {
541            mismatch_found = true;
542            break;
543          } else {
544            --num_mismatch;
545          }
546          pattern_iter.Advance();
547        }
548        if (mismatch_found || num_mismatch) {
549          return false;
550        }
551        break;
552      }
553      case kSSID: {
554        // TODO(samueltan): parse wake on SSID trigger when available.
555        break;
556      }
557      default: {
558        LOG(ERROR) << __func__ << ": Unrecognized trigger";
559        return false;
560      }
561    }
562  }
563  return true;
564}
565
566void WakeOnWiFi::AddWakeOnPacketConnection(const string &ip_endpoint,
567                                           Error *error) {
568#if !defined(DISABLE_WAKE_ON_WIFI)
569  if (wake_on_wifi_triggers_supported_.find(kPattern) ==
570      wake_on_wifi_triggers_supported_.end()) {
571    Error::PopulateAndLog(FROM_HERE, error, Error::kNotSupported,
572                          kWakeOnIPAddressPatternsNotSupported);
573    return;
574  }
575  IPAddress ip_addr(ip_endpoint);
576  if (!ip_addr.IsValid()) {
577    Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments,
578                          "Invalid ip_address " + ip_endpoint);
579    return;
580  }
581  if (wake_on_wifi_triggers_.size() >= wake_on_wifi_max_patterns_) {
582    Error::PopulateAndLog(
583        FROM_HERE, error, Error::kOperationFailed,
584        "Max number of IP address patterns already registered");
585    return;
586  }
587  wake_on_packet_connections_.AddUnique(ip_addr);
588#else
589  Error::PopulateAndLog(
590      FROM_HERE, error, Error::kNotSupported, kWakeOnWiFiDisabled);
591#endif  // DISABLE_WAKE_ON_WIFI
592}
593
594void WakeOnWiFi::RemoveWakeOnPacketConnection(const string &ip_endpoint,
595                                              Error *error) {
596#if !defined(DISABLE_WAKE_ON_WIFI)
597  if (wake_on_wifi_triggers_supported_.find(kPattern) ==
598      wake_on_wifi_triggers_supported_.end()) {
599    Error::PopulateAndLog(FROM_HERE, error, Error::kNotSupported,
600                          kWakeOnIPAddressPatternsNotSupported);
601    return;
602  }
603  IPAddress ip_addr(ip_endpoint);
604  if (!ip_addr.IsValid()) {
605    Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments,
606                          "Invalid ip_address " + ip_endpoint);
607    return;
608  }
609  if (!wake_on_packet_connections_.Contains(ip_addr)) {
610    Error::PopulateAndLog(FROM_HERE, error, Error::kNotFound,
611                          "No such IP address match registered to wake device");
612    return;
613  }
614  wake_on_packet_connections_.Remove(ip_addr);
615#else
616  Error::PopulateAndLog(
617      FROM_HERE, error, Error::kNotSupported, kWakeOnWiFiDisabled);
618#endif  // DISABLE_WAKE_ON_WIFI
619}
620
621void WakeOnWiFi::RemoveAllWakeOnPacketConnections(Error *error) {
622#if !defined(DISABLE_WAKE_ON_WIFI)
623  if (wake_on_wifi_triggers_supported_.find(kPattern) ==
624      wake_on_wifi_triggers_supported_.end()) {
625    Error::PopulateAndLog(FROM_HERE, error, Error::kNotSupported,
626                          kWakeOnIPAddressPatternsNotSupported);
627    return;
628  }
629  wake_on_packet_connections_.Clear();
630#else
631  Error::PopulateAndLog(
632      FROM_HERE, error, Error::kNotSupported, kWakeOnWiFiDisabled);
633#endif  // DISABLE_WAKE_ON_WIFI
634}
635
636void WakeOnWiFi::OnWakeOnWiFiSettingsErrorResponse(
637    NetlinkManager::AuxilliaryMessageType type,
638    const NetlinkMessage *raw_message) {
639  Error error(Error::kOperationFailed);
640  switch (type) {
641    case NetlinkManager::kErrorFromKernel:
642      if (!raw_message) {
643        error.Populate(Error::kOperationFailed, "Unknown error from kernel");
644        break;
645      }
646      if (raw_message->message_type() == ErrorAckMessage::GetMessageType()) {
647        const ErrorAckMessage *error_ack_message =
648            dynamic_cast<const ErrorAckMessage *>(raw_message);
649        if (error_ack_message->error() == EOPNOTSUPP) {
650          error.Populate(Error::kNotSupported);
651        }
652      }
653      break;
654
655    case NetlinkManager::kUnexpectedResponseType:
656      error.Populate(Error::kNotRegistered,
657                     "Message not handled by regular message handler:");
658      break;
659
660    case NetlinkManager::kTimeoutWaitingForResponse:
661      // CMD_SET_WOWLAN messages do not receive responses, so this error type
662      // is received when NetlinkManager times out the message handler. Return
663      // immediately rather than run the done callback since this event does
664      // not signify the completion of suspend actions.
665      return;
666      break;
667
668    default:
669      error.Populate(
670          Error::kOperationFailed,
671          "Unexpected auxilliary message type: " + std::to_string(type));
672      break;
673  }
674  RunAndResetSuspendActionsDoneCallback(error);
675}
676
677// static
678void WakeOnWiFi::OnSetWakeOnPacketConnectionResponse(
679    const Nl80211Message &nl80211_message) {
680  // NOP because kernel does not send a response to NL80211_CMD_SET_WOWLAN
681  // requests.
682}
683
684void WakeOnWiFi::RequestWakeOnPacketSettings() {
685  SLOG(this, 3) << __func__;
686  Error e;
687  GetWakeOnPacketConnMessage get_wowlan_msg;
688  if (!ConfigureGetWakeOnWiFiSettingsMessage(&get_wowlan_msg, wiphy_index_,
689                                             &e)) {
690    LOG(ERROR) << e.message();
691    return;
692  }
693  netlink_manager_->SendNl80211Message(
694      &get_wowlan_msg, Bind(&WakeOnWiFi::VerifyWakeOnWiFiSettings,
695                            weak_ptr_factory_.GetWeakPtr()),
696      Bind(&NetlinkManager::OnAckDoNothing),
697      Bind(&NetlinkManager::OnNetlinkMessageError));
698}
699
700void WakeOnWiFi::VerifyWakeOnWiFiSettings(
701    const Nl80211Message &nl80211_message) {
702  SLOG(this, 3) << __func__;
703  if (WakeOnWiFiSettingsMatch(nl80211_message, wake_on_wifi_triggers_,
704                              wake_on_packet_connections_)) {
705    SLOG(this, 2) << __func__ << ": "
706                  << "Wake-on-packet settings successfully verified";
707    metrics_->NotifyVerifyWakeOnWiFiSettingsResult(
708        Metrics::kVerifyWakeOnWiFiSettingsResultSuccess);
709    RunAndResetSuspendActionsDoneCallback(Error(Error::kSuccess));
710  } else {
711    LOG(ERROR) << __func__ << " failed: discrepancy between wake-on-packet "
712                              "settings on NIC and those in local data "
713                              "structure detected";
714    metrics_->NotifyVerifyWakeOnWiFiSettingsResult(
715        Metrics::kVerifyWakeOnWiFiSettingsResultFailure);
716    RetrySetWakeOnPacketConnections();
717  }
718}
719
720void WakeOnWiFi::ApplyWakeOnWiFiSettings() {
721  SLOG(this, 3) << __func__;
722  if (!wiphy_index_received_) {
723    LOG(ERROR) << "Interface index not yet received";
724    return;
725  }
726  if (wake_on_wifi_triggers_.empty()) {
727    LOG(INFO) << "No triggers to be programmed, so disable wake on WiFi";
728    DisableWakeOnWiFi();
729    return;
730  }
731  Error error;
732  SetWakeOnPacketConnMessage set_wowlan_msg;
733  if (!ConfigureSetWakeOnWiFiSettingsMessage(
734          &set_wowlan_msg, wake_on_wifi_triggers_, wake_on_packet_connections_,
735          wiphy_index_, &error)) {
736    LOG(ERROR) << error.message();
737    RunAndResetSuspendActionsDoneCallback(
738        Error(Error::kOperationFailed, error.message()));
739    return;
740  }
741  if (!netlink_manager_->SendNl80211Message(
742          &set_wowlan_msg,
743          Bind(&WakeOnWiFi::OnSetWakeOnPacketConnectionResponse),
744          Bind(&NetlinkManager::OnAckDoNothing),
745          Bind(&WakeOnWiFi::OnWakeOnWiFiSettingsErrorResponse,
746               weak_ptr_factory_.GetWeakPtr()))) {
747    RunAndResetSuspendActionsDoneCallback(
748        Error(Error::kOperationFailed, "SendNl80211Message failed"));
749    return;
750  }
751
752  verify_wake_on_packet_settings_callback_.Reset(
753      Bind(&WakeOnWiFi::RequestWakeOnPacketSettings,
754           weak_ptr_factory_.GetWeakPtr()));
755  dispatcher_->PostDelayedTask(
756      verify_wake_on_packet_settings_callback_.callback(),
757      kVerifyWakeOnWiFiSettingsDelayMilliseconds);
758}
759
760void WakeOnWiFi::DisableWakeOnWiFi() {
761  SLOG(this, 3) << __func__;
762  Error error;
763  SetWakeOnPacketConnMessage disable_wowlan_msg;
764  if (!ConfigureDisableWakeOnWiFiMessage(&disable_wowlan_msg, wiphy_index_,
765                                         &error)) {
766    LOG(ERROR) << error.message();
767    RunAndResetSuspendActionsDoneCallback(
768        Error(Error::kOperationFailed, error.message()));
769    return;
770  }
771  wake_on_wifi_triggers_.clear();
772  if (!netlink_manager_->SendNl80211Message(
773          &disable_wowlan_msg,
774          Bind(&WakeOnWiFi::OnSetWakeOnPacketConnectionResponse),
775          Bind(&NetlinkManager::OnAckDoNothing),
776          Bind(&WakeOnWiFi::OnWakeOnWiFiSettingsErrorResponse,
777               weak_ptr_factory_.GetWeakPtr()))) {
778    RunAndResetSuspendActionsDoneCallback(
779        Error(Error::kOperationFailed, "SendNl80211Message failed"));
780    return;
781  }
782
783  verify_wake_on_packet_settings_callback_.Reset(
784      Bind(&WakeOnWiFi::RequestWakeOnPacketSettings,
785           weak_ptr_factory_.GetWeakPtr()));
786  dispatcher_->PostDelayedTask(
787      verify_wake_on_packet_settings_callback_.callback(),
788      kVerifyWakeOnWiFiSettingsDelayMilliseconds);
789}
790
791void WakeOnWiFi::RetrySetWakeOnPacketConnections() {
792  SLOG(this, 3) << __func__;
793  if (num_set_wake_on_packet_retries_ < kMaxSetWakeOnPacketRetries) {
794    ApplyWakeOnWiFiSettings();
795    ++num_set_wake_on_packet_retries_;
796  } else {
797    SLOG(this, 3) << __func__ << ": max retry attempts reached";
798    num_set_wake_on_packet_retries_ = 0;
799    RunAndResetSuspendActionsDoneCallback(Error(Error::kOperationFailed));
800  }
801}
802
803bool WakeOnWiFi::WakeOnPacketEnabledAndSupported() {
804  if (wake_on_wifi_features_enabled_ == kWakeOnWiFiFeaturesEnabledNone ||
805      wake_on_wifi_features_enabled_ ==
806          kWakeOnWiFiFeaturesEnabledNotSupported ||
807      wake_on_wifi_features_enabled_ == kWakeOnWiFiFeaturesEnabledSSID) {
808    return false;
809  }
810  if (wake_on_wifi_triggers_supported_.find(kPattern) ==
811      wake_on_wifi_triggers_supported_.end()) {
812    return false;
813  }
814  return true;
815}
816
817bool WakeOnWiFi::WakeOnSSIDEnabledAndSupported() {
818  if (wake_on_wifi_features_enabled_ == kWakeOnWiFiFeaturesEnabledNone ||
819      wake_on_wifi_features_enabled_ ==
820          kWakeOnWiFiFeaturesEnabledNotSupported ||
821      wake_on_wifi_features_enabled_ == kWakeOnWiFiFeaturesEnabledPacket) {
822    return false;
823  }
824  if (wake_on_wifi_triggers_supported_.find(kDisconnect) ==
825          wake_on_wifi_triggers_supported_.end() ||
826      wake_on_wifi_triggers_supported_.find(kSSID) ==
827          wake_on_wifi_triggers_supported_.end()) {
828    return false;
829  }
830  return true;
831}
832
833void WakeOnWiFi::ReportMetrics() {
834  Metrics::WakeOnWiFiFeaturesEnabledState reported_state;
835  if (wake_on_wifi_features_enabled_ == kWakeOnWiFiFeaturesEnabledNone) {
836    reported_state = Metrics::kWakeOnWiFiFeaturesEnabledStateNone;
837  } else if (wake_on_wifi_features_enabled_ ==
838             kWakeOnWiFiFeaturesEnabledPacket) {
839    reported_state = Metrics::kWakeOnWiFiFeaturesEnabledStatePacket;
840  } else if (wake_on_wifi_features_enabled_ == kWakeOnWiFiFeaturesEnabledSSID) {
841    reported_state = Metrics::kWakeOnWiFiFeaturesEnabledStateSSID;
842  } else if (wake_on_wifi_features_enabled_ ==
843             kWakeOnWiFiFeaturesEnabledPacketSSID) {
844    reported_state = Metrics::kWakeOnWiFiFeaturesEnabledStatePacketSSID;
845  } else {
846    LOG(ERROR) << __func__ << ": "
847               << "Invalid wake on WiFi features state";
848    return;
849  }
850  metrics_->NotifyWakeOnWiFiFeaturesEnabledState(reported_state);
851  StartMetricsTimer();
852}
853
854void WakeOnWiFi::ParseWakeOnWiFiCapabilities(
855    const Nl80211Message &nl80211_message) {
856  // Verify NL80211_CMD_NEW_WIPHY.
857  if (nl80211_message.command() != NewWiphyMessage::kCommand) {
858    LOG(ERROR) << "Received unexpected command:" << nl80211_message.command();
859    return;
860  }
861  AttributeListConstRefPtr triggers_supported;
862  if (nl80211_message.const_attributes()->ConstGetNestedAttributeList(
863          NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, &triggers_supported)) {
864    bool disconnect_supported = false;
865    if (triggers_supported->GetFlagAttributeValue(
866            NL80211_WOWLAN_TRIG_DISCONNECT, &disconnect_supported)) {
867      if (disconnect_supported) {
868        wake_on_wifi_triggers_supported_.insert(WakeOnWiFi::kDisconnect);
869        SLOG(this, 7) << "Waking on disconnect supported by this WiFi device";
870      }
871    }
872    ByteString data;
873    if (triggers_supported->GetRawAttributeValue(
874            NL80211_WOWLAN_TRIG_PKT_PATTERN, &data)) {
875      struct nl80211_pattern_support *patt_support =
876          reinterpret_cast<struct nl80211_pattern_support *>(data.GetData());
877      // Determine the IPV4 and IPV6 pattern lengths we will use by
878      // constructing dummy patterns and getting their lengths.
879      ByteString dummy_pattern;
880      ByteString dummy_mask;
881      WakeOnWiFi::CreateIPV4PatternAndMask(IPAddress("192.168.0.20"),
882                                           &dummy_pattern, &dummy_mask);
883      size_t ipv4_pattern_len = dummy_pattern.GetLength();
884      WakeOnWiFi::CreateIPV6PatternAndMask(
885          IPAddress("FEDC:BA98:7654:3210:FEDC:BA98:7654:3210"), &dummy_pattern,
886          &dummy_mask);
887      size_t ipv6_pattern_len = dummy_pattern.GetLength();
888      // Check if the pattern matching capabilities of this WiFi device will
889      // allow IPV4 and IPV6 patterns to be used.
890      if (patt_support->min_pattern_len <=
891              std::min(ipv4_pattern_len, ipv6_pattern_len) &&
892          patt_support->max_pattern_len >=
893              std::max(ipv4_pattern_len, ipv6_pattern_len)) {
894        wake_on_wifi_triggers_supported_.insert(WakeOnWiFi::kPattern);
895        wake_on_wifi_max_patterns_ = patt_support->max_patterns;
896        SLOG(this, 7) << "Waking on up to " << wake_on_wifi_max_patterns_
897                      << " registered patterns of "
898                      << patt_support->min_pattern_len << "-"
899                      << patt_support->max_pattern_len
900                      << " bytes supported by this WiFi device";
901      }
902    }
903    // TODO(samueltan): remove this placeholder when wake on SSID capability
904    // can be parsed from NL80211 message.
905    wake_on_wifi_triggers_supported_.insert(WakeOnWiFi::kSSID);
906  }
907}
908
909void WakeOnWiFi::ParseWiphyIndex(const Nl80211Message &nl80211_message) {
910  // Verify NL80211_CMD_NEW_WIPHY.
911  if (nl80211_message.command() != NewWiphyMessage::kCommand) {
912    LOG(ERROR) << "Received unexpected command:" << nl80211_message.command();
913    return;
914  }
915  if (!nl80211_message.const_attributes()->GetU32AttributeValue(
916          NL80211_ATTR_WIPHY, &wiphy_index_)) {
917    LOG(ERROR) << "NL80211_CMD_NEW_WIPHY had no NL80211_ATTR_WIPHY";
918    return;
919  }
920  wiphy_index_received_ = true;
921}
922
923void WakeOnWiFi::OnBeforeSuspend(
924    bool is_connected,
925    bool has_service_configured_for_autoconnect,
926    const ResultCallback &done_callback,
927    const Closure &renew_dhcp_lease_callback,
928    const Closure &remove_supplicant_networks_callback,
929    bool have_dhcp_lease,
930    uint32_t time_to_next_lease_renewal) {
931  LOG(INFO) << __func__ << ": "
932            << (is_connected ? "connected" : "not connected");
933#if defined(DISABLE_WAKE_ON_WIFI)
934  // Wake on WiFi disabled, so immediately report success.
935  done_callback.Run(Error(Error::kSuccess));
936#else
937  suspend_actions_done_callback_ = done_callback;
938  dark_resumes_since_last_suspend_.Clear();
939  if (have_dhcp_lease && is_connected &&
940      time_to_next_lease_renewal < kImmediateDHCPLeaseRenewalThresholdSeconds) {
941    // Renew DHCP lease immediately if we have one that is expiring soon.
942    renew_dhcp_lease_callback.Run();
943    dispatcher_->PostTask(
944        Bind(&WakeOnWiFi::BeforeSuspendActions, weak_ptr_factory_.GetWeakPtr(),
945             is_connected, has_service_configured_for_autoconnect, false,
946             time_to_next_lease_renewal, remove_supplicant_networks_callback));
947  } else {
948    dispatcher_->PostTask(Bind(
949        &WakeOnWiFi::BeforeSuspendActions, weak_ptr_factory_.GetWeakPtr(),
950        is_connected, has_service_configured_for_autoconnect, have_dhcp_lease,
951        time_to_next_lease_renewal, remove_supplicant_networks_callback));
952  }
953#endif  // DISABLE_WAKE_ON_WIFI
954}
955
956void WakeOnWiFi::OnAfterResume() {
957  LOG(INFO) << __func__;
958#if !defined(DISABLE_WAKE_ON_WIFI)
959  wake_to_scan_timer_.Stop();
960  dhcp_lease_renewal_timer_.Stop();
961  if (WakeOnPacketEnabledAndSupported() || WakeOnSSIDEnabledAndSupported()) {
962    // Unconditionally disable wake on WiFi on resume if these features
963    // were enabled before the last suspend.
964    DisableWakeOnWiFi();
965  }
966#endif  // DISABLE_WAKE_ON_WIFI
967}
968
969void WakeOnWiFi::OnDarkResume(
970    bool is_connected,
971    bool has_service_configured_for_autoconnect,
972    const ResultCallback &done_callback,
973    const Closure &renew_dhcp_lease_callback,
974    const Closure &initiate_scan_callback,
975    const Closure &remove_supplicant_networks_callback) {
976  LOG(INFO) << __func__ << ": "
977            << (is_connected ? "connected" : "not connected");
978#if defined(DISABLE_WAKE_ON_WIFI)
979  done_callback.Run(Error(Error::kSuccess));
980#else
981  suspend_actions_done_callback_ = done_callback;
982  if (!is_connected) {
983    // Only record dark resume events where we wake up disconnected, since there
984    // are valid scenarios where we would dark resume frequently in a connected
985    // state (e.g. when wake on packet is enabled when there is a brief spike in
986    // incoming network traffic).
987    dark_resumes_since_last_suspend_.RecordEventAndExpireEventsBefore(
988        kDarkResumeFrequencySamplingPeriodMinutes * 60, true);
989  }
990  if (dark_resumes_since_last_suspend_.Size() >=
991      static_cast<size_t>(kMaxDarkResumesPerPeriod)) {
992    LOG(ERROR) << __func__ << ": "
993               << "Too many dark resumes; disabling wake on WiFi";
994    // If too many dark resumes have triggered recently, we are probably
995    // thrashing. Stop this by disabling wake on WiFi on the NIC and
996    // stopping all RTC timers that might trigger another dark resume.
997    dhcp_lease_renewal_timer_.Stop();
998    wake_to_scan_timer_.Stop();
999    DisableWakeOnWiFi();
1000    dark_resumes_since_last_suspend_.Clear();
1001    return;
1002  }
1003  // Only set dark resume to true after checking if we need to disable wake on
1004  // WiFi since calling WakeOnWiFi::DisableWakeOnWiFi directly bypasses
1005  // WakeOnWiFi::BeforeSuspendActions where |in_dark_resume_| is set to false.
1006  in_dark_resume_ = true;
1007  // Assume that we are disconnected if we time out. Consequently, we do not
1008  // need to start a DHCP lease renewal timer.
1009  dark_resume_actions_timeout_callback_.Reset(
1010      Bind(&WakeOnWiFi::BeforeSuspendActions, weak_ptr_factory_.GetWeakPtr(),
1011           false, has_service_configured_for_autoconnect, false, 0,
1012           remove_supplicant_networks_callback));
1013  dispatcher_->PostDelayedTask(dark_resume_actions_timeout_callback_.callback(),
1014                               DarkResumeActionsTimeoutMilliseconds);
1015
1016  if (is_connected) {
1017    renew_dhcp_lease_callback.Run();
1018  } else {
1019    remove_supplicant_networks_callback.Run();
1020    metrics_->NotifyDarkResumeInitiateScan();
1021    initiate_scan_callback.Run();
1022  }
1023#endif  // DISABLE_WAKE_ON_WIFI
1024}
1025
1026void WakeOnWiFi::BeforeSuspendActions(
1027    bool is_connected,
1028    bool has_service_configured_for_autoconnect,
1029    bool start_lease_renewal_timer,
1030    uint32_t time_to_next_lease_renewal,
1031    const Closure &remove_supplicant_networks_callback) {
1032  SLOG(this, 3) << __func__ << ": "
1033                << (is_connected ? "connected" : "not connected");
1034  // Note: No conditional compilation because all entry points to this functions
1035  // are already conditionally compiled based on DISABLE_WAKE_ON_WIFI.
1036
1037  // Create copy so callback can be run despite calling Cancel().
1038  Closure supplicant_callback_copy(remove_supplicant_networks_callback);
1039  dark_resume_actions_timeout_callback_.Cancel();
1040
1041  // Add relevant triggers to be programmed into the NIC.
1042  wake_on_wifi_triggers_.clear();
1043  if (!wake_on_packet_connections_.Empty() &&
1044      WakeOnPacketEnabledAndSupported() && is_connected) {
1045    SLOG(this, 3) << "Enabling wake on pattern";
1046    wake_on_wifi_triggers_.insert(kPattern);
1047  }
1048  if (WakeOnSSIDEnabledAndSupported()) {
1049    if (is_connected) {
1050      SLOG(this, 3) << "Enabling wake on disconnect";
1051      wake_on_wifi_triggers_.insert(kDisconnect);
1052      wake_on_wifi_triggers_.erase(kSSID);
1053      wake_to_scan_timer_.Stop();
1054      if (start_lease_renewal_timer) {
1055        // Timer callback is NO-OP since dark resume logic will initiate DHCP
1056        // lease renewal.
1057        dhcp_lease_renewal_timer_.Start(
1058            FROM_HERE, base::TimeDelta::FromSeconds(time_to_next_lease_renewal),
1059            Bind(&WakeOnWiFi::OnTimerWakeDoNothing, base::Unretained(this)));
1060      }
1061    } else {
1062      SLOG(this, 3) << "Enabling wake on SSID";
1063      // Force a disconnect in case supplicant is currently in the process of
1064      // connecting, and remove all networks so scans triggered in dark resume
1065      // are passive.
1066      supplicant_callback_copy.Run();
1067      wake_on_wifi_triggers_.erase(kDisconnect);
1068      wake_on_wifi_triggers_.insert(kSSID);
1069      dhcp_lease_renewal_timer_.Stop();
1070      if (has_service_configured_for_autoconnect) {
1071        // Only makes sense to wake to scan in dark resume if there is at least
1072        // one WiFi service that we can auto-connect to after the scan.
1073        // Timer callback is NO-OP since dark resume logic will initiate scan.
1074        wake_to_scan_timer_.Start(
1075            FROM_HERE,
1076            base::TimeDelta::FromSeconds(wake_to_scan_period_seconds_),
1077            Bind(&WakeOnWiFi::OnTimerWakeDoNothing, base::Unretained(this)));
1078      }
1079    }
1080  }
1081
1082  if (!in_dark_resume_ && wake_on_wifi_triggers_.empty()) {
1083    // No need program NIC on normal resume in this case since wake on WiFi
1084    // would already have been disabled on the last (non-dark) resume.
1085    LOG(INFO) << "No need to disable wake on WiFi on NIC in regular "
1086                 "suspend";
1087    RunAndResetSuspendActionsDoneCallback(Error(Error::kSuccess));
1088    return;
1089  }
1090
1091  in_dark_resume_ = false;
1092  ApplyWakeOnWiFiSettings();
1093}
1094
1095void WakeOnWiFi::OnDHCPLeaseObtained(bool start_lease_renewal_timer,
1096                                     uint32_t time_to_next_lease_renewal) {
1097  SLOG(this, 3) << __func__;
1098  if (in_dark_resume_) {
1099#if defined(DISABLE_WAKE_ON_WIFI)
1100    SLOG(this, 2) << "Wake on WiFi not supported, so do nothing";
1101#else
1102    // If we obtain a DHCP lease, we are connected, so the callback to have
1103    // supplicant remove networks will not be invoked in
1104    // WakeOnWiFi::BeforeSuspendActions. Likewise, we will not use the value of
1105    // |has_service_configured_for_autoconnect| argument, so pass an arbitrary
1106    // value.
1107    BeforeSuspendActions(true, true, start_lease_renewal_timer,
1108                         time_to_next_lease_renewal, base::Closure());
1109#endif  // DISABLE_WAKE_ON_WIFI
1110  } else {
1111    SLOG(this, 2) << "Not in dark resume, so do nothing";
1112  }
1113}
1114
1115void WakeOnWiFi::ReportConnectedToServiceAfterWake(bool is_connected) {
1116#if defined(DISABLE_WAKE_ON_WIFI)
1117  metrics_->NotifyConnectedToServiceAfterWake(
1118      is_connected
1119          ? Metrics::kWiFiConnetionStatusAfterWakeOnWiFiDisabledWakeConnected
1120          : Metrics::
1121                kWiFiConnetionStatusAfterWakeOnWiFiDisabledWakeNotConnected);
1122#else
1123  if (WakeOnSSIDEnabledAndSupported()) {
1124    // Only logged if wake on WiFi is supported and wake on SSID was enabled to
1125    // maintain connectivity while suspended.
1126    metrics_->NotifyConnectedToServiceAfterWake(
1127        is_connected
1128            ? Metrics::kWiFiConnetionStatusAfterWakeOnWiFiEnabledWakeConnected
1129            : Metrics::
1130                  kWiFiConnetionStatusAfterWakeOnWiFiEnabledWakeNotConnected);
1131  } else {
1132    metrics_->NotifyConnectedToServiceAfterWake(
1133        is_connected
1134            ? Metrics::kWiFiConnetionStatusAfterWakeOnWiFiDisabledWakeConnected
1135            : Metrics::
1136                  kWiFiConnetionStatusAfterWakeOnWiFiDisabledWakeNotConnected);
1137  }
1138#endif  // DISABLE_WAKE_ON_WIFI
1139}
1140
1141void WakeOnWiFi::OnNoAutoConnectableServicesAfterScan(
1142    bool has_service_configured_for_autoconnect,
1143    const Closure &remove_supplicant_networks_callback) {
1144#if !defined(DISABLE_WAKE_ON_WIFI)
1145  SLOG(this, 3) << __func__ << ": "
1146                << (in_dark_resume_ ? "In dark resume" : "Not in dark resume");
1147  if (in_dark_resume_) {
1148    // Assume that if there are no services available for auto-connect, then we
1149    // cannot be connected. Therefore, no need for lease renewal parameters.
1150    BeforeSuspendActions(false, has_service_configured_for_autoconnect, false,
1151                         0, remove_supplicant_networks_callback);
1152  }
1153#endif  // DISABLE_WAKE_ON_WIFI
1154}
1155
1156}  // namespace shill
1157