• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/chromeos/net/network_portal_detector_impl.h"
6 
7 #include <algorithm>
8 
9 #include "base/bind.h"
10 #include "base/command_line.h"
11 #include "base/logging.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/metrics/histogram.h"
14 #include "base/strings/stringprintf.h"
15 #include "chrome/browser/chrome_notification_types.h"
16 #include "chromeos/dbus/dbus_thread_manager.h"
17 #include "chromeos/dbus/shill_profile_client.h"
18 #include "chromeos/login/login_state.h"
19 #include "chromeos/network/network_event_log.h"
20 #include "chromeos/network/network_state.h"
21 #include "chromeos/network/network_state_handler.h"
22 #include "content/public/browser/notification_service.h"
23 #include "content/public/common/content_switches.h"
24 #include "net/http/http_status_code.h"
25 #include "third_party/cros_system_api/dbus/service_constants.h"
26 
27 using base::StringPrintf;
28 using captive_portal::CaptivePortalDetector;
29 
30 namespace chromeos {
31 
32 namespace {
33 
34 // Delay before portal detection caused by changes in proxy settings.
35 const int kProxyChangeDelaySec = 1;
36 
37 // Maximum number of reports from captive portal detector about
38 // offline state in a row before notification is sent to observers.
39 const int kMaxOfflineResultsBeforeReport = 3;
40 
41 // Delay before portal detection attempt after !ONLINE -> !ONLINE
42 // transition.
43 const int kShortInitialDelayBetweenAttemptsMs = 600;
44 
45 // Maximum timeout before portal detection attempts after !ONLINE ->
46 // !ONLINE transition.
47 const int kShortMaximumDelayBetweenAttemptsMs = 2 * 60 * 1000;
48 
49 // Delay before portal detection attempt after !ONLINE -> ONLINE
50 // transition.
51 const int kLongInitialDelayBetweenAttemptsMs = 30 * 1000;
52 
53 // Maximum timeout before portal detection attempts after !ONLINE ->
54 // ONLINE transition.
55 const int kLongMaximumDelayBetweenAttemptsMs = 5 * 60 * 1000;
56 
DefaultNetwork()57 const NetworkState* DefaultNetwork() {
58   return NetworkHandler::Get()->network_state_handler()->DefaultNetwork();
59 }
60 
InSession()61 bool InSession() {
62   return LoginState::IsInitialized() && LoginState::Get()->IsUserLoggedIn();
63 }
64 
RecordDetectionResult(NetworkPortalDetector::CaptivePortalStatus status)65 void RecordDetectionResult(NetworkPortalDetector::CaptivePortalStatus status) {
66   if (InSession()) {
67     UMA_HISTOGRAM_ENUMERATION(
68         NetworkPortalDetectorImpl::kSessionDetectionResultHistogram,
69         status,
70         NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT);
71   } else {
72     UMA_HISTOGRAM_ENUMERATION(
73         NetworkPortalDetectorImpl::kOobeDetectionResultHistogram,
74         status,
75         NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT);
76   }
77 }
78 
RecordDetectionDuration(const base::TimeDelta & duration)79 void RecordDetectionDuration(const base::TimeDelta& duration) {
80   if (InSession()) {
81     UMA_HISTOGRAM_MEDIUM_TIMES(
82         NetworkPortalDetectorImpl::kSessionDetectionDurationHistogram,
83         duration);
84   } else {
85     UMA_HISTOGRAM_MEDIUM_TIMES(
86         NetworkPortalDetectorImpl::kOobeDetectionDurationHistogram, duration);
87   }
88 }
89 
RecordDiscrepancyWithShill(const NetworkState * network,const NetworkPortalDetector::CaptivePortalStatus status)90 void RecordDiscrepancyWithShill(
91     const NetworkState* network,
92     const NetworkPortalDetector::CaptivePortalStatus status) {
93   if (InSession()) {
94     if (network->connection_state() == shill::kStateOnline) {
95       UMA_HISTOGRAM_ENUMERATION(
96           NetworkPortalDetectorImpl::kSessionShillOnlineHistogram,
97           status,
98           NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT);
99     } else if (network->connection_state() == shill::kStatePortal) {
100       UMA_HISTOGRAM_ENUMERATION(
101           NetworkPortalDetectorImpl::kSessionShillPortalHistogram,
102           status,
103           NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT);
104     } else if (network->connection_state() == shill::kStateOffline) {
105       UMA_HISTOGRAM_ENUMERATION(
106           NetworkPortalDetectorImpl::kSessionShillOfflineHistogram,
107           status,
108           NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT);
109     }
110   } else {
111     if (network->connection_state() == shill::kStateOnline) {
112       UMA_HISTOGRAM_ENUMERATION(
113           NetworkPortalDetectorImpl::kOobeShillOnlineHistogram,
114           status,
115           NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT);
116     } else if (network->connection_state() == shill::kStatePortal) {
117       UMA_HISTOGRAM_ENUMERATION(
118           NetworkPortalDetectorImpl::kOobeShillPortalHistogram,
119           status,
120           NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT);
121     } else if (network->connection_state() == shill::kStateOffline) {
122       UMA_HISTOGRAM_ENUMERATION(
123           NetworkPortalDetectorImpl::kOobeShillOfflineHistogram,
124           status,
125           NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT);
126     }
127   }
128 }
129 
RecordPortalToOnlineTransition(const base::TimeDelta & duration)130 void RecordPortalToOnlineTransition(const base::TimeDelta& duration) {
131   if (InSession()) {
132     UMA_HISTOGRAM_LONG_TIMES(
133         NetworkPortalDetectorImpl::kSessionPortalToOnlineHistogram,
134         duration);
135   } else {
136     UMA_HISTOGRAM_LONG_TIMES(
137         NetworkPortalDetectorImpl::kOobePortalToOnlineHistogram,
138         duration);
139   }
140 }
141 
142 }  // namespace
143 
144 ////////////////////////////////////////////////////////////////////////////////
145 // NetworkPortalDetectorImpl::DetectionAttemptCompletedLogState
146 
147 NetworkPortalDetectorImpl::DetectionAttemptCompletedReport::
DetectionAttemptCompletedReport()148     DetectionAttemptCompletedReport()
149     : result(captive_portal::RESULT_COUNT), response_code(-1) {
150 }
151 
152 NetworkPortalDetectorImpl::DetectionAttemptCompletedReport::
DetectionAttemptCompletedReport(const std::string network_name,const std::string network_id,captive_portal::CaptivePortalResult result,int response_code)153     DetectionAttemptCompletedReport(const std::string network_name,
154                                     const std::string network_id,
155                                     captive_portal::CaptivePortalResult result,
156                                     int response_code)
157     : network_name(network_name),
158       network_id(network_id),
159       result(result),
160       response_code(response_code) {
161 }
162 
Report() const163 void NetworkPortalDetectorImpl::DetectionAttemptCompletedReport::Report()
164     const {
165   // TODO (ygorshenin@): remove VLOG as soon as NET_LOG_EVENT will be dumped on
166   // a disk, crbug.com/293739.
167   VLOG(1) << "Detection attempt completed: "
168           << "name=" << network_name << ", "
169           << "id=" << network_id << ", "
170           << "result=" << captive_portal::CaptivePortalResultToString(result)
171           << ", "
172           << "response_code=" << response_code;
173   NET_LOG_EVENT(StringPrintf(
174                     "Portal detection completed: network_id=%s, result=%s, "
175                     "response_code=%d",
176                     network_id.c_str(),
177                     captive_portal::CaptivePortalResultToString(result).c_str(),
178                     response_code),
179                 network_name);
180 }
181 
Equals(const DetectionAttemptCompletedReport & o) const182 bool NetworkPortalDetectorImpl::DetectionAttemptCompletedReport::Equals(
183     const DetectionAttemptCompletedReport& o) const {
184   return network_name == o.network_name && network_id == o.network_id &&
185          result == o.result && response_code == o.response_code;
186 }
187 
188 ////////////////////////////////////////////////////////////////////////////////
189 // NetworkPortalDetectorImpl, public:
190 
191 const char NetworkPortalDetectorImpl::kOobeDetectionResultHistogram[] =
192     "CaptivePortal.OOBE.DetectionResult";
193 const char NetworkPortalDetectorImpl::kOobeDetectionDurationHistogram[] =
194     "CaptivePortal.OOBE.DetectionDuration";
195 const char NetworkPortalDetectorImpl::kOobeShillOnlineHistogram[] =
196     "CaptivePortal.OOBE.DiscrepancyWithShill_Online";
197 const char NetworkPortalDetectorImpl::kOobeShillPortalHistogram[] =
198     "CaptivePortal.OOBE.DiscrepancyWithShill_RestrictedPool";
199 const char NetworkPortalDetectorImpl::kOobeShillOfflineHistogram[] =
200     "CaptivePortal.OOBE.DiscrepancyWithShill_Offline";
201 const char NetworkPortalDetectorImpl::kOobePortalToOnlineHistogram[] =
202     "CaptivePortal.OOBE.PortalToOnlineTransition";
203 
204 const char NetworkPortalDetectorImpl::kSessionDetectionResultHistogram[] =
205     "CaptivePortal.Session.DetectionResult";
206 const char NetworkPortalDetectorImpl::kSessionDetectionDurationHistogram[] =
207     "CaptivePortal.Session.DetectionDuration";
208 const char NetworkPortalDetectorImpl::kSessionShillOnlineHistogram[] =
209     "CaptivePortal.Session.DiscrepancyWithShill_Online";
210 const char NetworkPortalDetectorImpl::kSessionShillPortalHistogram[] =
211     "CaptivePortal.Session.DiscrepancyWithShill_RestrictedPool";
212 const char NetworkPortalDetectorImpl::kSessionShillOfflineHistogram[] =
213     "CaptivePortal.Session.DiscrepancyWithShill_Offline";
214 const char NetworkPortalDetectorImpl::kSessionPortalToOnlineHistogram[] =
215     "CaptivePortal.Session.PortalToOnlineTransition";
216 
217 // static
Initialize(net::URLRequestContextGetter * url_context)218 void NetworkPortalDetectorImpl::Initialize(
219     net::URLRequestContextGetter* url_context) {
220   if (NetworkPortalDetector::set_for_testing())
221     return;
222   CHECK(!NetworkPortalDetector::network_portal_detector())
223       << "NetworkPortalDetector was initialized twice.";
224   if (CommandLine::ForCurrentProcess()->HasSwitch(::switches::kTestType))
225     set_network_portal_detector(new NetworkPortalDetectorStubImpl());
226   else
227     set_network_portal_detector(new NetworkPortalDetectorImpl(url_context));
228 }
229 
NetworkPortalDetectorImpl(const scoped_refptr<net::URLRequestContextGetter> & request_context)230 NetworkPortalDetectorImpl::NetworkPortalDetectorImpl(
231     const scoped_refptr<net::URLRequestContextGetter>& request_context)
232     : state_(STATE_IDLE),
233       test_url_(CaptivePortalDetector::kDefaultURL),
234       enabled_(false),
235       strategy_(PortalDetectorStrategy::CreateById(
236           PortalDetectorStrategy::STRATEGY_ID_LOGIN_SCREEN, this)),
237       last_detection_result_(CAPTIVE_PORTAL_STATUS_UNKNOWN),
238       same_detection_result_count_(0),
239       no_response_result_count_(0),
240       weak_factory_(this) {
241   captive_portal_detector_.reset(new CaptivePortalDetector(request_context));
242 
243   registrar_.Add(this,
244                  chrome::NOTIFICATION_LOGIN_PROXY_CHANGED,
245                  content::NotificationService::AllSources());
246   registrar_.Add(this,
247                  chrome::NOTIFICATION_AUTH_SUPPLIED,
248                  content::NotificationService::AllSources());
249   registrar_.Add(this,
250                  chrome::NOTIFICATION_AUTH_CANCELLED,
251                  content::NotificationService::AllSources());
252 
253   NetworkHandler::Get()->network_state_handler()->AddObserver(this, FROM_HERE);
254   StartDetectionIfIdle();
255 }
256 
~NetworkPortalDetectorImpl()257 NetworkPortalDetectorImpl::~NetworkPortalDetectorImpl() {
258   DCHECK(CalledOnValidThread());
259 
260   attempt_task_.Cancel();
261   attempt_timeout_.Cancel();
262 
263   captive_portal_detector_->Cancel();
264   captive_portal_detector_.reset();
265   observers_.Clear();
266   if (NetworkHandler::IsInitialized()) {
267     NetworkHandler::Get()->network_state_handler()->RemoveObserver(this,
268                                                                    FROM_HERE);
269   }
270 }
271 
AddObserver(Observer * observer)272 void NetworkPortalDetectorImpl::AddObserver(Observer* observer) {
273   DCHECK(CalledOnValidThread());
274   if (observer && !observers_.HasObserver(observer))
275     observers_.AddObserver(observer);
276 }
277 
AddAndFireObserver(Observer * observer)278 void NetworkPortalDetectorImpl::AddAndFireObserver(Observer* observer) {
279   DCHECK(CalledOnValidThread());
280   if (!observer)
281     return;
282   AddObserver(observer);
283   CaptivePortalState portal_state;
284   const NetworkState* network = DefaultNetwork();
285   if (network)
286     portal_state = GetCaptivePortalState(network->guid());
287   observer->OnPortalDetectionCompleted(network, portal_state);
288 }
289 
RemoveObserver(Observer * observer)290 void NetworkPortalDetectorImpl::RemoveObserver(Observer* observer) {
291   DCHECK(CalledOnValidThread());
292   if (observer)
293     observers_.RemoveObserver(observer);
294 }
295 
IsEnabled()296 bool NetworkPortalDetectorImpl::IsEnabled() { return enabled_; }
297 
Enable(bool start_detection)298 void NetworkPortalDetectorImpl::Enable(bool start_detection) {
299   DCHECK(CalledOnValidThread());
300   if (enabled_)
301     return;
302 
303   DCHECK(is_idle());
304   enabled_ = true;
305 
306   const NetworkState* network = DefaultNetwork();
307   if (!start_detection || !network)
308     return;
309   NET_LOG_EVENT(StringPrintf("Starting detection attempt: network_id=%s",
310                              network->guid().c_str()),
311                 network->name());
312   portal_state_map_.erase(network->guid());
313   StartDetection();
314 }
315 
316 NetworkPortalDetectorImpl::CaptivePortalState
GetCaptivePortalState(const std::string & guid)317 NetworkPortalDetectorImpl::GetCaptivePortalState(const std::string& guid) {
318   DCHECK(CalledOnValidThread());
319   CaptivePortalStateMap::const_iterator it = portal_state_map_.find(guid);
320   if (it == portal_state_map_.end())
321     return CaptivePortalState();
322   return it->second;
323 }
324 
StartDetectionIfIdle()325 bool NetworkPortalDetectorImpl::StartDetectionIfIdle() {
326   if (!is_idle())
327     return false;
328   StartDetection();
329   return true;
330 }
331 
SetStrategy(PortalDetectorStrategy::StrategyId id)332 void NetworkPortalDetectorImpl::SetStrategy(
333     PortalDetectorStrategy::StrategyId id) {
334   if (id == strategy_->Id())
335     return;
336   strategy_ = PortalDetectorStrategy::CreateById(id, this).Pass();
337   StopDetection();
338   StartDetectionIfIdle();
339 }
340 
DefaultNetworkChanged(const NetworkState * default_network)341 void NetworkPortalDetectorImpl::DefaultNetworkChanged(
342     const NetworkState* default_network) {
343   DCHECK(CalledOnValidThread());
344 
345   if (!default_network) {
346     NET_LOG_EVENT("Default network changed", "None");
347 
348     default_network_name_.clear();
349 
350     StopDetection();
351 
352     CaptivePortalState state;
353     state.status = CAPTIVE_PORTAL_STATUS_OFFLINE;
354     OnDetectionCompleted(NULL, state);
355     return;
356   }
357 
358   default_network_name_ = default_network->name();
359 
360   bool network_changed = (default_network_id_ != default_network->guid());
361   default_network_id_ = default_network->guid();
362 
363   bool connection_state_changed =
364       (default_connection_state_ != default_network->connection_state());
365   default_connection_state_ = default_network->connection_state();
366 
367   NET_LOG_EVENT(StringPrintf(
368                     "Default network changed: network_id=%s, state=%s, "
369                     "changed=%d, state_changed=%d",
370                     default_network_id_.c_str(),
371                     default_connection_state_.c_str(),
372                     network_changed,
373                     connection_state_changed),
374                 default_network_name_);
375 
376   if (network_changed || connection_state_changed)
377     StopDetection();
378 
379   if (is_idle() && NetworkState::StateIsConnected(default_connection_state_)) {
380     // Initiate Captive Portal detection if network's captive
381     // portal state is unknown (e.g. for freshly created networks),
382     // offline or if network connection state was changed.
383     CaptivePortalState state = GetCaptivePortalState(default_network->guid());
384     if (state.status == CAPTIVE_PORTAL_STATUS_UNKNOWN ||
385         state.status == CAPTIVE_PORTAL_STATUS_OFFLINE ||
386         (!network_changed && connection_state_changed)) {
387       ScheduleAttempt(base::TimeDelta());
388     }
389   }
390 }
391 
NoResponseResultCount()392 int NetworkPortalDetectorImpl::NoResponseResultCount() {
393   return no_response_result_count_;
394 }
395 
AttemptStartTime()396 base::TimeTicks NetworkPortalDetectorImpl::AttemptStartTime() {
397   return attempt_start_time_;
398 }
399 
GetCurrentTimeTicks()400 base::TimeTicks NetworkPortalDetectorImpl::GetCurrentTimeTicks() {
401   if (time_ticks_for_testing_.is_null())
402     return base::TimeTicks::Now();
403   return time_ticks_for_testing_;
404 }
405 
406 
407 ////////////////////////////////////////////////////////////////////////////////
408 // NetworkPortalDetectorImpl, private:
409 
StartDetection()410 void NetworkPortalDetectorImpl::StartDetection() {
411   DCHECK(is_idle());
412 
413   ResetStrategyAndCounters();
414   detection_start_time_ = GetCurrentTimeTicks();
415   ScheduleAttempt(base::TimeDelta());
416 }
417 
StopDetection()418 void NetworkPortalDetectorImpl::StopDetection() {
419   attempt_task_.Cancel();
420   attempt_timeout_.Cancel();
421   captive_portal_detector_->Cancel();
422   state_ = STATE_IDLE;
423   ResetStrategyAndCounters();
424 }
425 
ScheduleAttempt(const base::TimeDelta & delay)426 void NetworkPortalDetectorImpl::ScheduleAttempt(const base::TimeDelta& delay) {
427   DCHECK(is_idle());
428 
429   if (!IsEnabled())
430     return;
431 
432   attempt_task_.Cancel();
433   attempt_timeout_.Cancel();
434   state_ = STATE_PORTAL_CHECK_PENDING;
435 
436   next_attempt_delay_ = std::max(delay, strategy_->GetDelayTillNextAttempt());
437   attempt_task_.Reset(base::Bind(&NetworkPortalDetectorImpl::StartAttempt,
438                                  weak_factory_.GetWeakPtr()));
439   base::MessageLoop::current()->PostDelayedTask(
440       FROM_HERE, attempt_task_.callback(), next_attempt_delay_);
441 }
442 
StartAttempt()443 void NetworkPortalDetectorImpl::StartAttempt() {
444   DCHECK(is_portal_check_pending());
445 
446   state_ = STATE_CHECKING_FOR_PORTAL;
447   attempt_start_time_ = GetCurrentTimeTicks();
448 
449   captive_portal_detector_->DetectCaptivePortal(
450       test_url_,
451       base::Bind(&NetworkPortalDetectorImpl::OnAttemptCompleted,
452                  weak_factory_.GetWeakPtr()));
453   attempt_timeout_.Reset(
454       base::Bind(&NetworkPortalDetectorImpl::OnAttemptTimeout,
455                  weak_factory_.GetWeakPtr()));
456 
457   base::MessageLoop::current()->PostDelayedTask(
458       FROM_HERE,
459       attempt_timeout_.callback(),
460       strategy_->GetNextAttemptTimeout());
461 }
462 
OnAttemptTimeout()463 void NetworkPortalDetectorImpl::OnAttemptTimeout() {
464   DCHECK(CalledOnValidThread());
465   DCHECK(is_checking_for_portal());
466 
467   NET_LOG_ERROR(StringPrintf("Portal detection timeout: network_id=%s",
468                              default_network_id_.c_str()),
469                 default_network_name_);
470 
471   captive_portal_detector_->Cancel();
472   CaptivePortalDetector::Results results;
473   results.result = captive_portal::RESULT_NO_RESPONSE;
474   OnAttemptCompleted(results);
475 }
476 
OnAttemptCompleted(const CaptivePortalDetector::Results & results)477 void NetworkPortalDetectorImpl::OnAttemptCompleted(
478     const CaptivePortalDetector::Results& results) {
479   DCHECK(CalledOnValidThread());
480   DCHECK(is_checking_for_portal());
481 
482   captive_portal::CaptivePortalResult result = results.result;
483   int response_code = results.response_code;
484 
485   const NetworkState* network = DefaultNetwork();
486 
487   // If using a fake profile client, also fake being behind a captive portal
488   // if the default network is in portal state.
489   if (result != captive_portal::RESULT_NO_RESPONSE &&
490       DBusThreadManager::Get()->GetShillProfileClient()->GetTestInterface() &&
491       network && network->connection_state() == shill::kStatePortal) {
492     result = captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL;
493     response_code = 200;
494   }
495 
496   DetectionAttemptCompletedReport attempt_completed_report(
497       default_network_name_, default_network_id_, result, response_code);
498   if (!attempt_completed_report_.Equals(attempt_completed_report)) {
499     attempt_completed_report_ = attempt_completed_report;
500     attempt_completed_report_.Report();
501   }
502 
503   state_ = STATE_IDLE;
504   attempt_timeout_.Cancel();
505 
506   CaptivePortalState state;
507   state.response_code = response_code;
508   state.time = GetCurrentTimeTicks();
509   switch (result) {
510     case captive_portal::RESULT_NO_RESPONSE:
511       if (state.response_code == net::HTTP_PROXY_AUTHENTICATION_REQUIRED) {
512         state.status = CAPTIVE_PORTAL_STATUS_PROXY_AUTH_REQUIRED;
513       } else if (network &&
514                  (network->connection_state() == shill::kStatePortal)) {
515         // Take into account shill's detection results.
516         state.status = CAPTIVE_PORTAL_STATUS_PORTAL;
517       } else {
518         state.status = CAPTIVE_PORTAL_STATUS_OFFLINE;
519       }
520       break;
521     case captive_portal::RESULT_INTERNET_CONNECTED:
522       state.status = CAPTIVE_PORTAL_STATUS_ONLINE;
523       break;
524     case captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL:
525       state.status = CAPTIVE_PORTAL_STATUS_PORTAL;
526       break;
527     default:
528       break;
529   }
530 
531   if (last_detection_result_ != state.status) {
532     last_detection_result_ = state.status;
533     same_detection_result_count_ = 1;
534     net::BackoffEntry::Policy policy = strategy_->policy();
535     if (state.status == CAPTIVE_PORTAL_STATUS_ONLINE) {
536       policy.initial_delay_ms = kLongInitialDelayBetweenAttemptsMs;
537       policy.maximum_backoff_ms = kLongMaximumDelayBetweenAttemptsMs;
538     } else {
539       policy.initial_delay_ms = kShortInitialDelayBetweenAttemptsMs;
540       policy.maximum_backoff_ms = kShortMaximumDelayBetweenAttemptsMs;
541     }
542     strategy_->SetPolicyAndReset(policy);
543   } else {
544     ++same_detection_result_count_;
545   }
546   strategy_->OnDetectionCompleted();
547 
548   if (result == captive_portal::RESULT_NO_RESPONSE)
549     ++no_response_result_count_;
550   else
551     no_response_result_count_ = 0;
552 
553   if (state.status != CAPTIVE_PORTAL_STATUS_OFFLINE ||
554       same_detection_result_count_ >= kMaxOfflineResultsBeforeReport) {
555     OnDetectionCompleted(network, state);
556   }
557   ScheduleAttempt(results.retry_after_delta);
558 }
559 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)560 void NetworkPortalDetectorImpl::Observe(
561     int type,
562     const content::NotificationSource& source,
563     const content::NotificationDetails& details) {
564   if (type == chrome::NOTIFICATION_LOGIN_PROXY_CHANGED ||
565       type == chrome::NOTIFICATION_AUTH_SUPPLIED ||
566       type == chrome::NOTIFICATION_AUTH_CANCELLED) {
567     NET_LOG_EVENT(
568         "Restarting portal detection due to proxy change",
569         default_network_name_.empty() ? "None" : default_network_name_);
570     StopDetection();
571     ScheduleAttempt(base::TimeDelta::FromSeconds(kProxyChangeDelaySec));
572   }
573 }
574 
OnDetectionCompleted(const NetworkState * network,const CaptivePortalState & state)575 void NetworkPortalDetectorImpl::OnDetectionCompleted(
576     const NetworkState* network,
577     const CaptivePortalState& state) {
578   if (!network) {
579     NotifyDetectionCompleted(network, state);
580     return;
581   }
582 
583   CaptivePortalStateMap::const_iterator it =
584       portal_state_map_.find(network->guid());
585   if (it == portal_state_map_.end() || it->second.status != state.status ||
586       it->second.response_code != state.response_code) {
587     // Record detection duration iff detection result differs from the
588     // previous one for this network. The reason is to record all stats
589     // only when network changes it's state.
590     RecordDetectionStats(network, state.status);
591     if (it != portal_state_map_.end() &&
592         it->second.status == CAPTIVE_PORTAL_STATUS_PORTAL &&
593         state.status == CAPTIVE_PORTAL_STATUS_ONLINE) {
594       RecordPortalToOnlineTransition(state.time - it->second.time);
595     }
596 
597     portal_state_map_[network->guid()] = state;
598   }
599   NotifyDetectionCompleted(network, state);
600 }
601 
NotifyDetectionCompleted(const NetworkState * network,const CaptivePortalState & state)602 void NetworkPortalDetectorImpl::NotifyDetectionCompleted(
603     const NetworkState* network,
604     const CaptivePortalState& state) {
605   FOR_EACH_OBSERVER(
606       Observer, observers_, OnPortalDetectionCompleted(network, state));
607   notification_controller_.OnPortalDetectionCompleted(network, state);
608 }
609 
AttemptTimeoutIsCancelledForTesting() const610 bool NetworkPortalDetectorImpl::AttemptTimeoutIsCancelledForTesting() const {
611   return attempt_timeout_.IsCancelled();
612 }
613 
RecordDetectionStats(const NetworkState * network,CaptivePortalStatus status)614 void NetworkPortalDetectorImpl::RecordDetectionStats(
615     const NetworkState* network,
616     CaptivePortalStatus status) {
617   // Don't record stats for offline state.
618   if (!network)
619     return;
620 
621   if (!detection_start_time_.is_null())
622     RecordDetectionDuration(GetCurrentTimeTicks() - detection_start_time_);
623   RecordDetectionResult(status);
624 
625   switch (status) {
626     case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_UNKNOWN:
627       NOTREACHED();
628       break;
629     case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_OFFLINE:
630       if (network->connection_state() == shill::kStateOnline ||
631           network->connection_state() == shill::kStatePortal) {
632         RecordDiscrepancyWithShill(network, status);
633       }
634       break;
635     case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE:
636       if (network->connection_state() != shill::kStateOnline)
637         RecordDiscrepancyWithShill(network, status);
638       break;
639     case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PORTAL:
640       if (network->connection_state() != shill::kStatePortal)
641         RecordDiscrepancyWithShill(network, status);
642       break;
643     case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PROXY_AUTH_REQUIRED:
644       if (network->connection_state() != shill::kStateOnline)
645         RecordDiscrepancyWithShill(network, status);
646       break;
647     case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT:
648       NOTREACHED();
649       break;
650   }
651 }
652 
ResetStrategyAndCounters()653 void NetworkPortalDetectorImpl::ResetStrategyAndCounters() {
654   last_detection_result_ = CAPTIVE_PORTAL_STATUS_UNKNOWN;
655   same_detection_result_count_ = 0;
656   no_response_result_count_ = 0;
657   strategy_->Reset();
658 }
659 
660 }  // namespace chromeos
661