1//
2// Copyright (C) 2014 The Android Open Source Project
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//      http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16
17#include "shill/wifi/wake_on_wifi.h"
18
19#include <errno.h>
20#include <linux/nl80211.h>
21#include <stdio.h>
22
23#include <algorithm>
24#include <set>
25#include <string>
26#include <utility>
27#include <vector>
28
29#include <base/cancelable_callback.h>
30#if defined(__ANDROID__)
31#include <dbus/service_constants.h>
32#else
33#include <chromeos/dbus/service_constants.h>
34#endif  // __ANDROID__
35
36#include "shill/error.h"
37#include "shill/event_dispatcher.h"
38#include "shill/ip_address_store.h"
39#include "shill/logging.h"
40#include "shill/metrics.h"
41#include "shill/net/event_history.h"
42#include "shill/net/netlink_manager.h"
43#include "shill/net/nl80211_message.h"
44#include "shill/property_accessor.h"
45#include "shill/wifi/wifi.h"
46
47using base::Bind;
48using base::Closure;
49using std::pair;
50using std::set;
51using std::string;
52using std::vector;
53
54namespace shill {
55
56namespace Logging {
57static auto kModuleLogScope = ScopeLogger::kWiFi;
58static std::string ObjectID(WakeOnWiFi* w) { return "(wake_on_wifi)"; }
59}
60
61const char WakeOnWiFi::kWakeOnIPAddressPatternsNotSupported[] =
62    "Wake on IP address patterns not supported by this WiFi device";
63const char WakeOnWiFi::kWakeOnWiFiNotSupported[] = "Wake on WiFi not supported";
64const int WakeOnWiFi::kVerifyWakeOnWiFiSettingsDelayMilliseconds = 300;
65const int WakeOnWiFi::kMaxSetWakeOnPacketRetries = 2;
66const int WakeOnWiFi::kMetricsReportingFrequencySeconds = 600;
67const uint32_t WakeOnWiFi::kDefaultWakeToScanPeriodSeconds = 900;
68const uint32_t WakeOnWiFi::kDefaultNetDetectScanPeriodSeconds = 120;
69const uint32_t WakeOnWiFi::kImmediateDHCPLeaseRenewalThresholdSeconds = 60;
70// We tolerate no more than 3 dark resumes per minute and 10 dark resumes per
71// 10 minutes  before we disable wake on WiFi on the NIC.
72const int WakeOnWiFi::kDarkResumeFrequencySamplingPeriodShortMinutes = 1;
73const int WakeOnWiFi::kDarkResumeFrequencySamplingPeriodLongMinutes = 10;
74const int WakeOnWiFi::kMaxDarkResumesPerPeriodShort = 3;
75const int WakeOnWiFi::kMaxDarkResumesPerPeriodLong = 10;
76// If a connection is not established during dark resume, give up and prepare
77// the system to wake on SSID 1 second before suspending again.
78// TODO(samueltan): link this to
79// Manager::kTerminationActionsTimeoutMilliseconds rather than hard-coding
80// this value.
81int64_t WakeOnWiFi::DarkResumeActionsTimeoutMilliseconds = 18500;
82// Scanning 1 frequency takes ~100ms, so retrying 5 times on 8 frequencies will
83// take about 4 seconds, which is how long a full scan typically takes.
84const int WakeOnWiFi::kMaxFreqsForDarkResumeScanRetries = 8;
85const int WakeOnWiFi::kMaxDarkResumeScanRetries = 5;
86const char WakeOnWiFi::kWakeReasonStringPattern[] = "WiFi.Pattern";
87const char WakeOnWiFi::kWakeReasonStringDisconnect[] = "WiFi.Disconnect";
88const char WakeOnWiFi::kWakeReasonStringSSID[] = "WiFi.SSID";
89
90WakeOnWiFi::WakeOnWiFi(
91    NetlinkManager* netlink_manager, EventDispatcher* dispatcher,
92    Metrics* metrics,
93    RecordWakeReasonCallback record_wake_reason_callback)
94    : dispatcher_(dispatcher),
95      netlink_manager_(netlink_manager),
96      metrics_(metrics),
97      report_metrics_callback_(
98          Bind(&WakeOnWiFi::ReportMetrics, base::Unretained(this))),
99      num_set_wake_on_packet_retries_(0),
100      wake_on_wifi_max_patterns_(0),
101      wake_on_wifi_max_ssids_(0),
102      wiphy_index_(0),
103      wiphy_index_received_(false),
104#if defined(DISABLE_WAKE_ON_WIFI)
105      wake_on_wifi_features_enabled_(kWakeOnWiFiFeaturesEnabledNotSupported),
106#else
107      // Wake on WiFi features disabled by default at run-time for boards that
108      // support wake on WiFi. Rely on Chrome to enable appropriate features via
109      // DBus.
110      wake_on_wifi_features_enabled_(kWakeOnWiFiFeaturesEnabledNone),
111#endif  // DISABLE_WAKE_ON_WIFI
112      in_dark_resume_(false),
113      wake_to_scan_period_seconds_(kDefaultWakeToScanPeriodSeconds),
114      net_detect_scan_period_seconds_(kDefaultNetDetectScanPeriodSeconds),
115      last_wake_reason_(kWakeTriggerUnsupported),
116      force_wake_to_scan_timer_(false),
117      dark_resume_scan_retries_left_(0),
118      record_wake_reason_callback_(record_wake_reason_callback),
119      weak_ptr_factory_(this) {
120  netlink_manager_->AddBroadcastHandler(Bind(
121      &WakeOnWiFi::OnWakeupReasonReceived, weak_ptr_factory_.GetWeakPtr()));
122}
123
124WakeOnWiFi::~WakeOnWiFi() {}
125
126void WakeOnWiFi::InitPropertyStore(PropertyStore* store) {
127  store->RegisterDerivedString(
128      kWakeOnWiFiFeaturesEnabledProperty,
129      StringAccessor(new CustomAccessor<WakeOnWiFi, string>(
130          this, &WakeOnWiFi::GetWakeOnWiFiFeaturesEnabled,
131          &WakeOnWiFi::SetWakeOnWiFiFeaturesEnabled)));
132  store->RegisterUint32(kWakeToScanPeriodSecondsProperty,
133                        &wake_to_scan_period_seconds_);
134  store->RegisterUint32(kNetDetectScanPeriodSecondsProperty,
135                        &net_detect_scan_period_seconds_);
136  store->RegisterBool(kForceWakeToScanTimerProperty,
137                      &force_wake_to_scan_timer_);
138}
139
140void WakeOnWiFi::StartMetricsTimer() {
141#if !defined(DISABLE_WAKE_ON_WIFI)
142  dispatcher_->PostDelayedTask(report_metrics_callback_.callback(),
143                               kMetricsReportingFrequencySeconds * 1000);
144#endif  // DISABLE_WAKE_ON_WIFI
145}
146
147string WakeOnWiFi::GetWakeOnWiFiFeaturesEnabled(Error* error) {
148  return wake_on_wifi_features_enabled_;
149}
150
151bool WakeOnWiFi::SetWakeOnWiFiFeaturesEnabled(const std::string& enabled,
152                                              Error* error) {
153#if defined(DISABLE_WAKE_ON_WIFI)
154  error->Populate(Error::kNotSupported, kWakeOnWiFiNotSupported);
155  SLOG(this, 7) << __func__ << ": " << kWakeOnWiFiNotSupported;
156  return false;
157#else
158  if (wake_on_wifi_features_enabled_ == enabled) {
159    return false;
160  }
161  if (enabled != kWakeOnWiFiFeaturesEnabledPacket &&
162      enabled != kWakeOnWiFiFeaturesEnabledDarkConnect &&
163      enabled != kWakeOnWiFiFeaturesEnabledPacketDarkConnect &&
164      enabled != kWakeOnWiFiFeaturesEnabledNone) {
165    Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments,
166                          "Invalid Wake on WiFi feature");
167    return false;
168  }
169  wake_on_wifi_features_enabled_ = enabled;
170  return true;
171#endif  // DISABLE_WAKE_ON_WIFI
172}
173
174void WakeOnWiFi::RunAndResetSuspendActionsDoneCallback(const Error& error) {
175  if (!suspend_actions_done_callback_.is_null()) {
176    suspend_actions_done_callback_.Run(error);
177    suspend_actions_done_callback_.Reset();
178  }
179}
180
181// static
182bool WakeOnWiFi::ByteStringPairIsLessThan(
183    const std::pair<ByteString, ByteString>& lhs,
184    const std::pair<ByteString, ByteString>& rhs) {
185  // Treat the first value of the pair as the key.
186  return ByteString::IsLessThan(lhs.first, rhs.first);
187}
188
189// static
190void WakeOnWiFi::SetMask(ByteString* mask, uint32_t pattern_len,
191                         uint32_t offset) {
192  // Round up number of bytes required for the mask.
193  int result_mask_len = (pattern_len + 8 - 1) / 8;
194  vector<unsigned char> result_mask(result_mask_len, 0);
195  // Set mask bits from offset to (pattern_len - 1)
196  int mask_index;
197  for (uint32_t curr_mask_bit = offset; curr_mask_bit < pattern_len;
198       ++curr_mask_bit) {
199    mask_index = curr_mask_bit / 8;
200    result_mask[mask_index] |= 1 << (curr_mask_bit % 8);
201  }
202  mask->Clear();
203  mask->Append(ByteString(result_mask));
204}
205
206// static
207bool WakeOnWiFi::CreateIPAddressPatternAndMask(const IPAddress& ip_addr,
208                                               ByteString* pattern,
209                                               ByteString* mask) {
210  if (ip_addr.family() == IPAddress::kFamilyIPv4) {
211    WakeOnWiFi::CreateIPV4PatternAndMask(ip_addr, pattern, mask);
212    return true;
213  } else if (ip_addr.family() == IPAddress::kFamilyIPv6) {
214    WakeOnWiFi::CreateIPV6PatternAndMask(ip_addr, pattern, mask);
215    return true;
216  } else {
217    LOG(ERROR) << "Unrecognized IP Address type.";
218    return false;
219  }
220}
221
222// static
223void WakeOnWiFi::CreateIPV4PatternAndMask(const IPAddress& ip_addr,
224                                          ByteString* pattern,
225                                          ByteString* mask) {
226  struct {
227    struct ethhdr eth_hdr;
228    struct iphdr ipv4_hdr;
229  } __attribute__((__packed__)) pattern_bytes;
230  memset(&pattern_bytes, 0, sizeof(pattern_bytes));
231  CHECK_EQ(sizeof(pattern_bytes.ipv4_hdr.saddr), ip_addr.GetLength());
232  memcpy(&pattern_bytes.ipv4_hdr.saddr, ip_addr.GetConstData(),
233         ip_addr.GetLength());
234  int src_ip_offset =
235      reinterpret_cast<unsigned char*>(&pattern_bytes.ipv4_hdr.saddr) -
236      reinterpret_cast<unsigned char*>(&pattern_bytes);
237  int pattern_len = src_ip_offset + ip_addr.GetLength();
238  pattern->Clear();
239  pattern->Append(ByteString(
240      reinterpret_cast<const unsigned char*>(&pattern_bytes), pattern_len));
241  WakeOnWiFi::SetMask(mask, pattern_len, src_ip_offset);
242}
243
244// static
245void WakeOnWiFi::CreateIPV6PatternAndMask(const IPAddress& ip_addr,
246                                          ByteString* pattern,
247                                          ByteString* mask) {
248  struct {
249    struct ethhdr eth_hdr;
250    struct ip6_hdr ipv6_hdr;
251  } __attribute__((__packed__)) pattern_bytes;
252  memset(&pattern_bytes, 0, sizeof(pattern_bytes));
253  CHECK_EQ(sizeof(pattern_bytes.ipv6_hdr.ip6_src), ip_addr.GetLength());
254  memcpy(&pattern_bytes.ipv6_hdr.ip6_src, ip_addr.GetConstData(),
255         ip_addr.GetLength());
256  int src_ip_offset =
257      reinterpret_cast<unsigned char*>(&pattern_bytes.ipv6_hdr.ip6_src) -
258      reinterpret_cast<unsigned char*>(&pattern_bytes);
259  int pattern_len = src_ip_offset + ip_addr.GetLength();
260  pattern->Clear();
261  pattern->Append(ByteString(
262      reinterpret_cast<const unsigned char*>(&pattern_bytes), pattern_len));
263  WakeOnWiFi::SetMask(mask, pattern_len, src_ip_offset);
264}
265
266// static
267bool WakeOnWiFi::ConfigureWiphyIndex(Nl80211Message* msg, int32_t index) {
268  if (!msg->attributes()->CreateU32Attribute(NL80211_ATTR_WIPHY,
269                                             "WIPHY index")) {
270    return false;
271  }
272  if (!msg->attributes()->SetU32AttributeValue(NL80211_ATTR_WIPHY, index)) {
273    return false;
274  }
275  return true;
276}
277
278// static
279bool WakeOnWiFi::ConfigureDisableWakeOnWiFiMessage(
280    SetWakeOnPacketConnMessage* msg, uint32_t wiphy_index, Error* error) {
281  if (!ConfigureWiphyIndex(msg, wiphy_index)) {
282    Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
283                          "Failed to configure Wiphy index.");
284    return false;
285  }
286  return true;
287}
288
289// static
290bool WakeOnWiFi::ConfigureSetWakeOnWiFiSettingsMessage(
291    SetWakeOnPacketConnMessage* msg, const set<WakeOnWiFiTrigger>& trigs,
292    const IPAddressStore& addrs, uint32_t wiphy_index,
293    uint32_t net_detect_scan_period_seconds,
294    const vector<ByteString>& ssid_whitelist,
295    Error* error) {
296#if defined(DISABLE_WAKE_ON_WIFI)
297  return false;
298#else
299  if (trigs.empty()) {
300    Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments,
301                          "No triggers to configure.");
302    return false;
303  }
304  if (trigs.find(kWakeTriggerPattern) != trigs.end() && addrs.Empty()) {
305    Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments,
306                          "No IP addresses to configure.");
307    return false;
308  }
309  if (!ConfigureWiphyIndex(msg, wiphy_index)) {
310    Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
311                          "Failed to configure Wiphy index.");
312    return false;
313  }
314  if (!msg->attributes()->CreateNestedAttribute(NL80211_ATTR_WOWLAN_TRIGGERS,
315                                                "WoWLAN Triggers")) {
316    Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
317                          "Could not create nested attribute "
318                          "NL80211_ATTR_WOWLAN_TRIGGERS");
319    return false;
320  }
321  if (!msg->attributes()->SetNestedAttributeHasAValue(
322          NL80211_ATTR_WOWLAN_TRIGGERS)) {
323    Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
324                          "Could not set nested attribute "
325                          "NL80211_ATTR_WOWLAN_TRIGGERS");
326    return false;
327  }
328
329  AttributeListRefPtr triggers;
330  if (!msg->attributes()->GetNestedAttributeList(NL80211_ATTR_WOWLAN_TRIGGERS,
331                                                 &triggers)) {
332    Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
333                          "Could not get nested attribute list "
334                          "NL80211_ATTR_WOWLAN_TRIGGERS");
335    return false;
336  }
337  // Add triggers.
338  for (WakeOnWiFiTrigger t : trigs) {
339    switch (t) {
340      case kWakeTriggerDisconnect: {
341        if (!triggers->CreateFlagAttribute(NL80211_WOWLAN_TRIG_DISCONNECT,
342                                           "Wake on Disconnect")) {
343          LOG(ERROR) << __func__ << "Could not create flag attribute "
344                                    "NL80211_WOWLAN_TRIG_DISCONNECT";
345          return false;
346        }
347        if (!triggers->SetFlagAttributeValue(NL80211_WOWLAN_TRIG_DISCONNECT,
348                                             true)) {
349          LOG(ERROR) << __func__ << "Could not set flag attribute "
350                                    "NL80211_WOWLAN_TRIG_DISCONNECT";
351          return false;
352        }
353        break;
354      }
355      case kWakeTriggerPattern: {
356        if (!triggers->CreateNestedAttribute(NL80211_WOWLAN_TRIG_PKT_PATTERN,
357                                             "Pattern trigger")) {
358          Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
359                                "Could not create nested attribute "
360                                "NL80211_WOWLAN_TRIG_PKT_PATTERN");
361          return false;
362        }
363        if (!triggers->SetNestedAttributeHasAValue(
364                NL80211_WOWLAN_TRIG_PKT_PATTERN)) {
365          Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
366                                "Could not set nested attribute "
367                                "NL80211_WOWLAN_TRIG_PKT_PATTERN");
368          return false;
369        }
370        AttributeListRefPtr patterns;
371        if (!triggers->GetNestedAttributeList(NL80211_WOWLAN_TRIG_PKT_PATTERN,
372                                              &patterns)) {
373          Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
374                                "Could not get nested attribute list "
375                                "NL80211_WOWLAN_TRIG_PKT_PATTERN");
376          return false;
377        }
378        uint8_t patnum = 1;
379        for (const IPAddress& addr : addrs.GetIPAddresses()) {
380          if (!CreateSinglePattern(addr, patterns, patnum++, error)) {
381            return false;
382          }
383        }
384        break;
385      }
386      case kWakeTriggerSSID: {
387        if (!triggers->CreateNestedAttribute(NL80211_WOWLAN_TRIG_NET_DETECT,
388                                             "Wake on SSID trigger")) {
389          Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
390                                "Could not create nested attribute "
391                                "NL80211_WOWLAN_TRIG_NET_DETECT");
392          return false;
393        }
394        if (!triggers->SetNestedAttributeHasAValue(
395                NL80211_WOWLAN_TRIG_NET_DETECT)) {
396          Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
397                                "Could not set nested attribute "
398                                "NL80211_WOWLAN_TRIG_NET_DETECT");
399          return false;
400        }
401        AttributeListRefPtr scan_attributes;
402        if (!triggers->GetNestedAttributeList(NL80211_WOWLAN_TRIG_NET_DETECT,
403                                              &scan_attributes)) {
404          Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
405                                "Could not get nested attribute list "
406                                "NL80211_WOWLAN_TRIG_NET_DETECT");
407          return false;
408        }
409        if (!scan_attributes->CreateU32Attribute(
410                NL80211_ATTR_SCHED_SCAN_INTERVAL,
411                "NL80211_ATTR_SCHED_SCAN_INTERVAL")) {
412          Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
413                                "Could not get create U32 attribute "
414                                "NL80211_ATTR_SCHED_SCAN_INTERVAL");
415          return false;
416        }
417        if (!scan_attributes->SetU32AttributeValue(
418                NL80211_ATTR_SCHED_SCAN_INTERVAL,
419                net_detect_scan_period_seconds * 1000)) {
420          Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
421                                "Could not get set U32 attribute "
422                                "NL80211_ATTR_SCHED_SCAN_INTERVAL");
423          return false;
424        }
425        if (!scan_attributes->CreateNestedAttribute(
426                NL80211_ATTR_SCHED_SCAN_MATCH,
427                "NL80211_ATTR_SCHED_SCAN_MATCH")) {
428          Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
429                                "Could not create nested attribute list "
430                                "NL80211_ATTR_SCHED_SCAN_MATCH");
431          return false;
432        }
433        if (!scan_attributes->SetNestedAttributeHasAValue(
434                NL80211_ATTR_SCHED_SCAN_MATCH)) {
435          Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
436                                "Could not set nested attribute "
437                                "NL80211_ATTR_SCAN_SSIDS");
438          return false;
439        }
440        AttributeListRefPtr ssids;
441        if (!scan_attributes->GetNestedAttributeList(
442                NL80211_ATTR_SCHED_SCAN_MATCH, &ssids)) {
443          Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
444                                "Could not get nested attribute list "
445                                "NL80211_ATTR_SCHED_SCAN_MATCH");
446          return false;
447        }
448        int ssid_num = 0;
449        for (const ByteString& ssid_bytes :
450             ssid_whitelist) {
451          if (!ssids->CreateNestedAttribute(
452                  ssid_num, "NL80211_ATTR_SCHED_SCAN_MATCH_SINGLE")) {
453            Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
454                                  "Could not create nested attribute list "
455                                  "NL80211_ATTR_SCHED_SCAN_MATCH_SINGLE");
456            return false;
457          }
458          if (!ssids->SetNestedAttributeHasAValue(ssid_num)) {
459            Error::PopulateAndLog(
460                FROM_HERE, error, Error::kOperationFailed,
461                "Could not set value for nested attribute list "
462                "NL80211_ATTR_SCHED_SCAN_MATCH_SINGLE");
463            return false;
464          }
465          AttributeListRefPtr single_ssid;
466          if (!ssids->GetNestedAttributeList(ssid_num, &single_ssid)) {
467            Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
468                                  "Could not get nested attribute list "
469                                  "NL80211_ATTR_SCHED_SCAN_MATCH_SINGLE");
470            return false;
471          }
472          if (!single_ssid->CreateRawAttribute(
473                  NL80211_SCHED_SCAN_MATCH_ATTR_SSID,
474                  "NL80211_SCHED_SCAN_MATCH_ATTR_SSID")) {
475            Error::PopulateAndLog(
476                FROM_HERE, error, Error::kOperationFailed,
477                "Could not create NL80211_SCHED_SCAN_MATCH_ATTR_SSID");
478            return false;
479          }
480          if (!single_ssid->SetRawAttributeValue(
481                  NL80211_SCHED_SCAN_MATCH_ATTR_SSID, ssid_bytes)) {
482            Error::PopulateAndLog(
483                FROM_HERE, error, Error::kOperationFailed,
484                "Could not set NL80211_SCHED_SCAN_MATCH_ATTR_SSID");
485            return false;
486          }
487          ++ssid_num;
488        }
489        break;
490      }
491      default: {
492        LOG(ERROR) << __func__ << ": Unrecognized trigger";
493        return false;
494      }
495    }
496  }
497  return true;
498#endif  // DISABLE_WAKE_ON_WIFI
499}
500
501// static
502bool WakeOnWiFi::CreateSinglePattern(const IPAddress& ip_addr,
503                                     AttributeListRefPtr patterns,
504                                     uint8_t patnum, Error* error) {
505  ByteString pattern;
506  ByteString mask;
507  WakeOnWiFi::CreateIPAddressPatternAndMask(ip_addr, &pattern, &mask);
508  if (!patterns->CreateNestedAttribute(patnum, "Pattern info")) {
509    Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
510                          "Could not create nested attribute "
511                          "patnum for SetWakeOnPacketConnMessage.");
512    return false;
513  }
514  if (!patterns->SetNestedAttributeHasAValue(patnum)) {
515    Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
516                          "Could not set nested attribute "
517                          "patnum for SetWakeOnPacketConnMessage.");
518    return false;
519  }
520
521  AttributeListRefPtr pattern_info;
522  if (!patterns->GetNestedAttributeList(patnum, &pattern_info)) {
523    Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
524                          "Could not get nested attribute list "
525                          "patnum for SetWakeOnPacketConnMessage.");
526    return false;
527  }
528  // Add mask.
529  if (!pattern_info->CreateRawAttribute(NL80211_PKTPAT_MASK, "Mask")) {
530    Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
531                          "Could not add attribute NL80211_PKTPAT_MASK to "
532                          "pattern_info.");
533    return false;
534  }
535  if (!pattern_info->SetRawAttributeValue(NL80211_PKTPAT_MASK, mask)) {
536    Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
537                          "Could not set attribute NL80211_PKTPAT_MASK in "
538                          "pattern_info.");
539    return false;
540  }
541
542  // Add pattern.
543  if (!pattern_info->CreateRawAttribute(NL80211_PKTPAT_PATTERN, "Pattern")) {
544    Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
545                          "Could not add attribute NL80211_PKTPAT_PATTERN to "
546                          "pattern_info.");
547    return false;
548  }
549  if (!pattern_info->SetRawAttributeValue(NL80211_PKTPAT_PATTERN, pattern)) {
550    Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
551                          "Could not set attribute NL80211_PKTPAT_PATTERN in "
552                          "pattern_info.");
553    return false;
554  }
555
556  // Add offset.
557  if (!pattern_info->CreateU32Attribute(NL80211_PKTPAT_OFFSET, "Offset")) {
558    Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
559                          "Could not add attribute NL80211_PKTPAT_OFFSET to "
560                          "pattern_info.");
561    return false;
562  }
563  if (!pattern_info->SetU32AttributeValue(NL80211_PKTPAT_OFFSET, 0)) {
564    Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
565                          "Could not set attribute NL80211_PKTPAT_OFFSET in "
566                          "pattern_info.");
567    return false;
568  }
569  return true;
570}
571
572// static
573bool WakeOnWiFi::ConfigureGetWakeOnWiFiSettingsMessage(
574    GetWakeOnPacketConnMessage* msg, uint32_t wiphy_index, Error* error) {
575  if (!ConfigureWiphyIndex(msg, wiphy_index)) {
576    Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed,
577                          "Failed to configure Wiphy index.");
578    return false;
579  }
580  return true;
581}
582
583// static
584bool WakeOnWiFi::WakeOnWiFiSettingsMatch(
585    const Nl80211Message& msg, const set<WakeOnWiFiTrigger>& trigs,
586    const IPAddressStore& addrs, uint32_t net_detect_scan_period_seconds,
587    const vector<ByteString>& ssid_whitelist) {
588#if defined(DISABLE_WAKE_ON_WIFI)
589  return false;
590#else
591  if (msg.command() != NL80211_CMD_GET_WOWLAN &&
592      msg.command() != NL80211_CMD_SET_WOWLAN) {
593    LOG(ERROR) << __func__ << ": "
594               << "Invalid message command";
595    return false;
596  }
597  AttributeListConstRefPtr triggers;
598  if (!msg.const_attributes()->ConstGetNestedAttributeList(
599          NL80211_ATTR_WOWLAN_TRIGGERS, &triggers)) {
600    // No triggers in the returned message, which is valid iff we expect there
601    // to be no triggers programmed into the NIC.
602    return trigs.empty();
603  }
604  // If we find a trigger in |msg| that we do not have a corresponding flag
605  // for in |trigs|, we have a mismatch.
606  bool unused_flag;
607  AttributeListConstRefPtr unused_list;
608  if (triggers->GetFlagAttributeValue(NL80211_WOWLAN_TRIG_DISCONNECT,
609                                      &unused_flag) &&
610      trigs.find(kWakeTriggerDisconnect) == trigs.end()) {
611    SLOG(WiFi, nullptr, 3)
612        << __func__ << "Wake on disconnect trigger not expected but found";
613    return false;
614  }
615  if (triggers->ConstGetNestedAttributeList(NL80211_WOWLAN_TRIG_PKT_PATTERN,
616                                            &unused_list) &&
617      trigs.find(kWakeTriggerPattern) == trigs.end()) {
618    SLOG(WiFi, nullptr, 3) << __func__
619                           << "Wake on pattern trigger not expected but found";
620    return false;
621  }
622  if (triggers->ConstGetNestedAttributeList(NL80211_WOWLAN_TRIG_NET_DETECT,
623                                            &unused_list) &&
624      trigs.find(kWakeTriggerSSID) == trigs.end()) {
625    SLOG(WiFi, nullptr, 3) << __func__
626                           << "Wake on SSID trigger not expected but found";
627    return false;
628  }
629  // Check that each expected trigger is present in |msg| with matching
630  // setting values.
631  for (WakeOnWiFiTrigger t : trigs) {
632    switch (t) {
633      case kWakeTriggerDisconnect: {
634        bool wake_on_disconnect;
635        if (!triggers->GetFlagAttributeValue(NL80211_WOWLAN_TRIG_DISCONNECT,
636                                             &wake_on_disconnect)) {
637          LOG(ERROR) << __func__ << ": "
638                     << "Could not get the flag NL80211_WOWLAN_TRIG_DISCONNECT";
639          return false;
640        }
641        if (!wake_on_disconnect) {
642          SLOG(WiFi, nullptr, 3) << __func__
643                                 << "Wake on disconnect flag not set.";
644          return false;
645        }
646        break;
647      }
648      case kWakeTriggerPattern: {
649        // Create pattern and masks that we expect to find in |msg|.
650        set<pair<ByteString, ByteString>, decltype(&ByteStringPairIsLessThan)>
651            expected_patt_mask_pairs(ByteStringPairIsLessThan);
652        ByteString temp_pattern;
653        ByteString temp_mask;
654        for (const IPAddress& addr : addrs.GetIPAddresses()) {
655          temp_pattern.Clear();
656          temp_mask.Clear();
657          CreateIPAddressPatternAndMask(addr, &temp_pattern, &temp_mask);
658          expected_patt_mask_pairs.emplace(temp_pattern, temp_mask);
659        }
660        // Check these expected pattern and masks against those actually
661        // contained in |msg|.
662        AttributeListConstRefPtr patterns;
663        if (!triggers->ConstGetNestedAttributeList(
664                NL80211_WOWLAN_TRIG_PKT_PATTERN, &patterns)) {
665          LOG(ERROR) << __func__ << ": "
666                     << "Could not get nested attribute list "
667                        "NL80211_WOWLAN_TRIG_PKT_PATTERN";
668          return false;
669        }
670        bool pattern_mismatch_found = false;
671        size_t pattern_num_mismatch = expected_patt_mask_pairs.size();
672        int pattern_index;
673        AttributeIdIterator pattern_iter(*patterns);
674        AttributeListConstRefPtr pattern_info;
675        ByteString returned_mask;
676        ByteString returned_pattern;
677        while (!pattern_iter.AtEnd()) {
678          returned_mask.Clear();
679          returned_pattern.Clear();
680          pattern_index = pattern_iter.GetId();
681          if (!patterns->ConstGetNestedAttributeList(pattern_index,
682                                                     &pattern_info)) {
683            LOG(ERROR) << __func__ << ": "
684                       << "Could not get nested pattern attribute list #"
685                       << pattern_index;
686            return false;
687          }
688          if (!pattern_info->GetRawAttributeValue(NL80211_PKTPAT_MASK,
689                                                  &returned_mask)) {
690            LOG(ERROR) << __func__ << ": "
691                       << "Could not get attribute NL80211_PKTPAT_MASK";
692            return false;
693          }
694          if (!pattern_info->GetRawAttributeValue(NL80211_PKTPAT_PATTERN,
695                                                  &returned_pattern)) {
696            LOG(ERROR) << __func__ << ": "
697                       << "Could not get attribute NL80211_PKTPAT_PATTERN";
698            return false;
699          }
700          if (expected_patt_mask_pairs.find(pair<ByteString, ByteString>(
701                  returned_pattern, returned_mask)) ==
702              expected_patt_mask_pairs.end()) {
703            pattern_mismatch_found = true;
704            break;
705          } else {
706            --pattern_num_mismatch;
707          }
708          pattern_iter.Advance();
709        }
710        if (pattern_mismatch_found || pattern_num_mismatch) {
711          SLOG(WiFi, nullptr, 3) << __func__
712                                 << "Wake on pattern pattern/mask mismatch";
713          return false;
714        }
715        break;
716      }
717      case kWakeTriggerSSID: {
718        set<ByteString, decltype(&ByteString::IsLessThan)> expected_ssids(
719            ssid_whitelist.begin(), ssid_whitelist.end(),
720            ByteString::IsLessThan);
721        AttributeListConstRefPtr scan_attributes;
722        if (!triggers->ConstGetNestedAttributeList(
723                NL80211_WOWLAN_TRIG_NET_DETECT, &scan_attributes)) {
724          LOG(ERROR) << __func__ << ": "
725                     << "Could not get nested attribute list "
726                        "NL80211_WOWLAN_TRIG_NET_DETECT";
727          return false;
728        }
729        uint32_t interval;
730        if (!scan_attributes->GetU32AttributeValue(
731                NL80211_ATTR_SCHED_SCAN_INTERVAL, &interval)) {
732          LOG(ERROR) << __func__ << ": "
733                     << "Could not get set U32 attribute "
734                        "NL80211_ATTR_SCHED_SCAN_INTERVAL";
735          return false;
736        }
737        if (interval != net_detect_scan_period_seconds * 1000) {
738          SLOG(WiFi, nullptr, 3) << __func__
739                                 << "Net Detect scan period mismatch";
740          return false;
741        }
742        AttributeListConstRefPtr ssids;
743        if (!scan_attributes->ConstGetNestedAttributeList(
744                NL80211_ATTR_SCHED_SCAN_MATCH, &ssids)) {
745          LOG(ERROR) << __func__ << ": "
746                     << "Could not get nested attribute list "
747                        "NL80211_ATTR_SCHED_SCAN_MATCH";
748          return false;
749        }
750        bool ssid_mismatch_found = false;
751        size_t ssid_num_mismatch = expected_ssids.size();
752        AttributeIdIterator ssid_iter(*ssids);
753        AttributeListConstRefPtr single_ssid;
754        ByteString ssid;
755        int ssid_index;
756        while (!ssid_iter.AtEnd()) {
757          ssid.Clear();
758          ssid_index = ssid_iter.GetId();
759          if (!ssids->ConstGetNestedAttributeList(ssid_index, &single_ssid)) {
760            LOG(ERROR) << __func__ << ": "
761                       << "Could not get nested ssid attribute list #"
762                       << ssid_index;
763            return false;
764          }
765          if (!single_ssid->GetRawAttributeValue(
766                  NL80211_SCHED_SCAN_MATCH_ATTR_SSID, &ssid)) {
767            LOG(ERROR) << __func__ << ": "
768                       << "Could not get attribute "
769                          "NL80211_SCHED_SCAN_MATCH_ATTR_SSID";
770            return false;
771          }
772          if (expected_ssids.find(ssid) == expected_ssids.end()) {
773            ssid_mismatch_found = true;
774            break;
775          } else {
776            --ssid_num_mismatch;
777          }
778          ssid_iter.Advance();
779        }
780        if (ssid_mismatch_found || ssid_num_mismatch) {
781          SLOG(WiFi, nullptr, 3) << __func__ << "Net Detect SSID mismatch";
782          return false;
783        }
784        break;
785      }
786      default: {
787        LOG(ERROR) << __func__ << ": Unrecognized trigger";
788        return false;
789      }
790    }
791  }
792  return true;
793#endif  // DISABLE_WAKE_ON_WIFI
794}
795
796void WakeOnWiFi::AddWakeOnPacketConnection(const string& ip_endpoint,
797                                           Error* error) {
798#if !defined(DISABLE_WAKE_ON_WIFI)
799  if (wake_on_wifi_triggers_supported_.find(kWakeTriggerPattern) ==
800      wake_on_wifi_triggers_supported_.end()) {
801    Error::PopulateAndLog(FROM_HERE, error, Error::kNotSupported,
802                          kWakeOnIPAddressPatternsNotSupported);
803    return;
804  }
805  IPAddress ip_addr(ip_endpoint);
806  if (!ip_addr.IsValid()) {
807    Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments,
808                          "Invalid ip_address " + ip_endpoint);
809    return;
810  }
811  if (wake_on_packet_connections_.Count() >= wake_on_wifi_max_patterns_) {
812    Error::PopulateAndLog(
813        FROM_HERE, error, Error::kOperationFailed,
814        "Max number of IP address patterns already registered");
815    return;
816  }
817  wake_on_packet_connections_.AddUnique(ip_addr);
818#else
819  error->Populate(Error::kNotSupported, kWakeOnWiFiNotSupported);
820  SLOG(this, 7) << __func__ << ": " << kWakeOnWiFiNotSupported;
821#endif  // DISABLE_WAKE_ON_WIFI
822}
823
824void WakeOnWiFi::RemoveWakeOnPacketConnection(const string& ip_endpoint,
825                                              Error* error) {
826#if !defined(DISABLE_WAKE_ON_WIFI)
827  if (wake_on_wifi_triggers_supported_.find(kWakeTriggerPattern) ==
828      wake_on_wifi_triggers_supported_.end()) {
829    Error::PopulateAndLog(FROM_HERE, error, Error::kNotSupported,
830                          kWakeOnIPAddressPatternsNotSupported);
831    return;
832  }
833  IPAddress ip_addr(ip_endpoint);
834  if (!ip_addr.IsValid()) {
835    Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments,
836                          "Invalid ip_address " + ip_endpoint);
837    return;
838  }
839  if (!wake_on_packet_connections_.Contains(ip_addr)) {
840    Error::PopulateAndLog(FROM_HERE, error, Error::kNotFound,
841                          "No such IP address match registered to wake device");
842    return;
843  }
844  wake_on_packet_connections_.Remove(ip_addr);
845#else
846  error->Populate(Error::kNotSupported, kWakeOnWiFiNotSupported);
847  SLOG(this, 7) << __func__ << ": " << kWakeOnWiFiNotSupported;
848#endif  // DISABLE_WAKE_ON_WIFI
849}
850
851void WakeOnWiFi::RemoveAllWakeOnPacketConnections(Error* error) {
852#if !defined(DISABLE_WAKE_ON_WIFI)
853  if (wake_on_wifi_triggers_supported_.find(kWakeTriggerPattern) ==
854      wake_on_wifi_triggers_supported_.end()) {
855    Error::PopulateAndLog(FROM_HERE, error, Error::kNotSupported,
856                          kWakeOnIPAddressPatternsNotSupported);
857    return;
858  }
859  wake_on_packet_connections_.Clear();
860#else
861  error->Populate(Error::kNotSupported, kWakeOnWiFiNotSupported);
862  SLOG(this, 7) << __func__ << ": " << kWakeOnWiFiNotSupported;
863#endif  // DISABLE_WAKE_ON_WIFI
864}
865
866void WakeOnWiFi::OnWakeOnWiFiSettingsErrorResponse(
867    NetlinkManager::AuxilliaryMessageType type,
868    const NetlinkMessage* raw_message) {
869  Error error(Error::kOperationFailed);
870  switch (type) {
871    case NetlinkManager::kErrorFromKernel:
872      if (!raw_message) {
873        error.Populate(Error::kOperationFailed, "Unknown error from kernel");
874        break;
875      }
876      if (raw_message->message_type() == ErrorAckMessage::GetMessageType()) {
877        const ErrorAckMessage* error_ack_message =
878            static_cast<const ErrorAckMessage*>(raw_message);
879        if (error_ack_message->error() == EOPNOTSUPP) {
880          error.Populate(Error::kNotSupported);
881        }
882      }
883      break;
884
885    case NetlinkManager::kUnexpectedResponseType:
886      error.Populate(Error::kNotRegistered,
887                     "Message not handled by regular message handler:");
888      break;
889
890    case NetlinkManager::kTimeoutWaitingForResponse:
891      // CMD_SET_WOWLAN messages do not receive responses, so this error type
892      // is received when NetlinkManager times out the message handler. Return
893      // immediately rather than run the done callback since this event does
894      // not signify the completion of suspend actions.
895      return;
896      break;
897
898    default:
899      error.Populate(
900          Error::kOperationFailed,
901          "Unexpected auxilliary message type: " + std::to_string(type));
902      break;
903  }
904  RunAndResetSuspendActionsDoneCallback(error);
905}
906
907// static
908void WakeOnWiFi::OnSetWakeOnPacketConnectionResponse(
909    const Nl80211Message& nl80211_message) {
910  // NOP because kernel does not send a response to NL80211_CMD_SET_WOWLAN
911  // requests.
912}
913
914void WakeOnWiFi::RequestWakeOnPacketSettings() {
915  SLOG(this, 3) << __func__;
916  Error e;
917  GetWakeOnPacketConnMessage get_wowlan_msg;
918  CHECK(wiphy_index_received_);
919  if (!ConfigureGetWakeOnWiFiSettingsMessage(&get_wowlan_msg, wiphy_index_,
920                                             &e)) {
921    LOG(ERROR) << e.message();
922    return;
923  }
924  netlink_manager_->SendNl80211Message(
925      &get_wowlan_msg, Bind(&WakeOnWiFi::VerifyWakeOnWiFiSettings,
926                            weak_ptr_factory_.GetWeakPtr()),
927      Bind(&NetlinkManager::OnAckDoNothing),
928      Bind(&NetlinkManager::OnNetlinkMessageError));
929}
930
931void WakeOnWiFi::VerifyWakeOnWiFiSettings(
932    const Nl80211Message& nl80211_message) {
933  SLOG(this, 3) << __func__;
934  if (WakeOnWiFiSettingsMatch(nl80211_message, wake_on_wifi_triggers_,
935                              wake_on_packet_connections_,
936                              net_detect_scan_period_seconds_,
937                              wake_on_ssid_whitelist_)) {
938    SLOG(this, 2) << __func__ << ": "
939                  << "Wake on WiFi settings successfully verified";
940    metrics_->NotifyVerifyWakeOnWiFiSettingsResult(
941        Metrics::kVerifyWakeOnWiFiSettingsResultSuccess);
942    RunAndResetSuspendActionsDoneCallback(Error(Error::kSuccess));
943  } else {
944    LOG(ERROR) << __func__ << " failed: discrepancy between wake-on-packet "
945                              "settings on NIC and those in local data "
946                              "structure detected";
947    metrics_->NotifyVerifyWakeOnWiFiSettingsResult(
948        Metrics::kVerifyWakeOnWiFiSettingsResultFailure);
949    RetrySetWakeOnPacketConnections();
950  }
951}
952
953void WakeOnWiFi::ApplyWakeOnWiFiSettings() {
954  SLOG(this, 3) << __func__;
955  if (!wiphy_index_received_) {
956    LOG(ERROR) << "Interface index not yet received";
957    return;
958  }
959  if (wake_on_wifi_triggers_.empty()) {
960    SLOG(this, 1) << "No triggers to be programmed, so disable wake on WiFi";
961    DisableWakeOnWiFi();
962    return;
963  }
964
965  Error error;
966  SetWakeOnPacketConnMessage set_wowlan_msg;
967  if (!ConfigureSetWakeOnWiFiSettingsMessage(
968          &set_wowlan_msg, wake_on_wifi_triggers_, wake_on_packet_connections_,
969          wiphy_index_, net_detect_scan_period_seconds_,
970          wake_on_ssid_whitelist_, &error)) {
971    LOG(ERROR) << error.message();
972    RunAndResetSuspendActionsDoneCallback(
973        Error(Error::kOperationFailed, error.message()));
974    return;
975  }
976  if (!netlink_manager_->SendNl80211Message(
977          &set_wowlan_msg,
978          Bind(&WakeOnWiFi::OnSetWakeOnPacketConnectionResponse),
979          Bind(&NetlinkManager::OnAckDoNothing),
980          Bind(&WakeOnWiFi::OnWakeOnWiFiSettingsErrorResponse,
981               weak_ptr_factory_.GetWeakPtr()))) {
982    RunAndResetSuspendActionsDoneCallback(
983        Error(Error::kOperationFailed, "SendNl80211Message failed"));
984    return;
985  }
986
987  verify_wake_on_packet_settings_callback_.Reset(
988      Bind(&WakeOnWiFi::RequestWakeOnPacketSettings,
989           weak_ptr_factory_.GetWeakPtr()));
990  dispatcher_->PostDelayedTask(
991      verify_wake_on_packet_settings_callback_.callback(),
992      kVerifyWakeOnWiFiSettingsDelayMilliseconds);
993}
994
995void WakeOnWiFi::DisableWakeOnWiFi() {
996  SLOG(this, 3) << __func__;
997  Error error;
998  SetWakeOnPacketConnMessage disable_wowlan_msg;
999  CHECK(wiphy_index_received_);
1000  if (!ConfigureDisableWakeOnWiFiMessage(&disable_wowlan_msg, wiphy_index_,
1001                                         &error)) {
1002    LOG(ERROR) << error.message();
1003    RunAndResetSuspendActionsDoneCallback(
1004        Error(Error::kOperationFailed, error.message()));
1005    return;
1006  }
1007  wake_on_wifi_triggers_.clear();
1008  if (!netlink_manager_->SendNl80211Message(
1009          &disable_wowlan_msg,
1010          Bind(&WakeOnWiFi::OnSetWakeOnPacketConnectionResponse),
1011          Bind(&NetlinkManager::OnAckDoNothing),
1012          Bind(&WakeOnWiFi::OnWakeOnWiFiSettingsErrorResponse,
1013               weak_ptr_factory_.GetWeakPtr()))) {
1014    RunAndResetSuspendActionsDoneCallback(
1015        Error(Error::kOperationFailed, "SendNl80211Message failed"));
1016    return;
1017  }
1018
1019  verify_wake_on_packet_settings_callback_.Reset(
1020      Bind(&WakeOnWiFi::RequestWakeOnPacketSettings,
1021           weak_ptr_factory_.GetWeakPtr()));
1022  dispatcher_->PostDelayedTask(
1023      verify_wake_on_packet_settings_callback_.callback(),
1024      kVerifyWakeOnWiFiSettingsDelayMilliseconds);
1025}
1026
1027void WakeOnWiFi::RetrySetWakeOnPacketConnections() {
1028  SLOG(this, 3) << __func__;
1029  if (num_set_wake_on_packet_retries_ < kMaxSetWakeOnPacketRetries) {
1030    ApplyWakeOnWiFiSettings();
1031    ++num_set_wake_on_packet_retries_;
1032  } else {
1033    SLOG(this, 3) << __func__ << ": max retry attempts reached";
1034    num_set_wake_on_packet_retries_ = 0;
1035    RunAndResetSuspendActionsDoneCallback(Error(Error::kOperationFailed));
1036  }
1037}
1038
1039bool WakeOnWiFi::WakeOnWiFiPacketEnabledAndSupported() {
1040  if (wake_on_wifi_features_enabled_ == kWakeOnWiFiFeaturesEnabledNone ||
1041      wake_on_wifi_features_enabled_ ==
1042          kWakeOnWiFiFeaturesEnabledNotSupported ||
1043      wake_on_wifi_features_enabled_ == kWakeOnWiFiFeaturesEnabledDarkConnect) {
1044    return false;
1045  }
1046  if (wake_on_wifi_triggers_supported_.find(kWakeTriggerPattern) ==
1047      wake_on_wifi_triggers_supported_.end()) {
1048    return false;
1049  }
1050  return true;
1051}
1052
1053bool WakeOnWiFi::WakeOnWiFiDarkConnectEnabledAndSupported() {
1054  if (wake_on_wifi_features_enabled_ == kWakeOnWiFiFeaturesEnabledNone ||
1055      wake_on_wifi_features_enabled_ ==
1056          kWakeOnWiFiFeaturesEnabledNotSupported ||
1057      wake_on_wifi_features_enabled_ == kWakeOnWiFiFeaturesEnabledPacket) {
1058    return false;
1059  }
1060  if (wake_on_wifi_triggers_supported_.find(kWakeTriggerDisconnect) ==
1061          wake_on_wifi_triggers_supported_.end() ||
1062      wake_on_wifi_triggers_supported_.find(kWakeTriggerSSID) ==
1063          wake_on_wifi_triggers_supported_.end()) {
1064    return false;
1065  }
1066  return true;
1067}
1068
1069void WakeOnWiFi::ReportMetrics() {
1070  Metrics::WakeOnWiFiFeaturesEnabledState reported_state;
1071  if (wake_on_wifi_features_enabled_ == kWakeOnWiFiFeaturesEnabledNone) {
1072    reported_state = Metrics::kWakeOnWiFiFeaturesEnabledStateNone;
1073  } else if (wake_on_wifi_features_enabled_ ==
1074             kWakeOnWiFiFeaturesEnabledPacket) {
1075    reported_state = Metrics::kWakeOnWiFiFeaturesEnabledStatePacket;
1076  } else if (wake_on_wifi_features_enabled_ ==
1077             kWakeOnWiFiFeaturesEnabledDarkConnect) {
1078    reported_state = Metrics::kWakeOnWiFiFeaturesEnabledStateDarkConnect;
1079  } else if (wake_on_wifi_features_enabled_ ==
1080             kWakeOnWiFiFeaturesEnabledPacketDarkConnect) {
1081    reported_state = Metrics::kWakeOnWiFiFeaturesEnabledStatePacketDarkConnect;
1082  } else {
1083    LOG(ERROR) << __func__ << ": "
1084               << "Invalid wake on WiFi features state";
1085    return;
1086  }
1087  metrics_->NotifyWakeOnWiFiFeaturesEnabledState(reported_state);
1088  StartMetricsTimer();
1089}
1090
1091void WakeOnWiFi::ParseWakeOnWiFiCapabilities(
1092    const Nl80211Message& nl80211_message) {
1093  // Verify NL80211_CMD_NEW_WIPHY.
1094#if !defined(DISABLE_WAKE_ON_WIFI)
1095  if (nl80211_message.command() != NewWiphyMessage::kCommand) {
1096    LOG(ERROR) << "Received unexpected command:" << nl80211_message.command();
1097    return;
1098  }
1099  AttributeListConstRefPtr triggers_supported;
1100  if (nl80211_message.const_attributes()->ConstGetNestedAttributeList(
1101          NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, &triggers_supported)) {
1102    bool disconnect_supported = false;
1103    if (triggers_supported->GetFlagAttributeValue(
1104            NL80211_WOWLAN_TRIG_DISCONNECT, &disconnect_supported)) {
1105      if (disconnect_supported) {
1106        wake_on_wifi_triggers_supported_.insert(
1107            WakeOnWiFi::kWakeTriggerDisconnect);
1108        SLOG(this, 7) << "Waking on disconnect supported by this WiFi device";
1109      }
1110    }
1111    ByteString pattern_data;
1112    if (triggers_supported->GetRawAttributeValue(
1113            NL80211_WOWLAN_TRIG_PKT_PATTERN, &pattern_data)) {
1114      struct nl80211_pattern_support* patt_support =
1115          reinterpret_cast<struct nl80211_pattern_support*>(
1116              pattern_data.GetData());
1117      // Determine the IPV4 and IPV6 pattern lengths we will use by
1118      // constructing dummy patterns and getting their lengths.
1119      ByteString dummy_pattern;
1120      ByteString dummy_mask;
1121      WakeOnWiFi::CreateIPV4PatternAndMask(IPAddress("192.168.0.20"),
1122                                           &dummy_pattern, &dummy_mask);
1123      size_t ipv4_pattern_len = dummy_pattern.GetLength();
1124      WakeOnWiFi::CreateIPV6PatternAndMask(
1125          IPAddress("FEDC:BA98:7654:3210:FEDC:BA98:7654:3210"), &dummy_pattern,
1126          &dummy_mask);
1127      size_t ipv6_pattern_len = dummy_pattern.GetLength();
1128      // Check if the pattern matching capabilities of this WiFi device will
1129      // allow IPV4 and IPV6 patterns to be used.
1130      if (patt_support->min_pattern_len <=
1131              std::min(ipv4_pattern_len, ipv6_pattern_len) &&
1132          patt_support->max_pattern_len >=
1133              std::max(ipv4_pattern_len, ipv6_pattern_len)) {
1134        wake_on_wifi_triggers_supported_.insert(
1135            WakeOnWiFi::kWakeTriggerPattern);
1136        wake_on_wifi_max_patterns_ = patt_support->max_patterns;
1137        SLOG(this, 7) << "Waking on up to " << wake_on_wifi_max_patterns_
1138                      << " registered patterns of "
1139                      << patt_support->min_pattern_len << "-"
1140                      << patt_support->max_pattern_len
1141                      << " bytes supported by this WiFi device";
1142      }
1143    }
1144    if (triggers_supported->GetU32AttributeValue(NL80211_WOWLAN_TRIG_NET_DETECT,
1145                                                 &wake_on_wifi_max_ssids_)) {
1146      wake_on_wifi_triggers_supported_.insert(WakeOnWiFi::kWakeTriggerSSID);
1147      SLOG(this, 7) << "Waking on up to " << wake_on_wifi_max_ssids_
1148                    << " whitelisted SSIDs supported by this WiFi device";
1149    }
1150  }
1151#endif  // DISABLE_WAKE_ON_WIFI
1152}
1153
1154void WakeOnWiFi::OnWakeupReasonReceived(const NetlinkMessage& netlink_message) {
1155#if defined(DISABLE_WAKE_ON_WIFI)
1156  SLOG(this, 7) << __func__ << ": "
1157                << "Wake on WiFi not supported, so do nothing";
1158#else
1159  // We only handle wakeup reason messages in this handler, which is are
1160  // nl80211 messages with the NL80211_CMD_SET_WOWLAN command.
1161  if (netlink_message.message_type() != Nl80211Message::GetMessageType()) {
1162    SLOG(this, 7) << __func__ << ": "
1163                  << "Not a NL80211 Message";
1164    return;
1165  }
1166  const Nl80211Message& wakeup_reason_msg =
1167      *reinterpret_cast<const Nl80211Message*>(&netlink_message);
1168  if (wakeup_reason_msg.command() != SetWakeOnPacketConnMessage::kCommand) {
1169    SLOG(this, 7) << __func__ << ": "
1170                  << "Not a NL80211_CMD_SET_WOWLAN message";
1171    return;
1172  }
1173  uint32_t wiphy_index;
1174  if (!wakeup_reason_msg.const_attributes()->GetU32AttributeValue(
1175          NL80211_ATTR_WIPHY, &wiphy_index)) {
1176    LOG(ERROR) << "NL80211_CMD_NEW_WIPHY had no NL80211_ATTR_WIPHY";
1177    return;
1178  }
1179  if (!wiphy_index_received_) {
1180    SLOG(this, 7) << __func__ << ": "
1181                  << "Interface index not yet received";
1182    return;
1183  }
1184  if (wiphy_index != wiphy_index_) {
1185    SLOG(this, 7) << __func__ << ": "
1186                  << "Wakeup reason not meant for this interface";
1187    return;
1188  }
1189  metrics_->NotifyWakeupReasonReceived();
1190  SLOG(this, 3) << __func__ << ": "
1191                << "Parsing wakeup reason";
1192  AttributeListConstRefPtr triggers;
1193  if (!wakeup_reason_msg.const_attributes()->ConstGetNestedAttributeList(
1194          NL80211_ATTR_WOWLAN_TRIGGERS, &triggers)) {
1195    SLOG(this, 3) << __func__ << ": "
1196                  << "Wakeup reason: Not wake on WiFi related";
1197    return;
1198  }
1199  bool wake_flag;
1200  if (triggers->GetFlagAttributeValue(NL80211_WOWLAN_TRIG_DISCONNECT,
1201                                      &wake_flag)) {
1202    SLOG(this, 3) << __func__ << ": "
1203                  << "Wakeup reason: Disconnect";
1204    last_wake_reason_ = kWakeTriggerDisconnect;
1205    record_wake_reason_callback_.Run(kWakeReasonStringDisconnect);
1206    return;
1207  }
1208  uint32_t wake_pattern_index;
1209  if (triggers->GetU32AttributeValue(NL80211_WOWLAN_TRIG_PKT_PATTERN,
1210                                     &wake_pattern_index)) {
1211    SLOG(this, 3) << __func__ << ": "
1212                  << "Wakeup reason: Pattern " << wake_pattern_index;
1213    last_wake_reason_ = kWakeTriggerPattern;
1214    record_wake_reason_callback_.Run(kWakeReasonStringPattern);
1215    return;
1216  }
1217  AttributeListConstRefPtr results_list;
1218  if (triggers->ConstGetNestedAttributeList(
1219          NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS, &results_list)) {
1220    // It is possible that NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS is present
1221    // along with another wake trigger attribute. What this means is that the
1222    // firmware has detected a network, but the platform did not actually wake
1223    // on the detection of that network. In these cases, we will not parse the
1224    // net detect results; we return after parsing and reporting the actual
1225    // wakeup reason above.
1226    SLOG(this, 3) << __func__ << ": "
1227                  << "Wakeup reason: SSID";
1228    last_wake_reason_ = kWakeTriggerSSID;
1229    record_wake_reason_callback_.Run(kWakeReasonStringSSID);
1230    last_ssid_match_freqs_ = ParseWakeOnSSIDResults(results_list);
1231    return;
1232  }
1233  SLOG(this, 3) << __func__ << ": "
1234                << "Wakeup reason: Not supported";
1235#endif  // DISABLE_WAKE_ON_WIFI
1236}
1237
1238void WakeOnWiFi::OnBeforeSuspend(
1239    bool is_connected,
1240    const vector<ByteString>& ssid_whitelist,
1241    const ResultCallback& done_callback,
1242    const Closure& renew_dhcp_lease_callback,
1243    const Closure& remove_supplicant_networks_callback, bool have_dhcp_lease,
1244    uint32_t time_to_next_lease_renewal) {
1245#if defined(DISABLE_WAKE_ON_WIFI)
1246  // Wake on WiFi not supported, so immediately report success.
1247  done_callback.Run(Error(Error::kSuccess));
1248#else
1249  LOG(INFO) << __func__ << ": Wake on WiFi features enabled: "
1250            << wake_on_wifi_features_enabled_;
1251  suspend_actions_done_callback_ = done_callback;
1252  wake_on_ssid_whitelist_ = ssid_whitelist;
1253  dark_resume_history_.Clear();
1254  if (have_dhcp_lease && is_connected &&
1255      time_to_next_lease_renewal < kImmediateDHCPLeaseRenewalThresholdSeconds) {
1256    // Renew DHCP lease immediately if we have one that is expiring soon.
1257    renew_dhcp_lease_callback.Run();
1258    dispatcher_->PostTask(Bind(&WakeOnWiFi::BeforeSuspendActions,
1259                               weak_ptr_factory_.GetWeakPtr(), is_connected,
1260                               false, time_to_next_lease_renewal,
1261                               remove_supplicant_networks_callback));
1262  } else {
1263    dispatcher_->PostTask(Bind(&WakeOnWiFi::BeforeSuspendActions,
1264                               weak_ptr_factory_.GetWeakPtr(), is_connected,
1265                               have_dhcp_lease, time_to_next_lease_renewal,
1266                               remove_supplicant_networks_callback));
1267  }
1268#endif  // DISABLE_WAKE_ON_WIFI
1269}
1270
1271void WakeOnWiFi::OnAfterResume() {
1272#if !defined(DISABLE_WAKE_ON_WIFI)
1273  SLOG(this, 1) << __func__;
1274  wake_to_scan_timer_.Stop();
1275  dhcp_lease_renewal_timer_.Stop();
1276  if (WakeOnWiFiPacketEnabledAndSupported() ||
1277      WakeOnWiFiDarkConnectEnabledAndSupported()) {
1278    // Unconditionally disable wake on WiFi on resume if these features
1279    // were enabled before the last suspend.
1280    DisableWakeOnWiFi();
1281    metrics_->NotifySuspendWithWakeOnWiFiEnabledDone();
1282  }
1283#endif  // DISABLE_WAKE_ON_WIFI
1284}
1285
1286void WakeOnWiFi::OnDarkResume(
1287    bool is_connected,
1288    const vector<ByteString>& ssid_whitelist,
1289    const ResultCallback& done_callback,
1290    const Closure& renew_dhcp_lease_callback,
1291    const InitiateScanCallback& initiate_scan_callback,
1292    const Closure& remove_supplicant_networks_callback) {
1293#if defined(DISABLE_WAKE_ON_WIFI)
1294  done_callback.Run(Error(Error::kSuccess));
1295#else
1296  LOG(INFO) << __func__ << ": "
1297            << "Wake reason " << last_wake_reason_;
1298  metrics_->NotifyWakeOnWiFiOnDarkResume(last_wake_reason_);
1299  dark_resume_scan_retries_left_ = 0;
1300  suspend_actions_done_callback_ = done_callback;
1301  wake_on_ssid_whitelist_ = ssid_whitelist;
1302
1303  if (last_wake_reason_ == kWakeTriggerSSID ||
1304      last_wake_reason_ == kWakeTriggerDisconnect ||
1305      (last_wake_reason_ == kWakeTriggerUnsupported && !is_connected)) {
1306    // We want to disable wake on WiFi in two specific cases of thrashing:
1307    //   1) Repeatedly waking on SSID in the presence of an AP that the WiFi
1308    //      device cannot connect to
1309    //   2) Repeatedly waking on disconnect because of a an AP that repeatedly
1310    //      disconnects the WiFi device but allows it to reconnect immediately
1311    // Therefore, we only count dark resumes caused by either of these wake
1312    // reasons when deciding whether or not to throttle wake on WiFi.
1313    //
1314    // In case the WiFi driver does not support wake reason reporting, we use
1315    // the WiFi device's connection status on dark resume as a proxy for these
1316    // wake reasons (i.e. when we wake on either SSID or disconnect, we should
1317    // be disconnected). This is not reliable for wake on disconnect, as the
1318    // WiFi device will report that it is connected as it enters dark
1319    // resume (crbug.com/505072).
1320    dark_resume_history_.RecordEvent();
1321  }
1322  if (dark_resume_history_.CountEventsWithinInterval(
1323          kDarkResumeFrequencySamplingPeriodShortMinutes * 60,
1324          EventHistory::kClockTypeBoottime) >= kMaxDarkResumesPerPeriodShort ||
1325      dark_resume_history_.CountEventsWithinInterval(
1326          kDarkResumeFrequencySamplingPeriodLongMinutes * 60,
1327          EventHistory::kClockTypeBoottime) >= kMaxDarkResumesPerPeriodLong) {
1328    LOG(ERROR) << __func__ << ": "
1329               << "Too many dark resumes; disabling wake on WiFi temporarily";
1330    // If too many dark resumes have triggered recently, we are probably
1331    // thrashing. Stop this by disabling wake on WiFi on the NIC, and
1332    // starting the wake to scan timer so that normal wake on WiFi behavior
1333    // resumes only |wake_to_scan_period_seconds_| later.
1334    dhcp_lease_renewal_timer_.Stop();
1335    wake_to_scan_timer_.Start(
1336        FROM_HERE, base::TimeDelta::FromSeconds(wake_to_scan_period_seconds_),
1337        Bind(&WakeOnWiFi::OnTimerWakeDoNothing, base::Unretained(this)));
1338    DisableWakeOnWiFi();
1339    dark_resume_history_.Clear();
1340    metrics_->NotifyWakeOnWiFiThrottled();
1341    last_ssid_match_freqs_.clear();
1342    return;
1343  }
1344
1345  switch (last_wake_reason_) {
1346    case kWakeTriggerPattern: {
1347      // Go back to suspend immediately since packet would have been delivered
1348      // to userspace upon waking in dark resume. Do not reset the lease renewal
1349      // timer since we are not getting a new lease.
1350      dispatcher_->PostTask(Bind(
1351          &WakeOnWiFi::BeforeSuspendActions, weak_ptr_factory_.GetWeakPtr(),
1352          is_connected, false, 0, remove_supplicant_networks_callback));
1353      break;
1354    }
1355    case kWakeTriggerSSID:
1356    case kWakeTriggerDisconnect: {
1357      remove_supplicant_networks_callback.Run();
1358      metrics_->NotifyDarkResumeInitiateScan();
1359      InitiateScanInDarkResume(initiate_scan_callback,
1360                               last_wake_reason_ == kWakeTriggerSSID
1361                                   ? last_ssid_match_freqs_
1362                                   : WiFi::FreqSet());
1363      break;
1364    }
1365    case kWakeTriggerUnsupported:
1366    default: {
1367      if (is_connected) {
1368        renew_dhcp_lease_callback.Run();
1369      } else {
1370        remove_supplicant_networks_callback.Run();
1371        metrics_->NotifyDarkResumeInitiateScan();
1372        InitiateScanInDarkResume(initiate_scan_callback, WiFi::FreqSet());
1373      }
1374    }
1375  }
1376
1377  // Only set dark resume to true after checking if we need to disable wake on
1378  // WiFi since calling WakeOnWiFi::DisableWakeOnWiFi directly bypasses
1379  // WakeOnWiFi::BeforeSuspendActions where |in_dark_resume_| is set to false.
1380  in_dark_resume_ = true;
1381  // Assume that we are disconnected if we time out. Consequently, we do not
1382  // need to start a DHCP lease renewal timer.
1383  dark_resume_actions_timeout_callback_.Reset(
1384      Bind(&WakeOnWiFi::BeforeSuspendActions, weak_ptr_factory_.GetWeakPtr(),
1385           false, false, 0, remove_supplicant_networks_callback));
1386  dispatcher_->PostDelayedTask(dark_resume_actions_timeout_callback_.callback(),
1387                               DarkResumeActionsTimeoutMilliseconds);
1388#endif  // DISABLE_WAKE_ON_WIFI
1389}
1390
1391void WakeOnWiFi::BeforeSuspendActions(
1392    bool is_connected,
1393    bool start_lease_renewal_timer,
1394    uint32_t time_to_next_lease_renewal,
1395    const Closure& remove_supplicant_networks_callback) {
1396  LOG(INFO) << __func__ << ": "
1397            << (is_connected ? "connected" : "not connected");
1398  // Note: No conditional compilation because all entry points to this functions
1399  // are already conditionally compiled based on DISABLE_WAKE_ON_WIFI.
1400
1401  metrics_->NotifyBeforeSuspendActions(is_connected, in_dark_resume_);
1402  last_ssid_match_freqs_.clear();
1403  last_wake_reason_ = kWakeTriggerUnsupported;
1404  // Add relevant triggers to be programmed into the NIC.
1405  wake_on_wifi_triggers_.clear();
1406  if (!wake_on_packet_connections_.Empty() &&
1407      WakeOnWiFiPacketEnabledAndSupported() && is_connected) {
1408    SLOG(this, 3) << __func__ << ": "
1409                  << "Enabling wake on pattern";
1410    wake_on_wifi_triggers_.insert(kWakeTriggerPattern);
1411  }
1412  if (WakeOnWiFiDarkConnectEnabledAndSupported()) {
1413    if (is_connected) {
1414      SLOG(this, 3) << __func__ << ": "
1415                    << "Enabling wake on disconnect";
1416      wake_on_wifi_triggers_.insert(kWakeTriggerDisconnect);
1417      wake_on_wifi_triggers_.erase(kWakeTriggerSSID);
1418      wake_to_scan_timer_.Stop();
1419      if (start_lease_renewal_timer) {
1420        // Timer callback is NO-OP since dark resume logic (the
1421        // kWakeTriggerUnsupported case) will initiate DHCP lease renewal.
1422        dhcp_lease_renewal_timer_.Start(
1423            FROM_HERE, base::TimeDelta::FromSeconds(time_to_next_lease_renewal),
1424            Bind(&WakeOnWiFi::OnTimerWakeDoNothing, base::Unretained(this)));
1425      }
1426    } else {
1427      // Force a disconnect in case supplicant is currently in the process of
1428      // connecting, and remove all networks so scans triggered in dark resume
1429      // are passive.
1430      remove_supplicant_networks_callback.Run();
1431      dhcp_lease_renewal_timer_.Stop();
1432      wake_on_wifi_triggers_.erase(kWakeTriggerDisconnect);
1433      if (!wake_on_ssid_whitelist_.empty()) {
1434        SLOG(this, 3) << __func__ << ": "
1435                      << "Enabling wake on SSID";
1436        wake_on_wifi_triggers_.insert(kWakeTriggerSSID);
1437      }
1438      int num_extra_ssids =
1439          wake_on_ssid_whitelist_.size() - wake_on_wifi_max_ssids_;
1440      if (num_extra_ssids > 0 || force_wake_to_scan_timer_) {
1441        SLOG(this, 3) << __func__ << ": "
1442                      << "Starting wake to scan timer - "
1443                      << (num_extra_ssids > 0 ? "extra SSIDs" : "forced");
1444        if (num_extra_ssids > 0) {
1445          SLOG(this, 3) << __func__ << ": " << num_extra_ssids
1446                        << " extra SSIDs.";
1447        }
1448        // Start wake to scan timer in case the only SSIDs available for
1449        // auto-connect during suspend are the ones that we do not program our
1450        // NIC to wake on.
1451        // Timer callback is NO-OP since dark resume logic (the
1452        // kWakeTriggerUnsupported case) will initiate a passive scan.
1453        wake_to_scan_timer_.Start(
1454            FROM_HERE,
1455            base::TimeDelta::FromSeconds(wake_to_scan_period_seconds_),
1456            Bind(&WakeOnWiFi::OnTimerWakeDoNothing, base::Unretained(this)));
1457        // Trim SSID list to the max size that the NIC supports.
1458        wake_on_ssid_whitelist_.resize(wake_on_wifi_max_ssids_);
1459      }
1460    }
1461  }
1462
1463  // Only call Cancel() here since it deallocates the underlying callback that
1464  // |remove_supplicant_networks_callback| references, which is invoked above.
1465  dark_resume_actions_timeout_callback_.Cancel();
1466
1467  if (!in_dark_resume_ && wake_on_wifi_triggers_.empty()) {
1468    // No need program NIC on normal resume in this case since wake on WiFi
1469    // would already have been disabled on the last (non-dark) resume.
1470    SLOG(this, 1) << "No need to disable wake on WiFi on NIC in regular "
1471                     "suspend";
1472    RunAndResetSuspendActionsDoneCallback(Error(Error::kSuccess));
1473    return;
1474  }
1475
1476  in_dark_resume_ = false;
1477  ApplyWakeOnWiFiSettings();
1478}
1479
1480// static
1481WiFi::FreqSet WakeOnWiFi::ParseWakeOnSSIDResults(
1482    AttributeListConstRefPtr results_list) {
1483  WiFi::FreqSet freqs;
1484  AttributeIdIterator results_iter(*results_list);
1485  if (results_iter.AtEnd()) {
1486    SLOG(WiFi, nullptr, 3) << __func__ << ": "
1487                           << "Wake on SSID results not available";
1488    return freqs;
1489  }
1490  AttributeListConstRefPtr result;
1491  int ssid_num = 0;
1492  for (; !results_iter.AtEnd(); results_iter.Advance()) {
1493    if (!results_list->ConstGetNestedAttributeList(results_iter.GetId(),
1494                                                   &result)) {
1495      LOG(ERROR) << __func__ << ": "
1496                 << "Could not get result #" << results_iter.GetId()
1497                 << " in ssid_results";
1498      return freqs;
1499    }
1500    ByteString ssid_bytestring;
1501    if (!result->GetRawAttributeValue(NL80211_ATTR_SSID, &ssid_bytestring)) {
1502      // We assume that the SSID attribute must be present in each result.
1503      LOG(ERROR) << __func__ << ": "
1504                 << "No SSID available for result #" << results_iter.GetId();
1505      continue;
1506    }
1507    SLOG(WiFi, nullptr, 3) << "SSID " << ssid_num << ": "
1508                           << std::string(ssid_bytestring.GetConstData(),
1509                                          ssid_bytestring.GetConstData() +
1510                                              ssid_bytestring.GetLength());
1511    AttributeListConstRefPtr frequencies;
1512    uint32_t freq_value;
1513    if (result->ConstGetNestedAttributeList(NL80211_ATTR_SCAN_FREQUENCIES,
1514                                            &frequencies)) {
1515      AttributeIdIterator freq_iter(*frequencies);
1516      for (; !freq_iter.AtEnd(); freq_iter.Advance()) {
1517        if (frequencies->GetU32AttributeValue(freq_iter.GetId(), &freq_value)) {
1518          freqs.insert(freq_value);
1519          SLOG(WiFi, nullptr, 7) << "Frequency: " << freq_value;
1520        }
1521      }
1522    } else {
1523      SLOG(WiFi, nullptr, 3) << __func__ << ": "
1524                             << "No frequencies available for result #"
1525                             << results_iter.GetId();
1526    }
1527    ++ssid_num;
1528  }
1529  return freqs;
1530}
1531
1532void WakeOnWiFi::InitiateScanInDarkResume(
1533    const InitiateScanCallback& initiate_scan_callback,
1534    const WiFi::FreqSet& freqs) {
1535  SLOG(this, 3) << __func__;
1536  if (!freqs.empty() && freqs.size() <= kMaxFreqsForDarkResumeScanRetries) {
1537    SLOG(this, 3) << __func__ << ": "
1538                  << "Allowing up to " << kMaxDarkResumeScanRetries
1539                  << " retries for passive scan on " << freqs.size()
1540                  << " frequencies";
1541    dark_resume_scan_retries_left_ = kMaxDarkResumeScanRetries;
1542  }
1543  initiate_scan_callback.Run(freqs);
1544}
1545
1546void WakeOnWiFi::OnConnectedAndReachable(bool start_lease_renewal_timer,
1547                                         uint32_t time_to_next_lease_renewal) {
1548  SLOG(this, 3) << __func__;
1549  if (in_dark_resume_) {
1550#if defined(DISABLE_WAKE_ON_WIFI)
1551    SLOG(this, 3) << "Wake on WiFi not supported, so do nothing";
1552#else
1553    // If we obtain a DHCP lease, we are connected, so the callback to have
1554    // supplicant remove networks will not be invoked in
1555    // WakeOnWiFi::BeforeSuspendActions.
1556    BeforeSuspendActions(true, start_lease_renewal_timer,
1557                         time_to_next_lease_renewal, base::Closure());
1558#endif  // DISABLE_WAKE_ON_WIFI
1559  } else {
1560    SLOG(this, 3) << "Not in dark resume, so do nothing";
1561  }
1562}
1563
1564void WakeOnWiFi::ReportConnectedToServiceAfterWake(bool is_connected) {
1565#if defined(DISABLE_WAKE_ON_WIFI)
1566  metrics_->NotifyConnectedToServiceAfterWake(
1567      is_connected
1568          ? Metrics::kWiFiConnetionStatusAfterWakeOnWiFiDisabledWakeConnected
1569          : Metrics::
1570                kWiFiConnetionStatusAfterWakeOnWiFiDisabledWakeNotConnected);
1571#else
1572  if (WakeOnWiFiDarkConnectEnabledAndSupported()) {
1573    // Only logged if wake on WiFi is supported and wake on SSID was enabled to
1574    // maintain connectivity while suspended.
1575    metrics_->NotifyConnectedToServiceAfterWake(
1576        is_connected
1577            ? Metrics::kWiFiConnetionStatusAfterWakeOnWiFiEnabledWakeConnected
1578            : Metrics::
1579                  kWiFiConnetionStatusAfterWakeOnWiFiEnabledWakeNotConnected);
1580  } else {
1581    metrics_->NotifyConnectedToServiceAfterWake(
1582        is_connected
1583            ? Metrics::kWiFiConnetionStatusAfterWakeOnWiFiDisabledWakeConnected
1584            : Metrics::
1585                  kWiFiConnetionStatusAfterWakeOnWiFiDisabledWakeNotConnected);
1586  }
1587#endif  // DISABLE_WAKE_ON_WIFI
1588}
1589
1590void WakeOnWiFi::OnNoAutoConnectableServicesAfterScan(
1591    const vector<ByteString>& ssid_whitelist,
1592    const Closure& remove_supplicant_networks_callback,
1593    const InitiateScanCallback& initiate_scan_callback) {
1594#if !defined(DISABLE_WAKE_ON_WIFI)
1595  SLOG(this, 3) << __func__ << ": "
1596                << (in_dark_resume_ ? "In dark resume" : "Not in dark resume");
1597  if (!in_dark_resume_) {
1598    return;
1599  }
1600  if (dark_resume_scan_retries_left_) {
1601    --dark_resume_scan_retries_left_;
1602    SLOG(this, 3) << __func__ << ": "
1603                  << "Retrying dark resume scan ("
1604                  << dark_resume_scan_retries_left_ << " tries left)";
1605    metrics_->NotifyDarkResumeScanRetry();
1606    // Note: a scan triggered by supplicant in dark resume might cause a
1607    // retry, but we consider this acceptable.
1608    initiate_scan_callback.Run(last_ssid_match_freqs_);
1609  } else {
1610    wake_on_ssid_whitelist_ = ssid_whitelist;
1611    // Assume that if there are no services available for auto-connect, then we
1612    // cannot be connected. Therefore, no need for lease renewal parameters.
1613    BeforeSuspendActions(false, false, 0, remove_supplicant_networks_callback);
1614  }
1615#endif  // DISABLE_WAKE_ON_WIFI
1616}
1617
1618void WakeOnWiFi::OnWiphyIndexReceived(uint32_t index) {
1619  wiphy_index_ = index;
1620  wiphy_index_received_ = true;
1621}
1622
1623void WakeOnWiFi::OnScanStarted(bool is_active_scan) {
1624  if (!in_dark_resume_) {
1625    return;
1626  }
1627  if (last_wake_reason_ == kWakeTriggerUnsupported ||
1628      last_wake_reason_ == kWakeTriggerPattern) {
1629    // We don't expect active scans to be started when we wake on pattern or
1630    // RTC timers.
1631    if (is_active_scan) {
1632      LOG(ERROR) << "Unexpected active scan launched in dark resume";
1633    }
1634    metrics_->NotifyScanStartedInDarkResume(is_active_scan);
1635  }
1636}
1637
1638}  // namespace shill
1639