Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .idea/encodings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 0 additions & 9 deletions .idea/modules.xml

This file was deleted.

8 changes: 4 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.target>1.7</maven.compiler.target>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<mockito-core.version>2.7.17</mockito-core.version>
</properties>

Expand Down Expand Up @@ -78,8 +78,8 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
Expand Down
65 changes: 65 additions & 0 deletions src/se/vidstige/jadb/AdbServerController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package se.vidstige.jadb;

import java.io.IOException;
import java.util.Map;

public class AdbServerController {
private final String executable;
private Subprocess subprocess;

/**
* Creates a new controller loading ADB from the environment.
*
* @param subprocess the sub-process.
* @param environment the environment to use to locate the ADB executable.
*/
public AdbServerController(Subprocess subprocess, Map<String, String> environment) {
this(subprocess, findAdbExecutable(environment));
}

/**
* Creates a new controller with the specified ADB.
*
* @param subprocess the sub-process.
* @param executable the location of the ADB executable.
*/
public AdbServerController(Subprocess subprocess, String executable) {
this.subprocess = subprocess;
this.executable = executable;
}

private static String findAdbExecutable(Map<String, String> environment) {
String androidHome = environment.get("ANDROID_HOME");
if (androidHome == null || androidHome.isEmpty()) {
return "adb";
}
return androidHome + "/platform-tools/adb";
}

private void runWithAdb(String command) throws IOException, InterruptedException {
Process p = subprocess.execute(new String[]{executable, command});
p.waitFor();
int exitValue = p.exitValue();
if (exitValue != 0) throw new IOException("adb exited with exit code: " + exitValue);
}

public void launch() throws IOException, InterruptedException {
runWithAdb("start-server");
}

public void kill() throws IOException, InterruptedException {
runWithAdb("kill-server");
}

public void root() throws IOException, InterruptedException {
runWithAdb("root");
}

public void unroot() throws IOException, InterruptedException {
runWithAdb("unroot");
}

public void remount() throws IOException, InterruptedException {
runWithAdb("remount");
}
}
3 changes: 3 additions & 0 deletions src/se/vidstige/jadb/AdbServerLauncher.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@

/**
* Launches the ADB server
*
* @deprecated Use {@link AdbServerController} instead.
*/
@Deprecated
public class AdbServerLauncher {
private final String executable;
private Subprocess subprocess;
Expand Down
26 changes: 26 additions & 0 deletions src/se/vidstige/jadb/JadbDevice.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import se.vidstige.jadb.managers.Bash;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
Expand Down Expand Up @@ -219,6 +220,31 @@ public void pull(RemoteFile remote, File local) throws IOException, JadbExceptio
}
}

public void pull(RemoteDir remote, File local) throws IOException, JadbException {
if (!isDirectory(remote)) throw new JadbException("Remote path must be an exists directory");
if (!local.exists()) local.mkdirs();
if (!local.isDirectory()) throw new JadbException("Local path must be a directory");

InputStream is = executeShell("ls", "-1", remote.getPath());
String[] remoteFiles = Stream.readAll(is, StandardCharsets.UTF_8).trim().split("\n");
for (String remoteFile : remoteFiles) {
if (remoteFile.isEmpty() || ".".equals(remoteFile) || "..".equals(remoteFile)) continue;
RemoteFile remotePath = new RemoteFile(remote.getPath() + "/" + remoteFile);
if (isDirectory(remotePath)) {
// Pull file recursively
pull(new RemoteDir(remotePath), new File(local, remoteFile));
} else {
pull(remotePath, new File(local, remoteFile));
}
}
}

public boolean isDirectory(RemoteFile remote) throws IOException, JadbException {
InputStream is = executeShell("if [ -d \"" + remote.getPath() + "\" ]; then echo 'dir'; fi");
String result = Stream.readAll(is, StandardCharsets.UTF_8).trim();
return "dir".equals(result);
}

