aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/Device.java134
-rwxr-xr-xddms/libs/ddmlib/src/com/android/ddmlib/IDevice.java46
-rw-r--r--ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java24
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/AndroidLaunchController.java171
4 files changed, 236 insertions, 139 deletions
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/Device.java b/ddms/libs/ddmlib/src/com/android/ddmlib/Device.java
index d9d1275..e929431 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/Device.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/Device.java
@@ -16,8 +16,10 @@
package com.android.ddmlib;
+import com.android.ddmlib.SyncService.SyncResult;
import com.android.ddmlib.log.LogReceiver;
+import java.io.File;
import java.io.IOException;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
@@ -25,6 +27,8 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
@@ -50,11 +54,51 @@ final class Device implements IDevice {
private final ArrayList<Client> mClients = new ArrayList<Client>();
private DeviceMonitor mMonitor;
+ private static final String LOG_TAG = "Device";
+
/**
* Socket for the connection monitoring client connection/disconnection.
*/
private SocketChannel mSocketChannel;
+ /**
+ * Output receiver for "pm install package.apk" command line.
+ */
+ private static final class InstallReceiver extends MultiLineReceiver {
+
+ private static final String SUCCESS_OUTPUT = "Success"; //$NON-NLS-1$
+ private static final Pattern FAILURE_PATTERN = Pattern.compile("Failure\\s+\\[(.*)\\]"); //$NON-NLS-1$
+
+ private String mErrorMessage = null;
+
+ public InstallReceiver() {
+ }
+
+ @Override
+ public void processNewLines(String[] lines) {
+ for (String line : lines) {
+ if (line.length() > 0) {
+ if (line.startsWith(SUCCESS_OUTPUT)) {
+ mErrorMessage = null;
+ } else {
+ Matcher m = FAILURE_PATTERN.matcher(line);
+ if (m.matches()) {
+ mErrorMessage = m.group(1);
+ }
+ }
+ }
+ }
+ }
+
+ public boolean isCancelled() {
+ return false;
+ }
+
+ public String getErrorMessage() {
+ return mErrorMessage;
+ }
+ }
+
/*
* (non-Javadoc)
* @see com.android.ddmlib.IDevice#getSerialNumber()
@@ -373,4 +417,94 @@ final class Device implements IDevice {
void addProperty(String label, String value) {
mProperties.put(label, value);
}
+
+ /**
+ * {@inheritDoc}
+ */
+ public String installPackage(String packageFilePath, boolean reinstall)
+ throws IOException {
+ String remoteFilePath = syncPackageToDevice(packageFilePath);
+ String result = installRemotePackage(remoteFilePath, reinstall);
+ removeRemotePackage(remoteFilePath);
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String syncPackageToDevice(String localFilePath)
+ throws IOException {
+ try {
+ String packageFileName = getFileName(localFilePath);
+ String remoteFilePath = String.format("/data/local/tmp/%1$s", packageFileName); //$NON-NLS-1$
+
+ Log.i(packageFileName, String.format("Uploading %1$s onto device '%2$s'",
+ packageFileName, getSerialNumber()));
+
+ SyncService sync = getSyncService();
+ if (sync != null) {
+ String message = String.format("Uploading file onto device '%1$s'",
+ getSerialNumber());
+ Log.i(LOG_TAG, message);
+ SyncResult result = sync.pushFile(localFilePath, remoteFilePath,
+ SyncService.getNullProgressMonitor());
+
+ if (result.getCode() != SyncService.RESULT_OK) {
+ throw new IOException(String.format("Unable to upload file: %1$s",
+ result.getMessage()));
+ }
+ } else {
+ throw new IOException("Unable to open sync connection!");
+ }
+ return remoteFilePath;
+ } catch (IOException e) {
+ Log.e(LOG_TAG, String.format("Unable to open sync connection! reason: %1$s",
+ e.getMessage()));
+ throw e;
+ }
+ }
+
+ /**
+ * Helper method to retrieve the file name given a local file path
+ * @param filePath full directory path to file
+ * @return {@link String} file name
+ */
+ private String getFileName(String filePath) {
+ return new File(filePath).getName();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String installRemotePackage(String remoteFilePath, boolean reinstall)
+ throws IOException {
+ InstallReceiver receiver = new InstallReceiver();
+ String cmd = String.format(reinstall ? "pm install -r \"%1$s\"" : "pm install \"%1$s\"",
+ remoteFilePath);
+ executeShellCommand(cmd, receiver);
+ return receiver.getErrorMessage();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void removeRemotePackage(String remoteFilePath) throws IOException {
+ // now we delete the app we sync'ed
+ try {
+ executeShellCommand("rm " + remoteFilePath, new NullOutputReceiver());
+ } catch (IOException e) {
+ Log.e(LOG_TAG, String.format("Failed to delete temporary package: %1$s",
+ e.getMessage()));
+ throw e;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String uninstallPackage(String packageName) throws IOException {
+ InstallReceiver receiver = new InstallReceiver();
+ executeShellCommand("pm uninstall " + packageName, receiver);
+ return receiver.getErrorMessage();
+ }
}
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/IDevice.java b/ddms/libs/ddmlib/src/com/android/ddmlib/IDevice.java
index 3a2bd55..7e90878 100755
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/IDevice.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/IDevice.java
@@ -146,7 +146,7 @@ public interface IDevice {
/**
* Returns a {@link SyncService} object to push / pull files to and from the device.
- * @return <code>null</code> if the SyncService couldn't be created. This can happen if abd
+ * @return <code>null</code> if the SyncService couldn't be created. This can happen if adb
* refuse to open the connection because the {@link IDevice} is invalid (or got disconnected).
* @throws IOException if the connection with adb failed.
*/
@@ -211,4 +211,48 @@ public interface IDevice {
*/
public String getClientName(int pid);
+ /**
+ * Installs an Android application on device.
+ * This is a helper method that combines the syncPackageToDevice, installRemotePackage,
+ * and removePackage steps
+ * @param packageFilePath the absolute file system path to file on local host to install
+ * @param reinstall set to <code>true</code> if re-install of app should be performed
+ * @return a {@link String} with an error code, or <code>null</code> if success.
+ * @throws IOException
+ */
+ public String installPackage(String packageFilePath, boolean reinstall) throws IOException;
+
+ /**
+ * Pushes a file to device
+ * @param localFilePath the absolute path to file on local host
+ * @return {@link String} destination path on device for file
+ * @throws IOException if fatal error occurred when pushing file
+ */
+ public String syncPackageToDevice(String localFilePath)
+ throws IOException;
+
+ /**
+ * Installs the application package that was pushed to a temporary location on the device.
+ * @param remoteFilePath absolute file path to package file on device
+ * @param reinstall set to <code>true</code> if re-install of app should be performed
+ * @throws InstallException if installation failed
+ */
+ public String installRemotePackage(String remoteFilePath, boolean reinstall)
+ throws IOException;
+
+ /**
+ * Remove a file from device
+ * @param remoteFilePath path on device of file to remove
+ * @throws IOException if file removal failed
+ */
+ public void removeRemotePackage(String remoteFilePath) throws IOException;
+
+ /**
+ * Uninstall an package from the device.
+ * @param packageName the Android application package name to uninstall
+ * @return a {@link String} with an error code, or <code>null</code> if success.
+ * @throws IOException
+ */
+ public String uninstallPackage(String packageName) throws IOException;
+
}
diff --git a/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java b/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java
index 04b42cc..29ec9fe 100644
--- a/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java
+++ b/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java
@@ -220,6 +220,30 @@ public class RemoteAndroidTestRunnerTest extends TestCase {
return "";
}
+ public String installPackage(String packageFilePath, boolean reinstall)
+ throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ public String uninstallPackage(String packageName) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ public String installRemotePackage(String remoteFilePath,
+ boolean reinstall) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void removeRemotePackage(String remoteFilePath)
+ throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ public String syncPackageToDevice(String localFilePath)
+ throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
}
/**
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/AndroidLaunchController.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/AndroidLaunchController.java
index 5992fab..b1abba5 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/AndroidLaunchController.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/AndroidLaunchController.java
@@ -21,12 +21,9 @@ import com.android.ddmlib.Client;
import com.android.ddmlib.ClientData;
import com.android.ddmlib.IDevice;
import com.android.ddmlib.Log;
-import com.android.ddmlib.MultiLineReceiver;
-import com.android.ddmlib.SyncService;
import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
import com.android.ddmlib.AndroidDebugBridge.IDebugBridgeChangeListener;
import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener;
-import com.android.ddmlib.SyncService.SyncResult;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.launch.AndroidLaunchConfiguration.TargetMode;
import com.android.ide.eclipse.adt.internal.launch.DelayedLaunchInfo.InstallRetryMode;
@@ -74,8 +71,6 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
/**
* Controls the launch of Android application either on a device or on the
@@ -134,47 +129,6 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
/** static instance for singleton */
private static AndroidLaunchController sThis = new AndroidLaunchController();
-
-
- /**
- * Output receiver for "pm install package.apk" command line.
- */
- private static final class InstallReceiver extends MultiLineReceiver {
-
- private static final String SUCCESS_OUTPUT = "Success"; //$NON-NLS-1$
- private static final Pattern FAILURE_PATTERN = Pattern.compile("Failure\\s+\\[(.*)\\]"); //$NON-NLS-1$
-
- private String mSuccess = null;
-
- public InstallReceiver() {
- }
-
- @Override
- public void processNewLines(String[] lines) {
- for (String line : lines) {
- if (line.length() > 0) {
- if (line.startsWith(SUCCESS_OUTPUT)) {
- mSuccess = null;
- } else {
- Matcher m = FAILURE_PATTERN.matcher(line);
- if (m.matches()) {
- mSuccess = m.group(1);
- }
- }
- }
- }
- }
-
- public boolean isCancelled() {
- return false;
- }
-
- public String getSuccess() {
- return mSuccess;
- }
- }
-
-
/** private constructor to enforce singleton */
private AndroidLaunchController() {
AndroidDebugBridge.addDebugBridgeChangeListener(this);
@@ -847,70 +801,29 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
* @return true if the install succeeded.
*/
private boolean doSyncApp(DelayedLaunchInfo launchInfo, IDevice device) {
+ IPath path = launchInfo.getPackageFile().getLocation();
+ String fileName = path.lastSegment();
try {
- SyncService sync = device.getSyncService();
- if (sync != null) {
- IPath path = launchInfo.getPackageFile().getLocation();
- String message = String.format("Uploading %1$s onto device '%2$s'",
- path.lastSegment(), device.getSerialNumber());
- AdtPlugin.printToConsole(launchInfo.getProject(), message);
-
- String osLocalPath = path.toOSString();
- String apkName = launchInfo.getPackageFile().getName();
- String remotePath = "/data/local/tmp/" + apkName; //$NON-NLS-1$
-
- SyncResult result = sync.pushFile(osLocalPath, remotePath,
- SyncService.getNullProgressMonitor());
-
- if (result.getCode() != SyncService.RESULT_OK) {
- String msg = String.format("Failed to upload %1$s on '%2$s': %3$s",
- apkName, device.getSerialNumber(), result.getMessage());
- AdtPlugin.printErrorToConsole(launchInfo.getProject(), msg);
- return false;
- }
-
- // Now that the package is uploaded, we can install it properly.
- // This will check that there isn't another apk declaring the same package, or
- // that another install used a different key.
- boolean installResult = installPackage(launchInfo, remotePath, device);
-
- // now we delete the app we sync'ed
- try {
- device.executeShellCommand("rm " + remotePath, new MultiLineReceiver() { //$NON-NLS-1$
- @Override
- public void processNewLines(String[] lines) {
- // pass
- }
- public boolean isCancelled() {
- return false;
- }
- });
- } catch (IOException e) {
- AdtPlugin.printErrorToConsole(launchInfo.getProject(), String.format(
- "Failed to delete temporary package: %1$s", e.getMessage()));
- return false;
- }
-
- // if the installation succeeded, we register it.
- if (installResult) {
- ApkInstallManager.getInstance().registerInstallation(
- launchInfo.getProject(), device);
- }
-
- return installResult;
- } else {
- String msg = String.format(
- "Failed to upload %1$s on device '%2$s': Unable to open sync connection!",
- launchInfo.getPackageFile().getName(), device.getSerialNumber());
- AdtPlugin.printErrorToConsole(launchInfo.getProject(), msg);
+ String message = String.format("Uploading %1$s onto device '%2$s'",
+ fileName, device.getSerialNumber());
+ AdtPlugin.printToConsole(launchInfo.getProject(), message);
+
+ String remotePackagePath = device.syncPackageToDevice(path.toOSString());
+ boolean installResult = installPackage(launchInfo, remotePackagePath, device);
+ device.removeRemotePackage(remotePackagePath);
+
+ // if the installation succeeded, we register it.
+ if (installResult) {
+ ApkInstallManager.getInstance().registerInstallation(
+ launchInfo.getProject(), device);
}
- } catch (IOException e) {
- String msg = String.format(
- "Failed to upload %1$s on device '%2$s': Unable to open sync connection!",
- launchInfo.getPackageFile().getName(), device.getSerialNumber());
- AdtPlugin.printErrorToConsole(launchInfo.getProject(), msg, e.getMessage());
+ return installResult;
+ }
+ catch (IOException e) {
+ String msg = String.format("Failed to upload %1$s on device '%2$s'", fileName,
+ device.getSerialNumber());
+ AdtPlugin.printErrorToConsole(launchInfo.getProject(), msg, e);
}
-
return false;
}
@@ -988,22 +901,19 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
return dependencies;
}
-
-
/**
- * Installs the application package that was pushed to a temporary location on the device.
+ * Installs the application package on the device, and handles return result
* @param launchInfo The launch information
* @param remotePath The remote path of the package.
* @param device The device on which the launch is done.
*/
private boolean installPackage(DelayedLaunchInfo launchInfo, final String remotePath,
final IDevice device) {
-
String message = String.format("Installing %1$s...", launchInfo.getPackageFile().getName());
AdtPlugin.printToConsole(launchInfo.getProject(), message);
-
try {
- String result = doInstall(launchInfo, remotePath, device, false /* reinstall */);
+ // try a reinstall first, because the most common case is the app is already installed
+ String result = doInstall(launchInfo, remotePath, device, true /* reinstall */);
/* For now we force to retry the install (after uninstalling) because there's no
* other way around it: adb install does not want to update a package w/o uninstalling
@@ -1012,7 +922,10 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
return checkInstallResult(result, device, launchInfo, remotePath,
InstallRetryMode.ALWAYS);
} catch (IOException e) {
- // do nothing, we'll return false
+ String msg = String.format(
+ "Failed to install %1$s on device '%2$s!",
+ launchInfo.getPackageFile().getName(), device.getSerialNumber());
+ AdtPlugin.printErrorToConsole(launchInfo.getProject(), msg, e.getMessage());
}
return false;
@@ -1033,7 +946,9 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
if (result == null) {
AdtPlugin.printToConsole(launchInfo.getProject(), "Success!");
return true;
- } else if (result.equals("INSTALL_FAILED_ALREADY_EXISTS")) { //$NON-NLS-1$
+ }
+ else if (result.equals("INSTALL_FAILED_ALREADY_EXISTS")) { //$NON-NLS-1$
+ // this should never happen, since reinstall mode is used on the first attempt
if (retryMode == InstallRetryMode.PROMPT) {
boolean prompt = AdtPlugin.displayPrompt("Application Install",
"A previous installation needs to be uninstalled before the new package can be installed.\nDo you want to uninstall?");
@@ -1067,11 +982,10 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
AdtPlugin.printToConsole(launchInfo.getProject(),
"Application already exists. Attempting to re-install instead...");
- String res = doInstall(launchInfo, remotePath, device, true /* reinstall */);
+ String res = doInstall(launchInfo, remotePath, device, true /* reinstall */ );
return checkInstallResult(res, device, launchInfo, remotePath,
InstallRetryMode.NEVER);
}
-
AdtPlugin.printErrorToConsole(launchInfo.getProject(),
"Installation error! The package already exists.");
} else if (result.equals("INSTALL_FAILED_INVALID_APK")) { //$NON-NLS-1$
@@ -1110,18 +1024,14 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
*/
@SuppressWarnings("unused")
private String doUninstall(IDevice device, DelayedLaunchInfo launchInfo) throws IOException {
- InstallReceiver receiver = new InstallReceiver();
try {
- device.executeShellCommand("pm uninstall " + launchInfo.getPackageName(), //$NON-NLS-1$
- receiver);
+ return device.uninstallPackage(launchInfo.getPackageName());
} catch (IOException e) {
String msg = String.format(
"Failed to uninstall %1$s: %2$s", launchInfo.getPackageName(), e.getMessage());
AdtPlugin.printErrorToConsole(launchInfo.getProject(), msg);
throw e;
}
-
- return receiver.getSuccess();
}
/**
@@ -1136,22 +1046,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
*/
private String doInstall(DelayedLaunchInfo launchInfo, final String remotePath,
final IDevice device, boolean reinstall) throws IOException {
- InstallReceiver receiver = new InstallReceiver();
- try {
- String cmd = String.format(
- reinstall ? "pm install -r \"%1$s\"" : "pm install \"%1$s\"", //$NON-NLS-1$ //$NON-NLS-2$
- remotePath); //$NON-NLS-1$ //$NON-NLS-2$
- device.executeShellCommand(cmd, receiver);
- } catch (IOException e) {
- String msg = String.format(
- "Failed to install %1$s on device '%2$s': %3$s",
- launchInfo.getPackageFile().getName(), device.getSerialNumber(),
- e.getMessage());
- AdtPlugin.printErrorToConsole(launchInfo.getProject(), msg);
- throw e;
- }
-
- return receiver.getSuccess();
+ return device.installRemotePackage(remotePath, reinstall);
}
/**