1c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu//
2c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// Copyright (C) 2012 The Android Open Source Project
3c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu//
4c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// Licensed under the Apache License, Version 2.0 (the "License");
5c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// you may not use this file except in compliance with the License.
6c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// You may obtain a copy of the License at
7c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu//
8c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu//      http://www.apache.org/licenses/LICENSE-2.0
9c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu//
10c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// Unless required by applicable law or agreed to in writing, software
11c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// distributed under the License is distributed on an "AS IS" BASIS,
12c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// See the License for the specific language governing permissions and
14c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// limitations under the License.
15c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu//
163d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein
173d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein#ifndef SHILL_CONNECTIVITY_TRIAL_H_
183d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein#define SHILL_CONNECTIVITY_TRIAL_H_
193d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein
2022f1fbc11b69ee41af8370ec38f1b90577db6c3cBen Chan#include <memory>
213d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein#include <string>
223d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein#include <vector>
233d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein
243d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein#include <base/callback.h>
253d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein#include <base/cancelable_callback.h>
263d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein#include <base/memory/ref_counted.h>
273d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein#include <base/memory/weak_ptr.h>
283d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein#include <gtest/gtest_prod.h>  // for FRIEND_TEST
293d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein
303d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein#include "shill/http_request.h"
313d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein#include "shill/http_url.h"
328d6b59704591ba9fad57751858835dc332dbdd37Peter Qiu#include "shill/net/shill_time.h"
338d6b59704591ba9fad57751858835dc332dbdd37Peter Qiu#include "shill/net/sockets.h"
343d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein#include "shill/refptr_types.h"
353d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein
363d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silbersteinnamespace shill {
373d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein
383d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silbersteinclass ByteString;
393d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silbersteinclass EventDispatcher;
403d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silbersteinclass Time;
413d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein
423d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein// The ConnectivityTrial class implements a single portal detection
433d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein// trial.  Each trial checks if a connection has "general internet
443d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein// connectivity."
453d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein//
463d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein// ConnectivityTrial is responsible for managing the callbacks between the
473d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein// calling class requesting a connectivity trial and the HTTPRequest that is
483d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein// used to test connectivity.  ConnectivityTrial maps between the HTTPRequest
493d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein// response codes to higher-level connection-oriented status.
503d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein//
513d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein// ConnectivityTrial tests the connection by attempting to parse and access a
523d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein// given URL.  Any result that deviates from the expected behavior (DNS or HTTP
533d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein// errors, as well as retrieved content errors, and timeouts) are considered
543d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein// failures.
553d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein
563d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silbersteinclass ConnectivityTrial {
573d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein public:
583d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  enum Phase {
593d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein    kPhaseConnection,
603d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein    kPhaseDNS,
613d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein    kPhaseHTTP,
623d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein    kPhaseContent,
633d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein    kPhaseUnknown
643d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  };
653d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein
663d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  enum Status {
673d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein    kStatusFailure,
683d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein    kStatusSuccess,
693d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein    kStatusTimeout
703d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  };
713d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein
723d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  struct Result {
733d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein    Result()
743d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein        : phase(kPhaseUnknown), status(kStatusFailure) {}
753d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein    Result(Phase phase_in, Status status_in)
763d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein        : phase(phase_in), status(status_in) {}
773d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein    Phase phase;
783d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein    Status status;
793d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  };
803d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein
813d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  static const char kDefaultURL[];
823d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  static const char kResponseExpected[];
833d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein
843d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  ConnectivityTrial(ConnectionRefPtr connection,
85a794cd60a7339d576ea2eed263a4f0a20fb255afPaul Stewart                    EventDispatcher* dispatcher,
863d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein                    int trial_timeout_seconds,
87a794cd60a7339d576ea2eed263a4f0a20fb255afPaul Stewart                    const base::Callback<void(Result)>& trial_callback);
883d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  virtual ~ConnectivityTrial();
893d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein
903d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // Static method used to map a portal detection phase tp a string.  This
913d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // includes the phases for connection, DNS, HTTP, returned content and
923d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // unknown.
933d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  static const std::string PhaseToString(Phase phase);
943d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein
953d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // Static method to map from the result of a portal detection phase to a
963d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // status string. This method supports success, timeout and failure.
973d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  static const std::string StatusToString(Status status);
983d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein
993d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // Static method mapping from HTTPRequest responses to ConntectivityTrial
1003d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // phases for portal detection. For example, if the HTTPRequest result is
1013d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // HTTPRequest::kResultDNSFailure, this method returns a
1023d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // ConnectivityTrial::Result with the phase set to
1033d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // ConnectivityTrial::kPhaseDNS and the status set to
1043d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // ConnectivityTrial::kStatusFailure.
1053d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  static Result GetPortalResultForRequestResult(HTTPRequest::Result result);
1063d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein
1073d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // Start a ConnectivityTrial with the supplied URL and starting delay (ms).
1083d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // Returns trus if |url_string| correctly parses as a URL.  Returns false (and
1093d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // does not start) if the |url_string| fails to parse.
1103d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  //
1113d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // After a trial completes, the callback supplied in the constructor is
1123d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // called.
113a794cd60a7339d576ea2eed263a4f0a20fb255afPaul Stewart  virtual bool Start(const std::string& url_string,
1143d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein                     int start_delay_milliseconds);
1153d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein
1163d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // After a trial completes, the calling class may call Retry on the trial.
1173d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // This allows the underlying HTTPRequest object to be reused.  The URL is not
1183d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // reparsed and the original URL supplied in the Start command is used.  The
1193d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // |start_delay| is the time (ms) to wait before starting the trial.  Retry
1203d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // returns true if the underlying HTTPRequest is still available.  If the
1213d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // HTTPRequest was reset or never created, Retry will return false.
1223d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  virtual bool Retry(int start_delay_milliseconds);
1233d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein
1243d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // End the current attempt if one is in progress.  Will not call the callback
1253d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // with any intermediate results.
1263d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // The ConnectivityTrial will cancel any existing scheduled tasks and destroy
1273d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // the underlying HTTPRequest.
1283d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  virtual void Stop();
1293d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein
1303d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // Method to return if the connection is being actively tested.
1313d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  virtual bool IsActive();
1323d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein
1333d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein private:
1343d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  friend class PortalDetectorTest;
1353d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  FRIEND_TEST(PortalDetectorTest, StartAttemptFailed);
1363d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  FRIEND_TEST(PortalDetectorTest, StartAttemptRepeated);
1373d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  FRIEND_TEST(PortalDetectorTest, StartAttemptAfterDelay);
1383d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  FRIEND_TEST(PortalDetectorTest, AttemptCount);
1393d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  FRIEND_TEST(PortalDetectorTest, ReadBadHeadersRetry);
1403d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  FRIEND_TEST(PortalDetectorTest, ReadBadHeader);
1413d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  friend class ConnectivityTrialTest;
1423d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  FRIEND_TEST(ConnectivityTrialTest, StartAttemptFailed);
1433d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  FRIEND_TEST(ConnectivityTrialTest, TrialRetry);
1443d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  FRIEND_TEST(ConnectivityTrialTest, ReadBadHeadersRetry);
1453d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  FRIEND_TEST(ConnectivityTrialTest, IsActive);
1463d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein
1473d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // Start a ConnectivityTrial with the supplied delay in ms.
1483d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  void StartTrialAfterDelay(int start_delay_milliseconds);
1493d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein
1503d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // Internal method used to start the actual connectivity trial, called after
1513d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // the start delay completes.
1523d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  void StartTrialTask();
1533d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein
1543d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // Callback used to return data read from the HTTPRequest.
155a794cd60a7339d576ea2eed263a4f0a20fb255afPaul Stewart  void RequestReadCallback(const ByteString& response_data);
1563d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein
1573d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // Callback used to return the result of the HTTPRequest.
1583d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  void RequestResultCallback(HTTPRequest::Result result,
159a794cd60a7339d576ea2eed263a4f0a20fb255afPaul Stewart                             const ByteString& response_data);
1603d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein
1613d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // Internal method used to clean up state and call the original caller that
1623d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // created and triggered this ConnectivityTrial.
1633d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  void CompleteTrial(Result result);
1643d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein
1653d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // Internal method used to cancel the timeout timer and stop an active
1663d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // HTTPRequest.  If |reset_request| is true, this method resets the underlying
1673d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // HTTPRequest object.
1683d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  void CleanupTrial(bool reset_request);
1693d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein
1703d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // Callback used to cancel the underlying HTTPRequest in the event of a
1713d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  // timeout.
1723d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  void TimeoutTrialTask();
1733d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein
1743d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  ConnectionRefPtr connection_;
175a794cd60a7339d576ea2eed263a4f0a20fb255afPaul Stewart  EventDispatcher* dispatcher_;
1763d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  int trial_timeout_seconds_;
1773d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  base::Callback<void(Result)> trial_callback_;
1783d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  base::WeakPtrFactory<ConnectivityTrial> weak_ptr_factory_;
179a794cd60a7339d576ea2eed263a4f0a20fb255afPaul Stewart  base::Callback<void(const ByteString&)> request_read_callback_;
180a794cd60a7339d576ea2eed263a4f0a20fb255afPaul Stewart  base::Callback<void(HTTPRequest::Result, const ByteString&)>
1813d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein        request_result_callback_;
18222f1fbc11b69ee41af8370ec38f1b90577db6c3cBen Chan  std::unique_ptr<HTTPRequest> request_;
1833d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein
1843d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  Sockets sockets_;
1853d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  HTTPURL url_;
1863d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  base::CancelableClosure trial_;
1873d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  base::CancelableClosure trial_timeout_;
1883d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein  bool is_active_;
1893d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein};
1903d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein
1913d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein}  // namespace shill
1923d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein
1933d49ea435a59436f762c2cc5e750ff27ece0d3c5Rebecca Silberstein#endif  // SHILL_CONNECTIVITY_TRIAL_H_
194