private void send(Transport transport, String command) throws IOException, JadbException {
transport.send(command);
transport.verifyResponse();
Expand Down
11 changes: 11 additions & 0 deletions src/se/vidstige/jadb/RemoteDir.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package se.vidstige.jadb;

public class RemoteDir extends RemoteFile {
public RemoteDir(RemoteFile remoteFile) {
this(remoteFile.getPath());
}

public RemoteDir(String path) {
super(path);
}
}
22 changes: 18 additions & 4 deletions test/se/vidstige/jadb/test/integration/RealDeviceTestCases.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Objects;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.*;

public class RealDeviceTestCases {

Expand All @@ -30,7 +30,7 @@ public class RealDeviceTestCases {
@BeforeClass
public static void tryToStartAdbServer() {
try {
new AdbServerLauncher(new Subprocess(), System.getenv()).launch();
new AdbServerController(new Subprocess(), System.getenv()).launch();
} catch (IOException | InterruptedException e) {
System.out.println("Could not start adb-server");
}
Expand Down Expand Up @@ -98,6 +98,20 @@ public void testPullInvalidFile() throws Exception {
any.pull(new RemoteFile("/file/does/not/exist"), temporaryFolder.newFile("xyz"));
}

@Test
public void testPullDir() throws Exception {
JadbDevice any = jadb.getAnyDevice();
InputStream is = any.executeShell("mkdir", "-p", "/sdcard/jadb-test");
Stream.readAll(is, StandardCharsets.UTF_8);
File readme = new File("README.md");
any.push(readme, new RemoteFile("/sdcard/jadb-test/README.md"));
any.push(readme, new RemoteFile("/sdcard/jadb-test/README_copy.md"));

File local = temporaryFolder.newFolder("jadb-test");
any.pull(new RemoteDir("/sdcard/jadb-test"), local);
assertEquals(2, Objects.requireNonNull(local.list()).length);
}

@SuppressWarnings("deprecation")
@Test
public void testShellExecuteTwice() throws Exception {
Expand Down
126 changes: 126 additions & 0 deletions test/se/vidstige/jadb/test/unit/AdbServerControllerFixture.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package se.vidstige.jadb.test.unit;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import se.vidstige.jadb.AdbServerController;
import se.vidstige.jadb.test.fakes.FakeSubprocess;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class AdbServerControllerFixture {
private FakeSubprocess subprocess;
private Map<String, String> environment = new HashMap<>();

@Before
public void setUp() {
subprocess = new FakeSubprocess();
}

@After
public void tearDown() {
subprocess.verifyExpectations();
}

@Test
public void testStartServer() throws Exception {
subprocess.expect(new String[]{"/abc/platform-tools/adb", "start-server"}, 0);
Map<String, String> environment = new HashMap<>();
environment.put("ANDROID_HOME", "/abc");
new AdbServerController(subprocess, environment).launch();
}

@Test
public void testStartServerWithoutANDROID_HOME() throws Exception {
subprocess.expect(new String[]{"adb", "start-server"}, 0);
new AdbServerController(subprocess, environment).launch();
}

@Test(expected = IOException.class)
public void testStartServerFails() throws Exception {
subprocess.expect(new String[]{"adb", "start-server"}, -1);
new AdbServerController(subprocess, environment).launch();
}

@Test
public void testKillServer() throws Exception {
subprocess.expect(new String[]{"/abc/platform-tools/adb", "kill-server"}, 0);
Map<String, String> environment = new HashMap<>();
environment.put("ANDROID_HOME", "/abc");
new AdbServerController(subprocess, environment).kill();
}

@Test
public void testKillServerWithoutANDROID_HOME() throws Exception {
subprocess.expect(new String[]{"adb", "kill-server"}, 0);
new AdbServerController(subprocess, environment).kill();
}

@Test(expected = IOException.class)
public void testKillServerFails() throws Exception {
subprocess.expect(new String[]{"adb", "kill-server"}, -1);
new AdbServerController(subprocess, environment).kill();
}

@Test
public void testRootDevice() throws Exception {
subprocess.expect(new String[]{"/abc/platform-tools/adb", "root"}, 0);
Map<String, String> environment = new HashMap<>();
environment.put("ANDROID_HOME", "/abc");
new AdbServerController(subprocess, environment).root();
}

@Test
public void testRootDeviceWithoutANDROID_HOME() throws Exception {
subprocess.expect(new String[]{"adb", "root"}, 0);
new AdbServerController(subprocess, environment).root();
}

@Test(expected = IOException.class)
public void testRootDeviceFails() throws Exception {
subprocess.expect(new String[]{"adb", "root"}, -1);
new AdbServerController(subprocess, environment).root();
}

@Test
public void testUnrootDevice() throws Exception {
subprocess.expect(new String[]{"/abc/platform-tools/adb", "unroot"}, 0);
Map<String, String> environment = new HashMap<>();
environment.put("ANDROID_HOME", "/abc");
new AdbServerController(subprocess, environment).unroot();
}

@Test
public void testUnrootDeviceWithoutANDROID_HOME() throws Exception {
subprocess.expect(new String[]{"adb", "unroot"}, 0);
new AdbServerController(subprocess, environment).unroot();
}

@Test(expected = IOException.class)
public void testUnrootDeviceFails() throws Exception {
subprocess.expect(new String[]{"adb", "unroot"}, -1);
new AdbServerController(subprocess, environment).unroot();
}

@Test
public void testRemountDevice() throws Exception {
subprocess.expect(new String[]{"/abc/platform-tools/adb", "remount"}, 0);
Map<String, String> environment = new HashMap<>();
environment.put("ANDROID_HOME", "/abc");
new AdbServerController(subprocess, environment).remount();
}

@Test
public void testRemountDeviceWithoutANDROID_HOME() throws Exception {
subprocess.expect(new String[]{"adb", "remount"}, 0);
new AdbServerController(subprocess, environment).remount();
}

@Test(expected = IOException.class)
public void testRemountDeviceFails() throws Exception {
subprocess.expect(new String[]{"adb", "remount"}, -1);
new AdbServerController(subprocess, environment).remount();
}
}