1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.motorola.studio.android.adt; 18 19 import java.io.BufferedReader; 20 import java.io.File; 21 import java.io.IOException; 22 import java.io.InputStreamReader; 23 import java.io.OutputStream; 24 import java.util.ArrayList; 25 import java.util.Collection; 26 import java.util.HashMap; 27 import java.util.LinkedHashMap; 28 import java.util.LinkedList; 29 import java.util.List; 30 import java.util.Map; 31 32 import org.eclipse.core.resources.IFile; 33 import org.eclipse.core.runtime.CoreException; 34 import org.eclipse.core.runtime.IProgressMonitor; 35 import org.eclipse.core.runtime.IStatus; 36 import org.eclipse.core.runtime.MultiStatus; 37 import org.eclipse.core.runtime.NullProgressMonitor; 38 import org.eclipse.core.runtime.Platform; 39 import org.eclipse.core.runtime.Status; 40 import org.eclipse.debug.core.DebugPlugin; 41 import org.eclipse.debug.core.ILaunchConfiguration; 42 import org.eclipse.debug.core.ILaunchConfigurationType; 43 import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; 44 import org.eclipse.debug.core.ILaunchManager; 45 import org.eclipse.debug.ui.DebugUITools; 46 import org.eclipse.debug.ui.ILaunchGroup; 47 import org.eclipse.jface.dialogs.IDialogConstants; 48 import org.eclipse.jface.dialogs.MessageDialog; 49 import org.eclipse.jface.viewers.ISelection; 50 import org.eclipse.jface.viewers.IStructuredSelection; 51 import org.eclipse.jface.viewers.StructuredSelection; 52 import org.eclipse.jface.wizard.WizardDialog; 53 import org.eclipse.swt.widgets.Display; 54 import org.eclipse.swt.widgets.Shell; 55 import org.eclipse.ui.IWorkbenchWindow; 56 import org.eclipse.ui.PlatformUI; 57 import org.eclipse.ui.console.IOConsoleOutputStream; 58 59 import com.android.ddmlib.FileListingService; 60 import com.android.ddmlib.FileListingService.FileEntry; 61 import com.android.ddmlib.IDevice; 62 import com.android.ddmuilib.ScreenShotDialog; 63 import com.android.sdklib.IAndroidTarget; 64 import com.motorola.studio.android.AndroidPlugin; 65 import com.motorola.studio.android.adt.StudioAndroidEventManager.EventType; 66 import com.motorola.studio.android.common.log.StudioLogger; 67 import com.motorola.studio.android.common.log.UsageDataConstants; 68 import com.motorola.studio.android.common.utilities.EclipseUtils; 69 import com.motorola.studio.android.common.utilities.FileUtil; 70 import com.motorola.studio.android.i18n.AndroidNLS; 71 import com.motorola.studio.android.wizards.installapp.DeployWizard; 72 import com.motorola.studio.android.wizards.installapp.DeployWizard.INSTALL_TYPE; 73 import com.motorola.studio.android.wizards.installapp.UninstallAppWizard; 74 import com.motorola.studio.android.wizards.mat.DumpHPROFWizard; 75 import com.motorola.studio.android.wizards.monkey.IMonkeyConfigurationConstants; 76 77 public class DDMSUtils 78 { 79 private static final Map<String, FileListingService> deviceFileListingServiceMap = 80 new HashMap<String, FileListingService>(); 81 82 /** 83 * The APK extension 84 */ 85 private static final String APK_FILE_EXTENSION = "apk"; 86 87 /** 88 * Parameter for overwriting existing applications, if any 89 */ 90 private static final String ADB_INSTALL_OVERWRITE = "-r"; 91 92 /** 93 * Options to be used with adb to indicate package manager application 94 */ 95 private static final String PM_CMD = "pm"; 96 97 /** 98 * Options to be used with adb to run monkey application 99 */ 100 private static final String MONKEY_CMD = "monkey"; 101 102 /** 103 * Uninstall directive to the package manager 104 */ 105 private static final String PM_UNINSTALL_DIRECTIVE = "uninstall"; 106 107 /** 108 * List packages directive 109 */ 110 private static final String PM_LIST_DIRECTIVE = "list"; 111 112 /** 113 * List packages directive 114 */ 115 private static final String PM_PACKAGES_DIRECTIVE = "packages"; 116 117 /** 118 * List packages force directive 119 */ 120 private static final String PM_PACKAGES_DIRECTIVE_FORCE = "-f"; 121 122 /** 123 * Monkey packages directive 124 */ 125 private static final String MONKEY_PACKAGES_DIRECTIVE = " -p "; 126 127 /** 128 * Options to be used with adb to indicate install operation 129 */ 130 private static final String INSTALL_CMD = "install"; 131 132 /** 133 * This word must exist in the adb install commmand answer to indicate 134 * succefull installation 135 */ 136 private static final String SUCCESS_CONSTANT = "Success"; 137 138 private static final DdmsRunnable disconnectedListener = new DdmsRunnable() 139 { 140 public void run(String serialNumber) 141 { 142 synchronized (deviceFileListingServiceMap) 143 { 144 deviceFileListingServiceMap.remove(serialNumber); 145 } 146 } 147 }; 148 149 static 150 { 151 StudioAndroidEventManager.asyncAddDeviceChangeListeners(null, disconnectedListener); 152 } 153 154 public static void takeScreenshot(final String serialNumber) 155 { 156 Display.getDefault().asyncExec(new Runnable() 157 { 158 159 public void run() 160 { 161 Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(); 162 ScreenShotDialog sshot = new ScreenShotDialog(new Shell(shell)); 163 sshot.open(DDMSFacade.getDeviceBySerialNumber(serialNumber)); 164 } 165 }); 166 } 167 168 public static void getApplicationDatabases(String serialNumber, String applicationName, 169 IDatabaseListingListener listener) 170 { 171 LinkedList<String> pathSegments = new LinkedList<String>(); 172 pathSegments.add("data"); 173 pathSegments.add("data"); 174 pathSegments.add(applicationName); 175 pathSegments.add("databases"); 176 177 FileListingService fileListing = getFileListingService(serialNumber); 178 179 if (fileListing != null) 180 { 181 FileEntry root = fileListing.getRoot(); 182 FileEntry[] children = null; 183 184 children = 185 fileListing.getChildren(root, true, new FileListingReceiver(pathSegments, 186 fileListing, listener)); 187 /* 188 * If children isn't null means that file listing service found the 189 * files in it cache to speed up the operation 190 */ 191 if (children != null) 192 { 193 List<String> databases = new ArrayList<String>(); 194 FileEntry temp1 = null, temp2 = root; 195 // start from root 196 while ((children != null)) 197 { 198 // if we have something to search for 199 if (pathSegments.size() > 0) 200 { 201 String theSegment = pathSegments.remove(0); 202 temp1 = temp2.findChild(theSegment); 203 204 if (temp1 != null) 205 { 206 temp2 = temp1; 207 children = 208 fileListing.getChildren(temp2, true, new FileListingReceiver( 209 pathSegments, fileListing, listener)); 210 } 211 else 212 { 213 children = null; 214 listener.databasesFound(databases); 215 } 216 } 217 // else just notify the listener 218 else 219 { 220 if (children != null) 221 { 222 for (FileEntry child : children) 223 { 224 if (child.getName().endsWith(".db")) 225 { 226 databases.add(child.getName()); 227 } 228 } 229 children = null; 230 } 231 listener.databasesFound(databases); 232 } 233 } 234 } 235 } 236 } 237 238 public static List<String> getApplicationDatabases(String serialNumber, String applicationName) 239 throws IOException 240 { 241 List<String> dbs = new ArrayList<String>(); 242 243 String appDbPath = "/data/data/" + applicationName + "/databases/"; 244 245 Collection<String> commandOutput = 246 DDMSFacade 247 .execRemoteApp(serialNumber, "ls " + appDbPath, new NullProgressMonitor()); 248 List<String> dbPathCandidates = new ArrayList(commandOutput.size() + 10); 249 250 for (String commandOutline : commandOutput) 251 { 252 String[] strings = commandOutline.split(" "); 253 for (String string : strings) 254 { 255 if (string.trim().endsWith(".db")) 256 { 257 dbPathCandidates.add(string); 258 } 259 } 260 } 261 262 return dbPathCandidates; 263 } 264 265 /** 266 * @param serialNumber 267 * @return 268 */ 269 private static FileListingService getFileListingService(String serialNumber) 270 { 271 FileListingService fileListing = null; 272 IDevice dev = DDMSFacade.getDeviceBySerialNumber(serialNumber); 273 if (dev != null) 274 { 275 synchronized (dev) 276 { 277 fileListing = deviceFileListingServiceMap.get(serialNumber); 278 } 279 if (fileListing == null) 280 { 281 fileListing = dev.getFileListingService(); 282 synchronized (deviceFileListingServiceMap) 283 { 284 deviceFileListingServiceMap.put(serialNumber, fileListing); 285 } 286 287 } 288 } 289 return fileListing; 290 } 291 292 /** 293 * This method returns the current language and country in use by given 294 * emulation instance. 295 * 296 * @param serialNumber The serial number of emulation instance 297 * @return An array of Strings containing the command results. 298 */ 299 public static String[] getCurrentEmulatorLanguageAndCountry(final String serialNumber) 300 { 301 ArrayList<String[]> commands = createCurrentEmulatorLanguageAndCountryCommand(serialNumber); 302 String[] responses = new String[2]; 303 String[] languageCommand = commands.get(0); 304 String[] countryCommand = commands.get(1); 305 String languageCommandResult = null; 306 String countryCommandResult = null; 307 308 try 309 { 310 // Do not write the command output to the console 311 languageCommandResult = DDMSFacade.executeCommand(languageCommand, null); 312 countryCommandResult = DDMSFacade.executeCommand(countryCommand, null); 313 responses[0] = languageCommandResult.replaceAll("\\n$", ""); 314 responses[1] = countryCommandResult.replaceAll("\\n$", ""); 315 } 316 catch (IOException e) 317 { 318 StudioLogger.error("Deploy: Could not execute adb current language command."); 319 } 320 return responses; 321 } 322 323 public static InstallPackageBean installPackageWizard() 324 { 325 326 final InstallPackageBean bean = new InstallPackageBean(); 327 328 final Display display = PlatformUI.getWorkbench().getDisplay(); 329 display.syncExec(new Runnable() 330 { 331 public void run() 332 { 333 try 334 { 335 String defaultPath = null; 336 DeployWizard wizard; 337 IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); 338 if (window != null) 339 { 340 ISelection selection = window.getSelectionService().getSelection(); 341 if (selection instanceof IStructuredSelection) 342 { 343 IStructuredSelection workbenchSSelection = 344 (IStructuredSelection) selection; 345 for (Object o : workbenchSSelection.toList()) 346 { 347 if (o instanceof IFile) 348 { 349 IFile file = (IFile) o; 350 if (file.getFileExtension() 351 .equalsIgnoreCase(APK_FILE_EXTENSION)) 352 { 353 defaultPath = file.getLocation().toOSString(); 354 } 355 } 356 } 357 } 358 } 359 wizard = new DeployWizard(defaultPath); 360 wizard.init(PlatformUI.getWorkbench(), new StructuredSelection()); 361 WizardDialog dialog = 362 new WizardDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow() 363 .getShell(), wizard); 364 dialog.setPageSize(500, 200); 365 if (dialog.open() == IDialogConstants.OK_ID) 366 { 367 bean.setPackagePath(wizard.getPackagePath()); 368 bean.setCanOverwrite(wizard.canOverwrite()); 369 } 370 } 371 catch (Throwable e) 372 { 373 StudioLogger.error(DDMSFacade.class, "Error executing deploy wizard", e); 374 } 375 } 376 }); 377 378 return bean; 379 } 380 381 public static IStatus installPackage(final String serialNumber, InstallPackageBean bean) 382 { 383 IStatus status = Status.CANCEL_STATUS; 384 385 if ((bean.getPackagePath() != null) && (bean.getCanOverwrite() != null)) 386 { 387 OutputStream consoleOut = null; 388 try 389 { 390 consoleOut = EclipseUtils.getStudioConsoleOutputStream(true); 391 status = 392 installPackage(serialNumber, bean.getPackagePath(), bean.getCanOverwrite(), 393 consoleOut); 394 } 395 finally 396 { 397 try 398 { 399 if (consoleOut != null) 400 { 401 consoleOut.close(); 402 } 403 } 404 catch (IOException e) 405 { 406 StudioLogger.error("Install App: could not close console stream" 407 + e.getMessage()); 408 } 409 } 410 } 411 412 if (status.isOK()) 413 { 414 StudioAndroidEventManager.fireEvent(EventType.PACKAGE_INSTALLED, serialNumber); 415 } 416 417 return status; 418 } 419 420 /** 421 * Install an application on an emulator instance 422 * 423 * @param serialNumber 424 * The serial number of the device where the application will be 425 * installed 426 * @param path 427 * Path of the package containing the application to be installed 428 * @param installationType 429 * one of the {@link INSTALL_TYPE} install types available 430 * @param force 431 * Perform the operation without asking for user intervention 432 * 433 * @return the status of the operation (OK, Cancel or Error+ErrorMessage) 434 */ 435 public static IStatus installPackage(String serialNumber, String path, 436 INSTALL_TYPE installationType, OutputStream processOut) 437 { 438 IStatus status = null; 439 440 if (installationType.equals(INSTALL_TYPE.UNINSTALL)) 441 { 442 status = uninstallPackage(new File(path), serialNumber, processOut); 443 } 444 445 boolean overwrite = installationType.equals(INSTALL_TYPE.OVERWRITE); 446 status = installPackage(serialNumber, path, overwrite, processOut); 447 448 return status; 449 } 450 451 /** 452 * Uninstall the given package (if installed) in the given serialNumber 453 * device 454 * 455 * @param path 456 * the package path 457 * @param serialNumber 458 * the device serial number 459 */ 460 public static IStatus uninstallPackage(File path, String serialNumber, OutputStream processOut) 461 { 462 IStatus returnStatus = null; 463 if ((path != null) && path.exists() && path.isFile()) 464 { 465 IDevice dev = DDMSFacade.getDeviceBySerialNumber(serialNumber); 466 String apiLevel = dev.getProperty("ro.build.version.sdk"); 467 IAndroidTarget target = SdkUtils.getTargetByAPILevel(Integer.parseInt(apiLevel)); 468 String aaptPath = SdkUtils.getTargetAAPTPath(target); 469 if (aaptPath != null) 470 { 471 472 // resolve package name 473 String[] aaptComm = new String[] 474 { 475 aaptPath, "d", "badging", path.toString() 476 }; 477 478 InputStreamReader reader = null; 479 BufferedReader bufferedReader = null; 480 481 try 482 { 483 Process aapt = Runtime.getRuntime().exec(aaptComm); 484 485 reader = new InputStreamReader(aapt.getInputStream()); 486 bufferedReader = new BufferedReader(reader); 487 String infoLine = bufferedReader.readLine(); 488 489 infoLine = infoLine.split(" ")[1].split("=")[1].replaceAll("'", ""); 490 returnStatus = uninstallPackage(serialNumber, infoLine, processOut); 491 492 } 493 catch (Exception e) 494 { 495 returnStatus = 496 new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, 497 AndroidNLS.ERR_DDMSFacade_UninstallPackageException, e); 498 } 499 finally 500 { 501 try 502 { 503 if (reader != null) 504 { 505 reader.close(); 506 } 507 if (bufferedReader != null) 508 { 509 bufferedReader.close(); 510 } 511 } 512 catch (IOException e) 513 { 514 StudioLogger.error("Uninstall app could not close stream. " 515 + e.getMessage()); 516 } 517 518 } 519 } 520 else 521 { 522 StudioLogger 523 .error(DDMSFacade.class, 524 "Impossible to check APK package name. No android targets found inside SDK"); 525 } 526 527 } 528 else 529 { 530 returnStatus = 531 new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, 532 AndroidNLS.ERR_DDMSFacade_UninstallPackage); 533 } 534 return returnStatus; 535 } 536 537 /** 538 * Uninstall the given package within device with given serial number 539 * 540 * @param serialNumber 541 * @param packageName 542 * @param processOutput 543 * outputStream to write output of the process 544 */ 545 public static IStatus uninstallPackage(String serialNumber, String packageName, 546 OutputStream processOutput) 547 { 548 IStatus status = Status.OK_STATUS; 549 String command[] = createUninstallCommand(serialNumber, packageName); 550 551 try 552 { 553 String commandResult = DDMSFacade.executeCommand(command, processOutput); 554 if (!commandResult.toLowerCase().contains(SUCCESS_CONSTANT.toLowerCase())) 555 { 556 status = 557 new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, 558 AndroidNLS.ERR_DDMSFacade_UninstallPackageError + ": " 559 + packageName); 560 } 561 562 } 563 catch (Exception e) 564 { 565 status = 566 new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, 567 AndroidNLS.ERR_DDMSFacade_UninstallPackageException, e); 568 StudioLogger.error(DDMSFacade.class, "Failed to remove package: " + packageName + ". " 569 + e.getMessage()); 570 } 571 return status; 572 } 573 574 /** 575 * Run the Monkey command for the given package within device with given serial number 576 * 577 * @param serialNumber 578 * @param packageName 579 * @param processOutput 580 * @param otherCmd 581 * @return the status of the monkey process 582 */ 583 public static IStatus runMonkey(String serialNumber, String allPackages, 584 OutputStream processOutput, String otherCmd) 585 { 586 IStatus status = Status.OK_STATUS; 587 String command[] = createMonkeyCommand(serialNumber, allPackages, otherCmd); 588 589 try 590 { 591 DDMSFacade.executeCommand(command, processOutput); 592 593 } 594 catch (Exception e) 595 { 596 EclipseUtils.showErrorDialog(AndroidNLS.UI_MonkeyError_Title, 597 AndroidNLS.UI_MonkeyError_Msg); 598 StudioLogger.error(DDMSFacade.class, "Failed to run monkey command: " + command + " " 599 + e.getMessage()); 600 } 601 return status; 602 } 603 604 /** 605 * Uninstall packages from the given serialNumber device 606 * 607 * @param serialNumber 608 * @param packagesToUninstall 609 * @param outputStream 610 * @return the status of the uninstall process or null if no packages were 611 * uninstalled 612 */ 613 private static IStatus uninstallPackages(String serialNumber, 614 ArrayList<String> packagesToUninstall, OutputStream outputStream) 615 { 616 617 IStatus returnStatus = null; 618 for (String packageToUninstall : packagesToUninstall) 619 { 620 StudioLogger.info(DDMSUtils.class, "Removing package: " + packageToUninstall); 621 IStatus temp = uninstallPackage(serialNumber, packageToUninstall, outputStream); 622 if (!temp.isOK()) 623 { 624 if (returnStatus == null) 625 { 626 returnStatus = 627 new MultiStatus(AndroidPlugin.PLUGIN_ID, 0, 628 AndroidNLS.ERR_DDMSFacade_UninstallPackageError, null); 629 } 630 631 ((MultiStatus) returnStatus).add(temp); 632 } 633 } 634 return returnStatus == null ? Status.OK_STATUS : returnStatus; 635 } 636 637 /** 638 * Run monkey command on the given packages with the specified commands. 639 * 640 * @param serialNumber 641 * @param packagesToRunMonkey 642 * @param outputStream 643 * @param otherCmds 644 * @return the status of the monkey process or null if the process was not successful 645 */ 646 private static IStatus runMonkey(String serialNumber, ArrayList<String> packagesToRunMonkey, 647 OutputStream outputStream, String otherCmds) 648 { 649 650 IStatus returnStatus = null; 651 String allPackages = ""; 652 for (String packageToRunMonkey : packagesToRunMonkey) 653 { 654 allPackages += MONKEY_PACKAGES_DIRECTIVE + packageToRunMonkey; 655 } 656 StudioLogger.info(DDMSUtils.class, "Running monkey for: " + allPackages); 657 IStatus temp = runMonkey(serialNumber, allPackages, outputStream, otherCmds); 658 if (!temp.isOK()) 659 { 660 if (returnStatus == null) 661 { 662 returnStatus = 663 new MultiStatus(AndroidPlugin.PLUGIN_ID, 0, 664 AndroidNLS.ERR_DDMSFacade_MonkeyError, null); 665 } 666 667 ((MultiStatus) returnStatus).add(temp); 668 } 669 return returnStatus == null ? Status.OK_STATUS : returnStatus; 670 } 671 672 /** 673 * Uninstall packages from the given device. A Wizard will be opened asking 674 * the user which packages to uninstall 675 * 676 * @param serialNumber 677 * @return the status of the operation 678 */ 679 public static IStatus uninstallPackage(final String serialNumber) 680 { 681 final ArrayList<String> packagesToUninstall = new ArrayList<String>(); 682 IStatus status = null; 683 //wrap the dialog within a final variable 684 final UninstallAppWizard[] wizard = new UninstallAppWizard[1]; 685 686 // do package listing async 687 Thread listingThread = new Thread("listPackages") 688 { 689 690 @Override 691 public void run() 692 { 693 Map<String, String> installedPackages = null; 694 try 695 { 696 installedPackages = listInstalledPackages(serialNumber); 697 } 698 catch (IOException e1) 699 { 700 installedPackages = new HashMap<String, String>(0); 701 } 702 703 while (wizard[0] == null) 704 { 705 try 706 { 707 sleep(100); 708 } 709 catch (InterruptedException e) 710 { 711 //do nothing 712 } 713 } 714 wizard[0].setAvailablePackages(installedPackages); 715 }; 716 }; 717 718 listingThread.start(); 719 720 PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() 721 { 722 public void run() 723 { 724 UninstallAppWizard unAppWiz = new UninstallAppWizard(); 725 WizardDialog dialog = 726 new WizardDialog(new Shell(PlatformUI.getWorkbench() 727 .getActiveWorkbenchWindow().getShell()), unAppWiz); 728 wizard[0] = unAppWiz; 729 dialog.open(); 730 List<String> selectedPackages = wizard[0].getSelectedPackages(); 731 if (selectedPackages != null) 732 { 733 for (String aPackage : selectedPackages) 734 { 735 packagesToUninstall.add(aPackage); 736 } 737 } 738 } 739 }); 740 741 if (packagesToUninstall.size() > 0) 742 { 743 OutputStream consoleOut = null; 744 745 try 746 { 747 consoleOut = EclipseUtils.getStudioConsoleOutputStream(true); 748 status = uninstallPackages(serialNumber, packagesToUninstall, consoleOut); 749 } 750 finally 751 { 752 try 753 { 754 if (consoleOut != null) 755 { 756 consoleOut.close(); 757 } 758 } 759 catch (IOException e) 760 { 761 StudioLogger.error("Uninstall App: could not close console stream" 762 + e.getMessage()); 763 } 764 } 765 } 766 if (status != null) 767 { 768 if (status.isOK()) 769 { 770 Display.getDefault().asyncExec(new Runnable() 771 { 772 public void run() 773 { 774 MessageDialog.openInformation(PlatformUI.getWorkbench() 775 .getActiveWorkbenchWindow().getShell(), 776 AndroidNLS.UI_UninstallApp_SucessDialogTitle, 777 AndroidNLS.UI_UninstallApp_Message); 778 } 779 }); 780 StudioAndroidEventManager.fireEvent(EventType.PACKAGE_UNINSTALLED, serialNumber); 781 } 782 else 783 { 784 EclipseUtils.showErrorDialog(AndroidNLS.UI_UninstallApp_ERRDialogTitle, 785 AndroidNLS.UI_UninstallApp_ERRUninstallApp, status); 786 } 787 } 788 // all return is successful since operations are async 789 return Status.OK_STATUS; 790 791 } 792 793 /** 794 * If there is no Monkey Launch configuration for the given deviceName, it is created. 795 * @param deviceName 796 */ 797 private static ILaunchConfiguration createLaunchConfiguration(String deviceName) 798 { 799 ILaunchConfiguration config = null; 800 ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager(); 801 ILaunchConfigurationType motodevLaunchType = 802 launchManager 803 .getLaunchConfigurationType(IMonkeyConfigurationConstants.LAUNCH_CONFIGURATION_TYPE_EXTENSION_ID); 804 String launchConfigurationName = 805 launchManager 806 .generateUniqueLaunchConfigurationNameFrom(IMonkeyConfigurationConstants.NEW_CONFIGURATION_NAME); 807 try 808 { 809 ILaunchConfigurationWorkingCopy workingCopy = 810 motodevLaunchType.newInstance(null, launchConfigurationName); 811 workingCopy.setAttribute(IMonkeyConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME, 812 deviceName); 813 config = workingCopy.doSave(); 814 } 815 catch (CoreException e) 816 { 817 EclipseUtils.showErrorDialog(AndroidNLS.UI_MonkeyError_Title, e.getMessage()); 818 } 819 return config; 820 821 } 822 823 /** 824 * Run adb monkey. 825 * 826 * @param serialNumber 827 * @param deviceName 828 * @return the status of the operation 829 */ 830 public static IStatus runMonkey(final String serialNumber, final String deviceName) 831 { 832 833 ILaunchConfigurationType type = 834 DebugPlugin 835 .getDefault() 836 .getLaunchManager() 837 .getLaunchConfigurationType( 838 IMonkeyConfigurationConstants.LAUNCH_CONFIGURATION_TYPE_EXTENSION_ID); 839 ILaunchConfiguration[] launchs; 840 try 841 { 842 launchs = DebugPlugin.getDefault().getLaunchManager().getLaunchConfigurations(type); 843 844 ILaunchConfiguration launchConfig = null; 845 for (int i = 0; i < launchs.length; i++) 846 { 847 launchConfig = launchs[i]; 848 String configDeviceName = 849 launchConfig.getAttribute( 850 IMonkeyConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME, ""); 851 if (configDeviceName.equals(deviceName)) 852 { 853 break; 854 } 855 else 856 { 857 launchConfig = null; 858 } 859 860 } 861 if (launchConfig == null) 862 { 863 launchConfig = createLaunchConfiguration(deviceName); 864 865 } 866 867 final ILaunchGroup lc = 868 DebugUITools.getLaunchGroup(launchConfig, ILaunchManager.RUN_MODE); 869 870 final IStructuredSelection selection = new StructuredSelection(launchConfig); 871 872 PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() 873 { 874 public void run() 875 { 876 877 DebugUITools.openLaunchConfigurationDialogOnGroup(new Shell(PlatformUI 878 .getWorkbench().getActiveWorkbenchWindow().getShell()), selection, 879 lc.getIdentifier(), null); 880 881 } 882 }); 883 884 } 885 catch (CoreException e) 886 { 887 StudioLogger.error("Monkey: could not open the launch configuration dialog " 888 + e.getMessage()); 889 890 } 891 892 // UDC log for monkey execution 893 StudioLogger.collectUsageData(UsageDataConstants.WHAT_MONKEY_EXEC, //$NON-NLS-1$ 894 UsageDataConstants.KIND_MONKEY_EXEC, "Monkey executed", //$NON-NLS-1$ 895 AndroidPlugin.PLUGIN_ID, AndroidPlugin.getDefault().getBundle().getBundleContext() 896 .getBundle().getVersion().toString()); 897 898 // all return is successful since operations are async 899 return Status.OK_STATUS; 900 901 } 902 903 /** 904 * Run adb monkey. 905 * 906 * @param serialNumber 907 * @param packagesToRunMonkey 908 * @param otherCmds 909 * @return the status of the operation 910 */ 911 public static IStatus runMonkey(final String serialNumber, 912 ArrayList<String> packagesToRunMonkey, String otherCmds) 913 { 914 915 if (packagesToRunMonkey.size() > 0) 916 { 917 OutputStream consoleOut = null; 918 919 try 920 { 921 consoleOut = EclipseUtils.getStudioConsoleOutputStream(true); 922 runMonkey(serialNumber, packagesToRunMonkey, consoleOut, otherCmds); 923 } 924 finally 925 { 926 try 927 { 928 if (consoleOut != null) 929 { 930 consoleOut.close(); 931 } 932 } 933 catch (IOException e) 934 { 935 StudioLogger.error("Monkey: could not close console stream" + e.getMessage()); 936 } 937 } 938 } 939 940 // all return is successful since operations are async 941 return Status.OK_STATUS; 942 943 } 944 945 /** 946 * List the installed packages in the device with the serial number Each 947 * package entry carries their package location 948 * 949 * @param serialNumber 950 * @return a HashMap where keys are the package names and values are package 951 * location 952 * @throws IOException 953 */ 954 public static Map<String, String> listInstalledPackages(String serialNumber) throws IOException 955 { 956 Map<String, String> packages = new LinkedHashMap<String, String>(); 957 String sdkPath = SdkUtils.getSdkPath(); 958 String command[] = 959 new String[] 960 { 961 sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator 962 + DDMSFacade.ADB_COMMAND, DDMSFacade.ADB_INSTANCE_PARAMETER, 963 serialNumber, DDMSFacade.SHELL_CMD, PM_CMD, PM_LIST_DIRECTIVE, 964 PM_PACKAGES_DIRECTIVE, PM_PACKAGES_DIRECTIVE_FORCE 965 }; 966 967 String commResult = DDMSFacade.executeCommand(command, null); 968 String[] packageList = null; 969 if ((commResult != null) && (commResult.length() > 0) 970 && !commResult.contains("system running?")) 971 { 972 packageList = commResult.trim().replaceAll("\n\n", "\n").split("\n"); 973 int i = 0; 974 String aPackage = null; 975 String[] packageUnit = null; 976 while (i < packageList.length) 977 { 978 if (packageList[i].toLowerCase().contains("package:")) 979 { 980 String[] splittedPackage = packageList[i].split(":"); 981 if (splittedPackage.length >= 2) 982 { 983 aPackage = splittedPackage[1].trim(); 984 packageUnit = aPackage.split("="); 985 if (packageUnit.length > 1) 986 { 987 packages.put(packageUnit[1], packageUnit[0]); 988 } 989 } 990 } 991 i++; 992 } 993 } 994 return packages; 995 } 996 997 /** 998 * Install an application on an emulator instance 999 * 1000 * @param serialNumber 1001 * The serial number of the device where the application will be 1002 * installed 1003 * @param path 1004 * Path of the package containing the application to be installed 1005 * @param canOverwrite 1006 * If the application will be installed even if there is a 1007 * previous installation 1008 * @param force 1009 * Perform the operation without asking for user intervention 1010 * 1011 * @return the status of the operation (OK, Cancel or Error+ErrorMessage) 1012 */ 1013 public static IStatus installPackage(String serialNumber, String path, boolean canOverwrite, 1014 OutputStream processOut) 1015 { 1016 IStatus status = Status.OK_STATUS; 1017 1018 // Return if no instance is selected 1019 if (serialNumber == null) 1020 { 1021 StudioLogger.error("Abort deploy operation. Serial number is null."); 1022 status = 1023 new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, 1024 AndroidNLS.ERR_DDMSFacade_SerialNumberNullPointer); 1025 } 1026 1027 // Return if instance is not started 1028 if (status.isOK() && !DDMSFacade.isDeviceOnline(serialNumber)) 1029 { 1030 StudioLogger.error("Abort deploy operation. Device is not online."); 1031 status = new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, ""); 1032 } 1033 1034 String command_results = ""; 1035 if (status.isOK()) 1036 { 1037 try 1038 { 1039 String[] cmd = createInstallCommand(canOverwrite, path, serialNumber); 1040 command_results = DDMSFacade.executeCommand(cmd, processOut, serialNumber); 1041 1042 // Check if the result has a success message 1043 if (!command_results.contains(SUCCESS_CONSTANT)) 1044 { 1045 status = 1046 new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, 1047 "Error executing the operation. Execution results: " 1048 + command_results); 1049 } 1050 } 1051 catch (IOException e) 1052 { 1053 StudioLogger.error("Deploy: Could not execute adb install command."); 1054 status = new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, e.getMessage()); 1055 } 1056 } 1057 1058 return status; 1059 } 1060 1061 /** 1062 * Change the emulator language 1063 * 1064 * @param serialNumber 1065 * The serial number of the device 1066 * @param language 1067 * the language id 1068 * @param country 1069 * the country id 1070 * @return the status of the operation (OK, Error+ErrorMessage) 1071 */ 1072 public static void changeLanguage(final String serialNumber, final String language, 1073 final String country) 1074 { 1075 1076 if ((language != null) || (country != null)) 1077 { 1078 /* 1079 * A new thread is used since this command takes too long to be executed 1080 */ 1081 Thread thead = new Thread(new Runnable() 1082 { 1083 1084 public void run() 1085 { 1086 1087 String[] cmd = createChangeLanguageCommand(serialNumber, language, country); 1088 try 1089 { 1090 1091 IOConsoleOutputStream consoleOut = 1092 EclipseUtils.getStudioConsoleOutputStream(true); 1093 if (language != null) 1094 { 1095 consoleOut.write(AndroidNLS.UI_ChangeLang_Language + " " + language 1096 + "\n"); 1097 } 1098 if (country != null) 1099 { 1100 consoleOut.write(AndroidNLS.UI_ChangeLang_Country + " " + country 1101 + "\n"); 1102 } 1103 1104 DDMSFacade.executeCommand(cmd, consoleOut); 1105 consoleOut.write("\n " + serialNumber + ":" 1106 + AndroidNLS.UI_ChangeLang_Restart_Device_Manually + "\n\n"); 1107 StudioAndroidEventManager.fireEvent(EventType.LANGUAGE_CHANGED, 1108 serialNumber); 1109 } 1110 catch (IOException e) 1111 { 1112 StudioLogger 1113 .error("Language: Could not execute adb change language command."); 1114 } 1115 1116 } 1117 }); 1118 thead.start(); 1119 } 1120 1121 } 1122 1123 /** 1124 * Creates a string with the command that should be called in order to 1125 * change the device language 1126 * 1127 * @param serialNumber 1128 * The serial number of the device 1129 * @param language 1130 * the language id 1131 * @param country 1132 * the country id 1133 * @return the command to be used to change the device language 1134 */ 1135 private static String[] createChangeLanguageCommand(String serialNumber, String language, 1136 String country) 1137 { 1138 String cmd[]; 1139 String sdkPath = SdkUtils.getSdkPath(); 1140 1141 String CHANGE_LANGUAGE_CMD = ""; 1142 if (language != null) 1143 { 1144 CHANGE_LANGUAGE_CMD += "setprop persist.sys.language " + language; 1145 } 1146 if (country != null) 1147 { 1148 CHANGE_LANGUAGE_CMD += 1149 ((CHANGE_LANGUAGE_CMD.length() > 0) ? ";" : "") 1150 + "setprop persist.sys.country " + country; 1151 } 1152 1153 // The tools folder should exist and be here, but double-checking 1154 // once more wont kill 1155 File f = new File(sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator); 1156 if (!f.exists()) 1157 { 1158 StudioLogger.error("Language: Could not find tools folder on " + sdkPath 1159 + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator); 1160 } 1161 else 1162 { 1163 if (!f.isDirectory()) 1164 { 1165 StudioLogger.error("Language: Invalid tools folder " + sdkPath 1166 + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator); 1167 } 1168 } 1169 1170 String cmdTemp[] = 1171 { 1172 sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator 1173 + DDMSFacade.ADB_COMMAND, DDMSFacade.ADB_INSTANCE_PARAMETER, 1174 serialNumber, "shell", CHANGE_LANGUAGE_CMD 1175 }; 1176 cmd = cmdTemp; 1177 1178 return cmd; 1179 } 1180 1181 /** 1182 * Creates strings with the command that should be called in order to 1183 * retrieve the current language and country in use by given emulator instance. 1184 * 1185 * @param serialNumber The serial number of the device 1186 * @return An ArrayList with command strings to be used to get the 1187 * current emulator language and country. 1188 */ 1189 private static ArrayList<String[]> createCurrentEmulatorLanguageAndCountryCommand( 1190 String serialNumber) 1191 { 1192 String languageCommand[]; 1193 String countryCommand[]; 1194 String sdkPath = SdkUtils.getSdkPath(); 1195 String GET_LANGUAGE_CMD = "getprop persist.sys.language"; 1196 String GET_COUNTRY_CMD = "getprop persist.sys.country"; 1197 // The tools folder should exist and be here, but double-cheking 1198 // once more wont kill 1199 File f = new File(sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator); 1200 if (!f.exists()) 1201 { 1202 StudioLogger.error("Language: Could not find tools folder on " + sdkPath 1203 + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator); 1204 } 1205 else 1206 { 1207 if (!f.isDirectory()) 1208 { 1209 StudioLogger.error("Language: Invalid tools folder " + sdkPath 1210 + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator); 1211 } 1212 } 1213 String langCmdTemp[] = 1214 { 1215 sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator 1216 + DDMSFacade.ADB_COMMAND, DDMSFacade.ADB_INSTANCE_PARAMETER, 1217 serialNumber, "shell", GET_LANGUAGE_CMD 1218 }; 1219 String countryCmdTemp[] = 1220 { 1221 sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator 1222 + DDMSFacade.ADB_COMMAND, DDMSFacade.ADB_INSTANCE_PARAMETER, 1223 serialNumber, "shell", GET_COUNTRY_CMD 1224 }; 1225 languageCommand = langCmdTemp; 1226 countryCommand = countryCmdTemp; 1227 1228 ArrayList<String[]> commands = new ArrayList<String[]>(); 1229 commands.add(0, languageCommand); 1230 commands.add(1, countryCommand); 1231 return commands; 1232 } 1233 1234 /** 1235 * Creates a string with the command that should be called in order to 1236 * install the application 1237 * 1238 * @param canOverwrite 1239 * If true, than existent application will be overwritten 1240 * @param path 1241 * Location of the package containing the application 1242 * @param serialNumber 1243 * The serial number of the device to be targeted 1244 * 1245 * @return 1246 */ 1247 private static String[] createInstallCommand(boolean canOverwrite, String path, 1248 String serialNumber) 1249 { 1250 String cmd[]; 1251 String sdkPath = SdkUtils.getSdkPath(); 1252 1253 // The tools folder should exist and be here, but double-checking 1254 // once more wont kill 1255 File f = new File(sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator); 1256 if (!f.exists()) 1257 { 1258 StudioLogger.error("Deploy: Could not find tools folder on " + sdkPath 1259 + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator); 1260 } 1261 else 1262 { 1263 if (!f.isDirectory()) 1264 { 1265 StudioLogger.error("Deploy: Invalid tools folder " + sdkPath 1266 + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator); 1267 } 1268 } 1269 1270 if (canOverwrite) 1271 { 1272 // If overwrite option is checked, create command with the -r 1273 // paramater 1274 String cmdTemp[] = 1275 { 1276 sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator 1277 + DDMSFacade.ADB_COMMAND, DDMSFacade.ADB_INSTANCE_PARAMETER, 1278 serialNumber, INSTALL_CMD, ADB_INSTALL_OVERWRITE, path 1279 }; 1280 cmd = cmdTemp; 1281 } 1282 else 1283 { 1284 // If overwrite option is unchecked, create command without the -r 1285 // paramater 1286 String cmdTemp[] = 1287 { 1288 sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator 1289 + DDMSFacade.ADB_COMMAND, DDMSFacade.ADB_INSTANCE_PARAMETER, 1290 serialNumber, INSTALL_CMD, path 1291 }; 1292 cmd = cmdTemp; 1293 } 1294 1295 return cmd; 1296 } 1297 1298 private static String[] createUninstallCommand(String serialNumber, String packageName) 1299 { 1300 String sdkPath = SdkUtils.getSdkPath(); 1301 // The tools folder should exist and be here, but double-checking 1302 // once more wont kill 1303 File f = new File(sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator); 1304 if (!f.exists()) 1305 { 1306 StudioLogger.error("Run: Could not find tools folder on " + sdkPath 1307 + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator); 1308 } 1309 else 1310 { 1311 if (!f.isDirectory()) 1312 { 1313 StudioLogger.error("Run: Invalid tools folder " + sdkPath 1314 + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator); 1315 } 1316 } 1317 1318 String cmd[] = 1319 { 1320 sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator 1321 + DDMSFacade.ADB_COMMAND, DDMSFacade.ADB_INSTANCE_PARAMETER, 1322 serialNumber, DDMSFacade.SHELL_CMD, PM_CMD, PM_UNINSTALL_DIRECTIVE, 1323 packageName 1324 }; 1325 1326 return cmd; 1327 1328 } 1329 1330 /** 1331 * Mount the adb monkey command based on the given parameters. 1332 * @param serialNumber 1333 * @param packagesName 1334 * @param otherCmd 1335 * @return the array of strings containing the monkey command to be executed. 1336 */ 1337 private static String[] createMonkeyCommand(String serialNumber, String packagesName, 1338 String otherCmd) 1339 { 1340 String sdkPath = SdkUtils.getSdkPath(); 1341 // The tools folder should exist and be here, but double-checking 1342 // once more wont kill 1343 File f = new File(sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator); 1344 if (!f.exists()) 1345 { 1346 StudioLogger.error("Run: Could not find tools folder on " + sdkPath 1347 + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator); 1348 } 1349 else 1350 { 1351 if (!f.isDirectory()) 1352 { 1353 StudioLogger.error("Run: Invalid tools folder " + sdkPath 1354 + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator); 1355 } 1356 } 1357 1358 String cmd[] = 1359 { 1360 sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator 1361 + DDMSFacade.ADB_COMMAND, DDMSFacade.ADB_INSTANCE_PARAMETER, 1362 serialNumber, DDMSFacade.SHELL_CMD, MONKEY_CMD, packagesName, otherCmd 1363 }; 1364 1365 return cmd; 1366 1367 } 1368 1369 /** 1370 * Dump HPROF service implementation 1371 * @param serialNumber The device serial number 1372 * @param monitor 1373 * @return A IStatus describing if the service was successful or not 1374 */ 1375 public static IStatus dumpHPROF(final String serialNumber, IProgressMonitor monitor) 1376 { 1377 IStatus status; 1378 1379 // Selected app. The array should have only 1 element 1380 final String[] selectedAppSet = new String[1]; 1381 1382 // Instantiate the wizard and populate it with the retrieved process list 1383 PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() 1384 { 1385 public void run() 1386 { 1387 DumpHPROFWizard dumpHPROFWizard = new DumpHPROFWizard(serialNumber); 1388 WizardDialog dialog = 1389 new WizardDialog(new Shell(PlatformUI.getWorkbench() 1390 .getActiveWorkbenchWindow().getShell()), dumpHPROFWizard); 1391 dialog.open(); 1392 1393 // Get the selected application 1394 selectedAppSet[0] = dumpHPROFWizard.getSelectedApp(); 1395 1396 } 1397 }); 1398 1399 if (selectedAppSet[0] != null) 1400 { 1401 // Dump HPROF file based on the selected application 1402 status = DDMSFacade.dumpHprofFile(selectedAppSet[0], serialNumber, monitor); 1403 } 1404 else 1405 { 1406 status = Status.CANCEL_STATUS; 1407 } 1408 1409 return status; 1410 } 1411 1412 public static int getDeviceApiVersion(String serialNumber) 1413 { 1414 int deviceSdkVersion = -1; 1415 String deviceProperty = DDMSFacade.getDeviceProperty(serialNumber, "ro.build.version.sdk"); 1416 if (deviceProperty != null) 1417 { 1418 deviceSdkVersion = Integer.parseInt(deviceProperty); 1419 } 1420 1421 return deviceSdkVersion; 1422 } 1423 1424 public static boolean remoteFileExists(String serialNumber, String remotePath) 1425 throws IOException 1426 { 1427 boolean found = false; 1428 Collection<String> results = 1429 DDMSFacade.execRemoteApp(serialNumber, 1430 "ls " + FileUtil.getEscapedPath(remotePath, Platform.OS_LINUX), 1431 new NullProgressMonitor()); 1432 for (String result : results) 1433 { 1434 if (result.equals(remotePath)) 1435 { 1436 found = true; 1437 break; 1438 } 1439 } 1440 return found; 1441 } 1442 } 1443