Direct IP connect functionality for AppRTC Android demo.
This allows connecting between clients without using external servers, which is useful to OEMs if they are working in a network without internet connection. Implementation uses custom AppRTCClient that replaces WebSocketRTCClient if roomId looks like an IP. Instead of a web socket, this class uses direct TCP connection between peers as a signaling channel. Review-Url: https://codereview.webrtc.org/1963053002 Cr-Commit-Position: refs/heads/master@{#12789}
This commit is contained in:
@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Copyright 2016 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
package org.appspot.apprtc;
|
||||
|
||||
import org.appspot.apprtc.util.LooperExecutor;
|
||||
import org.appspot.apprtc.util.RobolectricLooperExecutor;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.shadows.ShadowLog;
|
||||
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.timeout;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(manifest = Config.NONE)
|
||||
public class TCPChannelClientTest {
|
||||
private static final int PORT = 8888;
|
||||
/**
|
||||
* How long we wait before trying to connect to the server. Chosen quite arbitrarily and
|
||||
* could be made smaller if need be.
|
||||
*/
|
||||
private static final int SERVER_WAIT = 10;
|
||||
private static final int CONNECT_TIMEOUT = 100;
|
||||
private static final int SEND_TIMEOUT = 100;
|
||||
private static final int DISCONNECT_TIMEOUT = 100;
|
||||
private static final String TEST_MESSAGE_SERVER = "Hello, Server!";
|
||||
private static final String TEST_MESSAGE_CLIENT = "Hello, Client!";
|
||||
|
||||
@Mock TCPChannelClient.TCPChannelEvents serverEvents;
|
||||
@Mock TCPChannelClient.TCPChannelEvents clientEvents;
|
||||
|
||||
private RobolectricLooperExecutor executor;
|
||||
private TCPChannelClient server;
|
||||
private TCPChannelClient client;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
ShadowLog.stream = System.out;
|
||||
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
executor = new RobolectricLooperExecutor();
|
||||
executor.requestStart();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
verifyNoMoreEvents();
|
||||
|
||||
executor.executeAndWait(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
client.disconnect();
|
||||
server.disconnect();
|
||||
}
|
||||
});
|
||||
|
||||
// Stop the executor thread
|
||||
executor.requestStop();
|
||||
try {
|
||||
executor.join();
|
||||
} catch (InterruptedException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConnectIPv4() {
|
||||
setUpIPv4Server();
|
||||
try {
|
||||
Thread.sleep(SERVER_WAIT);
|
||||
} catch (InterruptedException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
setUpIPv4Client();
|
||||
|
||||
verify(serverEvents, timeout(CONNECT_TIMEOUT)).onTCPConnected(true);
|
||||
verify(clientEvents, timeout(CONNECT_TIMEOUT)).onTCPConnected(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConnectIPv6() {
|
||||
setUpIPv6Server();
|
||||
try {
|
||||
Thread.sleep(SERVER_WAIT);
|
||||
} catch (InterruptedException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
setUpIPv6Client();
|
||||
|
||||
verify(serverEvents, timeout(CONNECT_TIMEOUT)).onTCPConnected(true);
|
||||
verify(clientEvents, timeout(CONNECT_TIMEOUT)).onTCPConnected(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendData() {
|
||||
testConnectIPv4();
|
||||
|
||||
executor.executeAndWait(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
client.send(TEST_MESSAGE_SERVER);
|
||||
server.send(TEST_MESSAGE_CLIENT);
|
||||
}
|
||||
});
|
||||
|
||||
verify(serverEvents, timeout(SEND_TIMEOUT)).onTCPMessage(TEST_MESSAGE_SERVER);
|
||||
verify(clientEvents, timeout(SEND_TIMEOUT)).onTCPMessage(TEST_MESSAGE_CLIENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDisconnectServer() {
|
||||
testConnectIPv4();
|
||||
executor.executeAndWait(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
server.disconnect();
|
||||
}
|
||||
});
|
||||
|
||||
verify(serverEvents, timeout(DISCONNECT_TIMEOUT)).onTCPClose();
|
||||
verify(clientEvents, timeout(DISCONNECT_TIMEOUT)).onTCPClose();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDisconnectClient() {
|
||||
testConnectIPv4();
|
||||
executor.executeAndWait(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
client.disconnect();
|
||||
}
|
||||
});
|
||||
|
||||
verify(serverEvents, timeout(DISCONNECT_TIMEOUT)).onTCPClose();
|
||||
verify(clientEvents, timeout(DISCONNECT_TIMEOUT)).onTCPClose();
|
||||
}
|
||||
|
||||
private void setUpIPv4Server() {
|
||||
setUpServer("0.0.0.0", PORT);
|
||||
}
|
||||
|
||||
private void setUpIPv4Client() {
|
||||
setUpClient("127.0.0.1", PORT);
|
||||
}
|
||||
|
||||
private void setUpIPv6Server() {
|
||||
setUpServer("::", PORT);
|
||||
}
|
||||
|
||||
private void setUpIPv6Client() {
|
||||
setUpClient("::1", PORT);
|
||||
}
|
||||
|
||||
private void setUpServer(String ip, int port) {
|
||||
server = new TCPChannelClient(executor, serverEvents, ip, port);
|
||||
}
|
||||
|
||||
private void setUpClient(String ip, int port) {
|
||||
client = new TCPChannelClient(executor, clientEvents, ip, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies no more server or client events have been issued
|
||||
*/
|
||||
private void verifyNoMoreEvents() {
|
||||
verifyNoMoreInteractions(serverEvents);
|
||||
verifyNoMoreInteractions(clientEvents);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright 2016 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
package org.appspot.apprtc.util;
|
||||
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* LooperExecutor that doesn't use Looper because its implementation in Robolectric is not suited
|
||||
* for our needs. Also implements executeAndWait that can be used to wait until the runnable has
|
||||
* been executed.
|
||||
*/
|
||||
public class RobolectricLooperExecutor extends LooperExecutor {
|
||||
private volatile boolean running = false;
|
||||
private static final int RUNNABLE_QUEUE_CAPACITY = 256;
|
||||
private final BlockingQueue<Runnable> runnableQueue
|
||||
= new ArrayBlockingQueue<>(RUNNABLE_QUEUE_CAPACITY);
|
||||
private long threadId;
|
||||
|
||||
/**
|
||||
* Executes the runnable passed to the constructor and sets isDone flag afterwards.
|
||||
*/
|
||||
private static class ExecuteAndWaitRunnable implements Runnable {
|
||||
public boolean isDone = false;
|
||||
private final Runnable runnable;
|
||||
|
||||
ExecuteAndWaitRunnable(Runnable runnable) {
|
||||
this.runnable = runnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
runnable.run();
|
||||
|
||||
synchronized (this) {
|
||||
isDone = true;
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
threadId = Thread.currentThread().getId();
|
||||
|
||||
while (running) {
|
||||
final Runnable runnable;
|
||||
|
||||
try {
|
||||
runnable = runnableQueue.take();
|
||||
} catch (InterruptedException e) {
|
||||
if (running) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
runnable.run();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void requestStart() {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
running = true;
|
||||
start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void requestStop() {
|
||||
running = false;
|
||||
interrupt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void execute(Runnable runnable) {
|
||||
try {
|
||||
runnableQueue.put(runnable);
|
||||
} catch (InterruptedException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues runnable to be run and waits for it to be executed by the executor thread
|
||||
*/
|
||||
public void executeAndWait(Runnable runnable) {
|
||||
ExecuteAndWaitRunnable executeAndWaitRunnable = new ExecuteAndWaitRunnable(runnable);
|
||||
execute(executeAndWaitRunnable);
|
||||
|
||||
synchronized (executeAndWaitRunnable) {
|
||||
while (!executeAndWaitRunnable.isDone) {
|
||||
try {
|
||||
executeAndWaitRunnable.wait();
|
||||
} catch (InterruptedException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkOnLooperThread() {
|
||||
return (Thread.currentThread().getId() == threadId);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user