• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright 2013 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 package org.webrtc;
12 
13 import static com.google.common.truth.Truth.assertThat;
14 import static org.junit.Assert.assertEquals;
15 import static org.junit.Assert.assertFalse;
16 import static org.junit.Assert.assertNotNull;
17 import static org.junit.Assert.assertNull;
18 import static org.junit.Assert.assertTrue;
19 
20 import android.support.test.InstrumentationRegistry;
21 import androidx.annotation.Nullable;
22 import androidx.test.filters.MediumTest;
23 import androidx.test.filters.SmallTest;
24 import java.lang.ref.WeakReference;
25 import java.nio.ByteBuffer;
26 import java.nio.charset.Charset;
27 import java.util.ArrayDeque;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Collections;
31 import java.util.HashSet;
32 import java.util.IdentityHashMap;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Queue;
36 import java.util.TreeSet;
37 import java.util.concurrent.CountDownLatch;
38 import java.util.concurrent.TimeUnit;
39 import org.junit.Before;
40 import org.junit.Test;
41 import org.webrtc.PeerConnection.IceConnectionState;
42 import org.webrtc.PeerConnection.IceGatheringState;
43 import org.webrtc.PeerConnection.PeerConnectionState;
44 import org.webrtc.PeerConnection.SignalingState;
45 
46 /** End-to-end tests for {@link PeerConnection}. */
47 public class PeerConnectionEndToEndTest {
48   private static final String TAG = "PeerConnectionEndToEndTest";
49   private static final int DEFAULT_TIMEOUT_SECONDS = 20;
50   private static final int SHORT_TIMEOUT_SECONDS = 5;
51 
52   @Before
setUp()53   public void setUp() {
54     PeerConnectionFactory.initialize(PeerConnectionFactory.InitializationOptions
55                                          .builder(InstrumentationRegistry.getTargetContext())
56                                          .setNativeLibraryName(TestConstants.NATIVE_LIBRARY)
57                                          .createInitializationOptions());
58   }
59 
60   private static class ObserverExpectations
61       implements PeerConnection.Observer, VideoSink, DataChannel.Observer, StatsObserver,
62                  RTCStatsCollectorCallback, RtpReceiver.Observer {
63     private final String name;
64     private int expectedIceCandidates;
65     private int expectedErrors;
66     private int expectedRenegotiations;
67     private int expectedWidth;
68     private int expectedHeight;
69     private int expectedFramesDelivered;
70     private int expectedTracksAdded;
71     private Queue<SignalingState> expectedSignalingChanges = new ArrayDeque<>();
72     private Queue<IceConnectionState> expectedIceConnectionChanges = new ArrayDeque<>();
73     private Queue<IceConnectionState> expectedStandardizedIceConnectionChanges = new ArrayDeque<>();
74     private Queue<PeerConnectionState> expectedConnectionChanges = new ArrayDeque<>();
75     private Queue<IceGatheringState> expectedIceGatheringChanges = new ArrayDeque<>();
76     private Queue<String> expectedAddStreamLabels = new ArrayDeque<>();
77     private Queue<String> expectedRemoveStreamLabels = new ArrayDeque<>();
78     private final List<IceCandidate> gotIceCandidates = new ArrayList<>();
79     private Map<MediaStream, WeakReference<VideoSink>> videoSinks = new IdentityHashMap<>();
80     private DataChannel dataChannel;
81     private Queue<DataChannel.Buffer> expectedBuffers = new ArrayDeque<>();
82     private Queue<DataChannel.State> expectedStateChanges = new ArrayDeque<>();
83     private Queue<String> expectedRemoteDataChannelLabels = new ArrayDeque<>();
84     private int expectedOldStatsCallbacks;
85     private int expectedNewStatsCallbacks;
86     private List<StatsReport[]> gotStatsReports = new ArrayList<>();
87     private final HashSet<MediaStream> gotRemoteStreams = new HashSet<>();
88     private int expectedFirstAudioPacket;
89     private int expectedFirstVideoPacket;
90 
ObserverExpectations(String name)91     public ObserverExpectations(String name) {
92       this.name = name;
93     }
94 
95     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
96     @SuppressWarnings("NoSynchronizedMethodCheck")
setDataChannel(DataChannel dataChannel)97     public synchronized void setDataChannel(DataChannel dataChannel) {
98       assertNull(this.dataChannel);
99       this.dataChannel = dataChannel;
100       this.dataChannel.registerObserver(this);
101       assertNotNull(this.dataChannel);
102     }
103 
104     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
105     @SuppressWarnings("NoSynchronizedMethodCheck")
expectIceCandidates(int count)106     public synchronized void expectIceCandidates(int count) {
107       expectedIceCandidates += count;
108     }
109 
110     @Override
111     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
112     @SuppressWarnings("NoSynchronizedMethodCheck")
onIceCandidate(IceCandidate candidate)113     public synchronized void onIceCandidate(IceCandidate candidate) {
114       Logging.d(TAG, "onIceCandidate: " + candidate.toString());
115       --expectedIceCandidates;
116 
117       // We don't assert expectedIceCandidates >= 0 because it's hard to know
118       // how many to expect, in general.  We only use expectIceCandidates to
119       // assert a minimal count.
120       synchronized (gotIceCandidates) {
121         gotIceCandidates.add(candidate);
122         gotIceCandidates.notifyAll();
123       }
124     }
125 
126     @Override
onIceCandidateError(IceCandidateErrorEvent event)127     public void onIceCandidateError(IceCandidateErrorEvent event) {}
128 
129     @Override
onIceCandidatesRemoved(IceCandidate[] candidates)130     public void onIceCandidatesRemoved(IceCandidate[] candidates) {}
131 
132     @Override
onSelectedCandidatePairChanged(CandidatePairChangeEvent event)133     public void onSelectedCandidatePairChanged(CandidatePairChangeEvent event) {}
134 
135     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
136     @SuppressWarnings("NoSynchronizedMethodCheck")
setExpectedResolution(int width, int height)137     public synchronized void setExpectedResolution(int width, int height) {
138       expectedWidth = width;
139       expectedHeight = height;
140     }
141 
142     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
143     @SuppressWarnings("NoSynchronizedMethodCheck")
expectFramesDelivered(int count)144     public synchronized void expectFramesDelivered(int count) {
145       expectedFramesDelivered += count;
146     }
147 
148     @Override
149     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
150     @SuppressWarnings("NoSynchronizedMethodCheck")
onFrame(VideoFrame frame)151     public synchronized void onFrame(VideoFrame frame) {
152       if (expectedFramesDelivered <= 0) {
153         return;
154       }
155       assertTrue(expectedWidth > 0);
156       assertTrue(expectedHeight > 0);
157       assertEquals(expectedWidth, frame.getRotatedWidth());
158       assertEquals(expectedHeight, frame.getRotatedHeight());
159       --expectedFramesDelivered;
160     }
161 
162     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
163     @SuppressWarnings("NoSynchronizedMethodCheck")
expectSignalingChange(SignalingState newState)164     public synchronized void expectSignalingChange(SignalingState newState) {
165       expectedSignalingChanges.add(newState);
166     }
167 
168     @Override
169     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
170     @SuppressWarnings("NoSynchronizedMethodCheck")
onSignalingChange(SignalingState newState)171     public synchronized void onSignalingChange(SignalingState newState) {
172       assertEquals(expectedSignalingChanges.remove(), newState);
173     }
174 
175     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
176     @SuppressWarnings("NoSynchronizedMethodCheck")
expectIceConnectionChange(IceConnectionState newState)177     public synchronized void expectIceConnectionChange(IceConnectionState newState) {
178       expectedIceConnectionChanges.add(newState);
179     }
180 
181     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
182     @SuppressWarnings("NoSynchronizedMethodCheck")
expectStandardizedIceConnectionChange(IceConnectionState newState)183     public synchronized void expectStandardizedIceConnectionChange(IceConnectionState newState) {
184       expectedStandardizedIceConnectionChanges.add(newState);
185     }
186 
187     @Override
188     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
189     @SuppressWarnings("NoSynchronizedMethodCheck")
onIceConnectionChange(IceConnectionState newState)190     public synchronized void onIceConnectionChange(IceConnectionState newState) {
191       // TODO(bemasc): remove once delivery of ICECompleted is reliable
192       // (https://code.google.com/p/webrtc/issues/detail?id=3021).
193       if (newState.equals(IceConnectionState.COMPLETED)) {
194         return;
195       }
196 
197       if (expectedIceConnectionChanges.isEmpty()) {
198         Logging.d(TAG, name + "Got an unexpected ICE connection change " + newState);
199         return;
200       }
201 
202       assertEquals(expectedIceConnectionChanges.remove(), newState);
203     }
204 
205     @Override
206     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
207     @SuppressWarnings("NoSynchronizedMethodCheck")
onStandardizedIceConnectionChange(IceConnectionState newState)208     public synchronized void onStandardizedIceConnectionChange(IceConnectionState newState) {
209       if (newState.equals(IceConnectionState.COMPLETED)) {
210         return;
211       }
212 
213       if (expectedIceConnectionChanges.isEmpty()) {
214         Logging.d(TAG, name + "Got an unexpected standardized ICE connection change " + newState);
215         return;
216       }
217 
218       assertEquals(expectedStandardizedIceConnectionChanges.remove(), newState);
219     }
220 
221     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
222     @SuppressWarnings("NoSynchronizedMethodCheck")
expectConnectionChange(PeerConnectionState newState)223     public synchronized void expectConnectionChange(PeerConnectionState newState) {
224       expectedConnectionChanges.add(newState);
225     }
226 
227     @Override
228     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
229     @SuppressWarnings("NoSynchronizedMethodCheck")
onConnectionChange(PeerConnectionState newState)230     public synchronized void onConnectionChange(PeerConnectionState newState) {
231       if (expectedConnectionChanges.isEmpty()) {
232         Logging.d(TAG, name + " got an unexpected DTLS connection change " + newState);
233         return;
234       }
235 
236       assertEquals(expectedConnectionChanges.remove(), newState);
237     }
238 
239     @Override
240     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
241     @SuppressWarnings("NoSynchronizedMethodCheck")
onIceConnectionReceivingChange(boolean receiving)242     public synchronized void onIceConnectionReceivingChange(boolean receiving) {
243       Logging.d(TAG, name + " got an ICE connection receiving change " + receiving);
244     }
245 
246     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
247     @SuppressWarnings("NoSynchronizedMethodCheck")
expectIceGatheringChange(IceGatheringState newState)248     public synchronized void expectIceGatheringChange(IceGatheringState newState) {
249       expectedIceGatheringChanges.add(newState);
250     }
251 
252     @Override
253     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
254     @SuppressWarnings("NoSynchronizedMethodCheck")
onIceGatheringChange(IceGatheringState newState)255     public synchronized void onIceGatheringChange(IceGatheringState newState) {
256       // It's fine to get a variable number of GATHERING messages before
257       // COMPLETE fires (depending on how long the test runs) so we don't assert
258       // any particular count.
259       if (newState == IceGatheringState.GATHERING) {
260         return;
261       }
262       if (expectedIceGatheringChanges.isEmpty()) {
263         Logging.d(TAG, name + "Got an unexpected ICE gathering change " + newState);
264       }
265       assertEquals(expectedIceGatheringChanges.remove(), newState);
266     }
267 
268     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
269     @SuppressWarnings("NoSynchronizedMethodCheck")
expectAddStream(String label)270     public synchronized void expectAddStream(String label) {
271       expectedAddStreamLabels.add(label);
272     }
273 
274     @Override
275     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
276     @SuppressWarnings("NoSynchronizedMethodCheck")
onAddStream(MediaStream stream)277     public synchronized void onAddStream(MediaStream stream) {
278       assertEquals(expectedAddStreamLabels.remove(), stream.getId());
279       for (AudioTrack track : stream.audioTracks) {
280         assertEquals("audio", track.kind());
281       }
282       for (VideoTrack track : stream.videoTracks) {
283         assertEquals("video", track.kind());
284         track.addSink(this);
285         assertNull(videoSinks.put(stream, new WeakReference<VideoSink>(this)));
286       }
287       gotRemoteStreams.add(stream);
288     }
289 
290     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
291     @SuppressWarnings("NoSynchronizedMethodCheck")
expectRemoveStream(String label)292     public synchronized void expectRemoveStream(String label) {
293       expectedRemoveStreamLabels.add(label);
294     }
295 
296     @Override
297     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
298     @SuppressWarnings("NoSynchronizedMethodCheck")
onRemoveStream(MediaStream stream)299     public synchronized void onRemoveStream(MediaStream stream) {
300       assertEquals(expectedRemoveStreamLabels.remove(), stream.getId());
301       WeakReference<VideoSink> videoSink = videoSinks.remove(stream);
302       assertNotNull(videoSink);
303       assertNotNull(videoSink.get());
304       for (VideoTrack videoTrack : stream.videoTracks) {
305         videoTrack.removeSink(videoSink.get());
306       }
307       gotRemoteStreams.remove(stream);
308     }
309 
310     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
311     @SuppressWarnings("NoSynchronizedMethodCheck")
expectDataChannel(String label)312     public synchronized void expectDataChannel(String label) {
313       expectedRemoteDataChannelLabels.add(label);
314     }
315 
316     @Override
317     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
318     @SuppressWarnings("NoSynchronizedMethodCheck")
onDataChannel(DataChannel remoteDataChannel)319     public synchronized void onDataChannel(DataChannel remoteDataChannel) {
320       assertEquals(expectedRemoteDataChannelLabels.remove(), remoteDataChannel.label());
321       setDataChannel(remoteDataChannel);
322       assertEquals(DataChannel.State.CONNECTING, dataChannel.state());
323     }
324 
325     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
326     @SuppressWarnings("NoSynchronizedMethodCheck")
expectRenegotiationNeeded()327     public synchronized void expectRenegotiationNeeded() {
328       ++expectedRenegotiations;
329     }
330 
331     @Override
332     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
333     @SuppressWarnings("NoSynchronizedMethodCheck")
onRenegotiationNeeded()334     public synchronized void onRenegotiationNeeded() {
335       assertTrue(--expectedRenegotiations >= 0);
336     }
337 
338     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
339     @SuppressWarnings("NoSynchronizedMethodCheck")
expectAddTrack(int expectedTracksAdded)340     public synchronized void expectAddTrack(int expectedTracksAdded) {
341       this.expectedTracksAdded = expectedTracksAdded;
342     }
343 
344     @Override
345     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
346     @SuppressWarnings("NoSynchronizedMethodCheck")
onAddTrack(RtpReceiver receiver, MediaStream[] mediaStreams)347     public synchronized void onAddTrack(RtpReceiver receiver, MediaStream[] mediaStreams) {
348       expectedTracksAdded--;
349     }
350 
351     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
352     @SuppressWarnings("NoSynchronizedMethodCheck")
expectMessage(ByteBuffer expectedBuffer, boolean expectedBinary)353     public synchronized void expectMessage(ByteBuffer expectedBuffer, boolean expectedBinary) {
354       expectedBuffers.add(new DataChannel.Buffer(expectedBuffer, expectedBinary));
355     }
356 
357     @Override
358     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
359     @SuppressWarnings("NoSynchronizedMethodCheck")
onMessage(DataChannel.Buffer buffer)360     public synchronized void onMessage(DataChannel.Buffer buffer) {
361       DataChannel.Buffer expected = expectedBuffers.remove();
362       assertEquals(expected.binary, buffer.binary);
363       assertTrue(expected.data.equals(buffer.data));
364     }
365 
366     @Override
367     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
368     @SuppressWarnings("NoSynchronizedMethodCheck")
onBufferedAmountChange(long previousAmount)369     public synchronized void onBufferedAmountChange(long previousAmount) {
370       assertFalse(previousAmount == dataChannel.bufferedAmount());
371     }
372 
373     @Override
374     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
375     @SuppressWarnings("NoSynchronizedMethodCheck")
onStateChange()376     public synchronized void onStateChange() {
377       assertEquals(expectedStateChanges.remove(), dataChannel.state());
378     }
379 
380     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
381     @SuppressWarnings("NoSynchronizedMethodCheck")
expectStateChange(DataChannel.State state)382     public synchronized void expectStateChange(DataChannel.State state) {
383       expectedStateChanges.add(state);
384     }
385 
386     // Old getStats callback.
387     @Override
388     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
389     @SuppressWarnings("NoSynchronizedMethodCheck")
onComplete(StatsReport[] reports)390     public synchronized void onComplete(StatsReport[] reports) {
391       if (--expectedOldStatsCallbacks < 0) {
392         throw new RuntimeException("Unexpected stats report: " + Arrays.toString(reports));
393       }
394       gotStatsReports.add(reports);
395     }
396 
397     // New getStats callback.
398     @Override
399     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
400     @SuppressWarnings("NoSynchronizedMethodCheck")
onStatsDelivered(RTCStatsReport report)401     public synchronized void onStatsDelivered(RTCStatsReport report) {
402       if (--expectedNewStatsCallbacks < 0) {
403         throw new RuntimeException("Unexpected stats report: " + report);
404       }
405     }
406 
407     @Override
408     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
409     @SuppressWarnings("NoSynchronizedMethodCheck")
onFirstPacketReceived(MediaStreamTrack.MediaType mediaType)410     public synchronized void onFirstPacketReceived(MediaStreamTrack.MediaType mediaType) {
411       if (mediaType == MediaStreamTrack.MediaType.MEDIA_TYPE_AUDIO) {
412         expectedFirstAudioPacket--;
413       } else {
414         expectedFirstVideoPacket--;
415       }
416       if (expectedFirstAudioPacket < 0 || expectedFirstVideoPacket < 0) {
417         throw new RuntimeException("Unexpected call of onFirstPacketReceived");
418       }
419     }
420 
421     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
422     @SuppressWarnings("NoSynchronizedMethodCheck")
expectFirstPacketReceived()423     public synchronized void expectFirstPacketReceived() {
424       expectedFirstAudioPacket = 1;
425       expectedFirstVideoPacket = 1;
426     }
427 
428     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
429     @SuppressWarnings("NoSynchronizedMethodCheck")
expectOldStatsCallback()430     public synchronized void expectOldStatsCallback() {
431       ++expectedOldStatsCallbacks;
432     }
433 
434     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
435     @SuppressWarnings("NoSynchronizedMethodCheck")
expectNewStatsCallback()436     public synchronized void expectNewStatsCallback() {
437       ++expectedNewStatsCallbacks;
438     }
439 
440     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
441     @SuppressWarnings("NoSynchronizedMethodCheck")
takeStatsReports()442     public synchronized List<StatsReport[]> takeStatsReports() {
443       List<StatsReport[]> got = gotStatsReports;
444       gotStatsReports = new ArrayList<StatsReport[]>();
445       return got;
446     }
447 
448     // Return a set of expectations that haven't been satisfied yet, possibly
449     // empty if no such expectations exist.
450     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
451     @SuppressWarnings("NoSynchronizedMethodCheck")
unsatisfiedExpectations()452     public synchronized TreeSet<String> unsatisfiedExpectations() {
453       TreeSet<String> stillWaitingForExpectations = new TreeSet<String>();
454       if (expectedIceCandidates > 0) { // See comment in onIceCandidate.
455         stillWaitingForExpectations.add("expectedIceCandidates");
456       }
457       if (expectedErrors != 0) {
458         stillWaitingForExpectations.add("expectedErrors: " + expectedErrors);
459       }
460       if (expectedSignalingChanges.size() != 0) {
461         stillWaitingForExpectations.add(
462             "expectedSignalingChanges: " + expectedSignalingChanges.size());
463       }
464       if (expectedIceConnectionChanges.size() != 0) {
465         stillWaitingForExpectations.add(
466             "expectedIceConnectionChanges: " + expectedIceConnectionChanges.size());
467       }
468       if (expectedIceGatheringChanges.size() != 0) {
469         stillWaitingForExpectations.add(
470             "expectedIceGatheringChanges: " + expectedIceGatheringChanges.size());
471       }
472       if (expectedAddStreamLabels.size() != 0) {
473         stillWaitingForExpectations.add(
474             "expectedAddStreamLabels: " + expectedAddStreamLabels.size());
475       }
476       if (expectedRemoveStreamLabels.size() != 0) {
477         stillWaitingForExpectations.add(
478             "expectedRemoveStreamLabels: " + expectedRemoveStreamLabels.size());
479       }
480       if (expectedFramesDelivered > 0) {
481         stillWaitingForExpectations.add("expectedFramesDelivered: " + expectedFramesDelivered);
482       }
483       if (!expectedBuffers.isEmpty()) {
484         stillWaitingForExpectations.add("expectedBuffers: " + expectedBuffers.size());
485       }
486       if (!expectedStateChanges.isEmpty()) {
487         stillWaitingForExpectations.add("expectedStateChanges: " + expectedStateChanges.size());
488       }
489       if (!expectedRemoteDataChannelLabels.isEmpty()) {
490         stillWaitingForExpectations.add(
491             "expectedRemoteDataChannelLabels: " + expectedRemoteDataChannelLabels.size());
492       }
493       if (expectedOldStatsCallbacks != 0) {
494         stillWaitingForExpectations.add("expectedOldStatsCallbacks: " + expectedOldStatsCallbacks);
495       }
496       if (expectedNewStatsCallbacks != 0) {
497         stillWaitingForExpectations.add("expectedNewStatsCallbacks: " + expectedNewStatsCallbacks);
498       }
499       if (expectedFirstAudioPacket > 0) {
500         stillWaitingForExpectations.add("expectedFirstAudioPacket: " + expectedFirstAudioPacket);
501       }
502       if (expectedFirstVideoPacket > 0) {
503         stillWaitingForExpectations.add("expectedFirstVideoPacket: " + expectedFirstVideoPacket);
504       }
505       if (expectedTracksAdded != 0) {
506         stillWaitingForExpectations.add("expectedAddedTrack: " + expectedTracksAdded);
507       }
508       return stillWaitingForExpectations;
509     }
510 
waitForAllExpectationsToBeSatisfied(int timeoutSeconds)511     public boolean waitForAllExpectationsToBeSatisfied(int timeoutSeconds) {
512       // TODO(fischman): problems with this approach:
513       // - come up with something better than a poll loop
514       // - avoid serializing expectations explicitly; the test is not as robust
515       //   as it could be because it must place expectations between wait
516       //   statements very precisely (e.g. frame must not arrive before its
517       //   expectation, and expectation must not be registered so early as to
518       //   stall a wait).  Use callbacks to fire off dependent steps instead of
519       //   explicitly waiting, so there can be just a single wait at the end of
520       //   the test.
521       long endTime = System.currentTimeMillis() + 1000 * timeoutSeconds;
522       TreeSet<String> prev = null;
523       TreeSet<String> stillWaitingForExpectations = unsatisfiedExpectations();
524       while (!stillWaitingForExpectations.isEmpty()) {
525         if (!stillWaitingForExpectations.equals(prev)) {
526           Logging.d(TAG,
527               name + " still waiting at\n    " + (new Throwable()).getStackTrace()[1]
528                   + "\n    for: " + Arrays.toString(stillWaitingForExpectations.toArray()));
529         }
530         if (endTime < System.currentTimeMillis()) {
531           Logging.d(TAG,
532               name + " timed out waiting for: "
533                   + Arrays.toString(stillWaitingForExpectations.toArray()));
534           return false;
535         }
536         try {
537           Thread.sleep(10);
538         } catch (InterruptedException e) {
539           throw new RuntimeException(e);
540         }
541         prev = stillWaitingForExpectations;
542         stillWaitingForExpectations = unsatisfiedExpectations();
543       }
544       if (prev == null) {
545         Logging.d(
546             TAG, name + " didn't need to wait at\n    " + (new Throwable()).getStackTrace()[1]);
547       }
548       return true;
549     }
550 
551     // This methods return a list of all currently gathered ice candidates or waits until
552     // 1 candidate have been gathered.
getAtLeastOneIceCandidate()553     public List<IceCandidate> getAtLeastOneIceCandidate() throws InterruptedException {
554       synchronized (gotIceCandidates) {
555         while (gotIceCandidates.isEmpty()) {
556           gotIceCandidates.wait();
557         }
558         return new ArrayList<IceCandidate>(gotIceCandidates);
559       }
560     }
561   }
562 
563   // Sets the expected resolution for an ObserverExpectations once a frame
564   // has been captured.
565   private static class ExpectedResolutionSetter implements VideoSink {
566     private ObserverExpectations observer;
567 
ExpectedResolutionSetter(ObserverExpectations observer)568     public ExpectedResolutionSetter(ObserverExpectations observer) {
569       this.observer = observer;
570     }
571 
572     @Override
573     // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
574     @SuppressWarnings("NoSynchronizedMethodCheck")
onFrame(VideoFrame frame)575     public synchronized void onFrame(VideoFrame frame) {
576       // Because different camera devices (fake & physical) produce different
577       // resolutions, we only sanity-check the set sizes,
578       assertTrue(frame.getRotatedWidth() > 0);
579       assertTrue(frame.getRotatedHeight() > 0);
580       observer.setExpectedResolution(frame.getRotatedWidth(), frame.getRotatedHeight());
581       frame.retain();
582     }
583   }
584 
585   private static class SdpObserverLatch implements SdpObserver {
586     private boolean success;
587     private @Nullable SessionDescription sdp;
588     private @Nullable String error;
589     private CountDownLatch latch = new CountDownLatch(1);
590 
SdpObserverLatch()591     public SdpObserverLatch() {}
592 
593     @Override
onCreateSuccess(SessionDescription sdp)594     public void onCreateSuccess(SessionDescription sdp) {
595       this.sdp = sdp;
596       onSetSuccess();
597     }
598 
599     @Override
onSetSuccess()600     public void onSetSuccess() {
601       success = true;
602       latch.countDown();
603     }
604 
605     @Override
onCreateFailure(String error)606     public void onCreateFailure(String error) {
607       onSetFailure(error);
608     }
609 
610     @Override
onSetFailure(String error)611     public void onSetFailure(String error) {
612       this.error = error;
613       latch.countDown();
614     }
615 
await()616     public boolean await() {
617       try {
618         assertTrue(latch.await(1000, TimeUnit.MILLISECONDS));
619         return getSuccess();
620       } catch (Exception e) {
621         throw new RuntimeException(e);
622       }
623     }
624 
getSuccess()625     public boolean getSuccess() {
626       return success;
627     }
628 
getSdp()629     public @Nullable SessionDescription getSdp() {
630       return sdp;
631     }
632 
getError()633     public @Nullable String getError() {
634       return error;
635     }
636   }
637 
638   // Return a weak reference to test that ownership is correctly held by
639   // PeerConnection, not by test code.
addTracksToPC(PeerConnectionFactory factory, PeerConnection pc, VideoSource videoSource, String streamLabel, String videoTrackId, String audioTrackId, VideoSink videoSink)640   private static WeakReference<MediaStream> addTracksToPC(PeerConnectionFactory factory,
641       PeerConnection pc, VideoSource videoSource, String streamLabel, String videoTrackId,
642       String audioTrackId, VideoSink videoSink) {
643     return addTracksToPC(factory, pc, videoSource, streamLabel, videoTrackId, audioTrackId,
644         videoSink, /*useAddStream=*/false);
645   }
addTracksToPC(PeerConnectionFactory factory, PeerConnection pc, VideoSource videoSource, String streamLabel, String videoTrackId, String audioTrackId, VideoSink videoSink, boolean useAddStream)646   private static WeakReference<MediaStream> addTracksToPC(PeerConnectionFactory factory,
647       PeerConnection pc, VideoSource videoSource, String streamLabel, String videoTrackId,
648       String audioTrackId, VideoSink videoSink, boolean useAddStream) {
649     MediaStream lMS = factory.createLocalMediaStream(streamLabel);
650     VideoTrack videoTrack = factory.createVideoTrack(videoTrackId, videoSource);
651     assertNotNull(videoTrack);
652     assertNotNull(videoSink);
653     videoTrack.addSink(videoSink);
654     lMS.addTrack(videoTrack);
655     // Just for fun, let's remove and re-add the track.
656     lMS.removeTrack(videoTrack);
657     lMS.addTrack(videoTrack);
658     lMS.addTrack(
659         factory.createAudioTrack(audioTrackId, factory.createAudioSource(new MediaConstraints())));
660     if (!useAddStream) {
661       // In Unified Plan, addTrack() is the preferred way of adding tracks.
662       for (AudioTrack track : lMS.audioTracks) {
663         pc.addTrack(track, Collections.singletonList(lMS.getId()));
664       }
665       for (VideoTrack track : lMS.videoTracks) {
666         pc.addTrack(track, Collections.singletonList(lMS.getId()));
667       }
668     } else {
669       // Only in Plan B is addStream() supported. Used by a legacy test not yet
670       // updated to Unified Plan.
671       // TODO(https://crbug.com/webrtc/13528): Remove use of addStream().
672       pc.addStream(lMS);
673     }
674     return new WeakReference<MediaStream>(lMS);
675   }
676 
677   @Test
678   @MediumTest
testCompleteSession()679   public void testCompleteSession() throws Exception {
680     Metrics.enable();
681     // Allow loopback interfaces too since our Android devices often don't
682     // have those.
683     PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
684     options.networkIgnoreMask = 0;
685     PeerConnectionFactory factory = PeerConnectionFactory.builder()
686                                         .setOptions(options)
687                                         .setVideoEncoderFactory(new SoftwareVideoEncoderFactory())
688                                         .setVideoDecoderFactory(new SoftwareVideoDecoderFactory())
689                                         .createPeerConnectionFactory();
690 
691     List<PeerConnection.IceServer> iceServers = new ArrayList<>();
692     iceServers.add(
693         PeerConnection.IceServer.builder("stun:stun.l.google.com:19302").createIceServer());
694     iceServers.add(PeerConnection.IceServer.builder("turn:fake.example.com")
695                        .setUsername("fakeUsername")
696                        .setPassword("fakePassword")
697                        .createIceServer());
698 
699     PeerConnection.RTCConfiguration rtcConfig = new PeerConnection.RTCConfiguration(iceServers);
700     rtcConfig.sdpSemantics = PeerConnection.SdpSemantics.UNIFIED_PLAN;
701 
702     ObserverExpectations offeringExpectations = new ObserverExpectations("PCTest:offerer");
703     PeerConnection offeringPC = factory.createPeerConnection(rtcConfig, offeringExpectations);
704     assertNotNull(offeringPC);
705 
706     ObserverExpectations answeringExpectations = new ObserverExpectations("PCTest:answerer");
707     PeerConnection answeringPC = factory.createPeerConnection(rtcConfig, answeringExpectations);
708     assertNotNull(answeringPC);
709 
710     // We want to use the same camera for offerer & answerer, so create it here
711     // instead of in addTracksToPC.
712     final CameraEnumerator enumerator = new Camera1Enumerator(false /* captureToTexture */);
713     final VideoCapturer videoCapturer =
714         enumerator.createCapturer(enumerator.getDeviceNames()[0], null /* eventsHandler */);
715     final SurfaceTextureHelper surfaceTextureHelper =
716         SurfaceTextureHelper.create("SurfaceTextureHelper", /* sharedContext= */ null);
717     final VideoSource videoSource = factory.createVideoSource(/* isScreencast= */ false);
718     videoCapturer.initialize(surfaceTextureHelper, InstrumentationRegistry.getTargetContext(),
719         videoSource.getCapturerObserver());
720     videoCapturer.startCapture(640, 480, 30);
721 
722     offeringExpectations.expectRenegotiationNeeded();
723     WeakReference<MediaStream> oLMS =
724         addTracksToPC(factory, offeringPC, videoSource, "offeredMediaStream", "offeredVideoTrack",
725             "offeredAudioTrack", new ExpectedResolutionSetter(answeringExpectations));
726 
727     offeringExpectations.expectAddTrack(2);
728     answeringExpectations.expectAddTrack(2);
729 
730     offeringExpectations.expectRenegotiationNeeded();
731     DataChannel offeringDC = offeringPC.createDataChannel("offeringDC", new DataChannel.Init());
732     assertEquals("offeringDC", offeringDC.label());
733 
734     offeringExpectations.setDataChannel(offeringDC);
735     SdpObserverLatch sdpLatch = new SdpObserverLatch();
736     offeringPC.createOffer(sdpLatch, new MediaConstraints());
737     assertTrue(sdpLatch.await());
738     SessionDescription offerSdp = sdpLatch.getSdp();
739     assertEquals(offerSdp.type, SessionDescription.Type.OFFER);
740     assertFalse(offerSdp.description.isEmpty());
741 
742     sdpLatch = new SdpObserverLatch();
743     answeringExpectations.expectSignalingChange(SignalingState.HAVE_REMOTE_OFFER);
744     answeringExpectations.expectAddStream("offeredMediaStream");
745     // SCTP DataChannels are announced via OPEN messages over the established
746     // connection (not via SDP), so answeringExpectations can only register
747     // expecting the channel during ICE, below.
748     answeringPC.setRemoteDescription(sdpLatch, offerSdp);
749     assertEquals(PeerConnection.SignalingState.STABLE, offeringPC.signalingState());
750     assertTrue(sdpLatch.await());
751     assertNull(sdpLatch.getSdp());
752 
753     answeringExpectations.expectRenegotiationNeeded();
754     WeakReference<MediaStream> aLMS = addTracksToPC(factory, answeringPC, videoSource,
755         "answeredMediaStream", "answeredVideoTrack", "answeredAudioTrack",
756         new ExpectedResolutionSetter(offeringExpectations));
757 
758     sdpLatch = new SdpObserverLatch();
759     answeringPC.createAnswer(sdpLatch, new MediaConstraints());
760     assertTrue(sdpLatch.await());
761     SessionDescription answerSdp = sdpLatch.getSdp();
762     assertEquals(answerSdp.type, SessionDescription.Type.ANSWER);
763     assertFalse(answerSdp.description.isEmpty());
764 
765     offeringExpectations.expectIceCandidates(2);
766     answeringExpectations.expectIceCandidates(2);
767 
768     offeringExpectations.expectIceGatheringChange(IceGatheringState.COMPLETE);
769     answeringExpectations.expectIceGatheringChange(IceGatheringState.COMPLETE);
770 
771     sdpLatch = new SdpObserverLatch();
772     answeringExpectations.expectSignalingChange(SignalingState.STABLE);
773     answeringExpectations.expectConnectionChange(PeerConnectionState.CONNECTING);
774     answeringPC.setLocalDescription(sdpLatch, answerSdp);
775     assertTrue(sdpLatch.await());
776     assertNull(sdpLatch.getSdp());
777 
778     sdpLatch = new SdpObserverLatch();
779     offeringExpectations.expectSignalingChange(SignalingState.HAVE_LOCAL_OFFER);
780     offeringExpectations.expectConnectionChange(PeerConnectionState.CONNECTING);
781     offeringPC.setLocalDescription(sdpLatch, offerSdp);
782     assertTrue(sdpLatch.await());
783     assertNull(sdpLatch.getSdp());
784     sdpLatch = new SdpObserverLatch();
785     offeringExpectations.expectSignalingChange(SignalingState.STABLE);
786     offeringExpectations.expectAddStream("answeredMediaStream");
787 
788     offeringExpectations.expectIceConnectionChange(IceConnectionState.CHECKING);
789     offeringExpectations.expectIceConnectionChange(IceConnectionState.CONNECTED);
790     offeringExpectations.expectStandardizedIceConnectionChange(IceConnectionState.CHECKING);
791     offeringExpectations.expectStandardizedIceConnectionChange(IceConnectionState.CONNECTED);
792     offeringExpectations.expectConnectionChange(PeerConnectionState.CONNECTED);
793     // TODO(bemasc): uncomment once delivery of ICECompleted is reliable
794     // (https://code.google.com/p/webrtc/issues/detail?id=3021).
795     //
796     // offeringExpectations.expectIceConnectionChange(
797     //     IceConnectionState.COMPLETED);
798     answeringExpectations.expectIceConnectionChange(IceConnectionState.CHECKING);
799     answeringExpectations.expectIceConnectionChange(IceConnectionState.CONNECTED);
800     answeringExpectations.expectStandardizedIceConnectionChange(IceConnectionState.CHECKING);
801     answeringExpectations.expectStandardizedIceConnectionChange(IceConnectionState.CONNECTED);
802     answeringExpectations.expectConnectionChange(PeerConnectionState.CONNECTED);
803 
804     offeringPC.setRemoteDescription(sdpLatch, answerSdp);
805     assertTrue(sdpLatch.await());
806     assertNull(sdpLatch.getSdp());
807 
808     assertEquals(offeringPC.getLocalDescription().type, offerSdp.type);
809     assertEquals(offeringPC.getRemoteDescription().type, answerSdp.type);
810     assertEquals(answeringPC.getLocalDescription().type, answerSdp.type);
811     assertEquals(answeringPC.getRemoteDescription().type, offerSdp.type);
812 
813     assertEquals(offeringPC.getSenders().size(), 2);
814     assertEquals(offeringPC.getReceivers().size(), 2);
815     assertEquals(answeringPC.getSenders().size(), 2);
816     assertEquals(answeringPC.getReceivers().size(), 2);
817 
818     offeringExpectations.expectFirstPacketReceived();
819     answeringExpectations.expectFirstPacketReceived();
820 
821     for (RtpReceiver receiver : offeringPC.getReceivers()) {
822       receiver.SetObserver(offeringExpectations);
823     }
824 
825     for (RtpReceiver receiver : answeringPC.getReceivers()) {
826       receiver.SetObserver(answeringExpectations);
827     }
828 
829     // Wait for at least some frames to be delivered at each end (number
830     // chosen arbitrarily).
831     offeringExpectations.expectFramesDelivered(10);
832     answeringExpectations.expectFramesDelivered(10);
833 
834     offeringExpectations.expectStateChange(DataChannel.State.OPEN);
835     // See commentary about SCTP DataChannels above for why this is here.
836     answeringExpectations.expectDataChannel("offeringDC");
837     answeringExpectations.expectStateChange(DataChannel.State.OPEN);
838 
839     // Wait for at least one ice candidate from the offering PC and forward them to the answering
840     // PC.
841     for (IceCandidate candidate : offeringExpectations.getAtLeastOneIceCandidate()) {
842       answeringPC.addIceCandidate(candidate);
843     }
844 
845     // Wait for at least one ice candidate from the answering PC and forward them to the offering
846     // PC.
847     for (IceCandidate candidate : answeringExpectations.getAtLeastOneIceCandidate()) {
848       offeringPC.addIceCandidate(candidate);
849     }
850 
851     assertTrue(offeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS));
852     assertTrue(answeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS));
853 
854     assertEquals(PeerConnection.SignalingState.STABLE, offeringPC.signalingState());
855     assertEquals(PeerConnection.SignalingState.STABLE, answeringPC.signalingState());
856 
857     // Test some of the RtpSender API.
858     RtpSender videoSender = null;
859     RtpSender audioSender = null;
860     for (RtpSender sender : offeringPC.getSenders()) {
861       if (sender.track().kind().equals("video")) {
862         videoSender = sender;
863       } else {
864         audioSender = sender;
865       }
866     }
867     assertNotNull(videoSender);
868     assertNotNull(audioSender);
869 
870     // Set a bitrate limit for the outgoing video stream for the offerer.
871     RtpParameters rtpParameters = videoSender.getParameters();
872     assertNotNull(rtpParameters);
873     assertEquals(1, rtpParameters.encodings.size());
874     assertNull(rtpParameters.encodings.get(0).maxBitrateBps);
875     assertNull(rtpParameters.encodings.get(0).minBitrateBps);
876     assertNull(rtpParameters.encodings.get(0).maxFramerate);
877     assertNull(rtpParameters.encodings.get(0).numTemporalLayers);
878     assertNull(rtpParameters.encodings.get(0).scaleResolutionDownBy);
879     assertTrue(rtpParameters.encodings.get(0).rid.isEmpty());
880 
881     rtpParameters.encodings.get(0).maxBitrateBps = 300000;
882     rtpParameters.encodings.get(0).minBitrateBps = 100000;
883     rtpParameters.encodings.get(0).maxFramerate = 20;
884     rtpParameters.encodings.get(0).numTemporalLayers = 2;
885     rtpParameters.encodings.get(0).scaleResolutionDownBy = 2.0;
886     assertTrue(videoSender.setParameters(rtpParameters));
887 
888     // Create a DTMF sender.
889     DtmfSender dtmfSender = audioSender.dtmf();
890     assertNotNull(dtmfSender);
891     assertTrue(dtmfSender.canInsertDtmf());
892     assertTrue(dtmfSender.insertDtmf("123", 300, 100));
893 
894     // Verify that we can read back the updated value.
895     rtpParameters = videoSender.getParameters();
896     assertEquals(300000, (int) rtpParameters.encodings.get(0).maxBitrateBps);
897     assertEquals(100000, (int) rtpParameters.encodings.get(0).minBitrateBps);
898     assertEquals(20, (int) rtpParameters.encodings.get(0).maxFramerate);
899     assertEquals(2, (int) rtpParameters.encodings.get(0).numTemporalLayers);
900     assertThat(rtpParameters.encodings.get(0).scaleResolutionDownBy).isEqualTo(2.0);
901 
902     // Test send & receive UTF-8 text.
903     answeringExpectations.expectMessage(
904         ByteBuffer.wrap("hello!".getBytes(Charset.forName("UTF-8"))), false);
905     DataChannel.Buffer buffer =
906         new DataChannel.Buffer(ByteBuffer.wrap("hello!".getBytes(Charset.forName("UTF-8"))), false);
907     assertTrue(offeringExpectations.dataChannel.send(buffer));
908     assertTrue(answeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS));
909 
910     // Construct this binary message two different ways to ensure no
911     // shortcuts are taken.
912     ByteBuffer expectedBinaryMessage = ByteBuffer.allocateDirect(5);
913     for (byte i = 1; i < 6; ++i) {
914       expectedBinaryMessage.put(i);
915     }
916     expectedBinaryMessage.flip();
917     offeringExpectations.expectMessage(expectedBinaryMessage, true);
918     assertTrue(answeringExpectations.dataChannel.send(
919         new DataChannel.Buffer(ByteBuffer.wrap(new byte[] {1, 2, 3, 4, 5}), true)));
920     assertTrue(offeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS));
921 
922     offeringExpectations.expectStateChange(DataChannel.State.CLOSING);
923     answeringExpectations.expectStateChange(DataChannel.State.CLOSING);
924     offeringExpectations.expectStateChange(DataChannel.State.CLOSED);
925     answeringExpectations.expectStateChange(DataChannel.State.CLOSED);
926     offeringExpectations.dataChannel.close();
927     assertTrue(offeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS));
928     assertTrue(answeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS));
929 
930     // Test SetBitrate.
931     assertTrue(offeringPC.setBitrate(100000, 5000000, 500000000));
932     assertFalse(offeringPC.setBitrate(3, 2, 1));
933 
934     // Test getStats by Sender interface
935     offeringExpectations.expectNewStatsCallback();
936     offeringPC.getStats(videoSender, offeringExpectations);
937     assertTrue(offeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS));
938 
939     // Test getStats by Receiver interface
940     RtpReceiver videoReceiver = null;
941     for (RtpReceiver receiver : answeringPC.getReceivers()) {
942       if (receiver.track().kind().equals("video")) {
943         videoReceiver = receiver;
944       }
945     }
946     assertNotNull(videoReceiver);
947     answeringExpectations.expectNewStatsCallback();
948     answeringPC.getStats(videoReceiver, answeringExpectations);
949     assertTrue(answeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS));
950 
951     // Free the Java-land objects and collect them.
952     shutdownPC(offeringPC, offeringExpectations);
953     offeringPC = null;
954     shutdownPC(answeringPC, answeringExpectations);
955     answeringPC = null;
956     videoCapturer.stopCapture();
957     videoCapturer.dispose();
958     videoSource.dispose();
959     surfaceTextureHelper.dispose();
960     factory.dispose();
961     System.gc();
962   }
963 
964   @Test
965   @MediumTest
testDataChannelOnlySession()966   public void testDataChannelOnlySession() throws Exception {
967     // Allow loopback interfaces too since our Android devices often don't
968     // have those.
969     PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
970     options.networkIgnoreMask = 0;
971     PeerConnectionFactory factory =
972         PeerConnectionFactory.builder().setOptions(options).createPeerConnectionFactory();
973 
974     List<PeerConnection.IceServer> iceServers = new ArrayList<>();
975     iceServers.add(
976         PeerConnection.IceServer.builder("stun:stun.l.google.com:19302").createIceServer());
977     iceServers.add(PeerConnection.IceServer.builder("turn:fake.example.com")
978                        .setUsername("fakeUsername")
979                        .setPassword("fakePassword")
980                        .createIceServer());
981 
982     PeerConnection.RTCConfiguration rtcConfig = new PeerConnection.RTCConfiguration(iceServers);
983     rtcConfig.sdpSemantics = PeerConnection.SdpSemantics.UNIFIED_PLAN;
984 
985     ObserverExpectations offeringExpectations = new ObserverExpectations("PCTest:offerer");
986     PeerConnection offeringPC = factory.createPeerConnection(rtcConfig, offeringExpectations);
987     assertNotNull(offeringPC);
988 
989     ObserverExpectations answeringExpectations = new ObserverExpectations("PCTest:answerer");
990     PeerConnection answeringPC = factory.createPeerConnection(rtcConfig, answeringExpectations);
991     assertNotNull(answeringPC);
992 
993     offeringExpectations.expectRenegotiationNeeded();
994     DataChannel offeringDC = offeringPC.createDataChannel("offeringDC", new DataChannel.Init());
995     assertEquals("offeringDC", offeringDC.label());
996 
997     offeringExpectations.setDataChannel(offeringDC);
998     SdpObserverLatch sdpLatch = new SdpObserverLatch();
999     offeringPC.createOffer(sdpLatch, new MediaConstraints());
1000     assertTrue(sdpLatch.await());
1001     SessionDescription offerSdp = sdpLatch.getSdp();
1002     assertEquals(offerSdp.type, SessionDescription.Type.OFFER);
1003     assertFalse(offerSdp.description.isEmpty());
1004 
1005     sdpLatch = new SdpObserverLatch();
1006     answeringExpectations.expectSignalingChange(SignalingState.HAVE_REMOTE_OFFER);
1007     // SCTP DataChannels are announced via OPEN messages over the established
1008     // connection (not via SDP), so answeringExpectations can only register
1009     // expecting the channel during ICE, below.
1010     answeringPC.setRemoteDescription(sdpLatch, offerSdp);
1011     assertEquals(PeerConnection.SignalingState.STABLE, offeringPC.signalingState());
1012     assertTrue(sdpLatch.await());
1013     assertNull(sdpLatch.getSdp());
1014 
1015     sdpLatch = new SdpObserverLatch();
1016     answeringPC.createAnswer(sdpLatch, new MediaConstraints());
1017     assertTrue(sdpLatch.await());
1018     SessionDescription answerSdp = sdpLatch.getSdp();
1019     assertEquals(answerSdp.type, SessionDescription.Type.ANSWER);
1020     assertFalse(answerSdp.description.isEmpty());
1021 
1022     offeringExpectations.expectIceCandidates(2);
1023     answeringExpectations.expectIceCandidates(2);
1024 
1025     offeringExpectations.expectIceGatheringChange(IceGatheringState.COMPLETE);
1026     answeringExpectations.expectIceGatheringChange(IceGatheringState.COMPLETE);
1027 
1028     sdpLatch = new SdpObserverLatch();
1029     answeringExpectations.expectSignalingChange(SignalingState.STABLE);
1030     answeringExpectations.expectConnectionChange(PeerConnectionState.CONNECTING);
1031     answeringPC.setLocalDescription(sdpLatch, answerSdp);
1032     assertTrue(sdpLatch.await());
1033     assertNull(sdpLatch.getSdp());
1034 
1035     sdpLatch = new SdpObserverLatch();
1036     offeringExpectations.expectSignalingChange(SignalingState.HAVE_LOCAL_OFFER);
1037     offeringExpectations.expectConnectionChange(PeerConnectionState.CONNECTING);
1038     offeringPC.setLocalDescription(sdpLatch, offerSdp);
1039     assertTrue(sdpLatch.await());
1040     assertNull(sdpLatch.getSdp());
1041     sdpLatch = new SdpObserverLatch();
1042     offeringExpectations.expectSignalingChange(SignalingState.STABLE);
1043 
1044     offeringExpectations.expectIceConnectionChange(IceConnectionState.CHECKING);
1045     offeringExpectations.expectIceConnectionChange(IceConnectionState.CONNECTED);
1046     offeringExpectations.expectStandardizedIceConnectionChange(IceConnectionState.CHECKING);
1047     offeringExpectations.expectStandardizedIceConnectionChange(IceConnectionState.CONNECTED);
1048     offeringExpectations.expectConnectionChange(PeerConnectionState.CONNECTED);
1049     // TODO(bemasc): uncomment once delivery of ICECompleted is reliable
1050     // (https://code.google.com/p/webrtc/issues/detail?id=3021).
1051     answeringExpectations.expectIceConnectionChange(IceConnectionState.CHECKING);
1052     answeringExpectations.expectIceConnectionChange(IceConnectionState.CONNECTED);
1053     answeringExpectations.expectStandardizedIceConnectionChange(IceConnectionState.CHECKING);
1054     answeringExpectations.expectStandardizedIceConnectionChange(IceConnectionState.CONNECTED);
1055     answeringExpectations.expectConnectionChange(PeerConnectionState.CONNECTED);
1056 
1057     offeringPC.setRemoteDescription(sdpLatch, answerSdp);
1058     assertTrue(sdpLatch.await());
1059     assertNull(sdpLatch.getSdp());
1060 
1061     assertEquals(offeringPC.getLocalDescription().type, offerSdp.type);
1062     assertEquals(offeringPC.getRemoteDescription().type, answerSdp.type);
1063     assertEquals(answeringPC.getLocalDescription().type, answerSdp.type);
1064     assertEquals(answeringPC.getRemoteDescription().type, offerSdp.type);
1065 
1066     offeringExpectations.expectStateChange(DataChannel.State.OPEN);
1067     // See commentary about SCTP DataChannels above for why this is here.
1068     answeringExpectations.expectDataChannel("offeringDC");
1069     answeringExpectations.expectStateChange(DataChannel.State.OPEN);
1070 
1071     // Wait for at least one ice candidate from the offering PC and forward them to the answering
1072     // PC.
1073     for (IceCandidate candidate : offeringExpectations.getAtLeastOneIceCandidate()) {
1074       answeringPC.addIceCandidate(candidate);
1075     }
1076 
1077     // Wait for at least one ice candidate from the answering PC and forward them to the offering
1078     // PC.
1079     for (IceCandidate candidate : answeringExpectations.getAtLeastOneIceCandidate()) {
1080       offeringPC.addIceCandidate(candidate);
1081     }
1082 
1083     assertTrue(offeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS));
1084     assertTrue(answeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS));
1085 
1086     assertEquals(PeerConnection.SignalingState.STABLE, offeringPC.signalingState());
1087     assertEquals(PeerConnection.SignalingState.STABLE, answeringPC.signalingState());
1088 
1089     // Test send & receive UTF-8 text.
1090     answeringExpectations.expectMessage(
1091         ByteBuffer.wrap("hello!".getBytes(Charset.forName("UTF-8"))), false);
1092     DataChannel.Buffer buffer =
1093         new DataChannel.Buffer(ByteBuffer.wrap("hello!".getBytes(Charset.forName("UTF-8"))), false);
1094     assertTrue(offeringExpectations.dataChannel.send(buffer));
1095     assertTrue(answeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS));
1096 
1097     // Construct this binary message two different ways to ensure no
1098     // shortcuts are taken.
1099     ByteBuffer expectedBinaryMessage = ByteBuffer.allocateDirect(5);
1100     for (byte i = 1; i < 6; ++i) {
1101       expectedBinaryMessage.put(i);
1102     }
1103     expectedBinaryMessage.flip();
1104     offeringExpectations.expectMessage(expectedBinaryMessage, true);
1105     assertTrue(answeringExpectations.dataChannel.send(
1106         new DataChannel.Buffer(ByteBuffer.wrap(new byte[] {1, 2, 3, 4, 5}), true)));
1107     assertTrue(offeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS));
1108 
1109     offeringExpectations.expectStateChange(DataChannel.State.CLOSING);
1110     answeringExpectations.expectStateChange(DataChannel.State.CLOSING);
1111     offeringExpectations.expectStateChange(DataChannel.State.CLOSED);
1112     answeringExpectations.expectStateChange(DataChannel.State.CLOSED);
1113     offeringExpectations.dataChannel.close();
1114     assertTrue(offeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS));
1115     assertTrue(answeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS));
1116 
1117     // Free the Java-land objects and collect them.
1118     shutdownPC(offeringPC, offeringExpectations);
1119     offeringPC = null;
1120     shutdownPC(answeringPC, answeringExpectations);
1121     answeringPC = null;
1122     factory.dispose();
1123     System.gc();
1124   }
1125 
1126   // Tests that ICE candidates that are not allowed by an ICE transport type, thus not being
1127   // signaled to the gathering PeerConnection, can be surfaced via configuration if allowed by the
1128   // new ICE transport type, when RTCConfiguration.surfaceIceCandidatesOnIceTransportTypeChanged is
1129   // true.
1130   @Test
1131   @SmallTest
testSurfaceIceCandidatesWhenIceTransportTypeChanged()1132   public void testSurfaceIceCandidatesWhenIceTransportTypeChanged() throws Exception {
1133     // For this test, we only need one PeerConnection to observe the behavior of gathering, and we
1134     // create only the offering PC below.
1135     //
1136     // Allow loopback interfaces too since our Android devices often don't
1137     // have those.
1138     PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
1139     options.networkIgnoreMask = 0;
1140     PeerConnectionFactory factory =
1141         PeerConnectionFactory.builder().setOptions(options).createPeerConnectionFactory();
1142 
1143     PeerConnection.RTCConfiguration rtcConfig =
1144         new PeerConnection.RTCConfiguration(Collections.emptyList());
1145     rtcConfig.sdpSemantics = PeerConnection.SdpSemantics.UNIFIED_PLAN;
1146     // NONE would prevent any candidate being signaled to the PC.
1147     rtcConfig.iceTransportsType = PeerConnection.IceTransportsType.NONE;
1148     // We must have the continual gathering enabled to allow the surfacing of candidates on the ICE
1149     // transport type change.
1150     rtcConfig.continualGatheringPolicy = PeerConnection.ContinualGatheringPolicy.GATHER_CONTINUALLY;
1151     rtcConfig.surfaceIceCandidatesOnIceTransportTypeChanged = true;
1152 
1153     ObserverExpectations offeringExpectations = new ObserverExpectations("PCTest:offerer");
1154     PeerConnection offeringPC = factory.createPeerConnection(rtcConfig, offeringExpectations);
1155     assertNotNull(offeringPC);
1156 
1157     // Create a data channel and set local description to kick off the ICE candidate gathering.
1158     offeringExpectations.expectRenegotiationNeeded();
1159     DataChannel offeringDC = offeringPC.createDataChannel("offeringDC", new DataChannel.Init());
1160     assertEquals("offeringDC", offeringDC.label());
1161 
1162     offeringExpectations.setDataChannel(offeringDC);
1163     SdpObserverLatch sdpLatch = new SdpObserverLatch();
1164     offeringPC.createOffer(sdpLatch, new MediaConstraints());
1165     assertTrue(sdpLatch.await());
1166     SessionDescription offerSdp = sdpLatch.getSdp();
1167     assertEquals(offerSdp.type, SessionDescription.Type.OFFER);
1168     assertFalse(offerSdp.description.isEmpty());
1169 
1170     sdpLatch = new SdpObserverLatch();
1171     offeringExpectations.expectSignalingChange(SignalingState.HAVE_LOCAL_OFFER);
1172     offeringPC.setLocalDescription(sdpLatch, offerSdp);
1173     assertTrue(sdpLatch.await());
1174     assertNull(sdpLatch.getSdp());
1175 
1176     assertEquals(offeringPC.getLocalDescription().type, offerSdp.type);
1177 
1178     // Wait until we satisfy all expectations in the setup.
1179     assertTrue(offeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS));
1180 
1181     // Add the expectation of gathering at least one candidate, which should however fail because of
1182     // the transport type NONE.
1183     offeringExpectations.expectIceCandidates(1);
1184     assertFalse(offeringExpectations.waitForAllExpectationsToBeSatisfied(SHORT_TIMEOUT_SECONDS));
1185 
1186     // Change the transport type and we should be able to meet the expectation of gathering this
1187     // time.
1188     rtcConfig.iceTransportsType = PeerConnection.IceTransportsType.ALL;
1189     offeringPC.setConfiguration(rtcConfig);
1190     assertTrue(offeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS));
1191   }
1192 
1193   @Test
1194   @MediumTest
testTrackRemovalAndAddition()1195   public void testTrackRemovalAndAddition() throws Exception {
1196     // Allow loopback interfaces too since our Android devices often don't
1197     // have those.
1198     PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
1199     options.networkIgnoreMask = 0;
1200     PeerConnectionFactory factory = PeerConnectionFactory.builder()
1201                                         .setOptions(options)
1202                                         .setVideoEncoderFactory(new SoftwareVideoEncoderFactory())
1203                                         .setVideoDecoderFactory(new SoftwareVideoDecoderFactory())
1204                                         .createPeerConnectionFactory();
1205 
1206     List<PeerConnection.IceServer> iceServers = new ArrayList<>();
1207     iceServers.add(
1208         PeerConnection.IceServer.builder("stun:stun.l.google.com:19302").createIceServer());
1209 
1210     PeerConnection.RTCConfiguration rtcConfig = new PeerConnection.RTCConfiguration(iceServers);
1211     // TODO(https://crbug.com/webrtc/13528): Update test not to use Plan B.
1212     rtcConfig.sdpSemantics = PeerConnection.SdpSemantics.PLAN_B;
1213 
1214     ObserverExpectations offeringExpectations = new ObserverExpectations("PCTest:offerer");
1215     PeerConnection offeringPC = factory.createPeerConnection(rtcConfig, offeringExpectations);
1216     assertNotNull(offeringPC);
1217 
1218     ObserverExpectations answeringExpectations = new ObserverExpectations("PCTest:answerer");
1219     PeerConnection answeringPC = factory.createPeerConnection(rtcConfig, answeringExpectations);
1220     assertNotNull(answeringPC);
1221 
1222     // We want to use the same camera for offerer & answerer, so create it here
1223     // instead of in addTracksToPC.
1224     final CameraEnumerator enumerator = new Camera1Enumerator(false /* captureToTexture */);
1225     final VideoCapturer videoCapturer =
1226         enumerator.createCapturer(enumerator.getDeviceNames()[0], null /* eventsHandler */);
1227     final SurfaceTextureHelper surfaceTextureHelper =
1228         SurfaceTextureHelper.create("SurfaceTextureHelper", /* sharedContext= */ null);
1229     final VideoSource videoSource = factory.createVideoSource(/* isScreencast= */ false);
1230     videoCapturer.initialize(surfaceTextureHelper, InstrumentationRegistry.getTargetContext(),
1231         videoSource.getCapturerObserver());
1232     videoCapturer.startCapture(640, 480, 30);
1233 
1234     // Add offerer media stream.
1235     offeringExpectations.expectRenegotiationNeeded();
1236     WeakReference<MediaStream> oLMS =
1237         addTracksToPC(factory, offeringPC, videoSource, "offeredMediaStream", "offeredVideoTrack",
1238             "offeredAudioTrack", new ExpectedResolutionSetter(answeringExpectations),
1239             /*useAddStream=*/true);
1240 
1241     offeringExpectations.expectAddTrack(2);
1242     answeringExpectations.expectAddTrack(2);
1243     // Create offer.
1244     SdpObserverLatch sdpLatch = new SdpObserverLatch();
1245     offeringPC.createOffer(sdpLatch, new MediaConstraints());
1246     assertTrue(sdpLatch.await());
1247     SessionDescription offerSdp = sdpLatch.getSdp();
1248     assertEquals(offerSdp.type, SessionDescription.Type.OFFER);
1249     assertFalse(offerSdp.description.isEmpty());
1250 
1251     // Set local description for offerer.
1252     sdpLatch = new SdpObserverLatch();
1253     offeringExpectations.expectSignalingChange(SignalingState.HAVE_LOCAL_OFFER);
1254     offeringExpectations.expectIceCandidates(2);
1255     offeringExpectations.expectIceGatheringChange(IceGatheringState.COMPLETE);
1256     offeringExpectations.expectConnectionChange(PeerConnectionState.CONNECTING);
1257     offeringPC.setLocalDescription(sdpLatch, offerSdp);
1258     assertTrue(sdpLatch.await());
1259     assertNull(sdpLatch.getSdp());
1260 
1261     // Set remote description for answerer.
1262     sdpLatch = new SdpObserverLatch();
1263     answeringExpectations.expectSignalingChange(SignalingState.HAVE_REMOTE_OFFER);
1264     answeringExpectations.expectAddStream("offeredMediaStream");
1265     answeringPC.setRemoteDescription(sdpLatch, offerSdp);
1266     assertTrue(sdpLatch.await());
1267     assertNull(sdpLatch.getSdp());
1268 
1269     // Add answerer media stream.
1270     answeringExpectations.expectRenegotiationNeeded();
1271     WeakReference<MediaStream> aLMS = addTracksToPC(factory, answeringPC, videoSource,
1272         "answeredMediaStream", "answeredVideoTrack", "answeredAudioTrack",
1273         new ExpectedResolutionSetter(offeringExpectations),
1274         /*useAddStream=*/true);
1275 
1276     // Create answer.
1277     sdpLatch = new SdpObserverLatch();
1278     answeringPC.createAnswer(sdpLatch, new MediaConstraints());
1279     assertTrue(sdpLatch.await());
1280     SessionDescription answerSdp = sdpLatch.getSdp();
1281     assertEquals(answerSdp.type, SessionDescription.Type.ANSWER);
1282     assertFalse(answerSdp.description.isEmpty());
1283 
1284     // Set local description for answerer.
1285     sdpLatch = new SdpObserverLatch();
1286     answeringExpectations.expectSignalingChange(SignalingState.STABLE);
1287     answeringExpectations.expectIceCandidates(2);
1288     answeringExpectations.expectIceGatheringChange(IceGatheringState.COMPLETE);
1289     answeringExpectations.expectConnectionChange(PeerConnectionState.CONNECTING);
1290     answeringPC.setLocalDescription(sdpLatch, answerSdp);
1291     assertTrue(sdpLatch.await());
1292     assertNull(sdpLatch.getSdp());
1293 
1294     // Set remote description for offerer.
1295     sdpLatch = new SdpObserverLatch();
1296     offeringExpectations.expectSignalingChange(SignalingState.STABLE);
1297     offeringExpectations.expectAddStream("answeredMediaStream");
1298 
1299     offeringExpectations.expectIceConnectionChange(IceConnectionState.CHECKING);
1300     offeringExpectations.expectIceConnectionChange(IceConnectionState.CONNECTED);
1301     offeringExpectations.expectStandardizedIceConnectionChange(IceConnectionState.CHECKING);
1302     offeringExpectations.expectStandardizedIceConnectionChange(IceConnectionState.CONNECTED);
1303     offeringExpectations.expectConnectionChange(PeerConnectionState.CONNECTED);
1304     // TODO(bemasc): uncomment once delivery of ICECompleted is reliable
1305     // (https://code.google.com/p/webrtc/issues/detail?id=3021).
1306     //
1307     // offeringExpectations.expectIceConnectionChange(
1308     //     IceConnectionState.COMPLETED);
1309     answeringExpectations.expectIceConnectionChange(IceConnectionState.CHECKING);
1310     answeringExpectations.expectIceConnectionChange(IceConnectionState.CONNECTED);
1311     answeringExpectations.expectStandardizedIceConnectionChange(IceConnectionState.CHECKING);
1312     answeringExpectations.expectStandardizedIceConnectionChange(IceConnectionState.CONNECTED);
1313     answeringExpectations.expectConnectionChange(PeerConnectionState.CONNECTED);
1314 
1315     offeringPC.setRemoteDescription(sdpLatch, answerSdp);
1316     assertTrue(sdpLatch.await());
1317     assertNull(sdpLatch.getSdp());
1318 
1319     // Wait for at least one ice candidate from the offering PC and forward them to the answering
1320     // PC.
1321     for (IceCandidate candidate : offeringExpectations.getAtLeastOneIceCandidate()) {
1322       answeringPC.addIceCandidate(candidate);
1323     }
1324 
1325     // Wait for at least one ice candidate from the answering PC and forward them to the offering
1326     // PC.
1327     for (IceCandidate candidate : answeringExpectations.getAtLeastOneIceCandidate()) {
1328       offeringPC.addIceCandidate(candidate);
1329     }
1330 
1331     // Wait for one frame of the correct size to be delivered.
1332     // Otherwise we could get a dummy black frame of unexpcted size when the
1333     // video track is removed.
1334     offeringExpectations.expectFramesDelivered(1);
1335     answeringExpectations.expectFramesDelivered(1);
1336 
1337     assertTrue(offeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS));
1338     assertTrue(answeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS));
1339 
1340     assertEquals(PeerConnection.SignalingState.STABLE, offeringPC.signalingState());
1341     assertEquals(PeerConnection.SignalingState.STABLE, answeringPC.signalingState());
1342 
1343     // Now do another negotiation, removing the video track from one peer.
1344     // This previously caused a crash on pc.dispose().
1345     // See: https://bugs.chromium.org/p/webrtc/issues/detail?id=5128
1346     VideoTrack offererVideoTrack = oLMS.get().videoTracks.get(0);
1347     // Note that when we call removeTrack, we regain responsibility for
1348     // disposing of the track.
1349     offeringExpectations.expectRenegotiationNeeded();
1350     oLMS.get().removeTrack(offererVideoTrack);
1351     negotiate(offeringPC, offeringExpectations, answeringPC, answeringExpectations);
1352 
1353     // Make sure the track was really removed.
1354     MediaStream aRMS = answeringExpectations.gotRemoteStreams.iterator().next();
1355     assertTrue(aRMS.videoTracks.isEmpty());
1356 
1357     // Add the video track to test if the answeringPC will create a new track
1358     // for the updated remote description.
1359     offeringExpectations.expectRenegotiationNeeded();
1360     oLMS.get().addTrack(offererVideoTrack);
1361     // The answeringPC sets the updated remote description with a track added.
1362     // So the onAddTrack callback is expected to be called once.
1363     answeringExpectations.expectAddTrack(1);
1364     offeringExpectations.expectAddTrack(0);
1365     negotiate(offeringPC, offeringExpectations, answeringPC, answeringExpectations);
1366 
1367     // Finally, remove both the audio and video tracks, which should completely
1368     // remove the remote stream. This used to trigger an assert.
1369     // See: https://bugs.chromium.org/p/webrtc/issues/detail?id=5128
1370     offeringExpectations.expectRenegotiationNeeded();
1371     oLMS.get().removeTrack(offererVideoTrack);
1372     AudioTrack offererAudioTrack = oLMS.get().audioTracks.get(0);
1373     offeringExpectations.expectRenegotiationNeeded();
1374     oLMS.get().removeTrack(offererAudioTrack);
1375 
1376     answeringExpectations.expectRemoveStream("offeredMediaStream");
1377     negotiate(offeringPC, offeringExpectations, answeringPC, answeringExpectations);
1378 
1379     // Make sure the stream was really removed.
1380     assertTrue(answeringExpectations.gotRemoteStreams.isEmpty());
1381 
1382     // Free the Java-land objects and collect them.
1383     shutdownPC(offeringPC, offeringExpectations);
1384     offeringPC = null;
1385     shutdownPC(answeringPC, answeringExpectations);
1386     answeringPC = null;
1387     offererVideoTrack.dispose();
1388     offererAudioTrack.dispose();
1389     videoCapturer.stopCapture();
1390     videoCapturer.dispose();
1391     videoSource.dispose();
1392     surfaceTextureHelper.dispose();
1393     factory.dispose();
1394     System.gc();
1395   }
1396 
1397   /**
1398    * Test that a Java MediaStream is updated when the native stream is.
1399    * <p>
1400    * Specifically, test that when remote tracks are indicated as being added or
1401    * removed from a MediaStream (via "a=ssrc" or "a=msid" in a remote
1402    * description), the existing remote MediaStream object is updated.
1403    * <p>
1404    * This test starts with just an audio track, adds a video track, then
1405    * removes it. It only applies remote offers, which is sufficient to test
1406    * this functionality and simplifies the test. This means that no media will
1407    * actually be sent/received; we're just testing that the Java MediaStream
1408    * object gets updated when the native object changes.
1409    */
1410   @Test
1411   @MediumTest
testRemoteStreamUpdatedWhenTracksAddedOrRemoved()1412   public void testRemoteStreamUpdatedWhenTracksAddedOrRemoved() throws Exception {
1413     PeerConnectionFactory factory = PeerConnectionFactory.builder()
1414                                         .setVideoEncoderFactory(new SoftwareVideoEncoderFactory())
1415                                         .setVideoDecoderFactory(new SoftwareVideoDecoderFactory())
1416                                         .createPeerConnectionFactory();
1417 
1418     // TODO(https://crbug.com/webrtc/13528): Update test not to use Plan B.
1419     PeerConnection.RTCConfiguration planBConfig =
1420         new PeerConnection.RTCConfiguration(Collections.emptyList());
1421     planBConfig.sdpSemantics = PeerConnection.SdpSemantics.PLAN_B;
1422 
1423     // Use OfferToReceiveAudio/Video to ensure every offer has an audio and
1424     // video m= section. Simplifies the test because it means we don't have to
1425     // actually apply the offer to "offeringPC"; it's just used as an SDP
1426     // factory.
1427     MediaConstraints offerConstraints = new MediaConstraints();
1428     offerConstraints.mandatory.add(
1429         new MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"));
1430     offerConstraints.mandatory.add(
1431         new MediaConstraints.KeyValuePair("OfferToReceiveVideo", "true"));
1432 
1433     // This PeerConnection will only be used to generate offers.
1434     ObserverExpectations offeringExpectations = new ObserverExpectations("offerer");
1435     PeerConnection offeringPC = factory.createPeerConnection(planBConfig, offeringExpectations);
1436     assertNotNull(offeringPC);
1437 
1438     ObserverExpectations expectations = new ObserverExpectations("PC under test");
1439     PeerConnection pcUnderTest = factory.createPeerConnection(planBConfig, expectations);
1440     assertNotNull(pcUnderTest);
1441 
1442     // Add offerer media stream with just an audio track.
1443     MediaStream localStream = factory.createLocalMediaStream("stream");
1444     AudioTrack localAudioTrack =
1445         factory.createAudioTrack("audio", factory.createAudioSource(new MediaConstraints()));
1446     localStream.addTrack(localAudioTrack);
1447     offeringExpectations.expectRenegotiationNeeded();
1448     RtpSender audioSender =
1449         offeringPC.addTrack(localAudioTrack, Collections.singletonList(localStream.getId()));
1450     // Create offer.
1451     SdpObserverLatch sdpLatch = new SdpObserverLatch();
1452     offeringPC.createOffer(sdpLatch, offerConstraints);
1453     assertTrue(sdpLatch.await());
1454     SessionDescription offerSdp = sdpLatch.getSdp();
1455 
1456     // Apply remote offer to PC under test.
1457     sdpLatch = new SdpObserverLatch();
1458     expectations.expectSignalingChange(SignalingState.HAVE_REMOTE_OFFER);
1459     expectations.expectAddStream("stream");
1460     pcUnderTest.setRemoteDescription(sdpLatch, offerSdp);
1461     assertTrue(sdpLatch.await());
1462     // Sanity check that we get one remote stream with one audio track.
1463     MediaStream remoteStream = expectations.gotRemoteStreams.iterator().next();
1464     assertEquals(remoteStream.audioTracks.size(), 1);
1465     assertEquals(remoteStream.videoTracks.size(), 0);
1466 
1467     // Add a video track...
1468     final CameraEnumerator enumerator = new Camera1Enumerator(false /* captureToTexture */);
1469     final VideoCapturer videoCapturer =
1470         enumerator.createCapturer(enumerator.getDeviceNames()[0], null /* eventsHandler */);
1471     final SurfaceTextureHelper surfaceTextureHelper =
1472         SurfaceTextureHelper.create("SurfaceTextureHelper", /* sharedContext= */ null);
1473     final VideoSource videoSource = factory.createVideoSource(/* isScreencast= */ false);
1474     videoCapturer.initialize(surfaceTextureHelper, InstrumentationRegistry.getTargetContext(),
1475         videoSource.getCapturerObserver());
1476     VideoTrack videoTrack = factory.createVideoTrack("video", videoSource);
1477     offeringExpectations.expectRenegotiationNeeded();
1478     localStream.addTrack(videoTrack);
1479     offeringPC.addTrack(videoTrack, Collections.singletonList(localStream.getId()));
1480     // ... and create an updated offer.
1481     sdpLatch = new SdpObserverLatch();
1482     offeringPC.createOffer(sdpLatch, offerConstraints);
1483     assertTrue(sdpLatch.await());
1484     offerSdp = sdpLatch.getSdp();
1485 
1486     // Apply remote offer with new video track to PC under test.
1487     sdpLatch = new SdpObserverLatch();
1488     pcUnderTest.setRemoteDescription(sdpLatch, offerSdp);
1489     assertTrue(sdpLatch.await());
1490     // The remote stream should now have a video track.
1491     assertEquals(remoteStream.audioTracks.size(), 1);
1492     assertEquals(remoteStream.videoTracks.size(), 1);
1493 
1494     // Finally, create another offer with the audio track removed.
1495     offeringExpectations.expectRenegotiationNeeded();
1496     localStream.removeTrack(localAudioTrack);
1497     localAudioTrack.dispose();
1498     offeringPC.removeTrack(audioSender);
1499     sdpLatch = new SdpObserverLatch();
1500     offeringPC.createOffer(sdpLatch, offerConstraints);
1501     assertTrue(sdpLatch.await());
1502     offerSdp = sdpLatch.getSdp();
1503 
1504     // Apply remote offer with just a video track to PC under test.
1505     sdpLatch = new SdpObserverLatch();
1506     pcUnderTest.setRemoteDescription(sdpLatch, offerSdp);
1507     assertTrue(sdpLatch.await());
1508     // The remote stream should no longer have an audio track.
1509     assertEquals(remoteStream.audioTracks.size(), 0);
1510     assertEquals(remoteStream.videoTracks.size(), 1);
1511 
1512     // Free the Java-land objects. Video capturer and source aren't owned by
1513     // the PeerConnection and need to be disposed separately.
1514     // TODO(deadbeef): Should all these events really occur on disposal?
1515     // "Gathering complete" is especially odd since gathering never started.
1516     // Note that this test isn't meant to test these events, but we must do
1517     // this or otherwise it will crash.
1518     offeringExpectations.expectIceConnectionChange(IceConnectionState.CLOSED);
1519     offeringExpectations.expectStandardizedIceConnectionChange(IceConnectionState.CLOSED);
1520     offeringExpectations.expectSignalingChange(SignalingState.CLOSED);
1521     offeringExpectations.expectIceGatheringChange(IceGatheringState.COMPLETE);
1522     offeringPC.dispose();
1523     expectations.expectIceConnectionChange(IceConnectionState.CLOSED);
1524     expectations.expectStandardizedIceConnectionChange(IceConnectionState.CLOSED);
1525     expectations.expectSignalingChange(SignalingState.CLOSED);
1526     expectations.expectIceGatheringChange(IceGatheringState.COMPLETE);
1527     pcUnderTest.dispose();
1528     videoCapturer.dispose();
1529     videoSource.dispose();
1530     surfaceTextureHelper.dispose();
1531     factory.dispose();
1532   }
1533 
1534   @Test
1535   @SmallTest
testRollback()1536   public void testRollback() throws Exception {
1537     PeerConnectionFactory factory = PeerConnectionFactory.builder().createPeerConnectionFactory();
1538     PeerConnection.RTCConfiguration config =
1539         new PeerConnection.RTCConfiguration(Collections.emptyList());
1540     config.sdpSemantics = PeerConnection.SdpSemantics.UNIFIED_PLAN;
1541 
1542     ObserverExpectations offeringExpectations = new ObserverExpectations("PCTest:offerer");
1543     PeerConnection pc = factory.createPeerConnection(config, offeringExpectations);
1544 
1545     SdpObserverLatch sdpLatch = new SdpObserverLatch();
1546     pc.createOffer(sdpLatch, new MediaConstraints());
1547     assertTrue(sdpLatch.await());
1548     SessionDescription offer = sdpLatch.getSdp();
1549 
1550     sdpLatch = new SdpObserverLatch();
1551     offeringExpectations.expectSignalingChange(SignalingState.HAVE_LOCAL_OFFER);
1552     pc.setLocalDescription(sdpLatch, offer);
1553     assertTrue(sdpLatch.await());
1554 
1555     SessionDescription rollback = new SessionDescription(SessionDescription.Type.ROLLBACK, "");
1556     sdpLatch = new SdpObserverLatch();
1557     offeringExpectations.expectSignalingChange(SignalingState.STABLE);
1558     // TODO(bugs.webrtc.org/11970): determine if triggering ONN (twice even) is correct.
1559     offeringExpectations.expectRenegotiationNeeded();
1560     offeringExpectations.expectRenegotiationNeeded();
1561     pc.setLocalDescription(sdpLatch, rollback);
1562     assertTrue(sdpLatch.await());
1563 
1564     assertTrue(offeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS));
1565   }
1566 
negotiate(PeerConnection offeringPC, ObserverExpectations offeringExpectations, PeerConnection answeringPC, ObserverExpectations answeringExpectations)1567   private static void negotiate(PeerConnection offeringPC,
1568       ObserverExpectations offeringExpectations, PeerConnection answeringPC,
1569       ObserverExpectations answeringExpectations) {
1570     // Create offer.
1571     SdpObserverLatch sdpLatch = new SdpObserverLatch();
1572     offeringPC.createOffer(sdpLatch, new MediaConstraints());
1573     assertTrue(sdpLatch.await());
1574     SessionDescription offerSdp = sdpLatch.getSdp();
1575     assertEquals(offerSdp.type, SessionDescription.Type.OFFER);
1576     assertFalse(offerSdp.description.isEmpty());
1577 
1578     // Set local description for offerer.
1579     sdpLatch = new SdpObserverLatch();
1580     offeringExpectations.expectSignalingChange(SignalingState.HAVE_LOCAL_OFFER);
1581     offeringPC.setLocalDescription(sdpLatch, offerSdp);
1582     assertTrue(sdpLatch.await());
1583     assertNull(sdpLatch.getSdp());
1584 
1585     // Set remote description for answerer.
1586     sdpLatch = new SdpObserverLatch();
1587     answeringExpectations.expectSignalingChange(SignalingState.HAVE_REMOTE_OFFER);
1588     answeringPC.setRemoteDescription(sdpLatch, offerSdp);
1589     assertTrue(sdpLatch.await());
1590     assertNull(sdpLatch.getSdp());
1591 
1592     // Create answer.
1593     sdpLatch = new SdpObserverLatch();
1594     answeringPC.createAnswer(sdpLatch, new MediaConstraints());
1595     assertTrue(sdpLatch.await());
1596     SessionDescription answerSdp = sdpLatch.getSdp();
1597     assertEquals(answerSdp.type, SessionDescription.Type.ANSWER);
1598     assertFalse(answerSdp.description.isEmpty());
1599 
1600     // Set local description for answerer.
1601     sdpLatch = new SdpObserverLatch();
1602     answeringExpectations.expectSignalingChange(SignalingState.STABLE);
1603     answeringPC.setLocalDescription(sdpLatch, answerSdp);
1604     assertTrue(sdpLatch.await());
1605     assertNull(sdpLatch.getSdp());
1606 
1607     // Set remote description for offerer.
1608     sdpLatch = new SdpObserverLatch();
1609     offeringExpectations.expectSignalingChange(SignalingState.STABLE);
1610     offeringPC.setRemoteDescription(sdpLatch, answerSdp);
1611     assertTrue(sdpLatch.await());
1612     assertNull(sdpLatch.getSdp());
1613   }
1614 
1615   @SuppressWarnings("deprecation") // TODO(sakal): getStats is deprecated
shutdownPC(PeerConnection pc, ObserverExpectations expectations)1616   private static void shutdownPC(PeerConnection pc, ObserverExpectations expectations) {
1617     if (expectations.dataChannel != null) {
1618       expectations.dataChannel.unregisterObserver();
1619       expectations.dataChannel.dispose();
1620     }
1621 
1622     // Call getStats (old implementation) before shutting down PC.
1623     expectations.expectOldStatsCallback();
1624     assertTrue(pc.getStats(expectations, null /* track */));
1625     assertTrue(expectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS));
1626 
1627     // Call the new getStats implementation as well.
1628     expectations.expectNewStatsCallback();
1629     pc.getStats(expectations);
1630     assertTrue(expectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS));
1631 
1632     expectations.expectIceConnectionChange(IceConnectionState.CLOSED);
1633     expectations.expectStandardizedIceConnectionChange(IceConnectionState.CLOSED);
1634     expectations.expectConnectionChange(PeerConnectionState.CLOSED);
1635     expectations.expectSignalingChange(SignalingState.CLOSED);
1636     pc.close();
1637     assertTrue(expectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS));
1638 
1639     // Call getStats (old implementation) after calling close(). Should still
1640     // work.
1641     expectations.expectOldStatsCallback();
1642     assertTrue(pc.getStats(expectations, null /* track */));
1643     assertTrue(expectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS));
1644 
1645     Logging.d(TAG, "FYI stats: ");
1646     int reportIndex = -1;
1647     for (StatsReport[] reports : expectations.takeStatsReports()) {
1648       Logging.d(TAG, " Report #" + (++reportIndex));
1649       for (int i = 0; i < reports.length; ++i) {
1650         Logging.d(TAG, "  " + reports[i].toString());
1651       }
1652     }
1653     assertEquals(1, reportIndex);
1654     Logging.d(TAG, "End stats.");
1655 
1656     pc.dispose();
1657   }
1658 }
1659