package com.github.sdbg.debug.core.internal.android; import com.github.sdbg.core.DartCore; import com.github.sdbg.debug.core.SDBGDebugCorePlugin; import com.github.sdbg.utilities.ProcessRunner; import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.StringReader; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.StringTokenizer; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; public class ADBManager { private static interface Converter<T> { T convert(String line) throws IOException, CoreException; } private static File adbExecutable; private Collection<String> forwards = new HashSet<String>(); private static void copy(String resource, File toDir) throws IOException { InputStream in = SDBGDebugCorePlugin.getPlugin().getBundle().getResource(resource).openStream(); try { toDir.mkdirs(); int pos = resource.lastIndexOf('/'); String fileName = pos >= 0 ? resource.substring(pos) : resource; OutputStream out = new FileOutputStream(new File(toDir, fileName)); try { byte[] buf = new byte[4086]; for (int read = -1; (read = in.read(buf)) >= 0;) { out.write(buf, 0, read); } } finally { out.close(); } } finally { in.close(); } } private static synchronized File getAdbExecutable() throws IOException { if (adbExecutable == null) { String platform = Platform.getOS() + "_" + Platform.getOSArch(); String platformDir = "/resources/adb/" + platform + "/"; File adbDir = new File(new File( new File(new File(System.getProperty("user.home")), ".sdbg"), "adb"), platform); String adbExecutableName = "adb" + (DartCore.isWindows() ? ".exe" : ""); if (platform.equals("win32_x86") || platform.equals("win32_x86_64")) { copy(platformDir + adbExecutableName, adbDir); copy(platformDir + "AdbWinApi.dll", adbDir); copy(platformDir + "AdbWinUsbApi.dll", adbDir); } else if (platform.equals("macosx_x86_64") || platform.equals("linux_x86") || platform.equals("linux_x86_64")) { copy(platformDir + adbExecutableName, adbDir); new ProcessRunner(new ProcessBuilder( "chmod", "+x", new File(adbDir, adbExecutableName).getAbsolutePath())).runSync(null); } else { throw new IOException("Unsupported platform: " + platform); } adbExecutable = new File(adbDir, adbExecutableName); } return adbExecutable; } public ADBManager() { } public void addForward(String deviceId, String local, String remote) throws CoreException { String forward = deviceId + "=" + local; if (!forwards.contains(forward)) { executeADB("-s", deviceId, "forward", local, remote); forwards.add(forward); } } public void addForwardNoRebind(String deviceId, String local, String remote) throws CoreException { String forward = deviceId + "=" + local; if (!forwards.contains(forward)) { executeADB("-s", deviceId, "forward", "--no-rebind", local, remote); forwards.add(forward); } } public Process asyncShell(String deviceId, String... commands) throws CoreException { try { List<String> all = new ArrayList<String>(Arrays.asList("-s", deviceId, "shell")); all.addAll(Arrays.asList(commands)); return prepareADB(all).start(); } catch (IOException e) { throw new CoreException(new Status( IStatus.ERROR, SDBGDebugCorePlugin.PLUGIN_ID, e.toString(), e)); } } public void dispose() { removeAllForwards(); } public List<ADBDeviceInfo> getDevices() throws CoreException { return getData(new Converter<ADBDeviceInfo>() { @Override public ADBDeviceInfo convert(String line) throws CoreException { if (!line.startsWith("*") && !line.contains("List of devices attached")) { StringTokenizer stok = new StringTokenizer(line, " \t"); if (stok.hasMoreTokens()) { String id = stok.nextToken(); List<String> data = getData(null, "-s", id, "shell", "getprop", "ro.product.model"); return new ADBDeviceInfo(id, data.isEmpty() ? null : data.get(0)); } } return null; } }, "devices"); } public void install(String deviceId, File apkLocation) throws CoreException { executeADB("-s", deviceId, "install", apkLocation.getAbsolutePath()); } public void killServer() throws CoreException { executeADB("kill-server"); } public void pull(String deviceId, String remote, File fileOrDir) throws CoreException { executeADB("-s", deviceId, "pull", remote, fileOrDir.getAbsolutePath()); } public void push(String deviceId, File fileOrDir, String remote) throws CoreException { executeADB("-s", deviceId, "push", fileOrDir.getAbsolutePath(), remote); } public void removeAllForwards() { while (!forwards.isEmpty()) { String forward = forwards.iterator().next(); String deviceId = forward.substring(0, forward.indexOf('=')); String local = forward.substring(forward.indexOf('=') + 1); try { removeForward(deviceId, local); } catch (CoreException e) { // Best effort SDBGDebugCorePlugin.logError(e); } } } public void removeForward(String deviceId, String local) throws CoreException { String forward = deviceId + "=" + local; if (forwards.contains(forward)) { try { executeADB("-s", deviceId, "forward", "--remove", local); } finally { forwards.remove(forward); } } } public void removeTCPForward(String deviceId, int localTCPPort) throws CoreException { removeForward(deviceId, "tcp:" + Integer.toString(localTCPPort)); } public void shell(String deviceId, String... commands) throws CoreException { List<String> all = new ArrayList<String>(Arrays.asList("-s", deviceId, "shell")); all.addAll(Arrays.asList(commands)); executeADB(all); } public void uninstall(String deviceId, String appId) throws CoreException { shell(deviceId, "pm", "uninstall", "-k", appId); } private String executeADB(List<String> arguments) throws CoreException { try { ProcessRunner runner = new ProcessRunner(prepareADB(arguments)); runner.runSync(null); if (runner.getExitCode() != 0) { throw new IOException("ADB returned unexpected error code: " + runner.getExitCode() + "\n\nERR STREAM: " + runner.getStdErr()); } return runner.getStdOut(); } catch (IOException e) { throw new CoreException(new Status( IStatus.ERROR, SDBGDebugCorePlugin.PLUGIN_ID, e.toString(), e)); } } private String executeADB(String... arguments) throws CoreException { return executeADB(Arrays.asList(arguments)); } private <T> List<T> getData(Converter<T> converter, List<String> commands) throws CoreException { try { List<T> data = new ArrayList<T>(); BufferedReader reader = new BufferedReader(new StringReader(executeADB(commands))); for (String line = reader.readLine(); line != null; line = reader.readLine()) { @SuppressWarnings("unchecked") T entry = converter != null ? converter.convert(line.trim()) : (T) line.trim(); if (entry != null) { data.add(entry); } } return data; } catch (IOException e) { throw new CoreException(new Status( IStatus.ERROR, SDBGDebugCorePlugin.PLUGIN_ID, e.toString(), e)); } } private <T> List<T> getData(Converter<T> converter, String... commands) throws CoreException { return getData(converter, Arrays.asList(commands)); } private ProcessBuilder prepareADB(List<String> arguments) throws CoreException { try { List<String> cmdLine = new ArrayList<String>(); cmdLine.add(getAdbExecutable().getAbsolutePath()); cmdLine.addAll(arguments); return new ProcessBuilder(cmdLine); } catch (IOException e) { throw new CoreException(new Status( IStatus.ERROR, SDBGDebugCorePlugin.PLUGIN_ID, e.toString(), e)); } } // // private ProcessBuilder prepareADB(String... arguments) throws CoreException { // return prepareADB(Arrays.asList(arguments)); // } }