diff --git a/.gitignore b/.gitignore
index f9917b3..f972353 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,15 +4,13 @@
################
# IntelliJ #
################
-/.idea/workspace.xml
-/.idea/tasks.xml
+/.idea/*
+jadb.iml
#############
# Maven #
#############
-.idea/workspace.xml
-.idea/libraries
out/
target/
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
deleted file mode 100644
index f329bae..0000000
--- a/.idea/compiler.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
deleted file mode 100644
index b6da4f3..0000000
--- a/.idea/misc.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/jadb.iml b/jadb.iml
deleted file mode 100644
index c419de5..0000000
--- a/jadb.iml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 1490413..30d7a96 100644
--- a/pom.xml
+++ b/pom.xml
@@ -30,6 +30,7 @@
UTF-8
1.7
1.7
+ 2.7.17
@@ -39,6 +40,13 @@
4.10
test
+
+
+
+ org.mockito
+ mockito-core
+ ${mockito-core.version}
+
@@ -114,7 +122,7 @@
**/*.java
-
+
se.vidstige.jadb.test.integration.*
**/data/*
@@ -131,8 +139,8 @@
**/*.java
-
- se.vidstige.jadb.test.unit.*
+
+ se.vidstige.jadb.test.integration.*
**/data/*
**/fakes/*
diff --git a/src/se/vidstige/jadb/ConnectionToRemoteDeviceException.java b/src/se/vidstige/jadb/ConnectionToRemoteDeviceException.java
new file mode 100644
index 0000000..f4719bb
--- /dev/null
+++ b/src/se/vidstige/jadb/ConnectionToRemoteDeviceException.java
@@ -0,0 +1,7 @@
+package se.vidstige.jadb;
+
+public class ConnectionToRemoteDeviceException extends Exception {
+ public ConnectionToRemoteDeviceException(String message) {
+ super(message);
+ }
+}
diff --git a/src/se/vidstige/jadb/HostConnectToRemoteTcpDevice.java b/src/se/vidstige/jadb/HostConnectToRemoteTcpDevice.java
new file mode 100644
index 0000000..2b1f300
--- /dev/null
+++ b/src/se/vidstige/jadb/HostConnectToRemoteTcpDevice.java
@@ -0,0 +1,75 @@
+package se.vidstige.jadb;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+class HostConnectToRemoteTcpDevice {
+ private final Transport transport;
+ private final ResponseValidator responseValidator;
+
+ HostConnectToRemoteTcpDevice(Transport transport) {
+ this.transport = transport;
+ this.responseValidator = new ResponseValidatorImp();
+ }
+
+ //Visible for testing
+ HostConnectToRemoteTcpDevice(Transport transport, ResponseValidator responseValidator) {
+ this.transport = transport;
+ this.responseValidator = responseValidator;
+ }
+
+ InetSocketAddress connect(InetSocketAddress inetSocketAddress)
+ throws IOException, JadbException, ConnectionToRemoteDeviceException {
+ transport.send(String.format("host:connect:%s:%d", inetSocketAddress.getHostString(), inetSocketAddress.getPort()));
+ verifyTransportLevel();
+ verifyProtocolLevel();
+
+ return inetSocketAddress;
+ }
+
+ private void verifyTransportLevel() throws IOException, JadbException {
+ transport.verifyResponse();
+ }
+
+ private void verifyProtocolLevel() throws IOException, ConnectionToRemoteDeviceException {
+ String status = transport.readString();
+ responseValidator.validate(status);
+ }
+
+ //@VisibleForTesting
+ interface ResponseValidator {
+ void validate(String response) throws ConnectionToRemoteDeviceException;
+ }
+
+ final static class ResponseValidatorImp implements ResponseValidator {
+ private final static String SUCCESSFULLY_CONNECTED = "connected to";
+ private final static String ALREADY_CONNECTED = "already connected to";
+
+
+ ResponseValidatorImp() {
+ }
+
+ public void validate(String response) throws ConnectionToRemoteDeviceException {
+ if (!checkIfConnectedSuccessfully(response) && !checkIfAlreadyConnected(response)) {
+ throw new ConnectionToRemoteDeviceException(extractError(response));
+ }
+ }
+
+ private boolean checkIfConnectedSuccessfully(String response) {
+ return response.startsWith(SUCCESSFULLY_CONNECTED);
+ }
+
+ private boolean checkIfAlreadyConnected(String response) {
+ return response.startsWith(ALREADY_CONNECTED);
+ }
+
+ private String extractError(String response) {
+ int lastColon = response.lastIndexOf(":");
+ if (lastColon != -1) {
+ return response.substring(lastColon, response.length());
+ } else {
+ return response;
+ }
+ }
+ }
+}
diff --git a/src/se/vidstige/jadb/HostDisconnectFromRemoteTcpDevice.java b/src/se/vidstige/jadb/HostDisconnectFromRemoteTcpDevice.java
new file mode 100644
index 0000000..1c9aead
--- /dev/null
+++ b/src/se/vidstige/jadb/HostDisconnectFromRemoteTcpDevice.java
@@ -0,0 +1,75 @@
+package se.vidstige.jadb;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+public class HostDisconnectFromRemoteTcpDevice {
+ private final Transport transport;
+ private final ResponseValidator responseValidator;
+
+ HostDisconnectFromRemoteTcpDevice(Transport transport) {
+ this.transport = transport;
+ this.responseValidator = new ResponseValidatorImp();
+ }
+
+ //Visible for testing
+ HostDisconnectFromRemoteTcpDevice(Transport transport, ResponseValidator responseValidator) {
+ this.transport = transport;
+ this.responseValidator = responseValidator;
+ }
+
+ InetSocketAddress disconnect(InetSocketAddress inetSocketAddress)
+ throws IOException, JadbException, ConnectionToRemoteDeviceException {
+ transport.send(String.format("host:disconnect:%s:%d", inetSocketAddress.getHostString(), inetSocketAddress.getPort()));
+ verifyTransportLevel();
+ verifyProtocolLevel();
+
+ return inetSocketAddress;
+ }
+
+ private void verifyTransportLevel() throws IOException, JadbException {
+ transport.verifyResponse();
+ }
+
+ private void verifyProtocolLevel() throws IOException, ConnectionToRemoteDeviceException {
+ String status = transport.readString();
+ responseValidator.validate(status);
+ }
+
+ //@VisibleForTesting
+ interface ResponseValidator {
+ void validate(String response) throws ConnectionToRemoteDeviceException;
+ }
+
+ final static class ResponseValidatorImp implements ResponseValidator {
+ private final static String SUCCESSFULLY_DISCONNECTED = "disconnected";
+ private final static String ALREADY_DISCONNECTED = "error: no such device";
+
+
+ ResponseValidatorImp() {
+ }
+
+ public void validate(String response) throws ConnectionToRemoteDeviceException {
+ if (!checkIfConnectedSuccessfully(response) && !checkIfAlreadyConnected(response)) {
+ throw new ConnectionToRemoteDeviceException(extractError(response));
+ }
+ }
+
+ private boolean checkIfConnectedSuccessfully(String response) {
+ return response.startsWith(SUCCESSFULLY_DISCONNECTED);
+ }
+
+ private boolean checkIfAlreadyConnected(String response) {
+ return response.startsWith(ALREADY_DISCONNECTED);
+ }
+
+ private String extractError(String response) {
+ int lastColon = response.lastIndexOf(":");
+ if (lastColon != -1) {
+ return response.substring(lastColon, response.length());
+ } else {
+ return response;
+ }
+ }
+ }
+}
diff --git a/src/se/vidstige/jadb/JadbConnection.java b/src/se/vidstige/jadb/JadbConnection.java
index 7a76ec1..2eee46d 100644
--- a/src/se/vidstige/jadb/JadbConnection.java
+++ b/src/se/vidstige/jadb/JadbConnection.java
@@ -1,6 +1,8 @@
package se.vidstige.jadb;
+
import java.io.IOException;
+import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
@@ -34,6 +36,26 @@ public class JadbConnection implements ITransportFactory {
return version;
}
+ public InetSocketAddress connectToTcpDevice(InetSocketAddress inetSocketAddress)
+ throws IOException, JadbException, ConnectionToRemoteDeviceException {
+ Transport transport = createTransport();
+ try {
+ return new HostConnectToRemoteTcpDevice(transport).connect(inetSocketAddress);
+ } finally {
+ transport.close();
+ }
+ }
+
+ public InetSocketAddress disconnectFromTcpDevice(InetSocketAddress tcpAddressEntity)
+ throws IOException, JadbException, ConnectionToRemoteDeviceException {
+ Transport transport = createTransport();
+ try {
+ return new HostDisconnectFromRemoteTcpDevice(transport).disconnect(tcpAddressEntity);
+ } finally {
+ transport.close();
+ }
+ }
+
public List getDevices() throws IOException, JadbException {
Transport devices = createTransport();
devices.send("host:devices");
diff --git a/test/se/vidstige/jadb/HostConnectToRemoteTcpDeviceTest.java b/test/se/vidstige/jadb/HostConnectToRemoteTcpDeviceTest.java
new file mode 100644
index 0000000..b3d5113
--- /dev/null
+++ b/test/se/vidstige/jadb/HostConnectToRemoteTcpDeviceTest.java
@@ -0,0 +1,91 @@
+package se.vidstige.jadb;
+
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+import static org.junit.Assert.*;
+import static org.mockito.ArgumentCaptor.forClass;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class HostConnectToRemoteTcpDeviceTest {
+
+ @Test
+ public void testNormalConnection() throws ConnectionToRemoteDeviceException, IOException, JadbException {
+ //Prepare
+ Transport transport = mock(Transport.class);
+ when(transport.readString()).thenReturn("connected to somehost:1");
+
+ InetSocketAddress inetSocketAddress = new InetSocketAddress("somehost", 1);
+
+ //Do
+ HostConnectToRemoteTcpDevice hostConnectToRemoteTcpDevice = new HostConnectToRemoteTcpDevice(transport);
+ InetSocketAddress resultTcpAddressEntity = hostConnectToRemoteTcpDevice.connect(inetSocketAddress);
+
+ //Validate
+ assertEquals(resultTcpAddressEntity, inetSocketAddress);
+
+ ArgumentCaptor argument = forClass(String.class);
+ verify(transport, times(1)).send(argument.capture());
+ assertEquals("host:connect:somehost:1", argument.getValue());
+ }
+
+ @Test(expected = JadbException.class)
+ public void testTransportLevelException() throws ConnectionToRemoteDeviceException, IOException, JadbException {
+ //Prepare
+ Transport transport = mock(Transport.class);
+ doThrow(new JadbException("Fake exception")).when(transport).verifyResponse();
+
+ InetSocketAddress tcpAddressEntity = new InetSocketAddress("somehost", 1);
+
+ //Do
+ HostConnectToRemoteTcpDevice hostConnectToRemoteTcpDevice = new HostConnectToRemoteTcpDevice(transport);
+ hostConnectToRemoteTcpDevice.connect(tcpAddressEntity);
+
+ //Validate
+ verify(transport, times(1)).send(anyString());
+ verify(transport, times(1)).verifyResponse();
+ }
+
+ @Test(expected = ConnectionToRemoteDeviceException.class)
+ public void testProtocolException() throws ConnectionToRemoteDeviceException, IOException, JadbException {
+ //Prepare
+ Transport transport = mock(Transport.class);
+ when(transport.readString()).thenReturn("connected to somehost:1");
+ HostConnectToRemoteTcpDevice.ResponseValidator responseValidator = mock(HostConnectToRemoteTcpDevice.ResponseValidator.class);
+ doThrow(new ConnectionToRemoteDeviceException("Fake exception")).when(responseValidator).validate(anyString());
+
+ InetSocketAddress tcpAddressEntity = new InetSocketAddress("somehost", 1);
+
+ //Do
+ HostConnectToRemoteTcpDevice hostConnectToRemoteTcpDevice = new HostConnectToRemoteTcpDevice(transport, responseValidator);
+ hostConnectToRemoteTcpDevice.connect(tcpAddressEntity);
+
+ //Validate
+ verify(transport, times(1)).send(anyString());
+ verify(transport, times(1)).verifyResponse();
+ verify(responseValidator, times(1)).validate(anyString());
+ }
+
+ @Test
+ public void testProtocolResponseValidatorSuccessfullyConnected() throws ConnectionToRemoteDeviceException, IOException, JadbException {
+ new HostConnectToRemoteTcpDevice.ResponseValidatorImp().validate("connected to somehost:1");
+ }
+
+ @Test
+ public void testProtocolResponseValidatorAlreadyConnected() throws ConnectionToRemoteDeviceException, IOException, JadbException {
+ new HostConnectToRemoteTcpDevice.ResponseValidatorImp().validate("already connected to somehost:1");
+ }
+
+ @Test(expected = ConnectionToRemoteDeviceException.class)
+ public void testProtocolResponseValidatorErrorInValidate() throws ConnectionToRemoteDeviceException, IOException, JadbException {
+ new HostConnectToRemoteTcpDevice.ResponseValidatorImp().validate("some error occurred");
+ }
+}
\ No newline at end of file
diff --git a/test/se/vidstige/jadb/HostDisconnectFromRemoteTcpDeviceTest.java b/test/se/vidstige/jadb/HostDisconnectFromRemoteTcpDeviceTest.java
new file mode 100644
index 0000000..86c87d1
--- /dev/null
+++ b/test/se/vidstige/jadb/HostDisconnectFromRemoteTcpDeviceTest.java
@@ -0,0 +1,74 @@
+package se.vidstige.jadb;
+
+import org.junit.Test;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class HostDisconnectFromRemoteTcpDeviceTest {
+
+ @Test
+ public void testNormalConnection() throws ConnectionToRemoteDeviceException, IOException, JadbException {
+ //Prepare
+ Transport transport = mock(Transport.class);
+ when(transport.readString()).thenReturn("disconnected host:1");
+
+ InetSocketAddress inetSocketAddress = new InetSocketAddress("host", 1);
+
+ //Do
+ HostDisconnectFromRemoteTcpDevice hostConnectToRemoteTcpDevice = new HostDisconnectFromRemoteTcpDevice(transport);
+ InetSocketAddress resultInetSocketAddress = hostConnectToRemoteTcpDevice.disconnect(inetSocketAddress);
+
+ //Validate
+ assertEquals(inetSocketAddress, resultInetSocketAddress);
+ }
+
+ @Test(expected = JadbException.class)
+ public void testTransportLevelException() throws ConnectionToRemoteDeviceException, IOException, JadbException {
+ //Prepare
+ Transport transport = mock(Transport.class);
+ doThrow(new JadbException("Fake exception")).when(transport).verifyResponse();
+
+ InetSocketAddress inetSocketAddress = new InetSocketAddress("host", 1);
+
+ //Do
+ HostDisconnectFromRemoteTcpDevice hostConnectToRemoteTcpDevice = new HostDisconnectFromRemoteTcpDevice(transport);
+ hostConnectToRemoteTcpDevice.disconnect(inetSocketAddress);
+ }
+
+ @Test(expected = ConnectionToRemoteDeviceException.class)
+ public void testProtocolException() throws ConnectionToRemoteDeviceException, IOException, JadbException {
+ //Prepare
+ Transport transport = mock(Transport.class);
+ when(transport.readString()).thenReturn("any string");
+ HostDisconnectFromRemoteTcpDevice.ResponseValidator responseValidator = mock(HostDisconnectFromRemoteTcpDevice.ResponseValidator.class);
+ doThrow(new ConnectionToRemoteDeviceException("Fake exception")).when(responseValidator).validate(anyString());
+
+ InetSocketAddress inetSocketAddress = new InetSocketAddress("host", 1);
+
+ //Do
+ HostDisconnectFromRemoteTcpDevice hostConnectToRemoteTcpDevice = new HostDisconnectFromRemoteTcpDevice(transport, responseValidator);
+ hostConnectToRemoteTcpDevice.disconnect(inetSocketAddress);
+ }
+
+ @Test
+ public void testProtocolResponseValidatorSuccessfullyConnected() throws ConnectionToRemoteDeviceException, IOException, JadbException {
+ new HostDisconnectFromRemoteTcpDevice.ResponseValidatorImp().validate("disconnected 127.0.0.1:10001");
+ }
+
+ @Test
+ public void testProtocolResponseValidatorAlreadyConnected() throws ConnectionToRemoteDeviceException, IOException, JadbException {
+ new HostDisconnectFromRemoteTcpDevice.ResponseValidatorImp().validate("error: no such device '127.0.0.1:10001'");
+ }
+
+ @Test(expected = ConnectionToRemoteDeviceException.class)
+ public void testProtocolResponseValidatorErrorInValidate() throws ConnectionToRemoteDeviceException, IOException, JadbException {
+ new HostDisconnectFromRemoteTcpDevice.ResponseValidatorImp().validate("some error occurred");
+ }
+}
diff --git a/test/se/vidstige/jadb/test/fakes/FakeAdbServer.java b/test/se/vidstige/jadb/test/fakes/FakeAdbServer.java
index a82a0dd..05b7547 100644
--- a/test/se/vidstige/jadb/test/fakes/FakeAdbServer.java
+++ b/test/se/vidstige/jadb/test/fakes/FakeAdbServer.java
@@ -20,7 +20,7 @@ import java.util.List;
*/
public class FakeAdbServer implements AdbResponder {
private final AdbServer server;
- private List devices = new ArrayList();
+ private final List devices = new ArrayList();
public FakeAdbServer(int port) {
server = new AdbServer(this, port);
diff --git a/test/se/vidstige/jadb/test/integration/RealDeviceTestCases.java b/test/se/vidstige/jadb/test/integration/RealDeviceTestCases.java
index be6120c..9bb1339 100644
--- a/test/se/vidstige/jadb/test/integration/RealDeviceTestCases.java
+++ b/test/se/vidstige/jadb/test/integration/RealDeviceTestCases.java
@@ -13,8 +13,13 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.net.InetSocketAddress;
import java.util.List;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
public class RealDeviceTestCases {
private JadbConnection jadb;
@@ -117,4 +122,40 @@ public class RealDeviceTestCases {
if (outputStream != null) outputStream.close();
}
}
+
+ /**
+ * This test requires emulator running on non-standard tcp port - this may be achieve by executing such command:
+ * ${ANDROID_HOME}/emulator -verbose -avd ${NAME} -ports 10000,10001
+ *
+ * @throws IOException
+ * @throws JadbException
+ * @throws ConnectionToRemoteDeviceException
+ */
+ @Test
+ public void testConnectionToTcpDevice() throws IOException, JadbException, ConnectionToRemoteDeviceException {
+ jadb.connectToTcpDevice(new InetSocketAddress("127.0.0.1", 10001));
+ List devices = jadb.getDevices();
+
+ assertNotNull(devices);
+ assertFalse(devices.isEmpty());
+ }
+
+ /**
+ * @see #testConnectionToTcpDevice()
+ *
+ * @throws IOException
+ * @throws JadbException
+ * @throws ConnectionToRemoteDeviceException
+ */
+ @Test
+ public void testDisconnectionToTcpDevice() throws IOException, JadbException, ConnectionToRemoteDeviceException {
+ testConnectionToTcpDevice();
+
+ jadb.disconnectFromTcpDevice(new InetSocketAddress("127.0.0.1", 10001));
+ jadb.getDevices();
+
+ List devices = jadb.getDevices();
+ assertNotNull(devices);
+ assertTrue(devices.isEmpty());
+ }
}