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