Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(325)

Unified Diff: dart/editor/tools/plugins/com.google.dart.tools.core/src/com/google/dart/tools/core/mobile/AndroidDebugBridge.java

Issue 337623003: Version 1.5.0-dev.4.11 (Closed) Base URL: http://dart.googlecode.com/svn/trunk/
Patch Set: Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: dart/editor/tools/plugins/com.google.dart.tools.core/src/com/google/dart/tools/core/mobile/AndroidDebugBridge.java
===================================================================
--- dart/editor/tools/plugins/com.google.dart.tools.core/src/com/google/dart/tools/core/mobile/AndroidDebugBridge.java (revision 37301)
+++ dart/editor/tools/plugins/com.google.dart.tools.core/src/com/google/dart/tools/core/mobile/AndroidDebugBridge.java (working copy)
@@ -13,14 +13,22 @@
*/
package com.google.dart.tools.core.mobile;
+import com.google.dart.engine.utilities.io.PrintStringWriter;
import com.google.dart.tools.core.DartCore;
import com.google.dart.tools.core.dart2js.ProcessRunner;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.StringReader;
+import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -30,14 +38,96 @@
*/
public class AndroidDebugBridge {
- private File adb;
+ /**
+ * Specialized process runner for ADB logcat
+ */
+ class LogcatRunner extends ProcessRunner {
- private static String[] INSTALL_CMD = new String[] {"install"};
- private static String[] DEVICES_CMD = new String[] {"devices"};
+ private final String msgPrefix;
+ private final PrintStringWriter output = new PrintStringWriter();
+ private final Object lock = new Object();
+ private IStatus result = null;
+ public LogcatRunner(ProcessBuilder processBuilder, String msgPrefix) {
+ super(processBuilder);
+ this.msgPrefix = msgPrefix;
+ }
+
+ public String getOutput() {
+ return output.toString();
+ }
+
+ /**
+ * Block until a result is available or the specified timeout expires.
+ *
+ * @return the status or {@code null} if the wait timed out
+ */
+ public IStatus waitForResult(long timeout) {
+ synchronized (lock) {
+ long endTime = System.currentTimeMillis() + timeout;
+ while (result == null) {
+ long delta = endTime - System.currentTimeMillis();
+ if (delta <= 0) {
+ break;
+ }
+ try {
+ lock.wait(delta);
+ } catch (InterruptedException e) {
+ //$FALL-THROUGH$
+ }
+ }
+ return result;
+ }
+ }
+
+ @Override
+ protected void pipeStdout(InputStream in, StringBuilder builder) {
+ try {
+ LineNumberReader reader = new LineNumberReader(new InputStreamReader(in, "UTF-8"));
+ while (true) {
+ String line = reader.readLine();
+ if (line == null) {
+ break;
+ }
+ int index = line.indexOf(msgPrefix);
+ if (index >= 0) {
+ String msg = line.substring(index + msgPrefix.length() + 1).trim();
+ output.println(msg);
+ if (msg.equals("Success")) {
+ setResult(Status.OK_STATUS);
+ } else if (msg.startsWith("Error:")) {
+ setResult(new Status(IStatus.ERROR, DartCore.PLUGIN_ID, msg.substring(6).trim()));
+ }
+ }
+ }
+ } catch (UnsupportedEncodingException e) {
+ DartCore.logError(e);
+ } catch (IOException e) {
+ // This exception is expected.
+ }
+ }
+
+ private void setResult(IStatus status) {
+ synchronized (lock) {
+ result = status;
+ lock.notifyAll();
+ }
+ }
+ }
+
+ private static final String CONTENT_SHELL_APK_ID = "org.chromium.content_shell_apk";
+ private static final String CONNECTION_TEST_APK_ID = "com.google.dart.editor.mobile.connection.service";
+
+ private static final String[] LIST_PACKAGES_CMD = new String[] {
+ "shell", "pm", "list", "packages", "-3"};
+ private static final String[] INSTALL_CMD = new String[] {"install", "-r"};
+ private static final String[] DEVICES_CMD = new String[] {"devices"};
+
private static final String DEVICE_CONNECTED_SUFFIX = "\tdevice";
private static final String UNAUTHORIZED_SUFFIX = "\tunauthorized";
+ private static final String[] START_SERVICE = new String[] {"shell", "am", "startservice"};
+
private static String[] LAUNCH_URL_IN_CC_CMD = new String[] {
"shell", "am", "start", "-n", "org.chromium.content_shell_apk/.ContentShellActivity", "-d"};
@@ -45,25 +135,19 @@
"shell", "am", "start", "-n", "com.android.chrome/com.google.android.apps.chrome.Main", "-d"};
private static String[] STOP_APP_CMD = new String[] {
- "shell", "am", "force-stop", "org.chromium.content_shell_apk"};
+ "shell", "am", "force-stop", CONTENT_SHELL_APK_ID};
private static final String CONTENT_SHELL_DEBUG_PORT = "localabstract:content_shell_devtools_remote";
private static final String CHROME_DEBUG_PORT = "localabstract:chrome_devtools_remote";
private static String[] PORT_FORWARD_CMD = new String[] {"forward"};
- private static String[] UNINSTALL_CMD = new String[] {
- "shell", "pm", "uninstall", "-k", "org.chromium.content_shell_apk"};
+ private static String[] UNINSTALL_CMD = new String[] {"shell", "pm", "uninstall", "-k"};
private static String[] START_SERVER_CMD = new String[] {"start-server"};
- private ProcessRunner runner;
+ private static final String[] LOGCAT_CMD = new String[] {"logcat"};
- /**
- * The identifiers of devices on which content shell has been installed during this session.
- */
- private static HashSet<String> contentShellInstallSet = new HashSet<String>();
-
private static AndroidDebugBridge androidDebugBridge = new AndroidDebugBridge(
AndroidSdkManager.getManager().getAdbExecutable());
@@ -71,8 +155,12 @@
return androidDebugBridge;
}
- AndroidDebugBridge(File adbExecutable) {
- this.adb = adbExecutable;
+ private File adbExecutable;
+ private ProcessRunner adbRunner;
+ private final HashMap<String, HashSet<String>> installSet = new HashMap<String, HashSet<String>>();
+
+ private AndroidDebugBridge(File adbExecutable) {
+ this.adbExecutable = adbExecutable;
}
/**
@@ -87,7 +175,7 @@
//04f5385f95d80610 device
//T062873654 unauthorized
String unauthorized = null;
- LineNumberReader reader = new LineNumberReader(new StringReader(runner.getStdOut()));
+ LineNumberReader reader = new LineNumberReader(new StringReader(adbRunner.getStdOut()));
try {
while (true) {
String line = reader.readLine();
@@ -119,31 +207,15 @@
* adb install path/to/apk
* </p>
*
- * @param deviceId the identifier of the device on which to install the content shell
+ * @param device the device on which to install the content shell
* @return true if install was successful
*/
- public boolean installContentShellApk(String deviceId) {
- if (contentShellInstallSet.contains(deviceId)) {
- return true;
- }
- // TODO(keertip): process error to check if apk is already installed
- List<String> args = buildAdbCommand(INSTALL_CMD);
- if (deviceId != null) {
- args.add(1, "-s");
- args.add(2, deviceId);
- }
- args.add(AndroidSdkManager.getManager().getContentShellApkLocation());
- if (runAdb(args, "ADB: install dart content shell browser", "This could take up to 30 seconds")) {
- String message = runner.getStdOut();
- // if the apk is present, message is Failure [INSTALL_FAILED_ALREADY_EXISTS]
- if (message.toLowerCase().contains("already_exists")) {
- // TODO(keertip): check version and reinstall
- // DartCore.getConsole().println(message);
- }
- contentShellInstallSet.add(deviceId);
- return true;
- }
- return false;
+ public boolean installContentShellApk(AndroidDevice device) {
+ return install(
+ device,
+ "dart content shell browser",
+ CONTENT_SHELL_APK_ID,
+ AndroidSdkManager.getManager().getContentShellApkLocation());
}
/**
@@ -155,6 +227,59 @@
}
/**
+ * Determine if the given URL is accessible from the mobile device.
+ */
+ public IStatus isHtmlPageAccessible(AndroidDevice device, String pageUrl) {
+ if (!installConnectionTestApk(device)) {
+ return new Status(IStatus.ERROR, DartCore.PLUGIN_ID, "Failed to install connection test");
+ }
+
+ String msgPrefix = "Connection test (" + System.currentTimeMillis() + ")";
+ LogcatRunner logcatRunner = new LogcatRunner(
+ new ProcessBuilder(buildAdbCommand(LOGCAT_CMD)),
+ msgPrefix);
+ try {
+ logcatRunner.runAsync();
+ } catch (IOException e) {
+ DartCore.logError(e);
+ DartCore.getConsole().println("Failed to launch ADB logcat");
+ return new Status(IStatus.ERROR, DartCore.PLUGIN_ID, "Failed to launch ADB logcat", e);
+ }
+
+ // Launch the service that tests the connection from mobile device to developer machine
+ List<String> args = buildAdbCommand(START_SERVICE);
+ args.add("-n");
+ args.add("com.google.dart.editor.mobile.connection.service/.ConnectionService");
+ args.add("-d");
+ args.add(pageUrl);
+ args.add("-e");
+ args.add("prefix");
+ args.add(msgPrefix);
+ try {
+ if (!runAdb(args, "ADB: check port forwarding")) {
+ return new Status(
+ IStatus.ERROR,
+ DartCore.PLUGIN_ID,
+ "Failed to launch port forwarding detection");
+ }
+ IStatus result = logcatRunner.waitForResult(3500);
+ if (result != null) {
+ if (!result.isOK()) {
+ //DartCore.getConsole().println(logcatRunner.getOutput());
+ DartCore.getConsole().println(result.getMessage());
+ }
+ return result;
+ }
+ return new Status(
+ IStatus.ERROR,
+ DartCore.PLUGIN_ID,
+ "Timeout waiting for port forwarding detection");
+ } finally {
+ logcatRunner.dispose();
+ }
+ }
+
+ /**
* Open the url in the chrome browser on the device
* <p>
* adb shell am start com.android.chrome/com.google.android.apps.chrome.Main -d url
@@ -208,7 +333,7 @@
*/
public void startAdbServer() {
List<String> args = buildAdbCommand(START_SERVER_CMD);
- runAdb(args, "");
+ runAdb(args);
}
/**
@@ -224,17 +349,22 @@
return runAdb(args, "ADB: stop application");
}
+ public void uninstallConnectionTestApk(AndroidDevice device) {
+ if (!uninstall(device, CONNECTION_TEST_APK_ID)) {
+ DartCore.logError("Failed to uninstall " + CONNECTION_TEST_APK_ID);
+ }
+ }
+
/**
- * Uninstall the content shell apk from the device
+ * Uninstall the content shell
*/
- void uninstallContentShellApk() {
- List<String> args = buildAdbCommand(UNINSTALL_CMD);
- runAdb(args, "ADB: uninstall browser");
+ public void uninstallContentShellApk(AndroidDevice device) {
+ uninstall(device, CONTENT_SHELL_APK_ID);
}
- private List<String> buildAdbCommand(String[] cmds) {
+ private List<String> buildAdbCommand(String... cmds) {
List<String> args = new ArrayList<String>();
- args.add(adb.getAbsolutePath());
+ args.add(adbExecutable.getAbsolutePath());
if (cmds != null) {
for (String string : cmds) {
args.add(string);
@@ -243,11 +373,95 @@
return args;
}
+ /**
+ * Install the specified APK onto the mobile device if this is the first installation during this
+ * sessions --or-- if the APK has been removed since the beginning of this session.
+ *
+ * @param device the device onto which the APK will be installed
+ * @param apkName the human readable name of the APK
+ * @param apkId TODO
+ * @param apkLocation the APK file to be installed
+ * @return {@code true} if the APK was installed or already resides on the mobile device
+ */
+ private boolean install(AndroidDevice device, String apkName, String apkId, String apkLocation) {
+
+ // Check if the APK has been installed and still resides on the mobile device
+ if (device != null) {
+ HashSet<String> apkIds = installSet.get(device.getDeviceId());
+ if (apkIds == null) {
+ apkIds = new HashSet<String>();
+ installSet.put(device.getDeviceId(), apkIds);
+ }
+ if (apkIds.contains(apkId) && isInstalled(device, apkId)) {
+ return true;
+ }
+ apkIds.add(apkId);
+ }
+
+ // Uninstall the old APK if it exists
+ //uninstall(device, apkId);
+
+ // Install the APK
+ // adb install -r <path> to replace previously installed APK if present
+ List<String> args = buildAdbCommand(INSTALL_CMD);
+ if (device != null) {
+ args.add(1, "-s");
+ args.add(2, device.getDeviceId());
+ }
+ args.add(apkLocation);
+ return runAdb(args, "ADB: install " + apkName, "This could take up to 30 seconds");
+ }
+
+ private boolean installConnectionTestApk(AndroidDevice device) {
+ return install(
+ device,
+ "connection test",
+ CONNECTION_TEST_APK_ID,
+ AndroidSdkManager.getManager().getConnectionTestApkLocation());
+ }
+
+ /**
+ * Query the mobile device to determine if the specified APK is installed.
+ *
+ * @param device the device
+ * @param apkId the APK identifier
+ * @return {@code true} if installed, else {@code false}
+ */
+ private boolean isInstalled(AndroidDevice device, String apkId) {
+ List<String> args = buildAdbCommand(LIST_PACKAGES_CMD);
+ if (device != null) {
+ args.add(1, "-s");
+ args.add(2, device.getDeviceId());
+ }
+ if (runAdb(args)) {
+ // Output is list of apk identifiers prefixed by "package:"
+ //package:com.google.dart.editor.mobile.connection.service
+ //package:org.chromium.content_shell_apk
+ String target = "package:" + apkId;
+ LineNumberReader reader = new LineNumberReader(new StringReader(adbRunner.getStdOut()));
+ try {
+ while (true) {
+ String line = reader.readLine();
+ if (line == null) {
+ break;
+ }
+ line = line.trim();
+ if (line.equals(target)) {
+ return true;
+ }
+ }
+ } catch (IOException e) {
+ //$FALL-THROUGH$
+ }
+ }
+ return false;
+ }
+
private boolean runAdb(List<String> args, String... message) {
int exitCode = 1;
ProcessBuilder builder = new ProcessBuilder();
builder.command(args);
- runner = new ProcessRunner(builder);
+ adbRunner = new ProcessRunner(builder);
for (int index = 0; index < message.length; index++) {
if (index == 0) {
DartCore.getConsole().printSeparator(message[index]);
@@ -256,9 +470,9 @@
}
}
try {
- exitCode = runner.runSync(null);
+ exitCode = adbRunner.runSync(null);
if (exitCode != 0) {
- DartCore.getConsole().println(runner.getStdErr());
+ DartCore.getConsole().println(adbRunner.getStdErr());
}
} catch (IOException e) {
@@ -266,4 +480,14 @@
}
return exitCode == 0 ? true : false;
}
+
+ private boolean uninstall(AndroidDevice device, String apkId) {
+ List<String> args = buildAdbCommand(UNINSTALL_CMD);
+ if (device != null) {
+ args.add(1, "-s");
+ args.add(2, device.getDeviceId());
+ }
+ args.add(apkId);
+ return runAdb(args, "ADB: uninstall " + apkId);
+ }
}

Powered by Google App Engine
This is Rietveld 408576698