|  | /* | 
|  | *  Copyright 2017 The WebRTC project authors. All Rights Reserved. | 
|  | * | 
|  | *  Use of this source code is governed by a BSD-style license | 
|  | *  that can be found in the LICENSE file in the root of the source | 
|  | *  tree. An additional intellectual property rights grant can be found | 
|  | *  in the file PATENTS.  All contributing project authors may | 
|  | *  be found in the AUTHORS file in the root of the source tree. | 
|  | */ | 
|  |  | 
|  | #include <memory> | 
|  | #include <set> | 
|  | #include <string> | 
|  | #include <utility> | 
|  | #include <vector> | 
|  |  | 
|  | #include "absl/types/optional.h" | 
|  | #include "api/call/call_factory_interface.h" | 
|  | #include "api/jsep.h" | 
|  | #include "api/jsep_session_description.h" | 
|  | #include "api/peer_connection_interface.h" | 
|  | #include "api/rtc_error.h" | 
|  | #include "api/scoped_refptr.h" | 
|  | #include "api/task_queue/default_task_queue_factory.h" | 
|  | #include "media/base/fake_media_engine.h" | 
|  | #include "p2p/base/mock_async_resolver.h" | 
|  | #include "p2p/base/port_allocator.h" | 
|  | #include "p2p/client/basic_port_allocator.h" | 
|  | #include "pc/peer_connection.h" | 
|  | #include "pc/peer_connection_factory.h" | 
|  | #include "pc/peer_connection_proxy.h" | 
|  | #include "pc/peer_connection_wrapper.h" | 
|  | #include "pc/sdp_utils.h" | 
|  | #include "pc/test/mock_peer_connection_observers.h" | 
|  | #include "pc/usage_pattern.h" | 
|  | #include "pc/webrtc_sdp.h" | 
|  | #include "rtc_base/arraysize.h" | 
|  | #include "rtc_base/checks.h" | 
|  | #include "rtc_base/fake_mdns_responder.h" | 
|  | #include "rtc_base/fake_network.h" | 
|  | #include "rtc_base/gunit.h" | 
|  | #include "rtc_base/ref_counted_object.h" | 
|  | #include "rtc_base/rtc_certificate_generator.h" | 
|  | #include "rtc_base/socket_address.h" | 
|  | #include "rtc_base/thread.h" | 
|  | #include "rtc_base/virtual_socket_server.h" | 
|  | #include "system_wrappers/include/metrics.h" | 
|  | #include "test/gmock.h" | 
|  |  | 
|  | namespace webrtc { | 
|  |  | 
|  | using RTCConfiguration = PeerConnectionInterface::RTCConfiguration; | 
|  | using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions; | 
|  | using ::testing::NiceMock; | 
|  | using ::testing::Values; | 
|  |  | 
|  | static const char kUsagePatternMetric[] = "WebRTC.PeerConnection.UsagePattern"; | 
|  | static constexpr int kDefaultTimeout = 10000; | 
|  | static const rtc::SocketAddress kLocalAddrs[2] = { | 
|  | rtc::SocketAddress("1.1.1.1", 0), rtc::SocketAddress("2.2.2.2", 0)}; | 
|  | static const rtc::SocketAddress kPrivateLocalAddress("10.1.1.1", 0); | 
|  | static const rtc::SocketAddress kPrivateIpv6LocalAddress("fd12:3456:789a:1::1", | 
|  | 0); | 
|  |  | 
|  | int MakeUsageFingerprint(std::set<UsageEvent> events) { | 
|  | int signature = 0; | 
|  | for (const auto it : events) { | 
|  | signature |= static_cast<int>(it); | 
|  | } | 
|  | return signature; | 
|  | } | 
|  |  | 
|  | class PeerConnectionFactoryForUsageHistogramTest | 
|  | : public rtc::RefCountedObject<PeerConnectionFactory> { | 
|  | public: | 
|  | PeerConnectionFactoryForUsageHistogramTest() | 
|  | : rtc::RefCountedObject<PeerConnectionFactory>([] { | 
|  | PeerConnectionFactoryDependencies dependencies; | 
|  | dependencies.network_thread = rtc::Thread::Current(); | 
|  | dependencies.worker_thread = rtc::Thread::Current(); | 
|  | dependencies.signaling_thread = rtc::Thread::Current(); | 
|  | dependencies.task_queue_factory = CreateDefaultTaskQueueFactory(); | 
|  | dependencies.media_engine = | 
|  | std::make_unique<cricket::FakeMediaEngine>(); | 
|  | dependencies.call_factory = CreateCallFactory(); | 
|  | return dependencies; | 
|  | }()) {} | 
|  | }; | 
|  |  | 
|  | class PeerConnectionWrapperForUsageHistogramTest; | 
|  |  | 
|  | typedef PeerConnectionWrapperForUsageHistogramTest* RawWrapperPtr; | 
|  |  | 
|  | class ObserverForUsageHistogramTest : public MockPeerConnectionObserver { | 
|  | public: | 
|  | void OnIceCandidate(const webrtc::IceCandidateInterface* candidate) override; | 
|  |  | 
|  | void OnInterestingUsage(int usage_pattern) override { | 
|  | interesting_usage_detected_ = usage_pattern; | 
|  | } | 
|  |  | 
|  | void PrepareToExchangeCandidates(RawWrapperPtr other) { | 
|  | candidate_target_ = other; | 
|  | } | 
|  |  | 
|  | bool HaveDataChannel() { return last_datachannel_; } | 
|  |  | 
|  | absl::optional<int> interesting_usage_detected() { | 
|  | return interesting_usage_detected_; | 
|  | } | 
|  |  | 
|  | void ClearInterestingUsageDetector() { | 
|  | interesting_usage_detected_ = absl::optional<int>(); | 
|  | } | 
|  |  | 
|  | bool candidate_gathered() const { return candidate_gathered_; } | 
|  |  | 
|  | private: | 
|  | absl::optional<int> interesting_usage_detected_; | 
|  | bool candidate_gathered_ = false; | 
|  | RawWrapperPtr candidate_target_;  // Note: Not thread-safe against deletions. | 
|  | }; | 
|  |  | 
|  | class PeerConnectionWrapperForUsageHistogramTest | 
|  | : public PeerConnectionWrapper { | 
|  | public: | 
|  | using PeerConnectionWrapper::PeerConnectionWrapper; | 
|  |  | 
|  | PeerConnection* GetInternalPeerConnection() { | 
|  | auto* pci = | 
|  | static_cast<PeerConnectionProxyWithInternal<PeerConnectionInterface>*>( | 
|  | pc()); | 
|  | return static_cast<PeerConnection*>(pci->internal()); | 
|  | } | 
|  |  | 
|  | // Override with different return type | 
|  | ObserverForUsageHistogramTest* observer() { | 
|  | return static_cast<ObserverForUsageHistogramTest*>( | 
|  | PeerConnectionWrapper::observer()); | 
|  | } | 
|  |  | 
|  | void PrepareToExchangeCandidates( | 
|  | PeerConnectionWrapperForUsageHistogramTest* other) { | 
|  | observer()->PrepareToExchangeCandidates(other); | 
|  | other->observer()->PrepareToExchangeCandidates(this); | 
|  | } | 
|  |  | 
|  | bool IsConnected() { | 
|  | return pc()->ice_connection_state() == | 
|  | PeerConnectionInterface::kIceConnectionConnected || | 
|  | pc()->ice_connection_state() == | 
|  | PeerConnectionInterface::kIceConnectionCompleted; | 
|  | } | 
|  |  | 
|  | bool HaveDataChannel() { | 
|  | return static_cast<ObserverForUsageHistogramTest*>(observer()) | 
|  | ->HaveDataChannel(); | 
|  | } | 
|  | void BufferIceCandidate(const webrtc::IceCandidateInterface* candidate) { | 
|  | std::string sdp; | 
|  | EXPECT_TRUE(candidate->ToString(&sdp)); | 
|  | std::unique_ptr<webrtc::IceCandidateInterface> candidate_copy( | 
|  | CreateIceCandidate(candidate->sdp_mid(), candidate->sdp_mline_index(), | 
|  | sdp, nullptr)); | 
|  | buffered_candidates_.push_back(std::move(candidate_copy)); | 
|  | } | 
|  |  | 
|  | void AddBufferedIceCandidates() { | 
|  | for (const auto& candidate : buffered_candidates_) { | 
|  | EXPECT_TRUE(pc()->AddIceCandidate(candidate.get())); | 
|  | } | 
|  | buffered_candidates_.clear(); | 
|  | } | 
|  |  | 
|  | // This method performs the following actions in sequence: | 
|  | // 1. Exchange Offer and Answer. | 
|  | // 2. Exchange ICE candidates after both caller and callee complete | 
|  | // gathering. | 
|  | // 3. Wait for ICE to connect. | 
|  | // | 
|  | // This guarantees a deterministic sequence of events and also rules out the | 
|  | // occurrence of prflx candidates if the offer/answer signaling and the | 
|  | // candidate trickling race in order. In case prflx candidates need to be | 
|  | // simulated, see the approach used by tests below for that. | 
|  | bool ConnectTo(PeerConnectionWrapperForUsageHistogramTest* callee) { | 
|  | PrepareToExchangeCandidates(callee); | 
|  | if (!ExchangeOfferAnswerWith(callee)) { | 
|  | return false; | 
|  | } | 
|  | // Wait until the gathering completes before we signal the candidate. | 
|  | WAIT(observer()->ice_gathering_complete_, kDefaultTimeout); | 
|  | WAIT(callee->observer()->ice_gathering_complete_, kDefaultTimeout); | 
|  | AddBufferedIceCandidates(); | 
|  | callee->AddBufferedIceCandidates(); | 
|  | WAIT(IsConnected(), kDefaultTimeout); | 
|  | WAIT(callee->IsConnected(), kDefaultTimeout); | 
|  | return IsConnected() && callee->IsConnected(); | 
|  | } | 
|  |  | 
|  | bool GenerateOfferAndCollectCandidates() { | 
|  | auto offer = CreateOffer(RTCOfferAnswerOptions()); | 
|  | if (!offer) { | 
|  | return false; | 
|  | } | 
|  | bool set_local_offer = | 
|  | SetLocalDescription(CloneSessionDescription(offer.get())); | 
|  | EXPECT_TRUE(set_local_offer); | 
|  | if (!set_local_offer) { | 
|  | return false; | 
|  | } | 
|  | EXPECT_TRUE_WAIT(observer()->ice_gathering_complete_, kDefaultTimeout); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | webrtc::PeerConnectionInterface::IceGatheringState ice_gathering_state() { | 
|  | return pc()->ice_gathering_state(); | 
|  | } | 
|  |  | 
|  | private: | 
|  | // Candidates that have been sent but not yet configured | 
|  | std::vector<std::unique_ptr<webrtc::IceCandidateInterface>> | 
|  | buffered_candidates_; | 
|  | }; | 
|  |  | 
|  | // Buffers candidates until we add them via AddBufferedIceCandidates. | 
|  | void ObserverForUsageHistogramTest::OnIceCandidate( | 
|  | const webrtc::IceCandidateInterface* candidate) { | 
|  | // If target is not set, ignore. This happens in one-ended unit tests. | 
|  | if (candidate_target_) { | 
|  | this->candidate_target_->BufferIceCandidate(candidate); | 
|  | } | 
|  | candidate_gathered_ = true; | 
|  | } | 
|  |  | 
|  | class PeerConnectionUsageHistogramTest : public ::testing::Test { | 
|  | protected: | 
|  | typedef std::unique_ptr<PeerConnectionWrapperForUsageHistogramTest> | 
|  | WrapperPtr; | 
|  |  | 
|  | PeerConnectionUsageHistogramTest() | 
|  | : vss_(new rtc::VirtualSocketServer()), main_(vss_.get()) { | 
|  | webrtc::metrics::Reset(); | 
|  | } | 
|  |  | 
|  | WrapperPtr CreatePeerConnection() { | 
|  | return CreatePeerConnection( | 
|  | RTCConfiguration(), PeerConnectionFactoryInterface::Options(), nullptr); | 
|  | } | 
|  |  | 
|  | WrapperPtr CreatePeerConnection(const RTCConfiguration& config) { | 
|  | return CreatePeerConnection( | 
|  | config, PeerConnectionFactoryInterface::Options(), nullptr); | 
|  | } | 
|  |  | 
|  | WrapperPtr CreatePeerConnectionWithMdns(const RTCConfiguration& config) { | 
|  | auto resolver_factory = | 
|  | std::make_unique<NiceMock<webrtc::MockAsyncResolverFactory>>(); | 
|  |  | 
|  | webrtc::PeerConnectionDependencies deps(nullptr /* observer_in */); | 
|  |  | 
|  | auto fake_network = NewFakeNetwork(); | 
|  | fake_network->set_mdns_responder( | 
|  | std::make_unique<webrtc::FakeMdnsResponder>(rtc::Thread::Current())); | 
|  | fake_network->AddInterface(NextLocalAddress()); | 
|  |  | 
|  | std::unique_ptr<cricket::BasicPortAllocator> port_allocator( | 
|  | new cricket::BasicPortAllocator(fake_network)); | 
|  |  | 
|  | deps.async_resolver_factory = std::move(resolver_factory); | 
|  | deps.allocator = std::move(port_allocator); | 
|  |  | 
|  | return CreatePeerConnection( | 
|  | config, PeerConnectionFactoryInterface::Options(), std::move(deps)); | 
|  | } | 
|  |  | 
|  | WrapperPtr CreatePeerConnectionWithImmediateReport() { | 
|  | RTCConfiguration configuration; | 
|  | configuration.report_usage_pattern_delay_ms = 0; | 
|  | return CreatePeerConnection( | 
|  | configuration, PeerConnectionFactoryInterface::Options(), nullptr); | 
|  | } | 
|  |  | 
|  | WrapperPtr CreatePeerConnectionWithPrivateLocalAddresses() { | 
|  | auto* fake_network = NewFakeNetwork(); | 
|  | fake_network->AddInterface(NextLocalAddress()); | 
|  | fake_network->AddInterface(kPrivateLocalAddress); | 
|  |  | 
|  | auto port_allocator = | 
|  | std::make_unique<cricket::BasicPortAllocator>(fake_network); | 
|  | return CreatePeerConnection(RTCConfiguration(), | 
|  | PeerConnectionFactoryInterface::Options(), | 
|  | std::move(port_allocator)); | 
|  | } | 
|  |  | 
|  | WrapperPtr CreatePeerConnectionWithPrivateIpv6LocalAddresses() { | 
|  | auto* fake_network = NewFakeNetwork(); | 
|  | fake_network->AddInterface(NextLocalAddress()); | 
|  | fake_network->AddInterface(kPrivateIpv6LocalAddress); | 
|  |  | 
|  | auto port_allocator = | 
|  | std::make_unique<cricket::BasicPortAllocator>(fake_network); | 
|  |  | 
|  | return CreatePeerConnection(RTCConfiguration(), | 
|  | PeerConnectionFactoryInterface::Options(), | 
|  | std::move(port_allocator)); | 
|  | } | 
|  |  | 
|  | WrapperPtr CreatePeerConnection( | 
|  | const RTCConfiguration& config, | 
|  | const PeerConnectionFactoryInterface::Options factory_options, | 
|  | std::unique_ptr<cricket::PortAllocator> allocator) { | 
|  | PeerConnectionDependencies deps(nullptr); | 
|  | deps.allocator = std::move(allocator); | 
|  |  | 
|  | return CreatePeerConnection(config, factory_options, std::move(deps)); | 
|  | } | 
|  |  | 
|  | WrapperPtr CreatePeerConnection( | 
|  | const RTCConfiguration& config, | 
|  | const PeerConnectionFactoryInterface::Options factory_options, | 
|  | PeerConnectionDependencies deps) { | 
|  | rtc::scoped_refptr<PeerConnectionFactoryForUsageHistogramTest> pc_factory( | 
|  | new PeerConnectionFactoryForUsageHistogramTest()); | 
|  | pc_factory->SetOptions(factory_options); | 
|  |  | 
|  | // If no allocator is provided, one will be created using a network manager | 
|  | // that uses the host network. This doesn't work on all trybots. | 
|  | if (!deps.allocator) { | 
|  | auto fake_network = NewFakeNetwork(); | 
|  | fake_network->AddInterface(NextLocalAddress()); | 
|  | deps.allocator = | 
|  | std::make_unique<cricket::BasicPortAllocator>(fake_network); | 
|  | } | 
|  |  | 
|  | auto observer = std::make_unique<ObserverForUsageHistogramTest>(); | 
|  | deps.observer = observer.get(); | 
|  |  | 
|  | auto pc = pc_factory->CreatePeerConnection(config, std::move(deps)); | 
|  | if (!pc) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | observer->SetPeerConnectionInterface(pc.get()); | 
|  | auto wrapper = std::make_unique<PeerConnectionWrapperForUsageHistogramTest>( | 
|  | pc_factory, pc, std::move(observer)); | 
|  | return wrapper; | 
|  | } | 
|  |  | 
|  | int ObservedFingerprint() { | 
|  | // This works correctly only if there is only one sample value | 
|  | // that has been counted. | 
|  | // Returns -1 for "not found". | 
|  | return webrtc::metrics::MinSample(kUsagePatternMetric); | 
|  | } | 
|  |  | 
|  | // The PeerConnection's port allocator is tied to the PeerConnection's | 
|  | // lifetime and expects the underlying NetworkManager to outlive it.  That | 
|  | // prevents us from having the PeerConnectionWrapper own the fake network. | 
|  | // Therefore, the test fixture will own all the fake networks even though | 
|  | // tests should access the fake network through the PeerConnectionWrapper. | 
|  | rtc::FakeNetworkManager* NewFakeNetwork() { | 
|  | fake_networks_.emplace_back(std::make_unique<rtc::FakeNetworkManager>()); | 
|  | return fake_networks_.back().get(); | 
|  | } | 
|  |  | 
|  | rtc::SocketAddress NextLocalAddress() { | 
|  | RTC_DCHECK(next_local_address_ < (int)arraysize(kLocalAddrs)); | 
|  | return kLocalAddrs[next_local_address_++]; | 
|  | } | 
|  |  | 
|  | std::vector<std::unique_ptr<rtc::FakeNetworkManager>> fake_networks_; | 
|  | int next_local_address_ = 0; | 
|  | std::unique_ptr<rtc::VirtualSocketServer> vss_; | 
|  | rtc::AutoSocketServerThread main_; | 
|  | }; | 
|  |  | 
|  | TEST_F(PeerConnectionUsageHistogramTest, UsageFingerprintHistogramFromTimeout) { | 
|  | auto pc = CreatePeerConnectionWithImmediateReport(); | 
|  |  | 
|  | int expected_fingerprint = MakeUsageFingerprint({}); | 
|  | EXPECT_METRIC_EQ_WAIT(1, webrtc::metrics::NumSamples(kUsagePatternMetric), | 
|  | kDefaultTimeout); | 
|  | EXPECT_METRIC_EQ( | 
|  | 1, webrtc::metrics::NumEvents(kUsagePatternMetric, expected_fingerprint)); | 
|  | } | 
|  |  | 
|  | #ifndef WEBRTC_ANDROID | 
|  | // These tests do not work on Android. Why is unclear. | 
|  | // https://bugs.webrtc.org/9461 | 
|  |  | 
|  | // Test getting the usage fingerprint for an audio/video connection. | 
|  | TEST_F(PeerConnectionUsageHistogramTest, FingerprintAudioVideo) { | 
|  | auto caller = CreatePeerConnection(); | 
|  | auto callee = CreatePeerConnection(); | 
|  | caller->AddAudioTrack("audio"); | 
|  | caller->AddVideoTrack("video"); | 
|  | ASSERT_TRUE(caller->ConnectTo(callee.get())); | 
|  | caller->pc()->Close(); | 
|  | callee->pc()->Close(); | 
|  | int expected_fingerprint = MakeUsageFingerprint( | 
|  | {UsageEvent::AUDIO_ADDED, UsageEvent::VIDEO_ADDED, | 
|  | UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED, | 
|  | UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED, | 
|  | UsageEvent::CANDIDATE_COLLECTED, UsageEvent::ADD_ICE_CANDIDATE_SUCCEEDED, | 
|  | UsageEvent::ICE_STATE_CONNECTED, UsageEvent::REMOTE_CANDIDATE_ADDED, | 
|  | UsageEvent::DIRECT_CONNECTION_SELECTED, UsageEvent::CLOSE_CALLED}); | 
|  | // In this case, we may or may not have PRIVATE_CANDIDATE_COLLECTED, | 
|  | // depending on the machine configuration. | 
|  | EXPECT_METRIC_EQ(2, webrtc::metrics::NumSamples(kUsagePatternMetric)); | 
|  | EXPECT_METRIC_TRUE( | 
|  | webrtc::metrics::NumEvents(kUsagePatternMetric, expected_fingerprint) == | 
|  | 2 || | 
|  | webrtc::metrics::NumEvents( | 
|  | kUsagePatternMetric, | 
|  | expected_fingerprint | | 
|  | static_cast<int>(UsageEvent::PRIVATE_CANDIDATE_COLLECTED)) == 2); | 
|  | } | 
|  |  | 
|  | // Test getting the usage fingerprint when the caller collects an mDNS | 
|  | // candidate. | 
|  | TEST_F(PeerConnectionUsageHistogramTest, FingerprintWithMdnsCaller) { | 
|  | RTCConfiguration config; | 
|  |  | 
|  | // Enable hostname candidates with mDNS names. | 
|  | auto caller = CreatePeerConnectionWithMdns(config); | 
|  | auto callee = CreatePeerConnection(config); | 
|  |  | 
|  | caller->AddAudioTrack("audio"); | 
|  | caller->AddVideoTrack("video"); | 
|  | ASSERT_TRUE(caller->ConnectTo(callee.get())); | 
|  | caller->pc()->Close(); | 
|  | callee->pc()->Close(); | 
|  |  | 
|  | int expected_fingerprint_caller = MakeUsageFingerprint( | 
|  | {UsageEvent::AUDIO_ADDED, UsageEvent::VIDEO_ADDED, | 
|  | UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED, | 
|  | UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED, | 
|  | UsageEvent::CANDIDATE_COLLECTED, UsageEvent::MDNS_CANDIDATE_COLLECTED, | 
|  | UsageEvent::ADD_ICE_CANDIDATE_SUCCEEDED, UsageEvent::ICE_STATE_CONNECTED, | 
|  | UsageEvent::REMOTE_CANDIDATE_ADDED, | 
|  | UsageEvent::DIRECT_CONNECTION_SELECTED, UsageEvent::CLOSE_CALLED}); | 
|  |  | 
|  | // Without a resolver, the callee cannot resolve the received mDNS candidate | 
|  | // but can still connect with the caller via a prflx candidate. As a result, | 
|  | // the bit for the direct connection should not be logged. | 
|  | int expected_fingerprint_callee = MakeUsageFingerprint( | 
|  | {UsageEvent::AUDIO_ADDED, UsageEvent::VIDEO_ADDED, | 
|  | UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED, | 
|  | UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED, | 
|  | UsageEvent::CANDIDATE_COLLECTED, UsageEvent::ADD_ICE_CANDIDATE_SUCCEEDED, | 
|  | UsageEvent::REMOTE_MDNS_CANDIDATE_ADDED, UsageEvent::ICE_STATE_CONNECTED, | 
|  | UsageEvent::REMOTE_CANDIDATE_ADDED, UsageEvent::CLOSE_CALLED}); | 
|  | EXPECT_METRIC_EQ(2, webrtc::metrics::NumSamples(kUsagePatternMetric)); | 
|  | EXPECT_METRIC_EQ(1, webrtc::metrics::NumEvents(kUsagePatternMetric, | 
|  | expected_fingerprint_caller)); | 
|  | EXPECT_METRIC_EQ(1, webrtc::metrics::NumEvents(kUsagePatternMetric, | 
|  | expected_fingerprint_callee)); | 
|  | } | 
|  |  | 
|  | // Test getting the usage fingerprint when the callee collects an mDNS | 
|  | // candidate. | 
|  | TEST_F(PeerConnectionUsageHistogramTest, FingerprintWithMdnsCallee) { | 
|  | RTCConfiguration config; | 
|  |  | 
|  | // Enable hostname candidates with mDNS names. | 
|  | auto caller = CreatePeerConnection(config); | 
|  | auto callee = CreatePeerConnectionWithMdns(config); | 
|  |  | 
|  | caller->AddAudioTrack("audio"); | 
|  | caller->AddVideoTrack("video"); | 
|  | ASSERT_TRUE(caller->ConnectTo(callee.get())); | 
|  | caller->pc()->Close(); | 
|  | callee->pc()->Close(); | 
|  |  | 
|  | // Similar to the test above, the caller connects with the callee via a prflx | 
|  | // candidate. | 
|  | int expected_fingerprint_caller = MakeUsageFingerprint( | 
|  | {UsageEvent::AUDIO_ADDED, UsageEvent::VIDEO_ADDED, | 
|  | UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED, | 
|  | UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED, | 
|  | UsageEvent::CANDIDATE_COLLECTED, UsageEvent::ADD_ICE_CANDIDATE_SUCCEEDED, | 
|  | UsageEvent::REMOTE_MDNS_CANDIDATE_ADDED, UsageEvent::ICE_STATE_CONNECTED, | 
|  | UsageEvent::REMOTE_CANDIDATE_ADDED, UsageEvent::CLOSE_CALLED}); | 
|  |  | 
|  | int expected_fingerprint_callee = MakeUsageFingerprint( | 
|  | {UsageEvent::AUDIO_ADDED, UsageEvent::VIDEO_ADDED, | 
|  | UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED, | 
|  | UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED, | 
|  | UsageEvent::CANDIDATE_COLLECTED, UsageEvent::MDNS_CANDIDATE_COLLECTED, | 
|  | UsageEvent::ADD_ICE_CANDIDATE_SUCCEEDED, UsageEvent::ICE_STATE_CONNECTED, | 
|  | UsageEvent::REMOTE_CANDIDATE_ADDED, | 
|  | UsageEvent::DIRECT_CONNECTION_SELECTED, UsageEvent::CLOSE_CALLED}); | 
|  | EXPECT_METRIC_EQ(2, webrtc::metrics::NumSamples(kUsagePatternMetric)); | 
|  | EXPECT_METRIC_EQ(1, webrtc::metrics::NumEvents(kUsagePatternMetric, | 
|  | expected_fingerprint_caller)); | 
|  | EXPECT_METRIC_EQ(1, webrtc::metrics::NumEvents(kUsagePatternMetric, | 
|  | expected_fingerprint_callee)); | 
|  | } | 
|  |  | 
|  | #ifdef WEBRTC_HAVE_SCTP | 
|  | TEST_F(PeerConnectionUsageHistogramTest, FingerprintDataOnly) { | 
|  | auto caller = CreatePeerConnection(); | 
|  | auto callee = CreatePeerConnection(); | 
|  | caller->CreateDataChannel("foodata"); | 
|  | ASSERT_TRUE(caller->ConnectTo(callee.get())); | 
|  | ASSERT_TRUE_WAIT(callee->HaveDataChannel(), kDefaultTimeout); | 
|  | caller->pc()->Close(); | 
|  | callee->pc()->Close(); | 
|  | int expected_fingerprint = MakeUsageFingerprint( | 
|  | {UsageEvent::DATA_ADDED, UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED, | 
|  | UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED, | 
|  | UsageEvent::CANDIDATE_COLLECTED, UsageEvent::ADD_ICE_CANDIDATE_SUCCEEDED, | 
|  | UsageEvent::ICE_STATE_CONNECTED, UsageEvent::REMOTE_CANDIDATE_ADDED, | 
|  | UsageEvent::DIRECT_CONNECTION_SELECTED, UsageEvent::CLOSE_CALLED}); | 
|  | EXPECT_METRIC_EQ(2, webrtc::metrics::NumSamples(kUsagePatternMetric)); | 
|  | EXPECT_METRIC_TRUE( | 
|  | webrtc::metrics::NumEvents(kUsagePatternMetric, expected_fingerprint) == | 
|  | 2 || | 
|  | webrtc::metrics::NumEvents( | 
|  | kUsagePatternMetric, | 
|  | expected_fingerprint | | 
|  | static_cast<int>(UsageEvent::PRIVATE_CANDIDATE_COLLECTED)) == 2); | 
|  | } | 
|  | #endif  // WEBRTC_HAVE_SCTP | 
|  | #endif  // WEBRTC_ANDROID | 
|  |  | 
|  | TEST_F(PeerConnectionUsageHistogramTest, FingerprintStunTurn) { | 
|  | RTCConfiguration configuration; | 
|  | PeerConnection::IceServer server; | 
|  | server.urls = {"stun:dummy.stun.server"}; | 
|  | configuration.servers.push_back(server); | 
|  | server.urls = {"turn:dummy.turn.server"}; | 
|  | server.username = "username"; | 
|  | server.password = "password"; | 
|  | configuration.servers.push_back(server); | 
|  | auto caller = CreatePeerConnection(configuration); | 
|  | ASSERT_TRUE(caller); | 
|  | caller->pc()->Close(); | 
|  | int expected_fingerprint = MakeUsageFingerprint( | 
|  | {UsageEvent::STUN_SERVER_ADDED, UsageEvent::TURN_SERVER_ADDED, | 
|  | UsageEvent::CLOSE_CALLED}); | 
|  | EXPECT_METRIC_EQ(1, webrtc::metrics::NumSamples(kUsagePatternMetric)); | 
|  | EXPECT_METRIC_EQ( | 
|  | 1, webrtc::metrics::NumEvents(kUsagePatternMetric, expected_fingerprint)); | 
|  | } | 
|  |  | 
|  | TEST_F(PeerConnectionUsageHistogramTest, FingerprintStunTurnInReconfiguration) { | 
|  | RTCConfiguration configuration; | 
|  | PeerConnection::IceServer server; | 
|  | server.urls = {"stun:dummy.stun.server"}; | 
|  | configuration.servers.push_back(server); | 
|  | server.urls = {"turn:dummy.turn.server"}; | 
|  | server.username = "username"; | 
|  | server.password = "password"; | 
|  | configuration.servers.push_back(server); | 
|  | auto caller = CreatePeerConnection(); | 
|  | ASSERT_TRUE(caller); | 
|  | ASSERT_TRUE(caller->pc()->SetConfiguration(configuration).ok()); | 
|  | caller->pc()->Close(); | 
|  | int expected_fingerprint = MakeUsageFingerprint( | 
|  | {UsageEvent::STUN_SERVER_ADDED, UsageEvent::TURN_SERVER_ADDED, | 
|  | UsageEvent::CLOSE_CALLED}); | 
|  | EXPECT_METRIC_EQ(1, webrtc::metrics::NumSamples(kUsagePatternMetric)); | 
|  | EXPECT_METRIC_EQ( | 
|  | 1, webrtc::metrics::NumEvents(kUsagePatternMetric, expected_fingerprint)); | 
|  | } | 
|  |  | 
|  | TEST_F(PeerConnectionUsageHistogramTest, FingerprintWithPrivateIPCaller) { | 
|  | auto caller = CreatePeerConnectionWithPrivateLocalAddresses(); | 
|  | auto callee = CreatePeerConnection(); | 
|  | caller->AddAudioTrack("audio"); | 
|  | ASSERT_TRUE(caller->ConnectTo(callee.get())); | 
|  | caller->pc()->Close(); | 
|  | callee->pc()->Close(); | 
|  |  | 
|  | int expected_fingerprint_caller = MakeUsageFingerprint( | 
|  | {UsageEvent::AUDIO_ADDED, UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED, | 
|  | UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED, | 
|  | UsageEvent::CANDIDATE_COLLECTED, UsageEvent::PRIVATE_CANDIDATE_COLLECTED, | 
|  | UsageEvent::ADD_ICE_CANDIDATE_SUCCEEDED, UsageEvent::ICE_STATE_CONNECTED, | 
|  | UsageEvent::REMOTE_CANDIDATE_ADDED, | 
|  | UsageEvent::DIRECT_CONNECTION_SELECTED, UsageEvent::CLOSE_CALLED}); | 
|  |  | 
|  | int expected_fingerprint_callee = MakeUsageFingerprint( | 
|  | {UsageEvent::AUDIO_ADDED, UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED, | 
|  | UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED, | 
|  | UsageEvent::CANDIDATE_COLLECTED, UsageEvent::ADD_ICE_CANDIDATE_SUCCEEDED, | 
|  | UsageEvent::REMOTE_PRIVATE_CANDIDATE_ADDED, | 
|  | UsageEvent::ICE_STATE_CONNECTED, UsageEvent::REMOTE_CANDIDATE_ADDED, | 
|  | UsageEvent::DIRECT_CONNECTION_SELECTED, UsageEvent::CLOSE_CALLED}); | 
|  | EXPECT_METRIC_EQ(2, webrtc::metrics::NumSamples(kUsagePatternMetric)); | 
|  | EXPECT_METRIC_EQ(1, webrtc::metrics::NumEvents(kUsagePatternMetric, | 
|  | expected_fingerprint_caller)); | 
|  | EXPECT_METRIC_EQ(1, webrtc::metrics::NumEvents(kUsagePatternMetric, | 
|  | expected_fingerprint_callee)); | 
|  | } | 
|  |  | 
|  | TEST_F(PeerConnectionUsageHistogramTest, FingerprintWithPrivateIpv6Callee) { | 
|  | auto caller = CreatePeerConnection(); | 
|  | auto callee = CreatePeerConnectionWithPrivateIpv6LocalAddresses(); | 
|  | caller->AddAudioTrack("audio"); | 
|  | ASSERT_TRUE(caller->ConnectTo(callee.get())); | 
|  | caller->pc()->Close(); | 
|  | callee->pc()->Close(); | 
|  |  | 
|  | int expected_fingerprint_caller = MakeUsageFingerprint( | 
|  | {UsageEvent::AUDIO_ADDED, UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED, | 
|  | UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED, | 
|  | UsageEvent::CANDIDATE_COLLECTED, UsageEvent::ADD_ICE_CANDIDATE_SUCCEEDED, | 
|  | UsageEvent::REMOTE_PRIVATE_CANDIDATE_ADDED, | 
|  | UsageEvent::ICE_STATE_CONNECTED, UsageEvent::REMOTE_CANDIDATE_ADDED, | 
|  | UsageEvent::REMOTE_IPV6_CANDIDATE_ADDED, | 
|  | UsageEvent::DIRECT_CONNECTION_SELECTED, UsageEvent::CLOSE_CALLED}); | 
|  |  | 
|  | int expected_fingerprint_callee = MakeUsageFingerprint( | 
|  | {UsageEvent::AUDIO_ADDED, UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED, | 
|  | UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED, | 
|  | UsageEvent::CANDIDATE_COLLECTED, UsageEvent::PRIVATE_CANDIDATE_COLLECTED, | 
|  | UsageEvent::IPV6_CANDIDATE_COLLECTED, | 
|  | UsageEvent::ADD_ICE_CANDIDATE_SUCCEEDED, | 
|  | UsageEvent::REMOTE_CANDIDATE_ADDED, UsageEvent::ICE_STATE_CONNECTED, | 
|  | UsageEvent::DIRECT_CONNECTION_SELECTED, UsageEvent::CLOSE_CALLED}); | 
|  | EXPECT_METRIC_EQ(2, webrtc::metrics::NumSamples(kUsagePatternMetric)); | 
|  | EXPECT_METRIC_EQ(1, webrtc::metrics::NumEvents(kUsagePatternMetric, | 
|  | expected_fingerprint_caller)); | 
|  | EXPECT_METRIC_EQ(1, webrtc::metrics::NumEvents(kUsagePatternMetric, | 
|  | expected_fingerprint_callee)); | 
|  | } | 
|  |  | 
|  | #ifndef WEBRTC_ANDROID | 
|  | #ifdef WEBRTC_HAVE_SCTP | 
|  | // Test that the usage pattern bits for adding remote (private IPv6) candidates | 
|  | // are set when the remote candidates are retrieved from the Offer SDP instead | 
|  | // of trickled ICE messages. | 
|  | TEST_F(PeerConnectionUsageHistogramTest, | 
|  | AddRemoteCandidatesFromRemoteDescription) { | 
|  | // We construct the following data-channel-only scenario. The caller collects | 
|  | // IPv6 private local candidates and appends them in the Offer as in | 
|  | // non-trickled sessions. The callee collects mDNS candidates that are not | 
|  | // contained in the Answer as in Trickle ICE. Only the Offer and Answer are | 
|  | // signaled and we expect a connection with prflx remote candidates at the | 
|  | // caller side. | 
|  | auto caller = CreatePeerConnectionWithPrivateIpv6LocalAddresses(); | 
|  | auto callee = CreatePeerConnectionWithMdns(RTCConfiguration()); | 
|  | caller->CreateDataChannel("test_channel"); | 
|  | ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer())); | 
|  | // Wait until the gathering completes so that the session description would | 
|  | // have contained ICE candidates. | 
|  | EXPECT_EQ_WAIT(webrtc::PeerConnectionInterface::kIceGatheringComplete, | 
|  | caller->ice_gathering_state(), kDefaultTimeout); | 
|  | EXPECT_TRUE(caller->observer()->candidate_gathered()); | 
|  | // Get the current offer that contains candidates and pass it to the callee. | 
|  | // | 
|  | // Note that we cannot use CloneSessionDescription on |cur_offer| to obtain an | 
|  | // SDP with candidates. The method above does not strictly copy everything, in | 
|  | // particular, not copying the ICE candidates. | 
|  | // TODO(qingsi): Technically, this is a bug. Fix it. | 
|  | auto cur_offer = caller->pc()->local_description(); | 
|  | ASSERT_TRUE(cur_offer); | 
|  | std::string sdp_with_candidates_str; | 
|  | cur_offer->ToString(&sdp_with_candidates_str); | 
|  | auto offer = std::make_unique<JsepSessionDescription>(SdpType::kOffer); | 
|  | ASSERT_TRUE(SdpDeserialize(sdp_with_candidates_str, offer.get(), | 
|  | nullptr /* error */)); | 
|  | ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer))); | 
|  |  | 
|  | // By default, the Answer created does not contain ICE candidates. | 
|  | auto answer = callee->CreateAnswer(); | 
|  | callee->SetLocalDescription(CloneSessionDescription(answer.get())); | 
|  | caller->SetRemoteDescription(std::move(answer)); | 
|  | EXPECT_TRUE_WAIT(caller->IsConnected(), kDefaultTimeout); | 
|  | EXPECT_TRUE_WAIT(callee->IsConnected(), kDefaultTimeout); | 
|  | // The callee needs to process the open message to have the data channel open. | 
|  | EXPECT_TRUE_WAIT(callee->observer()->last_datachannel_ != nullptr, | 
|  | kDefaultTimeout); | 
|  | caller->pc()->Close(); | 
|  | callee->pc()->Close(); | 
|  |  | 
|  | // The caller should not have added any remote candidate either via | 
|  | // AddIceCandidate or from the remote description. Also, the caller connects | 
|  | // with the callee via a prflx candidate and hence no direct connection bit | 
|  | // should be set. | 
|  | int expected_fingerprint_caller = MakeUsageFingerprint( | 
|  | {UsageEvent::DATA_ADDED, UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED, | 
|  | UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED, | 
|  | UsageEvent::CANDIDATE_COLLECTED, UsageEvent::PRIVATE_CANDIDATE_COLLECTED, | 
|  | UsageEvent::IPV6_CANDIDATE_COLLECTED, UsageEvent::ICE_STATE_CONNECTED, | 
|  | UsageEvent::CLOSE_CALLED}); | 
|  |  | 
|  | int expected_fingerprint_callee = MakeUsageFingerprint( | 
|  | {UsageEvent::DATA_ADDED, UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED, | 
|  | UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED, | 
|  | UsageEvent::CANDIDATE_COLLECTED, UsageEvent::MDNS_CANDIDATE_COLLECTED, | 
|  | UsageEvent::REMOTE_CANDIDATE_ADDED, | 
|  | UsageEvent::REMOTE_PRIVATE_CANDIDATE_ADDED, | 
|  | UsageEvent::REMOTE_IPV6_CANDIDATE_ADDED, UsageEvent::ICE_STATE_CONNECTED, | 
|  | UsageEvent::DIRECT_CONNECTION_SELECTED, UsageEvent::CLOSE_CALLED}); | 
|  | EXPECT_METRIC_EQ(2, webrtc::metrics::NumSamples(kUsagePatternMetric)); | 
|  | EXPECT_METRIC_EQ(1, webrtc::metrics::NumEvents(kUsagePatternMetric, | 
|  | expected_fingerprint_caller)); | 
|  | EXPECT_METRIC_EQ(1, webrtc::metrics::NumEvents(kUsagePatternMetric, | 
|  | expected_fingerprint_callee)); | 
|  | } | 
|  |  | 
|  | TEST_F(PeerConnectionUsageHistogramTest, NotableUsageNoted) { | 
|  | auto caller = CreatePeerConnection(); | 
|  | caller->CreateDataChannel("foo"); | 
|  | caller->GenerateOfferAndCollectCandidates(); | 
|  | caller->pc()->Close(); | 
|  | int expected_fingerprint = MakeUsageFingerprint( | 
|  | {UsageEvent::DATA_ADDED, UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED, | 
|  | UsageEvent::CANDIDATE_COLLECTED, UsageEvent::CLOSE_CALLED}); | 
|  | EXPECT_METRIC_EQ(1, webrtc::metrics::NumSamples(kUsagePatternMetric)); | 
|  | EXPECT_METRIC_TRUE( | 
|  | expected_fingerprint == ObservedFingerprint() || | 
|  | (expected_fingerprint | | 
|  | static_cast<int>(UsageEvent::PRIVATE_CANDIDATE_COLLECTED)) == | 
|  | ObservedFingerprint()); | 
|  | EXPECT_METRIC_EQ(absl::make_optional(ObservedFingerprint()), | 
|  | caller->observer()->interesting_usage_detected()); | 
|  | } | 
|  |  | 
|  | TEST_F(PeerConnectionUsageHistogramTest, NotableUsageOnEventFiring) { | 
|  | auto caller = CreatePeerConnection(); | 
|  | caller->CreateDataChannel("foo"); | 
|  | caller->GenerateOfferAndCollectCandidates(); | 
|  | int expected_fingerprint = MakeUsageFingerprint( | 
|  | {UsageEvent::DATA_ADDED, UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED, | 
|  | UsageEvent::CANDIDATE_COLLECTED}); | 
|  | EXPECT_METRIC_EQ(0, webrtc::metrics::NumSamples(kUsagePatternMetric)); | 
|  | caller->GetInternalPeerConnection()->RequestUsagePatternReportForTesting(); | 
|  | EXPECT_METRIC_EQ_WAIT(1, webrtc::metrics::NumSamples(kUsagePatternMetric), | 
|  | kDefaultTimeout); | 
|  | EXPECT_METRIC_TRUE( | 
|  | expected_fingerprint == ObservedFingerprint() || | 
|  | (expected_fingerprint | | 
|  | static_cast<int>(UsageEvent::PRIVATE_CANDIDATE_COLLECTED)) == | 
|  | ObservedFingerprint()); | 
|  | EXPECT_METRIC_EQ(absl::make_optional(ObservedFingerprint()), | 
|  | caller->observer()->interesting_usage_detected()); | 
|  | } | 
|  |  | 
|  | TEST_F(PeerConnectionUsageHistogramTest, | 
|  | NoNotableUsageOnEventFiringAfterClose) { | 
|  | auto caller = CreatePeerConnection(); | 
|  | caller->CreateDataChannel("foo"); | 
|  | caller->GenerateOfferAndCollectCandidates(); | 
|  | int expected_fingerprint = MakeUsageFingerprint( | 
|  | {UsageEvent::DATA_ADDED, UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED, | 
|  | UsageEvent::CANDIDATE_COLLECTED, UsageEvent::CLOSE_CALLED}); | 
|  | EXPECT_METRIC_EQ(0, webrtc::metrics::NumSamples(kUsagePatternMetric)); | 
|  | caller->pc()->Close(); | 
|  | EXPECT_METRIC_EQ(1, webrtc::metrics::NumSamples(kUsagePatternMetric)); | 
|  | caller->GetInternalPeerConnection()->RequestUsagePatternReportForTesting(); | 
|  | caller->observer()->ClearInterestingUsageDetector(); | 
|  | EXPECT_METRIC_EQ_WAIT(2, webrtc::metrics::NumSamples(kUsagePatternMetric), | 
|  | kDefaultTimeout); | 
|  | EXPECT_METRIC_TRUE( | 
|  | expected_fingerprint == ObservedFingerprint() || | 
|  | (expected_fingerprint | | 
|  | static_cast<int>(UsageEvent::PRIVATE_CANDIDATE_COLLECTED)) == | 
|  | ObservedFingerprint()); | 
|  | // After close, the usage-detection callback should NOT have been called. | 
|  | EXPECT_METRIC_FALSE(caller->observer()->interesting_usage_detected()); | 
|  | } | 
|  | #endif | 
|  | #endif | 
|  |  | 
|  | }  // namespace webrtc |