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