blob: 0067cd6ef9bf384b84bf2efe8877e4150de24f9b [file] [log] [blame]
Harald Alvestrand19793842018-06-25 12:03:50 +02001/*
2 * Copyright 2017 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include <tuple>
12
Karl Wiberg918f50c2018-07-05 11:40:33 +020013#include "absl/memory/memory.h"
Harald Alvestrand42386282018-07-12 07:56:05 +020014#include "api/jsep.h"
Harald Alvestrand19793842018-06-25 12:03:50 +020015#include "api/peerconnectionproxy.h"
16#include "media/base/fakemediaengine.h"
Harald Alvestrand056d8112018-07-16 19:18:58 +020017#include "p2p/client/basicportallocator.h"
Harald Alvestrand19793842018-06-25 12:03:50 +020018#include "pc/mediasession.h"
19#include "pc/peerconnection.h"
20#include "pc/peerconnectionfactory.h"
21#include "pc/peerconnectionwrapper.h"
22#include "pc/sdputils.h"
Harald Alvestrand19793842018-06-25 12:03:50 +020023#include "pc/test/fakesctptransport.h"
Harald Alvestrand056d8112018-07-16 19:18:58 +020024#include "rtc_base/fakenetwork.h"
Harald Alvestrand19793842018-06-25 12:03:50 +020025#include "rtc_base/gunit.h"
Harald Alvestrand19793842018-06-25 12:03:50 +020026#include "rtc_base/virtualsocketserver.h"
Qingsi Wang7fc821d2018-07-12 12:54:53 -070027#include "system_wrappers/include/metrics_default.h"
Harald Alvestrand19793842018-06-25 12:03:50 +020028
29namespace webrtc {
30
31using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
32using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions;
33using ::testing::Values;
34
35static constexpr int kDefaultTimeout = 10000;
Harald Alvestrand056d8112018-07-16 19:18:58 +020036static const rtc::SocketAddress kDefaultLocalAddress("1.1.1.1", 0);
37static const rtc::SocketAddress kPrivateLocalAddress("10.1.1.1", 0);
Harald Alvestrand19793842018-06-25 12:03:50 +020038
39int MakeUsageFingerprint(std::set<PeerConnection::UsageEvent> events) {
40 int signature = 0;
41 for (const auto it : events) {
42 signature |= static_cast<int>(it);
43 }
44 return signature;
45}
46
47class PeerConnectionFactoryForUsageHistogramTest
48 : public rtc::RefCountedObject<PeerConnectionFactory> {
49 public:
50 PeerConnectionFactoryForUsageHistogramTest()
51 : rtc::RefCountedObject<PeerConnectionFactory>(
52 rtc::Thread::Current(),
53 rtc::Thread::Current(),
54 rtc::Thread::Current(),
Karl Wiberg918f50c2018-07-05 11:40:33 +020055 absl::make_unique<cricket::FakeMediaEngine>(),
Harald Alvestrand19793842018-06-25 12:03:50 +020056 CreateCallFactory(),
57 nullptr) {}
58
59 void ActionsBeforeInitializeForTesting(PeerConnectionInterface* pc) override {
60 PeerConnection* internal_pc = static_cast<PeerConnection*>(pc);
61 if (return_histogram_very_quickly_) {
62 internal_pc->ReturnHistogramVeryQuicklyForTesting();
63 }
64 }
65
66 void ReturnHistogramVeryQuickly() { return_histogram_very_quickly_ = true; }
67
68 private:
Harald Alvestrand183e09d2018-06-28 12:04:41 +020069 bool return_histogram_very_quickly_ = false;
70};
71
72class PeerConnectionWrapperForUsageHistogramTest;
73typedef PeerConnectionWrapperForUsageHistogramTest* RawWrapperPtr;
74
75class ObserverForUsageHistogramTest : public MockPeerConnectionObserver {
76 public:
77 void OnIceCandidate(const webrtc::IceCandidateInterface* candidate) override;
78 void PrepareToExchangeCandidates(RawWrapperPtr other) {
79 candidate_target_ = other;
80 }
Harald Alvestrand183e09d2018-06-28 12:04:41 +020081 bool HaveDataChannel() { return last_datachannel_; }
82
83 private:
84 RawWrapperPtr candidate_target_; // Note: Not thread-safe against deletions.
Harald Alvestrand19793842018-06-25 12:03:50 +020085};
86
87class PeerConnectionWrapperForUsageHistogramTest
88 : public PeerConnectionWrapper {
89 public:
90 using PeerConnectionWrapper::PeerConnectionWrapper;
91
92 PeerConnection* GetInternalPeerConnection() {
93 auto* pci =
94 static_cast<PeerConnectionProxyWithInternal<PeerConnectionInterface>*>(
95 pc());
96 return static_cast<PeerConnection*>(pci->internal());
97 }
Harald Alvestrand183e09d2018-06-28 12:04:41 +020098
99 void PrepareToExchangeCandidates(
100 PeerConnectionWrapperForUsageHistogramTest* other) {
101 static_cast<ObserverForUsageHistogramTest*>(observer())
102 ->PrepareToExchangeCandidates(other);
103 static_cast<ObserverForUsageHistogramTest*>(other->observer())
104 ->PrepareToExchangeCandidates(this);
105 }
106
107 bool IsConnected() {
108 return pc()->ice_connection_state() ==
109 PeerConnectionInterface::kIceConnectionConnected ||
110 pc()->ice_connection_state() ==
111 PeerConnectionInterface::kIceConnectionCompleted;
112 }
113
114 bool HaveDataChannel() {
115 return static_cast<ObserverForUsageHistogramTest*>(observer())
116 ->HaveDataChannel();
117 }
Harald Alvestrand42386282018-07-12 07:56:05 +0200118 void AddOrBufferIceCandidate(const webrtc::IceCandidateInterface* candidate) {
119 if (!pc()->AddIceCandidate(candidate)) {
120 std::string sdp;
121 EXPECT_TRUE(candidate->ToString(&sdp));
122 std::unique_ptr<webrtc::IceCandidateInterface> candidate_copy(
123 CreateIceCandidate(candidate->sdp_mid(), candidate->sdp_mline_index(),
124 sdp, nullptr));
125 buffered_candidates_.push_back(std::move(candidate_copy));
126 }
127 }
Harald Alvestrand056d8112018-07-16 19:18:58 +0200128
Harald Alvestrand42386282018-07-12 07:56:05 +0200129 void AddBufferedIceCandidates() {
130 for (const auto& candidate : buffered_candidates_) {
131 EXPECT_TRUE(pc()->AddIceCandidate(candidate.get()));
132 }
133 buffered_candidates_.clear();
134 }
Harald Alvestrand056d8112018-07-16 19:18:58 +0200135
Harald Alvestrand42386282018-07-12 07:56:05 +0200136 bool ConnectTo(PeerConnectionWrapperForUsageHistogramTest* callee) {
137 PrepareToExchangeCandidates(callee);
Harald Alvestrand056d8112018-07-16 19:18:58 +0200138 if (!ExchangeOfferAnswerWith(callee)) {
139 return false;
140 }
Harald Alvestrand42386282018-07-12 07:56:05 +0200141 AddBufferedIceCandidates();
142 callee->AddBufferedIceCandidates();
Harald Alvestrand056d8112018-07-16 19:18:58 +0200143 WAIT(IsConnected(), kDefaultTimeout);
144 WAIT(callee->IsConnected(), kDefaultTimeout);
Harald Alvestrand42386282018-07-12 07:56:05 +0200145 return IsConnected() && callee->IsConnected();
146 }
147
Harald Alvestrand056d8112018-07-16 19:18:58 +0200148 bool GenerateOfferAndCollectCandidates() {
149 auto offer = CreateOffer(RTCOfferAnswerOptions());
150 if (!offer) {
151 return false;
152 }
153 bool set_local_offer =
154 SetLocalDescription(CloneSessionDescription(offer.get()));
155 EXPECT_TRUE(set_local_offer);
156 if (!set_local_offer) {
157 return false;
158 }
159 EXPECT_TRUE_WAIT(observer()->ice_gathering_complete_, kDefaultTimeout);
160 return true;
161 }
162
Harald Alvestrand42386282018-07-12 07:56:05 +0200163 private:
164 // Candidates that have been sent but not yet configured
165 std::vector<std::unique_ptr<webrtc::IceCandidateInterface>>
166 buffered_candidates_;
Harald Alvestrand19793842018-06-25 12:03:50 +0200167};
168
Harald Alvestrand183e09d2018-06-28 12:04:41 +0200169void ObserverForUsageHistogramTest::OnIceCandidate(
170 const webrtc::IceCandidateInterface* candidate) {
171 if (candidate_target_) {
Harald Alvestrand42386282018-07-12 07:56:05 +0200172 this->candidate_target_->AddOrBufferIceCandidate(candidate);
Harald Alvestrand183e09d2018-06-28 12:04:41 +0200173 }
Harald Alvestrand056d8112018-07-16 19:18:58 +0200174 // If target is not set, ignore. This happens in one-ended unit tests.
Harald Alvestrand183e09d2018-06-28 12:04:41 +0200175}
176
Harald Alvestrand19793842018-06-25 12:03:50 +0200177class PeerConnectionUsageHistogramTest : public ::testing::Test {
178 protected:
179 typedef std::unique_ptr<PeerConnectionWrapperForUsageHistogramTest>
180 WrapperPtr;
181
182 PeerConnectionUsageHistogramTest()
183 : vss_(new rtc::VirtualSocketServer()), main_(vss_.get()) {
Qingsi Wang7fc821d2018-07-12 12:54:53 -0700184 webrtc::metrics::Reset();
Harald Alvestrand19793842018-06-25 12:03:50 +0200185 }
186
187 WrapperPtr CreatePeerConnection() {
Harald Alvestrand056d8112018-07-16 19:18:58 +0200188 return CreatePeerConnection(RTCConfiguration(),
189 PeerConnectionFactoryInterface::Options(),
190 nullptr, false);
Harald Alvestrand19793842018-06-25 12:03:50 +0200191 }
192
Harald Alvestrandb2a74782018-06-28 13:54:07 +0200193 WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
194 return CreatePeerConnection(
Harald Alvestrand056d8112018-07-16 19:18:58 +0200195 config, PeerConnectionFactoryInterface::Options(), nullptr, false);
Harald Alvestrandb2a74782018-06-28 13:54:07 +0200196 }
197
Harald Alvestrand19793842018-06-25 12:03:50 +0200198 WrapperPtr CreatePeerConnectionWithImmediateReport() {
Harald Alvestrand056d8112018-07-16 19:18:58 +0200199 return CreatePeerConnection(RTCConfiguration(),
200 PeerConnectionFactoryInterface::Options(),
201 nullptr, true);
202 }
203
204 WrapperPtr CreatePeerConnectionWithPrivateLocalAddresses() {
205 fake_network_manager_.reset(new rtc::FakeNetworkManager());
206 fake_network_manager_->AddInterface(kDefaultLocalAddress);
207 fake_network_manager_->AddInterface(kPrivateLocalAddress);
208 std::unique_ptr<cricket::BasicPortAllocator> port_allocator(
209 new cricket::BasicPortAllocator(fake_network_manager_.get()));
210 return CreatePeerConnection(RTCConfiguration(),
211 PeerConnectionFactoryInterface::Options(),
212 std::move(port_allocator), false);
Harald Alvestrand19793842018-06-25 12:03:50 +0200213 }
214
215 WrapperPtr CreatePeerConnection(
216 const RTCConfiguration& config,
217 const PeerConnectionFactoryInterface::Options factory_options,
Harald Alvestrand056d8112018-07-16 19:18:58 +0200218 std::unique_ptr<cricket::PortAllocator> allocator,
Harald Alvestrand19793842018-06-25 12:03:50 +0200219 bool immediate_report) {
220 rtc::scoped_refptr<PeerConnectionFactoryForUsageHistogramTest> pc_factory(
221 new PeerConnectionFactoryForUsageHistogramTest());
222 pc_factory->SetOptions(factory_options);
223 RTC_CHECK(pc_factory->Initialize());
224 if (immediate_report) {
225 pc_factory->ReturnHistogramVeryQuickly();
226 }
Karl Wiberg918f50c2018-07-05 11:40:33 +0200227 auto observer = absl::make_unique<ObserverForUsageHistogramTest>();
Harald Alvestrand056d8112018-07-16 19:18:58 +0200228 auto pc = pc_factory->CreatePeerConnection(config, std::move(allocator),
229 nullptr, observer.get());
Harald Alvestrand19793842018-06-25 12:03:50 +0200230 if (!pc) {
231 return nullptr;
232 }
233
Karl Wiberg918f50c2018-07-05 11:40:33 +0200234 auto wrapper =
235 absl::make_unique<PeerConnectionWrapperForUsageHistogramTest>(
236 pc_factory, pc, std::move(observer));
Harald Alvestrand19793842018-06-25 12:03:50 +0200237 return wrapper;
238 }
239
Harald Alvestrand056d8112018-07-16 19:18:58 +0200240 std::unique_ptr<rtc::FakeNetworkManager> fake_network_manager_;
Harald Alvestrand19793842018-06-25 12:03:50 +0200241 std::unique_ptr<rtc::VirtualSocketServer> vss_;
242 rtc::AutoSocketServerThread main_;
243};
244
245TEST_F(PeerConnectionUsageHistogramTest, UsageFingerprintHistogramFromTimeout) {
246 auto pc = CreatePeerConnectionWithImmediateReport();
247
Harald Alvestrand19793842018-06-25 12:03:50 +0200248 int expected_fingerprint = MakeUsageFingerprint({});
Qingsi Wang7fc821d2018-07-12 12:54:53 -0700249 ASSERT_TRUE_WAIT(
250 1u == webrtc::metrics::NumSamples("WebRTC.PeerConnection.UsagePattern"),
251 kDefaultTimeout);
252 EXPECT_EQ(1, webrtc::metrics::NumEvents("WebRTC.PeerConnection.UsagePattern",
253 expected_fingerprint));
Harald Alvestrand19793842018-06-25 12:03:50 +0200254}
255
Harald Alvestrand183e09d2018-06-28 12:04:41 +0200256#ifndef WEBRTC_ANDROID
257// These tests do not work on Android. Why is unclear.
258// https://bugs.webrtc.org/9461
259
260// Test getting the usage fingerprint for an audio/video connection.
261TEST_F(PeerConnectionUsageHistogramTest, FingerprintAudioVideo) {
262 auto caller = CreatePeerConnection();
263 auto callee = CreatePeerConnection();
Harald Alvestrand183e09d2018-06-28 12:04:41 +0200264 caller->AddAudioTrack("audio");
265 caller->AddVideoTrack("video");
Harald Alvestrand42386282018-07-12 07:56:05 +0200266 ASSERT_TRUE(caller->ConnectTo(callee.get()));
Harald Alvestrand183e09d2018-06-28 12:04:41 +0200267 caller->pc()->Close();
268 callee->pc()->Close();
269 int expected_fingerprint = MakeUsageFingerprint(
270 {PeerConnection::UsageEvent::AUDIO_ADDED,
271 PeerConnection::UsageEvent::VIDEO_ADDED,
272 PeerConnection::UsageEvent::SET_LOCAL_DESCRIPTION_CALLED,
273 PeerConnection::UsageEvent::SET_REMOTE_DESCRIPTION_CALLED,
274 PeerConnection::UsageEvent::CANDIDATE_COLLECTED,
275 PeerConnection::UsageEvent::REMOTE_CANDIDATE_ADDED,
276 PeerConnection::UsageEvent::ICE_STATE_CONNECTED,
277 PeerConnection::UsageEvent::CLOSE_CALLED});
Harald Alvestrand056d8112018-07-16 19:18:58 +0200278 // In this case, we may or may not have PRIVATE_CANDIDATE_COLLECTED,
279 // depending on the machine configuration.
280 EXPECT_EQ(2,
281 webrtc::metrics::NumSamples("WebRTC.PeerConnection.UsagePattern"));
282 EXPECT_TRUE(
283 webrtc::metrics::NumEvents("WebRTC.PeerConnection.UsagePattern",
284 expected_fingerprint) == 2 ||
285 webrtc::metrics::NumEvents(
286 "WebRTC.PeerConnection.UsagePattern",
287 expected_fingerprint |
288 static_cast<int>(
289 PeerConnection::UsageEvent::PRIVATE_CANDIDATE_COLLECTED)) ==
290 2);
291}
292
293// Test getting the usage fingerprint when there are no host candidates.
294TEST_F(PeerConnectionUsageHistogramTest, FingerprintWithNoHostCandidates) {
295 RTCConfiguration config;
296 config.type = PeerConnectionInterface::kNoHost;
297 auto caller = CreatePeerConnection(config);
298 auto callee = CreatePeerConnection(config);
299 caller->AddAudioTrack("audio");
300 caller->AddVideoTrack("video");
301 // Under some bot configurations, this will fail - presumably bots where
302 // no working non-host addresses exist.
303 if (!caller->ConnectTo(callee.get())) {
304 return;
305 }
306 // If we manage to connect, we should get this precise fingerprint.
307 caller->pc()->Close();
308 callee->pc()->Close();
309 int expected_fingerprint = MakeUsageFingerprint(
310 {PeerConnection::UsageEvent::AUDIO_ADDED,
311 PeerConnection::UsageEvent::VIDEO_ADDED,
312 PeerConnection::UsageEvent::SET_LOCAL_DESCRIPTION_CALLED,
313 PeerConnection::UsageEvent::SET_REMOTE_DESCRIPTION_CALLED,
314 PeerConnection::UsageEvent::CANDIDATE_COLLECTED,
315 PeerConnection::UsageEvent::REMOTE_CANDIDATE_ADDED,
316 PeerConnection::UsageEvent::ICE_STATE_CONNECTED,
317 PeerConnection::UsageEvent::CLOSE_CALLED});
Qingsi Wang7fc821d2018-07-12 12:54:53 -0700318 EXPECT_EQ(2,
319 webrtc::metrics::NumSamples("WebRTC.PeerConnection.UsagePattern"));
320 EXPECT_EQ(2, webrtc::metrics::NumEvents("WebRTC.PeerConnection.UsagePattern",
321 expected_fingerprint));
Harald Alvestrand183e09d2018-06-28 12:04:41 +0200322}
323
324#ifdef HAVE_SCTP
325TEST_F(PeerConnectionUsageHistogramTest, FingerprintDataOnly) {
326 auto caller = CreatePeerConnection();
327 auto callee = CreatePeerConnection();
Harald Alvestrand183e09d2018-06-28 12:04:41 +0200328 caller->CreateDataChannel("foodata");
Harald Alvestrand42386282018-07-12 07:56:05 +0200329 ASSERT_TRUE(caller->ConnectTo(callee.get()));
Harald Alvestrand183e09d2018-06-28 12:04:41 +0200330 ASSERT_TRUE_WAIT(callee->HaveDataChannel(), kDefaultTimeout);
331 caller->pc()->Close();
332 callee->pc()->Close();
333 int expected_fingerprint = MakeUsageFingerprint(
334 {PeerConnection::UsageEvent::DATA_ADDED,
335 PeerConnection::UsageEvent::SET_LOCAL_DESCRIPTION_CALLED,
336 PeerConnection::UsageEvent::SET_REMOTE_DESCRIPTION_CALLED,
337 PeerConnection::UsageEvent::CANDIDATE_COLLECTED,
338 PeerConnection::UsageEvent::REMOTE_CANDIDATE_ADDED,
339 PeerConnection::UsageEvent::ICE_STATE_CONNECTED,
340 PeerConnection::UsageEvent::CLOSE_CALLED});
Qingsi Wang7fc821d2018-07-12 12:54:53 -0700341 EXPECT_EQ(2,
342 webrtc::metrics::NumSamples("WebRTC.PeerConnection.UsagePattern"));
Harald Alvestrand056d8112018-07-16 19:18:58 +0200343 EXPECT_TRUE(
344 webrtc::metrics::NumEvents("WebRTC.PeerConnection.UsagePattern",
345 expected_fingerprint) == 2 ||
346 webrtc::metrics::NumEvents(
347 "WebRTC.PeerConnection.UsagePattern",
348 expected_fingerprint |
349 static_cast<int>(
350 PeerConnection::UsageEvent::PRIVATE_CANDIDATE_COLLECTED)) ==
351 2);
Harald Alvestrand183e09d2018-06-28 12:04:41 +0200352}
353#endif // HAVE_SCTP
354#endif // WEBRTC_ANDROID
355
Harald Alvestrandb2a74782018-06-28 13:54:07 +0200356TEST_F(PeerConnectionUsageHistogramTest, FingerprintStunTurn) {
357 RTCConfiguration configuration;
358 PeerConnection::IceServer server;
359 server.urls = {"stun:dummy.stun.server/"};
360 configuration.servers.push_back(server);
361 server.urls = {"turn:dummy.turn.server/"};
362 server.username = "username";
363 server.password = "password";
364 configuration.servers.push_back(server);
365 auto caller = CreatePeerConnection(configuration);
366 ASSERT_TRUE(caller);
Harald Alvestrandb2a74782018-06-28 13:54:07 +0200367 caller->pc()->Close();
368 int expected_fingerprint =
369 MakeUsageFingerprint({PeerConnection::UsageEvent::STUN_SERVER_ADDED,
370 PeerConnection::UsageEvent::TURN_SERVER_ADDED,
371 PeerConnection::UsageEvent::CLOSE_CALLED});
Qingsi Wang7fc821d2018-07-12 12:54:53 -0700372 EXPECT_EQ(1,
373 webrtc::metrics::NumSamples("WebRTC.PeerConnection.UsagePattern"));
374 EXPECT_EQ(1, webrtc::metrics::NumEvents("WebRTC.PeerConnection.UsagePattern",
375 expected_fingerprint));
Harald Alvestrandb2a74782018-06-28 13:54:07 +0200376}
377
378TEST_F(PeerConnectionUsageHistogramTest, FingerprintStunTurnInReconfiguration) {
379 RTCConfiguration configuration;
380 PeerConnection::IceServer server;
381 server.urls = {"stun:dummy.stun.server/"};
382 configuration.servers.push_back(server);
383 server.urls = {"turn:dummy.turn.server/"};
384 server.username = "username";
385 server.password = "password";
386 configuration.servers.push_back(server);
387 auto caller = CreatePeerConnection();
388 ASSERT_TRUE(caller);
Harald Alvestrandb2a74782018-06-28 13:54:07 +0200389 RTCError error;
390 caller->pc()->SetConfiguration(configuration, &error);
391 ASSERT_TRUE(error.ok());
392 caller->pc()->Close();
393 int expected_fingerprint =
394 MakeUsageFingerprint({PeerConnection::UsageEvent::STUN_SERVER_ADDED,
395 PeerConnection::UsageEvent::TURN_SERVER_ADDED,
396 PeerConnection::UsageEvent::CLOSE_CALLED});
Qingsi Wang7fc821d2018-07-12 12:54:53 -0700397 EXPECT_EQ(1,
398 webrtc::metrics::NumSamples("WebRTC.PeerConnection.UsagePattern"));
399 EXPECT_EQ(1, webrtc::metrics::NumEvents("WebRTC.PeerConnection.UsagePattern",
400 expected_fingerprint));
Harald Alvestrandb2a74782018-06-28 13:54:07 +0200401}
402
Harald Alvestrand056d8112018-07-16 19:18:58 +0200403TEST_F(PeerConnectionUsageHistogramTest, FingerprintWithPrivateIP) {
404 auto caller = CreatePeerConnectionWithPrivateLocalAddresses();
405 caller->AddAudioTrack("audio");
406 ASSERT_TRUE(caller->GenerateOfferAndCollectCandidates());
407 caller->pc()->Close();
408 int expected_fingerprint = MakeUsageFingerprint(
409 {PeerConnection::UsageEvent::AUDIO_ADDED,
410 PeerConnection::UsageEvent::SET_LOCAL_DESCRIPTION_CALLED,
411 PeerConnection::UsageEvent::CANDIDATE_COLLECTED,
412 PeerConnection::UsageEvent::CLOSE_CALLED,
413 PeerConnection::UsageEvent::PRIVATE_CANDIDATE_COLLECTED});
414 EXPECT_EQ(1,
415 webrtc::metrics::NumSamples("WebRTC.PeerConnection.UsagePattern"));
416 EXPECT_EQ(1, webrtc::metrics::NumEvents("WebRTC.PeerConnection.UsagePattern",
417 expected_fingerprint));
418}
419
Harald Alvestrand19793842018-06-25 12:03:50 +0200420} // namespace webrtc