diff --git a/talk/app/webrtc/fakemetricsobserver.cc b/talk/app/webrtc/fakemetricsobserver.cc new file mode 100644 index 0000000000..66e1c51f2a --- /dev/null +++ b/talk/app/webrtc/fakemetricsobserver.cc @@ -0,0 +1,81 @@ +/* + * libjingle + * Copyright 2015 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "talk/app/webrtc/fakemetricsobserver.h" +#include "webrtc/base/checks.h" + +namespace webrtc { + +FakeMetricsObserver::FakeMetricsObserver() { + Reset(); +} + +void FakeMetricsObserver::Reset() { + DCHECK(thread_checker_.CalledOnValidThread()); + memset(counters_, 0, sizeof(counters_)); + memset(int_histogram_samples_, 0, sizeof(int_histogram_samples_)); + for (std::string& type : string_histogram_samples_) { + type.clear(); + } +} + +void FakeMetricsObserver::IncrementCounter(PeerConnectionMetricsCounter type) { + DCHECK(thread_checker_.CalledOnValidThread()); + ++counters_[type]; +} + +void FakeMetricsObserver::AddHistogramSample(PeerConnectionMetricsName type, + int value) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK_EQ(int_histogram_samples_[type], 0); + int_histogram_samples_[type] = value; +} + +void FakeMetricsObserver::AddHistogramSample(PeerConnectionMetricsName type, + const std::string& value) { + DCHECK(thread_checker_.CalledOnValidThread()); + string_histogram_samples_[type].assign(value); +} + +int FakeMetricsObserver::GetCounter(PeerConnectionMetricsCounter type) const { + DCHECK(thread_checker_.CalledOnValidThread()); + return counters_[type]; +} + +int FakeMetricsObserver::GetIntHistogramSample( + PeerConnectionMetricsName type) const { + DCHECK(thread_checker_.CalledOnValidThread()); + return int_histogram_samples_[type]; +} + +const std::string& FakeMetricsObserver::GetStringHistogramSample( + PeerConnectionMetricsName type) const { + DCHECK(thread_checker_.CalledOnValidThread()); + return string_histogram_samples_[type]; +} + +} // namespace webrtc diff --git a/talk/app/webrtc/fakemetricsobserver.h b/talk/app/webrtc/fakemetricsobserver.h new file mode 100644 index 0000000000..e9e49749bf --- /dev/null +++ b/talk/app/webrtc/fakemetricsobserver.h @@ -0,0 +1,68 @@ +/* + * libjingle + * Copyright 2015 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TALK_APP_WEBRTC_FAKEMETRICSOBSERVER_H_ +#define TALK_APP_WEBRTC_FAKEMETRICSOBSERVER_H_ + +#include +#include + +#include "talk/app/webrtc/peerconnectioninterface.h" +#include "webrtc/base/thread_checker.h" + +namespace webrtc { + +class FakeMetricsObserver : public MetricsObserverInterface { + public: + FakeMetricsObserver(); + void Reset(); + + void IncrementCounter(PeerConnectionMetricsCounter type) override; + void AddHistogramSample(PeerConnectionMetricsName type, + int value) override; + void AddHistogramSample(PeerConnectionMetricsName type, + const std::string& value) override; + + // Accessors to be used by the tests. + int GetCounter(PeerConnectionMetricsCounter type) const; + int GetIntHistogramSample(PeerConnectionMetricsName type) const; + const std::string& GetStringHistogramSample( + PeerConnectionMetricsName type) const; + + protected: + ~FakeMetricsObserver() {} + + private: + rtc::ThreadChecker thread_checker_; + int counters_[kPeerConnectionMetricsCounter_Max]; + int int_histogram_samples_[kPeerConnectionMetricsCounter_Max]; + std::string string_histogram_samples_[kPeerConnectionMetricsName_Max]; +}; + +} // namespace webrtc + +#endif // TALK_APP_WEBRTC_FAKEMETRICSOBSERVER_H_ diff --git a/talk/app/webrtc/peerconnection_unittest.cc b/talk/app/webrtc/peerconnection_unittest.cc index b9d80196e6..fd58ecdff6 100644 --- a/talk/app/webrtc/peerconnection_unittest.cc +++ b/talk/app/webrtc/peerconnection_unittest.cc @@ -33,6 +33,7 @@ #include #include "talk/app/webrtc/dtmfsender.h" +#include "talk/app/webrtc/fakemetricsobserver.h" #include "talk/app/webrtc/fakeportallocatorfactory.h" #include "talk/app/webrtc/localaudiosource.h" #include "talk/app/webrtc/mediastreaminterface.h" @@ -1336,17 +1337,26 @@ TEST_F(JsepPeerConnectionP2PTestClient, GetDtls12None) { PeerConnectionFactory::Options recv_options; recv_options.ssl_max_version = rtc::SSL_PROTOCOL_DTLS_10; ASSERT_TRUE(CreateTestClients(NULL, &init_options, NULL, &recv_options)); + rtc::scoped_refptr + init_observer = new rtc::RefCountedObject(); + initializing_client()->pc()->RegisterUMAObserver(init_observer); LocalP2PTest(); EXPECT_EQ_WAIT( rtc::SSLStreamAdapter::GetDefaultSslCipher(rtc::SSL_PROTOCOL_DTLS_10), initializing_client()->GetDtlsCipherStats(), kMaxWaitForStatsMs); + EXPECT_EQ( + rtc::SSLStreamAdapter::GetDefaultSslCipher(rtc::SSL_PROTOCOL_DTLS_10), + init_observer->GetStringHistogramSample(webrtc::kAudioSslCipher)); EXPECT_EQ_WAIT( kDefaultSrtpCipher, initializing_client()->GetSrtpCipherStats(), kMaxWaitForStatsMs); + EXPECT_EQ( + kDefaultSrtpCipher, + init_observer->GetStringHistogramSample(webrtc::kAudioSrtpCipher)); } // Test that DTLS 1.2 is used if both ends support it. @@ -1356,17 +1366,26 @@ TEST_F(JsepPeerConnectionP2PTestClient, GetDtls12Both) { PeerConnectionFactory::Options recv_options; recv_options.ssl_max_version = rtc::SSL_PROTOCOL_DTLS_12; ASSERT_TRUE(CreateTestClients(NULL, &init_options, NULL, &recv_options)); + rtc::scoped_refptr + init_observer = new rtc::RefCountedObject(); + initializing_client()->pc()->RegisterUMAObserver(init_observer); LocalP2PTest(); EXPECT_EQ_WAIT( rtc::SSLStreamAdapter::GetDefaultSslCipher(rtc::SSL_PROTOCOL_DTLS_12), initializing_client()->GetDtlsCipherStats(), kMaxWaitForStatsMs); + EXPECT_EQ( + rtc::SSLStreamAdapter::GetDefaultSslCipher(rtc::SSL_PROTOCOL_DTLS_12), + init_observer->GetStringHistogramSample(webrtc::kAudioSslCipher)); EXPECT_EQ_WAIT( kDefaultSrtpCipher, initializing_client()->GetSrtpCipherStats(), kMaxWaitForStatsMs); + EXPECT_EQ( + kDefaultSrtpCipher, + init_observer->GetStringHistogramSample(webrtc::kAudioSrtpCipher)); } // Test that DTLS 1.0 is used if the initator supports DTLS 1.2 and the @@ -1377,17 +1396,26 @@ TEST_F(JsepPeerConnectionP2PTestClient, GetDtls12Init) { PeerConnectionFactory::Options recv_options; recv_options.ssl_max_version = rtc::SSL_PROTOCOL_DTLS_10; ASSERT_TRUE(CreateTestClients(NULL, &init_options, NULL, &recv_options)); + rtc::scoped_refptr + init_observer = new rtc::RefCountedObject(); + initializing_client()->pc()->RegisterUMAObserver(init_observer); LocalP2PTest(); EXPECT_EQ_WAIT( rtc::SSLStreamAdapter::GetDefaultSslCipher(rtc::SSL_PROTOCOL_DTLS_10), initializing_client()->GetDtlsCipherStats(), kMaxWaitForStatsMs); + EXPECT_EQ( + rtc::SSLStreamAdapter::GetDefaultSslCipher(rtc::SSL_PROTOCOL_DTLS_10), + init_observer->GetStringHistogramSample(webrtc::kAudioSslCipher)); EXPECT_EQ_WAIT( kDefaultSrtpCipher, initializing_client()->GetSrtpCipherStats(), kMaxWaitForStatsMs); + EXPECT_EQ( + kDefaultSrtpCipher, + init_observer->GetStringHistogramSample(webrtc::kAudioSrtpCipher)); } // Test that DTLS 1.0 is used if the initator supports DTLS 1.0 and the @@ -1398,17 +1426,26 @@ TEST_F(JsepPeerConnectionP2PTestClient, GetDtls12Recv) { PeerConnectionFactory::Options recv_options; recv_options.ssl_max_version = rtc::SSL_PROTOCOL_DTLS_12; ASSERT_TRUE(CreateTestClients(NULL, &init_options, NULL, &recv_options)); + rtc::scoped_refptr + init_observer = new rtc::RefCountedObject(); + initializing_client()->pc()->RegisterUMAObserver(init_observer); LocalP2PTest(); EXPECT_EQ_WAIT( rtc::SSLStreamAdapter::GetDefaultSslCipher(rtc::SSL_PROTOCOL_DTLS_10), initializing_client()->GetDtlsCipherStats(), kMaxWaitForStatsMs); + EXPECT_EQ( + rtc::SSLStreamAdapter::GetDefaultSslCipher(rtc::SSL_PROTOCOL_DTLS_10), + init_observer->GetStringHistogramSample(webrtc::kAudioSslCipher)); EXPECT_EQ_WAIT( kDefaultSrtpCipher, initializing_client()->GetSrtpCipherStats(), kMaxWaitForStatsMs); + EXPECT_EQ( + kDefaultSrtpCipher, + init_observer->GetStringHistogramSample(webrtc::kAudioSrtpCipher)); } // This test sets up a call between two parties with audio, video and data. diff --git a/talk/app/webrtc/peerconnectioninterface.h b/talk/app/webrtc/peerconnectioninterface.h index 3e4cb556fb..8f6f7bb3ff 100644 --- a/talk/app/webrtc/peerconnectioninterface.h +++ b/talk/app/webrtc/peerconnectioninterface.h @@ -127,6 +127,9 @@ class MetricsObserverInterface : public rtc::RefCountInterface { virtual void IncrementCounter(PeerConnectionMetricsCounter type) = 0; virtual void AddHistogramSample(PeerConnectionMetricsName type, int value) = 0; + // TODO(jbauch): Make method abstract when it is implemented by Chromium. + virtual void AddHistogramSample(PeerConnectionMetricsName type, + const std::string& value) {} protected: virtual ~MetricsObserverInterface() {} diff --git a/talk/app/webrtc/umametrics.h b/talk/app/webrtc/umametrics.h index d029a698bd..bb9e05230d 100644 --- a/talk/app/webrtc/umametrics.h +++ b/talk/app/webrtc/umametrics.h @@ -65,6 +65,12 @@ enum PeerConnectionMetricsName { kTimeToConnect, // In milliseconds. kLocalCandidates_IPv4, // Number of IPv4 local candidates. kLocalCandidates_IPv6, // Number of IPv6 local candidates. + kAudioSrtpCipher, // Name of SRTP cipher used in audio channel. + kAudioSslCipher, // Name of SSL cipher used in audio channel. + kVideoSrtpCipher, // Name of SRTP cipher used in video channel. + kVideoSslCipher, // Name of SSL cipher used in video channel. + kDataSrtpCipher, // Name of SRTP cipher used in data channel. + kDataSslCipher, // Name of SSL cipher used in data channel. kPeerConnectionMetricsName_Max }; diff --git a/talk/app/webrtc/webrtcsession.cc b/talk/app/webrtc/webrtcsession.cc index 5c8b2df754..c0898ff79f 100644 --- a/talk/app/webrtc/webrtcsession.cc +++ b/talk/app/webrtc/webrtcsession.cc @@ -44,6 +44,7 @@ #include "talk/session/media/channelmanager.h" #include "talk/session/media/mediasession.h" #include "webrtc/base/basictypes.h" +#include "webrtc/base/checks.h" #include "webrtc/base/helpers.h" #include "webrtc/base/logging.h" #include "webrtc/base/stringencode.h" @@ -1392,7 +1393,11 @@ void WebRtcSession::OnTransportCompleted(cricket::Transport* transport) { SetIceConnectionState(PeerConnectionInterface::kIceConnectionCompleted); // Only report once when Ice connection is completed. if (old_state != PeerConnectionInterface::kIceConnectionCompleted) { - ReportBestConnectionState(transport); + cricket::TransportStats stats; + if (metrics_observer_ && transport->GetStats(&stats)) { + ReportBestConnectionState(stats); + ReportNegotiatedCiphers(stats); + } } } @@ -1872,16 +1877,9 @@ bool WebRtcSession::ReadyToUseRemoteCandidate( // Walk through the ConnectionInfos to gather best connection usage // for IPv4 and IPv6. -void WebRtcSession::ReportBestConnectionState(cricket::Transport* transport) { - if (!metrics_observer_) { - return; - } - - cricket::TransportStats stats; - if (!transport->GetStats(&stats)) { - return; - } - +void WebRtcSession::ReportBestConnectionState( + const cricket::TransportStats& stats) { + DCHECK(metrics_observer_ != NULL); for (cricket::TransportChannelStatsList::const_iterator it = stats.channel_stats.begin(); it != stats.channel_stats.end(); ++it) { @@ -1897,11 +1895,48 @@ void WebRtcSession::ReportBestConnectionState(cricket::Transport* transport) { AF_INET6) { metrics_observer_->IncrementCounter(kBestConnections_IPv6); } else { - ASSERT(false); + RTC_NOTREACHED(); } return; } } } +void WebRtcSession::ReportNegotiatedCiphers( + const cricket::TransportStats& stats) { + DCHECK(metrics_observer_ != NULL); + if (!dtls_enabled_ || stats.channel_stats.empty()) { + return; + } + + const std::string& srtp_cipher = stats.channel_stats[0].srtp_cipher; + const std::string& ssl_cipher = stats.channel_stats[0].ssl_cipher; + if (srtp_cipher.empty() && ssl_cipher.empty()) { + return; + } + + PeerConnectionMetricsName srtp_name; + PeerConnectionMetricsName ssl_name; + if (stats.content_name == cricket::CN_AUDIO) { + srtp_name = kAudioSrtpCipher; + ssl_name = kAudioSslCipher; + } else if (stats.content_name == cricket::CN_VIDEO) { + srtp_name = kVideoSrtpCipher; + ssl_name = kVideoSslCipher; + } else if (stats.content_name == cricket::CN_DATA) { + srtp_name = kDataSrtpCipher; + ssl_name = kDataSslCipher; + } else { + RTC_NOTREACHED(); + return; + } + + if (!srtp_cipher.empty()) { + metrics_observer_->AddHistogramSample(srtp_name, srtp_cipher); + } + if (!ssl_cipher.empty()) { + metrics_observer_->AddHistogramSample(ssl_name, ssl_cipher); + } +} + } // namespace webrtc diff --git a/talk/app/webrtc/webrtcsession.h b/talk/app/webrtc/webrtcsession.h index 2d477e3beb..821d59db57 100644 --- a/talk/app/webrtc/webrtcsession.h +++ b/talk/app/webrtc/webrtcsession.h @@ -370,7 +370,9 @@ class WebRtcSession : public cricket::BaseSession, // Invoked when OnTransportCompleted is signaled to gather the usage // of IPv4/IPv6 as best connection. - void ReportBestConnectionState(cricket::Transport* transport); + void ReportBestConnectionState(const cricket::TransportStats& stats); + + void ReportNegotiatedCiphers(const cricket::TransportStats& stats); rtc::scoped_ptr voice_channel_; rtc::scoped_ptr video_channel_; diff --git a/talk/app/webrtc/webrtcsession_unittest.cc b/talk/app/webrtc/webrtcsession_unittest.cc index 3a09edab91..c0938d11ea 100644 --- a/talk/app/webrtc/webrtcsession_unittest.cc +++ b/talk/app/webrtc/webrtcsession_unittest.cc @@ -26,6 +26,7 @@ */ #include "talk/app/webrtc/audiotrack.h" +#include "talk/app/webrtc/fakemetricsobserver.h" #include "talk/app/webrtc/jsepicecandidate.h" #include "talk/app/webrtc/jsepsessiondescription.h" #include "talk/app/webrtc/mediastreamsignaling.h" @@ -81,6 +82,7 @@ using webrtc::CreateSessionDescriptionRequest; using webrtc::DTLSIdentityRequestObserver; using webrtc::DTLSIdentityServiceInterface; using webrtc::FakeConstraints; +using webrtc::FakeMetricsObserver; using webrtc::IceCandidateCollection; using webrtc::JsepIceCandidate; using webrtc::JsepSessionDescription; @@ -165,33 +167,6 @@ static void InjectAfter(const std::string& line, tmp.c_str(), tmp.length(), message); } -class FakeMetricsObserver : public webrtc::MetricsObserverInterface { - public: - FakeMetricsObserver() { Reset(); } - void Reset() { - memset(peer_connection_metrics_counters_, 0, - sizeof(peer_connection_metrics_counters_)); - memset(peer_connection_metrics_name_, 0, - sizeof(peer_connection_metrics_name_)); - } - - void IncrementCounter(webrtc::PeerConnectionMetricsCounter type) override { - peer_connection_metrics_counters_[type]++; - } - void AddHistogramSample(webrtc::PeerConnectionMetricsName type, - int value) override { - ASSERT(peer_connection_metrics_name_[type] == 0); - peer_connection_metrics_name_[type] = value; - } - - int peer_connection_metrics_counters_ - [webrtc::kPeerConnectionMetricsCounter_Max]; - int peer_connection_metrics_name_[webrtc::kPeerConnectionMetricsCounter_Max]; - - int AddRef() override { return 1; } - int Release() override { return 1; } -}; - class MockIceObserver : public webrtc::IceObserver { public: MockIceObserver() @@ -360,7 +335,8 @@ class WebRtcSessionTest : public testing::Test { stun_server_(cricket::TestStunServer::Create(Thread::Current(), stun_socket_addr_)), turn_server_(Thread::Current(), kTurnUdpIntAddr, kTurnUdpExtAddr), - mediastream_signaling_(channel_manager_.get()) { + mediastream_signaling_(channel_manager_.get()), + metrics_observer_(new rtc::RefCountedObject()) { tdesc_factory_->set_protocol(cricket::ICEPROTO_HYBRID); cricket::ServerAddresses stun_servers; @@ -398,7 +374,7 @@ class WebRtcSessionTest : public testing::Test { EXPECT_TRUE(session_->Initialize(options_, constraints_.get(), identity_service, rtc_configuration)); - session_->set_metrics_observer(&metrics_observer_); + session_->set_metrics_observer(metrics_observer_); } void Init() { @@ -1022,20 +998,18 @@ class WebRtcSessionTest : public testing::Test { ExpectedBestConnection best_connection_after_initial_ice_converged_; void VerifyBestConnectionAfterIceConverge( - const FakeMetricsObserver& metrics_observer) const { + const rtc::scoped_refptr metrics_observer) const { Verify(metrics_observer, best_connection_after_initial_ice_converged_); } private: - void Verify(const FakeMetricsObserver& metrics_observer, + void Verify(const rtc::scoped_refptr metrics_observer, const ExpectedBestConnection& expected) const { EXPECT_EQ( - metrics_observer - .peer_connection_metrics_counters_[webrtc::kBestConnections_IPv4], + metrics_observer->GetCounter(webrtc::kBestConnections_IPv4), expected.ipv4_count_); EXPECT_EQ( - metrics_observer - .peer_connection_metrics_counters_[webrtc::kBestConnections_IPv6], + metrics_observer->GetCounter(webrtc::kBestConnections_IPv6), expected.ipv6_count_); } }; @@ -1135,7 +1109,7 @@ class WebRtcSessionTest : public testing::Test { observer_.ice_connection_state_, kIceCandidatesTimeout); - metrics_observer_.Reset(); + metrics_observer_->Reset(); // Clearing the rules, session should move back to completed state. loopback_network_manager.ClearRules(fss_.get()); @@ -1280,7 +1254,7 @@ class WebRtcSessionTest : public testing::Test { MockIceObserver observer_; cricket::FakeVideoMediaChannel* video_channel_; cricket::FakeVoiceMediaChannel* voice_channel_; - FakeMetricsObserver metrics_observer_; + rtc::scoped_refptr metrics_observer_; }; TEST_F(WebRtcSessionTest, TestInitializeWithDtls) { diff --git a/talk/libjingle_tests.gyp b/talk/libjingle_tests.gyp index c1f9ab309f..571e9ee83d 100755 --- a/talk/libjingle_tests.gyp +++ b/talk/libjingle_tests.gyp @@ -199,6 +199,8 @@ 'app/webrtc/datachannel_unittest.cc', 'app/webrtc/dtlsidentitystore_unittest.cc', 'app/webrtc/dtmfsender_unittest.cc', + 'app/webrtc/fakemetricsobserver.cc', + 'app/webrtc/fakemetricsobserver.h', 'app/webrtc/jsepsessiondescription_unittest.cc', 'app/webrtc/localaudiosource_unittest.cc', 'app/webrtc/mediastream_unittest.cc',