1c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// found in the LICENSE file.
4c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
5c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/browser/chromeos/net/network_portal_detector_impl.h"
6c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
7a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include <algorithm>
8a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
9c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/bind.h"
10c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/command_line.h"
11c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/logging.h"
129ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch#include "base/message_loop/message_loop.h"
13c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/metrics/histogram.h"
1403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#include "base/strings/stringprintf.h"
157dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "chrome/browser/chrome_notification_types.h"
16a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "chromeos/dbus/dbus_thread_manager.h"
17a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "chromeos/dbus/shill_profile_client.h"
18116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "chromeos/login/login_state.h"
1903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#include "chromeos/network/network_event_log.h"
2090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chromeos/network/network_state.h"
2190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chromeos/network/network_state_handler.h"
22c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "content/public/browser/notification_service.h"
23116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "content/public/common/content_switches.h"
24c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "net/http/http_status_code.h"
2590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "third_party/cros_system_api/dbus/service_constants.h"
26c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
2703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)using base::StringPrintf;
28c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)using captive_portal::CaptivePortalDetector;
29c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
30c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)namespace chromeos {
31c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
32c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)namespace {
33c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
34c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Delay before portal detection caused by changes in proxy settings.
35c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const int kProxyChangeDelaySec = 1;
36c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// Maximum number of reports from captive portal detector about
385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// offline state in a row before notification is sent to observers.
395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)const int kMaxOfflineResultsBeforeReport = 3;
405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// Delay before portal detection attempt after !ONLINE -> !ONLINE
425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// transition.
435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)const int kShortInitialDelayBetweenAttemptsMs = 600;
445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// Maximum timeout before portal detection attempts after !ONLINE ->
465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// !ONLINE transition.
475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)const int kShortMaximumDelayBetweenAttemptsMs = 2 * 60 * 1000;
485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// Delay before portal detection attempt after !ONLINE -> ONLINE
505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// transition.
515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)const int kLongInitialDelayBetweenAttemptsMs = 30 * 1000;
525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// Maximum timeout before portal detection attempts after !ONLINE ->
545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// ONLINE transition.
555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)const int kLongMaximumDelayBetweenAttemptsMs = 5 * 60 * 1000;
565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
57a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)const NetworkState* DefaultNetwork() {
58a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return NetworkHandler::Get()->network_state_handler()->DefaultNetwork();
59a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
60c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
61effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochbool InSession() {
62116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  return LoginState::IsInitialized() && LoginState::Get()->IsUserLoggedIn();
63effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
64effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
65effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochvoid RecordDetectionResult(NetworkPortalDetector::CaptivePortalStatus status) {
66effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (InSession()) {
675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    UMA_HISTOGRAM_ENUMERATION(
68effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        NetworkPortalDetectorImpl::kSessionDetectionResultHistogram,
695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        status,
705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT);
71effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  } else {
725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    UMA_HISTOGRAM_ENUMERATION(
73effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        NetworkPortalDetectorImpl::kOobeDetectionResultHistogram,
745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        status,
755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT);
76c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
77c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
78c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
79effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochvoid RecordDetectionDuration(const base::TimeDelta& duration) {
80effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (InSession()) {
81effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    UMA_HISTOGRAM_MEDIUM_TIMES(
82effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        NetworkPortalDetectorImpl::kSessionDetectionDurationHistogram,
83effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        duration);
84effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  } else {
85effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    UMA_HISTOGRAM_MEDIUM_TIMES(
86effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        NetworkPortalDetectorImpl::kOobeDetectionDurationHistogram, duration);
87effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  }
88effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
89effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
90effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochvoid RecordDiscrepancyWithShill(
91effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    const NetworkState* network,
92effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    const NetworkPortalDetector::CaptivePortalStatus status) {
93effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (InSession()) {
94effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    if (network->connection_state() == shill::kStateOnline) {
95effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      UMA_HISTOGRAM_ENUMERATION(
96effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch          NetworkPortalDetectorImpl::kSessionShillOnlineHistogram,
97effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch          status,
98effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch          NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT);
99effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    } else if (network->connection_state() == shill::kStatePortal) {
100effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      UMA_HISTOGRAM_ENUMERATION(
101effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch          NetworkPortalDetectorImpl::kSessionShillPortalHistogram,
102effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch          status,
103effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch          NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT);
104effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    } else if (network->connection_state() == shill::kStateOffline) {
105effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      UMA_HISTOGRAM_ENUMERATION(
106effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch          NetworkPortalDetectorImpl::kSessionShillOfflineHistogram,
107effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch          status,
108effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch          NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT);
109effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    }
110effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  } else {
111effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    if (network->connection_state() == shill::kStateOnline) {
112effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      UMA_HISTOGRAM_ENUMERATION(
113effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch          NetworkPortalDetectorImpl::kOobeShillOnlineHistogram,
114effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch          status,
115effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch          NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT);
116effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    } else if (network->connection_state() == shill::kStatePortal) {
117effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      UMA_HISTOGRAM_ENUMERATION(
118effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch          NetworkPortalDetectorImpl::kOobeShillPortalHistogram,
119effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch          status,
120effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch          NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT);
121effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    } else if (network->connection_state() == shill::kStateOffline) {
122effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      UMA_HISTOGRAM_ENUMERATION(
123effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch          NetworkPortalDetectorImpl::kOobeShillOfflineHistogram,
124effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch          status,
125effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch          NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT);
126effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    }
127effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  }
128effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
129effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
130e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdochvoid RecordPortalToOnlineTransition(const base::TimeDelta& duration) {
131e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  if (InSession()) {
132e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    UMA_HISTOGRAM_LONG_TIMES(
133e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        NetworkPortalDetectorImpl::kSessionPortalToOnlineHistogram,
134e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        duration);
135e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  } else {
136e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    UMA_HISTOGRAM_LONG_TIMES(
137e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        NetworkPortalDetectorImpl::kOobePortalToOnlineHistogram,
138e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        duration);
139e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  }
140e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch}
141e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
142c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}  // namespace
143c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
14490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)////////////////////////////////////////////////////////////////////////////////
145116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch// NetworkPortalDetectorImpl::DetectionAttemptCompletedLogState
146116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
147116680a4aac90f2aa7413d9095a592090648e557Ben MurdochNetworkPortalDetectorImpl::DetectionAttemptCompletedReport::
148116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    DetectionAttemptCompletedReport()
149116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    : result(captive_portal::RESULT_COUNT), response_code(-1) {
150116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
151116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
152116680a4aac90f2aa7413d9095a592090648e557Ben MurdochNetworkPortalDetectorImpl::DetectionAttemptCompletedReport::
153116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    DetectionAttemptCompletedReport(const std::string network_name,
154116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                                    const std::string network_id,
155116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                                    captive_portal::CaptivePortalResult result,
156116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                                    int response_code)
157116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    : network_name(network_name),
158116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      network_id(network_id),
159116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      result(result),
160116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      response_code(response_code) {
161116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
162116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
163116680a4aac90f2aa7413d9095a592090648e557Ben Murdochvoid NetworkPortalDetectorImpl::DetectionAttemptCompletedReport::Report()
164116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    const {
16503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  // TODO (ygorshenin@): remove VLOG as soon as NET_LOG_EVENT will be dumped on
16603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  // a disk, crbug.com/293739.
167116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  VLOG(1) << "Detection attempt completed: "
168116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          << "name=" << network_name << ", "
169116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          << "id=" << network_id << ", "
170116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          << "result=" << captive_portal::CaptivePortalResultToString(result)
171116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          << ", "
172116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          << "response_code=" << response_code;
17303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  NET_LOG_EVENT(StringPrintf(
17403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                    "Portal detection completed: network_id=%s, result=%s, "
17503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                    "response_code=%d",
17603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                    network_id.c_str(),
17703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                    captive_portal::CaptivePortalResultToString(result).c_str(),
17803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                    response_code),
17903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                network_name);
180116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
181116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
182116680a4aac90f2aa7413d9095a592090648e557Ben Murdochbool NetworkPortalDetectorImpl::DetectionAttemptCompletedReport::Equals(
183116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    const DetectionAttemptCompletedReport& o) const {
184116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  return network_name == o.network_name && network_id == o.network_id &&
185116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch         result == o.result && response_code == o.response_code;
186116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
187116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
188116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch////////////////////////////////////////////////////////////////////////////////
18990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// NetworkPortalDetectorImpl, public:
19090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
191effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochconst char NetworkPortalDetectorImpl::kOobeDetectionResultHistogram[] =
192a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    "CaptivePortal.OOBE.DetectionResult";
193effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochconst char NetworkPortalDetectorImpl::kOobeDetectionDurationHistogram[] =
194a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    "CaptivePortal.OOBE.DetectionDuration";
195effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochconst char NetworkPortalDetectorImpl::kOobeShillOnlineHistogram[] =
196a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    "CaptivePortal.OOBE.DiscrepancyWithShill_Online";
197effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochconst char NetworkPortalDetectorImpl::kOobeShillPortalHistogram[] =
198a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    "CaptivePortal.OOBE.DiscrepancyWithShill_RestrictedPool";
199effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochconst char NetworkPortalDetectorImpl::kOobeShillOfflineHistogram[] =
200a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    "CaptivePortal.OOBE.DiscrepancyWithShill_Offline";
201e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdochconst char NetworkPortalDetectorImpl::kOobePortalToOnlineHistogram[] =
202e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    "CaptivePortal.OOBE.PortalToOnlineTransition";
2035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
204effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochconst char NetworkPortalDetectorImpl::kSessionDetectionResultHistogram[] =
205effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    "CaptivePortal.Session.DetectionResult";
206effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochconst char NetworkPortalDetectorImpl::kSessionDetectionDurationHistogram[] =
207effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    "CaptivePortal.Session.DetectionDuration";
208effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochconst char NetworkPortalDetectorImpl::kSessionShillOnlineHistogram[] =
209effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    "CaptivePortal.Session.DiscrepancyWithShill_Online";
210effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochconst char NetworkPortalDetectorImpl::kSessionShillPortalHistogram[] =
211effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    "CaptivePortal.Session.DiscrepancyWithShill_RestrictedPool";
212effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochconst char NetworkPortalDetectorImpl::kSessionShillOfflineHistogram[] =
213effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    "CaptivePortal.Session.DiscrepancyWithShill_Offline";
214e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdochconst char NetworkPortalDetectorImpl::kSessionPortalToOnlineHistogram[] =
215e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    "CaptivePortal.Session.PortalToOnlineTransition";
216effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
217116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch// static
218116680a4aac90f2aa7413d9095a592090648e557Ben Murdochvoid NetworkPortalDetectorImpl::Initialize(
219116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    net::URLRequestContextGetter* url_context) {
220116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (NetworkPortalDetector::set_for_testing())
221116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return;
222116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  CHECK(!NetworkPortalDetector::network_portal_detector())
223116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      << "NetworkPortalDetector was initialized twice.";
224116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (CommandLine::ForCurrentProcess()->HasSwitch(::switches::kTestType))
225116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    set_network_portal_detector(new NetworkPortalDetectorStubImpl());
226116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  else
227116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    set_network_portal_detector(new NetworkPortalDetectorImpl(url_context));
228116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
229116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
230c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)NetworkPortalDetectorImpl::NetworkPortalDetectorImpl(
231c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const scoped_refptr<net::URLRequestContextGetter>& request_context)
2328bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    : state_(STATE_IDLE),
2338bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      test_url_(CaptivePortalDetector::kDefaultURL),
234c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      enabled_(false),
235a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      strategy_(PortalDetectorStrategy::CreateById(
2365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          PortalDetectorStrategy::STRATEGY_ID_LOGIN_SCREEN, this)),
2375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      last_detection_result_(CAPTIVE_PORTAL_STATUS_UNKNOWN),
2385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      same_detection_result_count_(0),
2395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      no_response_result_count_(0),
240116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      weak_factory_(this) {
241c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  captive_portal_detector_.reset(new CaptivePortalDetector(request_context));
242c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
243c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  registrar_.Add(this,
244c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 chrome::NOTIFICATION_LOGIN_PROXY_CHANGED,
245c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 content::NotificationService::AllSources());
246c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  registrar_.Add(this,
247c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 chrome::NOTIFICATION_AUTH_SUPPLIED,
248c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 content::NotificationService::AllSources());
249c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  registrar_.Add(this,
250c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 chrome::NOTIFICATION_AUTH_CANCELLED,
251c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 content::NotificationService::AllSources());
252c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
253a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  NetworkHandler::Get()->network_state_handler()->AddObserver(this, FROM_HERE);
254010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  StartDetectionIfIdle();
255c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
256c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
2578bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)NetworkPortalDetectorImpl::~NetworkPortalDetectorImpl() {
258c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK(CalledOnValidThread());
259c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
260a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  attempt_task_.Cancel();
261a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  attempt_timeout_.Cancel();
262c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
263c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  captive_portal_detector_->Cancel();
264c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  captive_portal_detector_.reset();
265c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  observers_.Clear();
2667d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  if (NetworkHandler::IsInitialized()) {
267a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    NetworkHandler::Get()->network_state_handler()->RemoveObserver(this,
268a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                                                   FROM_HERE);
2697d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  }
270c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
271c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
272c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void NetworkPortalDetectorImpl::AddObserver(Observer* observer) {
273c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK(CalledOnValidThread());
274a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (observer && !observers_.HasObserver(observer))
275a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    observers_.AddObserver(observer);
276c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
277c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
278c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void NetworkPortalDetectorImpl::AddAndFireObserver(Observer* observer) {
279c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK(CalledOnValidThread());
280c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!observer)
281c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
282c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  AddObserver(observer);
283a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  CaptivePortalState portal_state;
284a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  const NetworkState* network = DefaultNetwork();
285a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (network)
286116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    portal_state = GetCaptivePortalState(network->guid());
287a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  observer->OnPortalDetectionCompleted(network, portal_state);
288c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
289c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
290c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void NetworkPortalDetectorImpl::RemoveObserver(Observer* observer) {
291c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK(CalledOnValidThread());
292c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (observer)
293c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    observers_.RemoveObserver(observer);
294c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
295c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
296a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)bool NetworkPortalDetectorImpl::IsEnabled() { return enabled_; }
297c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
298c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void NetworkPortalDetectorImpl::Enable(bool start_detection) {
299c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK(CalledOnValidThread());
300c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (enabled_)
301c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
302a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
303a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DCHECK(is_idle());
304c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  enabled_ = true;
305a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
306a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  const NetworkState* network = DefaultNetwork();
307a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!start_detection || !network)
308c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
30903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  NET_LOG_EVENT(StringPrintf("Starting detection attempt: network_id=%s",
31003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                             network->guid().c_str()),
31103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                network->name());
312116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  portal_state_map_.erase(network->guid());
313a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  StartDetection();
314c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
315c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
316c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)NetworkPortalDetectorImpl::CaptivePortalState
317116680a4aac90f2aa7413d9095a592090648e557Ben MurdochNetworkPortalDetectorImpl::GetCaptivePortalState(const std::string& guid) {
318c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK(CalledOnValidThread());
319116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  CaptivePortalStateMap::const_iterator it = portal_state_map_.find(guid);
3205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (it == portal_state_map_.end())
321c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return CaptivePortalState();
322c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return it->second;
323c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
324c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
32590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)bool NetworkPortalDetectorImpl::StartDetectionIfIdle() {
326a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!is_idle())
32790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    return false;
328a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  StartDetection();
32990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  return true;
33090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}
33190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
332010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)void NetworkPortalDetectorImpl::SetStrategy(
333010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    PortalDetectorStrategy::StrategyId id) {
334010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  if (id == strategy_->Id())
335010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    return;
3365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  strategy_ = PortalDetectorStrategy::CreateById(id, this).Pass();
337010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  StopDetection();
338010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  StartDetectionIfIdle();
339010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
340010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
341d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)void NetworkPortalDetectorImpl::DefaultNetworkChanged(
342d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    const NetworkState* default_network) {
343c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK(CalledOnValidThread());
344a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
34590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  if (!default_network) {
34603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    NET_LOG_EVENT("Default network changed", "None");
34703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
348f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    default_network_name_.clear();
349a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
350a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    StopDetection();
351a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
352a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    CaptivePortalState state;
353a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    state.status = CAPTIVE_PORTAL_STATUS_OFFLINE;
354a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    OnDetectionCompleted(NULL, state);
355c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
35690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  }
357c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
358f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  default_network_name_ = default_network->name();
359c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
360116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  bool network_changed = (default_network_id_ != default_network->guid());
361116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  default_network_id_ = default_network->guid();
362c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
363a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  bool connection_state_changed =
364a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      (default_connection_state_ != default_network->connection_state());
36590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  default_connection_state_ = default_network->connection_state();
366c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
36703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  NET_LOG_EVENT(StringPrintf(
36803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                    "Default network changed: network_id=%s, state=%s, "
36903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                    "changed=%d, state_changed=%d",
37003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                    default_network_id_.c_str(),
37103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                    default_connection_state_.c_str(),
37203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                    network_changed,
37303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                    connection_state_changed),
37403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                default_network_name_);
375116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
376a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (network_changed || connection_state_changed)
377a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    StopDetection();
378c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
3795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (is_idle() && NetworkState::StateIsConnected(default_connection_state_)) {
380c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // Initiate Captive Portal detection if network's captive
381c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // portal state is unknown (e.g. for freshly created networks),
382c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // offline or if network connection state was changed.
383116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    CaptivePortalState state = GetCaptivePortalState(default_network->guid());
384c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (state.status == CAPTIVE_PORTAL_STATUS_UNKNOWN ||
385c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        state.status == CAPTIVE_PORTAL_STATUS_OFFLINE ||
386c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        (!network_changed && connection_state_changed)) {
387a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      ScheduleAttempt(base::TimeDelta());
388c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
389c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
390c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
391c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
3925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)int NetworkPortalDetectorImpl::NoResponseResultCount() {
3935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  return no_response_result_count_;
3945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
395a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
396a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)base::TimeTicks NetworkPortalDetectorImpl::AttemptStartTime() {
397a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return attempt_start_time_;
398a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
399a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
400a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)base::TimeTicks NetworkPortalDetectorImpl::GetCurrentTimeTicks() {
401a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (time_ticks_for_testing_.is_null())
402a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return base::TimeTicks::Now();
403a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return time_ticks_for_testing_;
404a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
405a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
406a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
40790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)////////////////////////////////////////////////////////////////////////////////
40890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// NetworkPortalDetectorImpl, private:
40990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
410a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void NetworkPortalDetectorImpl::StartDetection() {
4115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  DCHECK(is_idle());
4125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
4135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  ResetStrategyAndCounters();
414a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  detection_start_time_ = GetCurrentTimeTicks();
415a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  ScheduleAttempt(base::TimeDelta());
416c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
417c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
418a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void NetworkPortalDetectorImpl::StopDetection() {
419a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  attempt_task_.Cancel();
420a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  attempt_timeout_.Cancel();
421a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  captive_portal_detector_->Cancel();
422a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  state_ = STATE_IDLE;
4235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  ResetStrategyAndCounters();
424a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
425a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
426a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void NetworkPortalDetectorImpl::ScheduleAttempt(const base::TimeDelta& delay) {
4275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  DCHECK(is_idle());
428c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
429c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!IsEnabled())
430c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
431c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
432a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  attempt_task_.Cancel();
433a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  attempt_timeout_.Cancel();
434c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  state_ = STATE_PORTAL_CHECK_PENDING;
435c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
436a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  next_attempt_delay_ = std::max(delay, strategy_->GetDelayTillNextAttempt());
437a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  attempt_task_.Reset(base::Bind(&NetworkPortalDetectorImpl::StartAttempt,
438a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                 weak_factory_.GetWeakPtr()));
43990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  base::MessageLoop::current()->PostDelayedTask(
440a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      FROM_HERE, attempt_task_.callback(), next_attempt_delay_);
441c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
442c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
443a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void NetworkPortalDetectorImpl::StartAttempt() {
444a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DCHECK(is_portal_check_pending());
445c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
446c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  state_ = STATE_CHECKING_FOR_PORTAL;
447c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  attempt_start_time_ = GetCurrentTimeTicks();
448c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
449c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  captive_portal_detector_->DetectCaptivePortal(
450c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      test_url_,
451a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      base::Bind(&NetworkPortalDetectorImpl::OnAttemptCompleted,
452a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                 weak_factory_.GetWeakPtr()));
453a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  attempt_timeout_.Reset(
454a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      base::Bind(&NetworkPortalDetectorImpl::OnAttemptTimeout,
455a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                 weak_factory_.GetWeakPtr()));
456a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
45790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  base::MessageLoop::current()->PostDelayedTask(
458a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      FROM_HERE,
459a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      attempt_timeout_.callback(),
460a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      strategy_->GetNextAttemptTimeout());
461c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
462c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
463a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void NetworkPortalDetectorImpl::OnAttemptTimeout() {
464c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK(CalledOnValidThread());
465a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DCHECK(is_checking_for_portal());
466c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
46703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  NET_LOG_ERROR(StringPrintf("Portal detection timeout: network_id=%s",
46803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                             default_network_id_.c_str()),
46903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                default_network_name_);
470c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
471c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  captive_portal_detector_->Cancel();
472c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  CaptivePortalDetector::Results results;
473c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  results.result = captive_portal::RESULT_NO_RESPONSE;
474a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  OnAttemptCompleted(results);
475c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
476c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
477a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void NetworkPortalDetectorImpl::OnAttemptCompleted(
478c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const CaptivePortalDetector::Results& results) {
479c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK(CalledOnValidThread());
480a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DCHECK(is_checking_for_portal());
481c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
4825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  captive_portal::CaptivePortalResult result = results.result;
4835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  int response_code = results.response_code;
484c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
485a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  const NetworkState* network = DefaultNetwork();
486a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
487a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // If using a fake profile client, also fake being behind a captive portal
488a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // if the default network is in portal state.
489a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (result != captive_portal::RESULT_NO_RESPONSE &&
490a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      DBusThreadManager::Get()->GetShillProfileClient()->GetTestInterface() &&
491a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      network && network->connection_state() == shill::kStatePortal) {
492a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    result = captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL;
493a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    response_code = 200;
494a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
495c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
4965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  DetectionAttemptCompletedReport attempt_completed_report(
4975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      default_network_name_, default_network_id_, result, response_code);
4985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (!attempt_completed_report_.Equals(attempt_completed_report)) {
4995f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    attempt_completed_report_ = attempt_completed_report;
5005f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    attempt_completed_report_.Report();
5015f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  }
5025f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
5035f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  state_ = STATE_IDLE;
5045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  attempt_timeout_.Cancel();
5055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
506c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  CaptivePortalState state;
507c2db58bd994c04d98e4ee2cd7565b71548655fe3Ben Murdoch  state.response_code = response_code;
508e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  state.time = GetCurrentTimeTicks();
509c2db58bd994c04d98e4ee2cd7565b71548655fe3Ben Murdoch  switch (result) {
510c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    case captive_portal::RESULT_NO_RESPONSE:
5115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      if (state.response_code == net::HTTP_PROXY_AUTHENTICATION_REQUIRED) {
512010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        state.status = CAPTIVE_PORTAL_STATUS_PROXY_AUTH_REQUIRED;
513a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      } else if (network &&
514a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                 (network->connection_state() == shill::kStatePortal)) {
515a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        // Take into account shill's detection results.
516a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        state.status = CAPTIVE_PORTAL_STATUS_PORTAL;
517c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      } else {
518a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        state.status = CAPTIVE_PORTAL_STATUS_OFFLINE;
519c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      }
520c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      break;
521c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    case captive_portal::RESULT_INTERNET_CONNECTED:
522c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      state.status = CAPTIVE_PORTAL_STATUS_ONLINE;
523c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      break;
524c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    case captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL:
525c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      state.status = CAPTIVE_PORTAL_STATUS_PORTAL;
526c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      break;
527c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    default:
528c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      break;
529c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
530c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
5315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (last_detection_result_ != state.status) {
5325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    last_detection_result_ = state.status;
5335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    same_detection_result_count_ = 1;
5345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    net::BackoffEntry::Policy policy = strategy_->policy();
5355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if (state.status == CAPTIVE_PORTAL_STATUS_ONLINE) {
5365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      policy.initial_delay_ms = kLongInitialDelayBetweenAttemptsMs;
5375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      policy.maximum_backoff_ms = kLongMaximumDelayBetweenAttemptsMs;
5385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    } else {
5395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      policy.initial_delay_ms = kShortInitialDelayBetweenAttemptsMs;
5405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      policy.maximum_backoff_ms = kShortMaximumDelayBetweenAttemptsMs;
5415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    }
5425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    strategy_->SetPolicyAndReset(policy);
5435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  } else {
5445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    ++same_detection_result_count_;
5455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  }
5465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  strategy_->OnDetectionCompleted();
5475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
5485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (result == captive_portal::RESULT_NO_RESPONSE)
5495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    ++no_response_result_count_;
5505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  else
5515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    no_response_result_count_ = 0;
5525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
5535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (state.status != CAPTIVE_PORTAL_STATUS_OFFLINE ||
5545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      same_detection_result_count_ >= kMaxOfflineResultsBeforeReport) {
5555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    OnDetectionCompleted(network, state);
5565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  }
5575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  ScheduleAttempt(results.retry_after_delta);
558c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
559c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
560c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void NetworkPortalDetectorImpl::Observe(
561c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    int type,
562c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const content::NotificationSource& source,
563c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const content::NotificationDetails& details) {
564c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (type == chrome::NOTIFICATION_LOGIN_PROXY_CHANGED ||
565c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      type == chrome::NOTIFICATION_AUTH_SUPPLIED ||
566c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      type == chrome::NOTIFICATION_AUTH_CANCELLED) {
56703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    NET_LOG_EVENT(
56803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        "Restarting portal detection due to proxy change",
56903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        default_network_name_.empty() ? "None" : default_network_name_);
570a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    StopDetection();
571a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    ScheduleAttempt(base::TimeDelta::FromSeconds(kProxyChangeDelaySec));
572c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
573c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
574c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
575a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void NetworkPortalDetectorImpl::OnDetectionCompleted(
57690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    const NetworkState* network,
577c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const CaptivePortalState& state) {
57890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  if (!network) {
579a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    NotifyDetectionCompleted(network, state);
58090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    return;
58190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  }
58290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
583c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  CaptivePortalStateMap::const_iterator it =
584116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      portal_state_map_.find(network->guid());
585a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (it == portal_state_map_.end() || it->second.status != state.status ||
586c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      it->second.response_code != state.response_code) {
5875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // Record detection duration iff detection result differs from the
5885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // previous one for this network. The reason is to record all stats
5895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // only when network changes it's state.
5905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    RecordDetectionStats(network, state.status);
591e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    if (it != portal_state_map_.end() &&
592e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        it->second.status == CAPTIVE_PORTAL_STATUS_PORTAL &&
593e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        state.status == CAPTIVE_PORTAL_STATUS_ONLINE) {
594e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch      RecordPortalToOnlineTransition(state.time - it->second.time);
595e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    }
5965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
597116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    portal_state_map_[network->guid()] = state;
598c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
599a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  NotifyDetectionCompleted(network, state);
600c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
601c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
602a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void NetworkPortalDetectorImpl::NotifyDetectionCompleted(
60390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    const NetworkState* network,
604c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const CaptivePortalState& state) {
605a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  FOR_EACH_OBSERVER(
606a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      Observer, observers_, OnPortalDetectionCompleted(network, state));
607a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  notification_controller_.OnPortalDetectionCompleted(network, state);
608c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
609c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
610a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)bool NetworkPortalDetectorImpl::AttemptTimeoutIsCancelledForTesting() const {
611a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return attempt_timeout_.IsCancelled();
61290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}
61390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
6145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void NetworkPortalDetectorImpl::RecordDetectionStats(
6155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const NetworkState* network,
6165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    CaptivePortalStatus status) {
6175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Don't record stats for offline state.
6185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!network)
6195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
6205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
621effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (!detection_start_time_.is_null())
622effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    RecordDetectionDuration(GetCurrentTimeTicks() - detection_start_time_);
623effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  RecordDetectionResult(status);
624effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
6255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  switch (status) {
6265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_UNKNOWN:
6275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      NOTREACHED();
6285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      break;
6295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_OFFLINE:
6305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if (network->connection_state() == shill::kStateOnline ||
6315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          network->connection_state() == shill::kStatePortal) {
6325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        RecordDiscrepancyWithShill(network, status);
6335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      }
6345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      break;
6355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE:
6365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if (network->connection_state() != shill::kStateOnline)
6375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        RecordDiscrepancyWithShill(network, status);
6385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      break;
6395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PORTAL:
6405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if (network->connection_state() != shill::kStatePortal)
6415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        RecordDiscrepancyWithShill(network, status);
6425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      break;
6435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PROXY_AUTH_REQUIRED:
6445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if (network->connection_state() != shill::kStateOnline)
6455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        RecordDiscrepancyWithShill(network, status);
6465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      break;
6475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT:
6485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      NOTREACHED();
6495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      break;
6505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
6515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
6525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)void NetworkPortalDetectorImpl::ResetStrategyAndCounters() {
6545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  last_detection_result_ = CAPTIVE_PORTAL_STATUS_UNKNOWN;
6555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  same_detection_result_count_ = 0;
6565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  no_response_result_count_ = 0;
6575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  strategy_->Reset();
6585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
6595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
660c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}  // namespace chromeos
661