/* | |
* Copyright (C) 2012 The Android Open Source Project | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package com.motorola.studio.android.adt; | |
import java.io.BufferedReader; | |
import java.io.File; | |
import java.io.IOException; | |
import java.io.InputStreamReader; | |
import java.io.OutputStream; | |
import java.util.ArrayList; | |
import java.util.Collection; | |
import java.util.HashMap; | |
import java.util.LinkedHashMap; | |
import java.util.LinkedList; | |
import java.util.List; | |
import java.util.Map; | |
import org.eclipse.core.resources.IFile; | |
import org.eclipse.core.runtime.CoreException; | |
import org.eclipse.core.runtime.IProgressMonitor; | |
import org.eclipse.core.runtime.IStatus; | |
import org.eclipse.core.runtime.MultiStatus; | |
import org.eclipse.core.runtime.NullProgressMonitor; | |
import org.eclipse.core.runtime.Platform; | |
import org.eclipse.core.runtime.Status; | |
import org.eclipse.debug.core.DebugPlugin; | |
import org.eclipse.debug.core.ILaunchConfiguration; | |
import org.eclipse.debug.core.ILaunchConfigurationType; | |
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; | |
import org.eclipse.debug.core.ILaunchManager; | |
import org.eclipse.debug.ui.DebugUITools; | |
import org.eclipse.debug.ui.ILaunchGroup; | |
import org.eclipse.jface.dialogs.IDialogConstants; | |
import org.eclipse.jface.dialogs.MessageDialog; | |
import org.eclipse.jface.viewers.ISelection; | |
import org.eclipse.jface.viewers.IStructuredSelection; | |
import org.eclipse.jface.viewers.StructuredSelection; | |
import org.eclipse.jface.wizard.WizardDialog; | |
import org.eclipse.swt.widgets.Display; | |
import org.eclipse.swt.widgets.Shell; | |
import org.eclipse.ui.IWorkbenchWindow; | |
import org.eclipse.ui.PlatformUI; | |
import org.eclipse.ui.console.IOConsoleOutputStream; | |
import com.android.ddmlib.FileListingService; | |
import com.android.ddmlib.FileListingService.FileEntry; | |
import com.android.ddmlib.IDevice; | |
import com.android.ddmuilib.ScreenShotDialog; | |
import com.android.sdklib.IAndroidTarget; | |
import com.motorola.studio.android.AndroidPlugin; | |
import com.motorola.studio.android.adt.StudioAndroidEventManager.EventType; | |
import com.motorola.studio.android.common.log.StudioLogger; | |
import com.motorola.studio.android.common.log.UsageDataConstants; | |
import com.motorola.studio.android.common.utilities.EclipseUtils; | |
import com.motorola.studio.android.common.utilities.FileUtil; | |
import com.motorola.studio.android.i18n.AndroidNLS; | |
import com.motorola.studio.android.wizards.installapp.DeployWizard; | |
import com.motorola.studio.android.wizards.installapp.DeployWizard.INSTALL_TYPE; | |
import com.motorola.studio.android.wizards.installapp.UninstallAppWizard; | |
import com.motorola.studio.android.wizards.mat.DumpHPROFWizard; | |
import com.motorola.studio.android.wizards.monkey.IMonkeyConfigurationConstants; | |
public class DDMSUtils | |
{ | |
private static final Map<String, FileListingService> deviceFileListingServiceMap = | |
new HashMap<String, FileListingService>(); | |
/** | |
* The APK extension | |
*/ | |
private static final String APK_FILE_EXTENSION = "apk"; | |
/** | |
* Parameter for overwriting existing applications, if any | |
*/ | |
private static final String ADB_INSTALL_OVERWRITE = "-r"; | |
/** | |
* Options to be used with adb to indicate package manager application | |
*/ | |
private static final String PM_CMD = "pm"; | |
/** | |
* Options to be used with adb to run monkey application | |
*/ | |
private static final String MONKEY_CMD = "monkey"; | |
/** | |
* Uninstall directive to the package manager | |
*/ | |
private static final String PM_UNINSTALL_DIRECTIVE = "uninstall"; | |
/** | |
* List packages directive | |
*/ | |
private static final String PM_LIST_DIRECTIVE = "list"; | |
/** | |
* List packages directive | |
*/ | |
private static final String PM_PACKAGES_DIRECTIVE = "packages"; | |
/** | |
* List packages force directive | |
*/ | |
private static final String PM_PACKAGES_DIRECTIVE_FORCE = "-f"; | |
/** | |
* Monkey packages directive | |
*/ | |
private static final String MONKEY_PACKAGES_DIRECTIVE = " -p "; | |
/** | |
* Options to be used with adb to indicate install operation | |
*/ | |
private static final String INSTALL_CMD = "install"; | |
/** | |
* This word must exist in the adb install commmand answer to indicate | |
* succefull installation | |
*/ | |
private static final String SUCCESS_CONSTANT = "Success"; | |
private static final DdmsRunnable disconnectedListener = new DdmsRunnable() | |
{ | |
public void run(String serialNumber) | |
{ | |
synchronized (deviceFileListingServiceMap) | |
{ | |
deviceFileListingServiceMap.remove(serialNumber); | |
} | |
} | |
}; | |
static | |
{ | |
StudioAndroidEventManager.asyncAddDeviceChangeListeners(null, disconnectedListener); | |
} | |
public static void takeScreenshot(final String serialNumber) | |
{ | |
Display.getDefault().asyncExec(new Runnable() | |
{ | |
public void run() | |
{ | |
Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(); | |
ScreenShotDialog sshot = new ScreenShotDialog(new Shell(shell)); | |
sshot.open(DDMSFacade.getDeviceBySerialNumber(serialNumber)); | |
} | |
}); | |
} | |
public static void getApplicationDatabases(String serialNumber, String applicationName, | |
IDatabaseListingListener listener) | |
{ | |
LinkedList<String> pathSegments = new LinkedList<String>(); | |
pathSegments.add("data"); | |
pathSegments.add("data"); | |
pathSegments.add(applicationName); | |
pathSegments.add("databases"); | |
FileListingService fileListing = getFileListingService(serialNumber); | |
if (fileListing != null) | |
{ | |
FileEntry root = fileListing.getRoot(); | |
FileEntry[] children = null; | |
children = | |
fileListing.getChildren(root, true, new FileListingReceiver(pathSegments, | |
fileListing, listener)); | |
/* | |
* If children isn't null means that file listing service found the | |
* files in it cache to speed up the operation | |
*/ | |
if (children != null) | |
{ | |
List<String> databases = new ArrayList<String>(); | |
FileEntry temp1 = null, temp2 = root; | |
// start from root | |
while ((children != null)) | |
{ | |
// if we have something to search for | |
if (pathSegments.size() > 0) | |
{ | |
String theSegment = pathSegments.remove(0); | |
temp1 = temp2.findChild(theSegment); | |
if (temp1 != null) | |
{ | |
temp2 = temp1; | |
children = | |
fileListing.getChildren(temp2, true, new FileListingReceiver( | |
pathSegments, fileListing, listener)); | |
} | |
else | |
{ | |
children = null; | |
listener.databasesFound(databases); | |
} | |
} | |
// else just notify the listener | |
else | |
{ | |
if (children != null) | |
{ | |
for (FileEntry child : children) | |
{ | |
if (child.getName().endsWith(".db")) | |
{ | |
databases.add(child.getName()); | |
} | |
} | |
children = null; | |
} | |
listener.databasesFound(databases); | |
} | |
} | |
} | |
} | |
} | |
public static List<String> getApplicationDatabases(String serialNumber, String applicationName) | |
throws IOException | |
{ | |
List<String> dbs = new ArrayList<String>(); | |
String appDbPath = "/data/data/" + applicationName + "/databases/"; | |
Collection<String> commandOutput = | |
DDMSFacade | |
.execRemoteApp(serialNumber, "ls " + appDbPath, new NullProgressMonitor()); | |
List<String> dbPathCandidates = new ArrayList(commandOutput.size() + 10); | |
for (String commandOutline : commandOutput) | |
{ | |
String[] strings = commandOutline.split(" "); | |
for (String string : strings) | |
{ | |
if (string.trim().endsWith(".db")) | |
{ | |
dbPathCandidates.add(string); | |
} | |
} | |
} | |
return dbPathCandidates; | |
} | |
/** | |
* @param serialNumber | |
* @return | |
*/ | |
private static FileListingService getFileListingService(String serialNumber) | |
{ | |
FileListingService fileListing = null; | |
IDevice dev = DDMSFacade.getDeviceBySerialNumber(serialNumber); | |
if (dev != null) | |
{ | |
synchronized (dev) | |
{ | |
fileListing = deviceFileListingServiceMap.get(serialNumber); | |
} | |
if (fileListing == null) | |
{ | |
fileListing = dev.getFileListingService(); | |
synchronized (deviceFileListingServiceMap) | |
{ | |
deviceFileListingServiceMap.put(serialNumber, fileListing); | |
} | |
} | |
} | |
return fileListing; | |
} | |
/** | |
* This method returns the current language and country in use by given | |
* emulation instance. | |
* | |
* @param serialNumber The serial number of emulation instance | |
* @return An array of Strings containing the command results. | |
*/ | |
public static String[] getCurrentEmulatorLanguageAndCountry(final String serialNumber) | |
{ | |
ArrayList<String[]> commands = createCurrentEmulatorLanguageAndCountryCommand(serialNumber); | |
String[] responses = new String[2]; | |
String[] languageCommand = commands.get(0); | |
String[] countryCommand = commands.get(1); | |
String languageCommandResult = null; | |
String countryCommandResult = null; | |
try | |
{ | |
// Do not write the command output to the console | |
languageCommandResult = DDMSFacade.executeCommand(languageCommand, null); | |
countryCommandResult = DDMSFacade.executeCommand(countryCommand, null); | |
responses[0] = languageCommandResult.replaceAll("\\n$", ""); | |
responses[1] = countryCommandResult.replaceAll("\\n$", ""); | |
} | |
catch (IOException e) | |
{ | |
StudioLogger.error("Deploy: Could not execute adb current language command."); | |
} | |
return responses; | |
} | |
public static InstallPackageBean installPackageWizard() | |
{ | |
final InstallPackageBean bean = new InstallPackageBean(); | |
final Display display = PlatformUI.getWorkbench().getDisplay(); | |
display.syncExec(new Runnable() | |
{ | |
public void run() | |
{ | |
try | |
{ | |
String defaultPath = null; | |
DeployWizard wizard; | |
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); | |
if (window != null) | |
{ | |
ISelection selection = window.getSelectionService().getSelection(); | |
if (selection instanceof IStructuredSelection) | |
{ | |
IStructuredSelection workbenchSSelection = | |
(IStructuredSelection) selection; | |
for (Object o : workbenchSSelection.toList()) | |
{ | |
if (o instanceof IFile) | |
{ | |
IFile file = (IFile) o; | |
if (file.getFileExtension() | |
.equalsIgnoreCase(APK_FILE_EXTENSION)) | |
{ | |
defaultPath = file.getLocation().toOSString(); | |
} | |
} | |
} | |
} | |
} | |
wizard = new DeployWizard(defaultPath); | |
wizard.init(PlatformUI.getWorkbench(), new StructuredSelection()); | |
WizardDialog dialog = | |
new WizardDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow() | |
.getShell(), wizard); | |
dialog.setPageSize(500, 200); | |
if (dialog.open() == IDialogConstants.OK_ID) | |
{ | |
bean.setPackagePath(wizard.getPackagePath()); | |
bean.setCanOverwrite(wizard.canOverwrite()); | |
} | |
} | |
catch (Throwable e) | |
{ | |
StudioLogger.error(DDMSFacade.class, "Error executing deploy wizard", e); | |
} | |
} | |
}); | |
return bean; | |
} | |
public static IStatus installPackage(final String serialNumber, InstallPackageBean bean) | |
{ | |
IStatus status = Status.CANCEL_STATUS; | |
if ((bean.getPackagePath() != null) && (bean.getCanOverwrite() != null)) | |
{ | |
OutputStream consoleOut = null; | |
try | |
{ | |
consoleOut = EclipseUtils.getStudioConsoleOutputStream(true); | |
status = | |
installPackage(serialNumber, bean.getPackagePath(), bean.getCanOverwrite(), | |
consoleOut); | |
} | |
finally | |
{ | |
try | |
{ | |
if (consoleOut != null) | |
{ | |
consoleOut.close(); | |
} | |
} | |
catch (IOException e) | |
{ | |
StudioLogger.error("Install App: could not close console stream" | |
+ e.getMessage()); | |
} | |
} | |
} | |
if (status.isOK()) | |
{ | |
StudioAndroidEventManager.fireEvent(EventType.PACKAGE_INSTALLED, serialNumber); | |
} | |
return status; | |
} | |
/** | |
* Install an application on an emulator instance | |
* | |
* @param serialNumber | |
* The serial number of the device where the application will be | |
* installed | |
* @param path | |
* Path of the package containing the application to be installed | |
* @param installationType | |
* one of the {@link INSTALL_TYPE} install types available | |
* @param force | |
* Perform the operation without asking for user intervention | |
* | |
* @return the status of the operation (OK, Cancel or Error+ErrorMessage) | |
*/ | |
public static IStatus installPackage(String serialNumber, String path, | |
INSTALL_TYPE installationType, OutputStream processOut) | |
{ | |
IStatus status = null; | |
if (installationType.equals(INSTALL_TYPE.UNINSTALL)) | |
{ | |
status = uninstallPackage(new File(path), serialNumber, processOut); | |
} | |
boolean overwrite = installationType.equals(INSTALL_TYPE.OVERWRITE); | |
status = installPackage(serialNumber, path, overwrite, processOut); | |
return status; | |
} | |
/** | |
* Uninstall the given package (if installed) in the given serialNumber | |
* device | |
* | |
* @param path | |
* the package path | |
* @param serialNumber | |
* the device serial number | |
*/ | |
public static IStatus uninstallPackage(File path, String serialNumber, OutputStream processOut) | |
{ | |
IStatus returnStatus = null; | |
if ((path != null) && path.exists() && path.isFile()) | |
{ | |
IDevice dev = DDMSFacade.getDeviceBySerialNumber(serialNumber); | |
String apiLevel = dev.getProperty("ro.build.version.sdk"); | |
IAndroidTarget target = SdkUtils.getTargetByAPILevel(Integer.parseInt(apiLevel)); | |
String aaptPath = SdkUtils.getTargetAAPTPath(target); | |
if (aaptPath != null) | |
{ | |
// resolve package name | |
String[] aaptComm = new String[] | |
{ | |
aaptPath, "d", "badging", path.toString() | |
}; | |
InputStreamReader reader = null; | |
BufferedReader bufferedReader = null; | |
try | |
{ | |
Process aapt = Runtime.getRuntime().exec(aaptComm); | |
reader = new InputStreamReader(aapt.getInputStream()); | |
bufferedReader = new BufferedReader(reader); | |
String infoLine = bufferedReader.readLine(); | |
infoLine = infoLine.split(" ")[1].split("=")[1].replaceAll("'", ""); | |
returnStatus = uninstallPackage(serialNumber, infoLine, processOut); | |
} | |
catch (Exception e) | |
{ | |
returnStatus = | |
new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, | |
AndroidNLS.ERR_DDMSFacade_UninstallPackageException, e); | |
} | |
finally | |
{ | |
try | |
{ | |
if (reader != null) | |
{ | |
reader.close(); | |
} | |
if (bufferedReader != null) | |
{ | |
bufferedReader.close(); | |
} | |
} | |
catch (IOException e) | |
{ | |
StudioLogger.error("Uninstall app could not close stream. " | |
+ e.getMessage()); | |
} | |
} | |
} | |
else | |
{ | |
StudioLogger | |
.error(DDMSFacade.class, | |
"Impossible to check APK package name. No android targets found inside SDK"); | |
} | |
} | |
else | |
{ | |
returnStatus = | |
new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, | |
AndroidNLS.ERR_DDMSFacade_UninstallPackage); | |
} | |
return returnStatus; | |
} | |
/** | |
* Uninstall the given package within device with given serial number | |
* | |
* @param serialNumber | |
* @param packageName | |
* @param processOutput | |
* outputStream to write output of the process | |
*/ | |
public static IStatus uninstallPackage(String serialNumber, String packageName, | |
OutputStream processOutput) | |
{ | |
IStatus status = Status.OK_STATUS; | |
String command[] = createUninstallCommand(serialNumber, packageName); | |
try | |
{ | |
String commandResult = DDMSFacade.executeCommand(command, processOutput); | |
if (!commandResult.toLowerCase().contains(SUCCESS_CONSTANT.toLowerCase())) | |
{ | |
status = | |
new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, | |
AndroidNLS.ERR_DDMSFacade_UninstallPackageError + ": " | |
+ packageName); | |
} | |
} | |
catch (Exception e) | |
{ | |
status = | |
new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, | |
AndroidNLS.ERR_DDMSFacade_UninstallPackageException, e); | |
StudioLogger.error(DDMSFacade.class, "Failed to remove package: " + packageName + ". " | |
+ e.getMessage()); | |
} | |
return status; | |
} | |
/** | |
* Run the Monkey command for the given package within device with given serial number | |
* | |
* @param serialNumber | |
* @param packageName | |
* @param processOutput | |
* @param otherCmd | |
* @return the status of the monkey process | |
*/ | |
public static IStatus runMonkey(String serialNumber, String allPackages, | |
OutputStream processOutput, String otherCmd) | |
{ | |
IStatus status = Status.OK_STATUS; | |
String command[] = createMonkeyCommand(serialNumber, allPackages, otherCmd); | |
try | |
{ | |
DDMSFacade.executeCommand(command, processOutput); | |
} | |
catch (Exception e) | |
{ | |
EclipseUtils.showErrorDialog(AndroidNLS.UI_MonkeyError_Title, | |
AndroidNLS.UI_MonkeyError_Msg); | |
StudioLogger.error(DDMSFacade.class, "Failed to run monkey command: " + command + " " | |
+ e.getMessage()); | |
} | |
return status; | |
} | |
/** | |
* Uninstall packages from the given serialNumber device | |
* | |
* @param serialNumber | |
* @param packagesToUninstall | |
* @param outputStream | |
* @return the status of the uninstall process or null if no packages were | |
* uninstalled | |
*/ | |
private static IStatus uninstallPackages(String serialNumber, | |
ArrayList<String> packagesToUninstall, OutputStream outputStream) | |
{ | |
IStatus returnStatus = null; | |
for (String packageToUninstall : packagesToUninstall) | |
{ | |
StudioLogger.info(DDMSUtils.class, "Removing package: " + packageToUninstall); | |
IStatus temp = uninstallPackage(serialNumber, packageToUninstall, outputStream); | |
if (!temp.isOK()) | |
{ | |
if (returnStatus == null) | |
{ | |
returnStatus = | |
new MultiStatus(AndroidPlugin.PLUGIN_ID, 0, | |
AndroidNLS.ERR_DDMSFacade_UninstallPackageError, null); | |
} | |
((MultiStatus) returnStatus).add(temp); | |
} | |
} | |
return returnStatus == null ? Status.OK_STATUS : returnStatus; | |
} | |
/** | |
* Run monkey command on the given packages with the specified commands. | |
* | |
* @param serialNumber | |
* @param packagesToRunMonkey | |
* @param outputStream | |
* @param otherCmds | |
* @return the status of the monkey process or null if the process was not successful | |
*/ | |
private static IStatus runMonkey(String serialNumber, ArrayList<String> packagesToRunMonkey, | |
OutputStream outputStream, String otherCmds) | |
{ | |
IStatus returnStatus = null; | |
String allPackages = ""; | |
for (String packageToRunMonkey : packagesToRunMonkey) | |
{ | |
allPackages += MONKEY_PACKAGES_DIRECTIVE + packageToRunMonkey; | |
} | |
StudioLogger.info(DDMSUtils.class, "Running monkey for: " + allPackages); | |
IStatus temp = runMonkey(serialNumber, allPackages, outputStream, otherCmds); | |
if (!temp.isOK()) | |
{ | |
if (returnStatus == null) | |
{ | |
returnStatus = | |
new MultiStatus(AndroidPlugin.PLUGIN_ID, 0, | |
AndroidNLS.ERR_DDMSFacade_MonkeyError, null); | |
} | |
((MultiStatus) returnStatus).add(temp); | |
} | |
return returnStatus == null ? Status.OK_STATUS : returnStatus; | |
} | |
/** | |
* Uninstall packages from the given device. A Wizard will be opened asking | |
* the user which packages to uninstall | |
* | |
* @param serialNumber | |
* @return the status of the operation | |
*/ | |
public static IStatus uninstallPackage(final String serialNumber) | |
{ | |
final ArrayList<String> packagesToUninstall = new ArrayList<String>(); | |
IStatus status = null; | |
//wrap the dialog within a final variable | |
final UninstallAppWizard[] wizard = new UninstallAppWizard[1]; | |
// do package listing async | |
Thread listingThread = new Thread("listPackages") | |
{ | |
@Override | |
public void run() | |
{ | |
Map<String, String> installedPackages = null; | |
try | |
{ | |
installedPackages = listInstalledPackages(serialNumber); | |
} | |
catch (IOException e1) | |
{ | |
installedPackages = new HashMap<String, String>(0); | |
} | |
while (wizard[0] == null) | |
{ | |
try | |
{ | |
sleep(100); | |
} | |
catch (InterruptedException e) | |
{ | |
//do nothing | |
} | |
} | |
wizard[0].setAvailablePackages(installedPackages); | |
}; | |
}; | |
listingThread.start(); | |
PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() | |
{ | |
public void run() | |
{ | |
UninstallAppWizard unAppWiz = new UninstallAppWizard(); | |
WizardDialog dialog = | |
new WizardDialog(new Shell(PlatformUI.getWorkbench() | |
.getActiveWorkbenchWindow().getShell()), unAppWiz); | |
wizard[0] = unAppWiz; | |
dialog.open(); | |
List<String> selectedPackages = wizard[0].getSelectedPackages(); | |
if (selectedPackages != null) | |
{ | |
for (String aPackage : selectedPackages) | |
{ | |
packagesToUninstall.add(aPackage); | |
} | |
} | |
} | |
}); | |
if (packagesToUninstall.size() > 0) | |
{ | |
OutputStream consoleOut = null; | |
try | |
{ | |
consoleOut = EclipseUtils.getStudioConsoleOutputStream(true); | |
status = uninstallPackages(serialNumber, packagesToUninstall, consoleOut); | |
} | |
finally | |
{ | |
try | |
{ | |
if (consoleOut != null) | |
{ | |
consoleOut.close(); | |
} | |
} | |
catch (IOException e) | |
{ | |
StudioLogger.error("Uninstall App: could not close console stream" | |
+ e.getMessage()); | |
} | |
} | |
} | |
if (status != null) | |
{ | |
if (status.isOK()) | |
{ | |
Display.getDefault().asyncExec(new Runnable() | |
{ | |
public void run() | |
{ | |
MessageDialog.openInformation(PlatformUI.getWorkbench() | |
.getActiveWorkbenchWindow().getShell(), | |
AndroidNLS.UI_UninstallApp_SucessDialogTitle, | |
AndroidNLS.UI_UninstallApp_Message); | |
} | |
}); | |
StudioAndroidEventManager.fireEvent(EventType.PACKAGE_UNINSTALLED, serialNumber); | |
} | |
else | |
{ | |
EclipseUtils.showErrorDialog(AndroidNLS.UI_UninstallApp_ERRDialogTitle, | |
AndroidNLS.UI_UninstallApp_ERRUninstallApp, status); | |
} | |
} | |
// all return is successful since operations are async | |
return Status.OK_STATUS; | |
} | |
/** | |
* If there is no Monkey Launch configuration for the given deviceName, it is created. | |
* @param deviceName | |
*/ | |
private static ILaunchConfiguration createLaunchConfiguration(String deviceName) | |
{ | |
ILaunchConfiguration config = null; | |
ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager(); | |
ILaunchConfigurationType motodevLaunchType = | |
launchManager | |
.getLaunchConfigurationType(IMonkeyConfigurationConstants.LAUNCH_CONFIGURATION_TYPE_EXTENSION_ID); | |
String launchConfigurationName = | |
launchManager | |
.generateUniqueLaunchConfigurationNameFrom(IMonkeyConfigurationConstants.NEW_CONFIGURATION_NAME); | |
try | |
{ | |
ILaunchConfigurationWorkingCopy workingCopy = | |
motodevLaunchType.newInstance(null, launchConfigurationName); | |
workingCopy.setAttribute(IMonkeyConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME, | |
deviceName); | |
config = workingCopy.doSave(); | |
} | |
catch (CoreException e) | |
{ | |
EclipseUtils.showErrorDialog(AndroidNLS.UI_MonkeyError_Title, e.getMessage()); | |
} | |
return config; | |
} | |
/** | |
* Run adb monkey. | |
* | |
* @param serialNumber | |
* @param deviceName | |
* @return the status of the operation | |
*/ | |
public static IStatus runMonkey(final String serialNumber, final String deviceName) | |
{ | |
ILaunchConfigurationType type = | |
DebugPlugin | |
.getDefault() | |
.getLaunchManager() | |
.getLaunchConfigurationType( | |
IMonkeyConfigurationConstants.LAUNCH_CONFIGURATION_TYPE_EXTENSION_ID); | |
ILaunchConfiguration[] launchs; | |
try | |
{ | |
launchs = DebugPlugin.getDefault().getLaunchManager().getLaunchConfigurations(type); | |
ILaunchConfiguration launchConfig = null; | |
for (int i = 0; i < launchs.length; i++) | |
{ | |
launchConfig = launchs[i]; | |
String configDeviceName = | |
launchConfig.getAttribute( | |
IMonkeyConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME, ""); | |
if (configDeviceName.equals(deviceName)) | |
{ | |
break; | |
} | |
else | |
{ | |
launchConfig = null; | |
} | |
} | |
if (launchConfig == null) | |
{ | |
launchConfig = createLaunchConfiguration(deviceName); | |
} | |
final ILaunchGroup lc = | |
DebugUITools.getLaunchGroup(launchConfig, ILaunchManager.RUN_MODE); | |
final IStructuredSelection selection = new StructuredSelection(launchConfig); | |
PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() | |
{ | |
public void run() | |
{ | |
DebugUITools.openLaunchConfigurationDialogOnGroup(new Shell(PlatformUI | |
.getWorkbench().getActiveWorkbenchWindow().getShell()), selection, | |
lc.getIdentifier(), null); | |
} | |
}); | |
} | |
catch (CoreException e) | |
{ | |
StudioLogger.error("Monkey: could not open the launch configuration dialog " | |
+ e.getMessage()); | |
} | |
// UDC log for monkey execution | |
StudioLogger.collectUsageData(UsageDataConstants.WHAT_MONKEY_EXEC, //$NON-NLS-1$ | |
UsageDataConstants.KIND_MONKEY_EXEC, "Monkey executed", //$NON-NLS-1$ | |
AndroidPlugin.PLUGIN_ID, AndroidPlugin.getDefault().getBundle().getBundleContext() | |
.getBundle().getVersion().toString()); | |
// all return is successful since operations are async | |
return Status.OK_STATUS; | |
} | |
/** | |
* Run adb monkey. | |
* | |
* @param serialNumber | |
* @param packagesToRunMonkey | |
* @param otherCmds | |
* @return the status of the operation | |
*/ | |
public static IStatus runMonkey(final String serialNumber, | |
ArrayList<String> packagesToRunMonkey, String otherCmds) | |
{ | |
if (packagesToRunMonkey.size() > 0) | |
{ | |
OutputStream consoleOut = null; | |
try | |
{ | |
consoleOut = EclipseUtils.getStudioConsoleOutputStream(true); | |
runMonkey(serialNumber, packagesToRunMonkey, consoleOut, otherCmds); | |
} | |
finally | |
{ | |
try | |
{ | |
if (consoleOut != null) | |
{ | |
consoleOut.close(); | |
} | |
} | |
catch (IOException e) | |
{ | |
StudioLogger.error("Monkey: could not close console stream" + e.getMessage()); | |
} | |
} | |
} | |
// all return is successful since operations are async | |
return Status.OK_STATUS; | |
} | |
/** | |
* List the installed packages in the device with the serial number Each | |
* package entry carries their package location | |
* | |
* @param serialNumber | |
* @return a HashMap where keys are the package names and values are package | |
* location | |
* @throws IOException | |
*/ | |
public static Map<String, String> listInstalledPackages(String serialNumber) throws IOException | |
{ | |
Map<String, String> packages = new LinkedHashMap<String, String>(); | |
String sdkPath = SdkUtils.getSdkPath(); | |
String command[] = | |
new String[] | |
{ | |
sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator | |
+ DDMSFacade.ADB_COMMAND, DDMSFacade.ADB_INSTANCE_PARAMETER, | |
serialNumber, DDMSFacade.SHELL_CMD, PM_CMD, PM_LIST_DIRECTIVE, | |
PM_PACKAGES_DIRECTIVE, PM_PACKAGES_DIRECTIVE_FORCE | |
}; | |
String commResult = DDMSFacade.executeCommand(command, null); | |
String[] packageList = null; | |
if ((commResult != null) && (commResult.length() > 0) | |
&& !commResult.contains("system running?")) | |
{ | |
packageList = commResult.trim().replaceAll("\n\n", "\n").split("\n"); | |
int i = 0; | |
String aPackage = null; | |
String[] packageUnit = null; | |
while (i < packageList.length) | |
{ | |
if (packageList[i].toLowerCase().contains("package:")) | |
{ | |
String[] splittedPackage = packageList[i].split(":"); | |
if (splittedPackage.length >= 2) | |
{ | |
aPackage = splittedPackage[1].trim(); | |
packageUnit = aPackage.split("="); | |
if (packageUnit.length > 1) | |
{ | |
packages.put(packageUnit[1], packageUnit[0]); | |
} | |
} | |
} | |
i++; | |
} | |
} | |
return packages; | |
} | |
/** | |
* Install an application on an emulator instance | |
* | |
* @param serialNumber | |
* The serial number of the device where the application will be | |
* installed | |
* @param path | |
* Path of the package containing the application to be installed | |
* @param canOverwrite | |
* If the application will be installed even if there is a | |
* previous installation | |
* @param force | |
* Perform the operation without asking for user intervention | |
* | |
* @return the status of the operation (OK, Cancel or Error+ErrorMessage) | |
*/ | |
public static IStatus installPackage(String serialNumber, String path, boolean canOverwrite, | |
OutputStream processOut) | |
{ | |
IStatus status = Status.OK_STATUS; | |
// Return if no instance is selected | |
if (serialNumber == null) | |
{ | |
StudioLogger.error("Abort deploy operation. Serial number is null."); | |
status = | |
new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, | |
AndroidNLS.ERR_DDMSFacade_SerialNumberNullPointer); | |
} | |
// Return if instance is not started | |
if (status.isOK() && !DDMSFacade.isDeviceOnline(serialNumber)) | |
{ | |
StudioLogger.error("Abort deploy operation. Device is not online."); | |
status = new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, ""); | |
} | |
String command_results = ""; | |
if (status.isOK()) | |
{ | |
try | |
{ | |
String[] cmd = createInstallCommand(canOverwrite, path, serialNumber); | |
command_results = DDMSFacade.executeCommand(cmd, processOut, serialNumber); | |
// Check if the result has a success message | |
if (!command_results.contains(SUCCESS_CONSTANT)) | |
{ | |
status = | |
new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, | |
"Error executing the operation. Execution results: " | |
+ command_results); | |
} | |
} | |
catch (IOException e) | |
{ | |
StudioLogger.error("Deploy: Could not execute adb install command."); | |
status = new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, e.getMessage()); | |
} | |
} | |
return status; | |
} | |
/** | |
* Change the emulator language | |
* | |
* @param serialNumber | |
* The serial number of the device | |
* @param language | |
* the language id | |
* @param country | |
* the country id | |
* @return the status of the operation (OK, Error+ErrorMessage) | |
*/ | |
public static void changeLanguage(final String serialNumber, final String language, | |
final String country) | |
{ | |
if ((language != null) || (country != null)) | |
{ | |
/* | |
* A new thread is used since this command takes too long to be executed | |
*/ | |
Thread thead = new Thread(new Runnable() | |
{ | |
public void run() | |
{ | |
String[] cmd = createChangeLanguageCommand(serialNumber, language, country); | |
try | |
{ | |
IOConsoleOutputStream consoleOut = | |
EclipseUtils.getStudioConsoleOutputStream(true); | |
if (language != null) | |
{ | |
consoleOut.write(AndroidNLS.UI_ChangeLang_Language + " " + language | |
+ "\n"); | |
} | |
if (country != null) | |
{ | |
consoleOut.write(AndroidNLS.UI_ChangeLang_Country + " " + country | |
+ "\n"); | |
} | |
DDMSFacade.executeCommand(cmd, consoleOut); | |
consoleOut.write("\n " + serialNumber + ":" | |
+ AndroidNLS.UI_ChangeLang_Restart_Device_Manually + "\n\n"); | |
StudioAndroidEventManager.fireEvent(EventType.LANGUAGE_CHANGED, | |
serialNumber); | |
} | |
catch (IOException e) | |
{ | |
StudioLogger | |
.error("Language: Could not execute adb change language command."); | |
} | |
} | |
}); | |
thead.start(); | |
} | |
} | |
/** | |
* Creates a string with the command that should be called in order to | |
* change the device language | |
* | |
* @param serialNumber | |
* The serial number of the device | |
* @param language | |
* the language id | |
* @param country | |
* the country id | |
* @return the command to be used to change the device language | |
*/ | |
private static String[] createChangeLanguageCommand(String serialNumber, String language, | |
String country) | |
{ | |
String cmd[]; | |
String sdkPath = SdkUtils.getSdkPath(); | |
String CHANGE_LANGUAGE_CMD = ""; | |
if (language != null) | |
{ | |
CHANGE_LANGUAGE_CMD += "setprop persist.sys.language " + language; | |
} | |
if (country != null) | |
{ | |
CHANGE_LANGUAGE_CMD += | |
((CHANGE_LANGUAGE_CMD.length() > 0) ? ";" : "") | |
+ "setprop persist.sys.country " + country; | |
} | |
// The tools folder should exist and be here, but double-checking | |
// once more wont kill | |
File f = new File(sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator); | |
if (!f.exists()) | |
{ | |
StudioLogger.error("Language: Could not find tools folder on " + sdkPath | |
+ DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator); | |
} | |
else | |
{ | |
if (!f.isDirectory()) | |
{ | |
StudioLogger.error("Language: Invalid tools folder " + sdkPath | |
+ DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator); | |
} | |
} | |
String cmdTemp[] = | |
{ | |
sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator | |
+ DDMSFacade.ADB_COMMAND, DDMSFacade.ADB_INSTANCE_PARAMETER, | |
serialNumber, "shell", CHANGE_LANGUAGE_CMD | |
}; | |
cmd = cmdTemp; | |
return cmd; | |
} | |
/** | |
* Creates strings with the command that should be called in order to | |
* retrieve the current language and country in use by given emulator instance. | |
* | |
* @param serialNumber The serial number of the device | |
* @return An ArrayList with command strings to be used to get the | |
* current emulator language and country. | |
*/ | |
private static ArrayList<String[]> createCurrentEmulatorLanguageAndCountryCommand( | |
String serialNumber) | |
{ | |
String languageCommand[]; | |
String countryCommand[]; | |
String sdkPath = SdkUtils.getSdkPath(); | |
String GET_LANGUAGE_CMD = "getprop persist.sys.language"; | |
String GET_COUNTRY_CMD = "getprop persist.sys.country"; | |
// The tools folder should exist and be here, but double-cheking | |
// once more wont kill | |
File f = new File(sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator); | |
if (!f.exists()) | |
{ | |
StudioLogger.error("Language: Could not find tools folder on " + sdkPath | |
+ DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator); | |
} | |
else | |
{ | |
if (!f.isDirectory()) | |
{ | |
StudioLogger.error("Language: Invalid tools folder " + sdkPath | |
+ DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator); | |
} | |
} | |
String langCmdTemp[] = | |
{ | |
sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator | |
+ DDMSFacade.ADB_COMMAND, DDMSFacade.ADB_INSTANCE_PARAMETER, | |
serialNumber, "shell", GET_LANGUAGE_CMD | |
}; | |
String countryCmdTemp[] = | |
{ | |
sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator | |
+ DDMSFacade.ADB_COMMAND, DDMSFacade.ADB_INSTANCE_PARAMETER, | |
serialNumber, "shell", GET_COUNTRY_CMD | |
}; | |
languageCommand = langCmdTemp; | |
countryCommand = countryCmdTemp; | |
ArrayList<String[]> commands = new ArrayList<String[]>(); | |
commands.add(0, languageCommand); | |
commands.add(1, countryCommand); | |
return commands; | |
} | |
/** | |
* Creates a string with the command that should be called in order to | |
* install the application | |
* | |
* @param canOverwrite | |
* If true, than existent application will be overwritten | |
* @param path | |
* Location of the package containing the application | |
* @param serialNumber | |
* The serial number of the device to be targeted | |
* | |
* @return | |
*/ | |
private static String[] createInstallCommand(boolean canOverwrite, String path, | |
String serialNumber) | |
{ | |
String cmd[]; | |
String sdkPath = SdkUtils.getSdkPath(); | |
// The tools folder should exist and be here, but double-checking | |
// once more wont kill | |
File f = new File(sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator); | |
if (!f.exists()) | |
{ | |
StudioLogger.error("Deploy: Could not find tools folder on " + sdkPath | |
+ DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator); | |
} | |
else | |
{ | |
if (!f.isDirectory()) | |
{ | |
StudioLogger.error("Deploy: Invalid tools folder " + sdkPath | |
+ DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator); | |
} | |
} | |
if (canOverwrite) | |
{ | |
// If overwrite option is checked, create command with the -r | |
// paramater | |
String cmdTemp[] = | |
{ | |
sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator | |
+ DDMSFacade.ADB_COMMAND, DDMSFacade.ADB_INSTANCE_PARAMETER, | |
serialNumber, INSTALL_CMD, ADB_INSTALL_OVERWRITE, path | |
}; | |
cmd = cmdTemp; | |
} | |
else | |
{ | |
// If overwrite option is unchecked, create command without the -r | |
// paramater | |
String cmdTemp[] = | |
{ | |
sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator | |
+ DDMSFacade.ADB_COMMAND, DDMSFacade.ADB_INSTANCE_PARAMETER, | |
serialNumber, INSTALL_CMD, path | |
}; | |
cmd = cmdTemp; | |
} | |
return cmd; | |
} | |
private static String[] createUninstallCommand(String serialNumber, String packageName) | |
{ | |
String sdkPath = SdkUtils.getSdkPath(); | |
// The tools folder should exist and be here, but double-checking | |
// once more wont kill | |
File f = new File(sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator); | |
if (!f.exists()) | |
{ | |
StudioLogger.error("Run: Could not find tools folder on " + sdkPath | |
+ DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator); | |
} | |
else | |
{ | |
if (!f.isDirectory()) | |
{ | |
StudioLogger.error("Run: Invalid tools folder " + sdkPath | |
+ DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator); | |
} | |
} | |
String cmd[] = | |
{ | |
sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator | |
+ DDMSFacade.ADB_COMMAND, DDMSFacade.ADB_INSTANCE_PARAMETER, | |
serialNumber, DDMSFacade.SHELL_CMD, PM_CMD, PM_UNINSTALL_DIRECTIVE, | |
packageName | |
}; | |
return cmd; | |
} | |
/** | |
* Mount the adb monkey command based on the given parameters. | |
* @param serialNumber | |
* @param packagesName | |
* @param otherCmd | |
* @return the array of strings containing the monkey command to be executed. | |
*/ | |
private static String[] createMonkeyCommand(String serialNumber, String packagesName, | |
String otherCmd) | |
{ | |
String sdkPath = SdkUtils.getSdkPath(); | |
// The tools folder should exist and be here, but double-checking | |
// once more wont kill | |
File f = new File(sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator); | |
if (!f.exists()) | |
{ | |
StudioLogger.error("Run: Could not find tools folder on " + sdkPath | |
+ DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator); | |
} | |
else | |
{ | |
if (!f.isDirectory()) | |
{ | |
StudioLogger.error("Run: Invalid tools folder " + sdkPath | |
+ DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator); | |
} | |
} | |
String cmd[] = | |
{ | |
sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator | |
+ DDMSFacade.ADB_COMMAND, DDMSFacade.ADB_INSTANCE_PARAMETER, | |
serialNumber, DDMSFacade.SHELL_CMD, MONKEY_CMD, packagesName, otherCmd | |
}; | |
return cmd; | |
} | |
/** | |
* Dump HPROF service implementation | |
* @param serialNumber The device serial number | |
* @param monitor | |
* @return A IStatus describing if the service was successful or not | |
*/ | |
public static IStatus dumpHPROF(final String serialNumber, IProgressMonitor monitor) | |
{ | |
IStatus status; | |
// Selected app. The array should have only 1 element | |
final String[] selectedAppSet = new String[1]; | |
// Instantiate the wizard and populate it with the retrieved process list | |
PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() | |
{ | |
public void run() | |
{ | |
DumpHPROFWizard dumpHPROFWizard = new DumpHPROFWizard(serialNumber); | |
WizardDialog dialog = | |
new WizardDialog(new Shell(PlatformUI.getWorkbench() | |
.getActiveWorkbenchWindow().getShell()), dumpHPROFWizard); | |
dialog.open(); | |
// Get the selected application | |
selectedAppSet[0] = dumpHPROFWizard.getSelectedApp(); | |
} | |
}); | |
if (selectedAppSet[0] != null) | |
{ | |
// Dump HPROF file based on the selected application | |
status = DDMSFacade.dumpHprofFile(selectedAppSet[0], serialNumber, monitor); | |
} | |
else | |
{ | |
status = Status.CANCEL_STATUS; | |
} | |
return status; | |
} | |
public static int getDeviceApiVersion(String serialNumber) | |
{ | |
int deviceSdkVersion = -1; | |
String deviceProperty = DDMSFacade.getDeviceProperty(serialNumber, "ro.build.version.sdk"); | |
if (deviceProperty != null) | |
{ | |
deviceSdkVersion = Integer.parseInt(deviceProperty); | |
} | |
return deviceSdkVersion; | |
} | |
public static boolean remoteFileExists(String serialNumber, String remotePath) | |
throws IOException | |
{ | |
boolean found = false; | |
Collection<String> results = | |
DDMSFacade.execRemoteApp(serialNumber, | |
"ls " + FileUtil.getEscapedPath(remotePath, Platform.OS_LINUX), | |
new NullProgressMonitor()); | |
for (String result : results) | |
{ | |
if (result.equals(remotePath)) | |
{ | |
found = true; | |
break; | |
} | |
} | |
return found; | |
} | |
} |