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.FileNotFoundException; 22 import java.io.IOException; 23 import java.io.InputStream; 24 import java.io.InputStreamReader; 25 import java.io.OutputStream; 26 import java.util.ArrayList; 27 import java.util.Collection; 28 import java.util.HashMap; 29 import java.util.HashSet; 30 import java.util.LinkedHashMap; 31 import java.util.List; 32 import java.util.Map; 33 import java.util.Properties; 34 import java.util.Set; 35 import java.util.concurrent.TimeoutException; 36 import java.util.regex.Matcher; 37 import java.util.regex.Pattern; 38 39 import org.eclipse.core.runtime.IProgressMonitor; 40 import org.eclipse.core.runtime.IStatus; 41 import org.eclipse.core.runtime.NullProgressMonitor; 42 import org.eclipse.core.runtime.Path; 43 import org.eclipse.core.runtime.Status; 44 import org.eclipse.core.runtime.SubMonitor; 45 import org.eclipse.jface.preference.IPreferenceStore; 46 import org.eclipse.osgi.util.NLS; 47 import org.eclipse.swt.widgets.Shell; 48 import org.eclipse.ui.PlatformUI; 49 50 import com.android.ddmlib.AdbCommandRejectedException; 51 import com.android.ddmlib.AndroidDebugBridge; 52 import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener; 53 import com.android.ddmlib.Client; 54 import com.android.ddmlib.ClientData; 55 import com.android.ddmlib.EmulatorConsole; 56 import com.android.ddmlib.FileListingService; 57 import com.android.ddmlib.FileListingService.FileEntry; 58 import com.android.ddmlib.IDevice; 59 import com.android.ddmlib.IDevice.DeviceState; 60 import com.android.ddmlib.MultiLineReceiver; 61 import com.android.ddmlib.SyncException; 62 import com.android.ddmlib.SyncService; 63 import com.android.ddmlib.SyncService.ISyncProgressMonitor; 64 import com.android.ide.eclipse.adt.AdtPlugin; 65 import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs; 66 import com.android.ide.eclipse.ddms.DdmsPlugin; 67 import com.motorola.studio.android.AndroidPlugin; 68 import com.motorola.studio.android.adt.StudioAndroidEventManager.EventType; 69 import com.motorola.studio.android.common.IAndroidConstants; 70 import com.motorola.studio.android.common.exception.AndroidException; 71 import com.motorola.studio.android.common.log.StudioLogger; 72 import com.motorola.studio.android.i18n.AndroidNLS; 73 import com.motorola.studio.android.utilities.TelnetFrameworkAndroid; 74 75 /** 76 * DESCRIPTION: 77 * This class is the common interface to functionalities from DDMS 78 * 79 * RESPONSIBILITY: 80 * Centralizes the access to DDMS. 81 * 82 * COLABORATORS: 83 * None. 84 * 85 * USAGE: 86 * Use the public methods to use DDMS 87 */ 88 public class DDMSFacade 89 { 90 /** 91 * Command for switching back to USB connection mode. 92 */ 93 private static final String USB_SWITCH_BACK_COMMAND = "usb"; //$NON-NLS-1$ 94 95 /** 96 * Argument which indicates the device to apply a certain command. 97 */ 98 private static final String DEVICE_ID_INDICATOR = "-s"; //$NON-NLS-1$ 99 100 private static final String DEFAULT_WIRELESS_DEVICE_PROPERTY = "tiwlan0"; //$NON-NLS-1$ 101 102 /** 103 * Map containing all connected devices. 104 * It is being kept for us not to depend on ADT every time we need one, preventing 105 * deadlocks. 106 */ 107 private static final Map<String, IDevice> connectedDevices = new HashMap<String, IDevice>(); 108 109 /** 110 * Set containing the serial numbers of the devices completely loaded. 111 * A device is considered completely loaded if it has already loaded the HOME application. 112 */ 113 private static final Set<String> completelyUpDevices = new HashSet<String>(); 114 115 /** 116 * Folder located inside the SDK folder containing some sdk tools. 117 */ 118 static final String TOOLS_FOLDER = IAndroidConstants.FD_TOOLS; 119 120 /** 121 * Folder located inside the SDK folder and containing the ADB. 122 */ 123 static final String PLATFORM_TOOLS_FOLDER = IAndroidConstants.FD_PLATFORM_TOOLS; 124 125 /** 126 * adb (android debug bridge) command. 127 */ 128 static final String ADB_COMMAND = "adb"; //$NON-NLS-1$ 129 130 /** 131 * Command to concatenate with "adb" to have the device shell. 132 */ 133 static final String SHELL_CMD = "shell"; //$NON-NLS-1$ 134 135 /** 136 * Options to be used with adb to indicate run operation. 137 */ 138 private static final String AM_CMD = "am"; //$NON-NLS-1$ 139 140 /** 141 * Command to concatenate with "am" to have an activity executed at the device. 142 */ 143 private static final String START_CMD = "start"; //$NON-NLS-1$ 144 145 /** 146 * Parameter for running in debug mode. 147 */ 148 private static final String ADB_AM_DEBUG = "-D"; //$NON-NLS-1$ 149 150 /** 151 * Parameter provided before the application package/name. 152 */ 153 private static final String ADB_AM_NAME = "-n"; //$NON-NLS-1$ 154 155 /** 156 * Parameter for selecting emulator instance. 157 */ 158 static final String ADB_INSTANCE_PARAMETER = DEVICE_ID_INDICATOR; //$NON-NLS-1$ 159 160 /** 161 * Folder for the SDK. 162 */ 163 private static final String SDCARD_FOLDER = "sdcard"; //$NON-NLS-1$ 164 165 /** 166 * Folder for the SDK. 167 */ 168 private static final String MNT_SDCARD_FOLDER = "mnt/sdcard"; //$NON-NLS-1$ 169 170 /* 171 * TCP/IP 172 */ 173 private static final String CONNECT_TCPIP_CMD = "connect"; //$NON-NLS-1$ 174 175 private static final String DISCONNECT_TCPIP_CMD = "disconnect"; //$NON-NLS-1$ 176 177 private static final String TCPIP_CMD = "tcpip"; //$NON-NLS-1$ 178 179 private static final String IFCONFIG_CMD = "ifconfig"; //$NON-NLS-1$ 180 181 static Object consoleLock = new Object(); 182 183 private static Map<String, String> avdNameMap = new HashMap<String, String>(); 184 185 /** 186 * Property from device which represents the wi-fi value to use ipconfig command. 187 */ 188 private static final String WIFI_INTERFACE_DEVICE_PROPERTY = "wifi.interface"; //$NON-NLS-1$ 189 190 // IP validation 191 private static final String ZERO_TO_255_PATTERN = 192 "((\\d)|(\\d\\d)|([0-1]\\d\\d)|(2[0-4]\\d)|(25[0-5]))"; //$NON-NLS-1$ 193 194 private static final String IP_PATTERN = "(" + ZERO_TO_255_PATTERN + "\\." //$NON-NLS-1$ //$NON-NLS-2$ 195 + ZERO_TO_255_PATTERN + "\\." + ZERO_TO_255_PATTERN + "\\." + ZERO_TO_255_PATTERN //$NON-NLS-1$ //$NON-NLS-2$ 196 + ")+"; //$NON-NLS-1$ 197 198 /** 199 * Must be called only once, during AndroidPlugin start-up. 200 * This method configures all necessary device listeners. 201 */ 202 public static void setup() 203 { 204 AndroidPlugin.getDefault().addSDKLoaderListener(new Runnable() 205 { 206 207 public void run() 208 { 209 AndroidDebugBridge adb = AndroidDebugBridge.getBridge(); 210 if (adb == null) 211 { 212 AndroidDebugBridge.disconnectBridge(); 213 DdmsPlugin.setToolsLocation(AdtPlugin.getOsAbsoluteAdb(), true, 214 AdtPlugin.getOsAbsoluteHprofConv(), AdtPlugin.getOsAbsoluteTraceview()); 215 } 216 217 if (adb != null) 218 { 219 IDevice[] x = adb.getDevices(); 220 IDevice[] newDevices = x; 221 List<IDevice> oldDevList = new ArrayList<IDevice>(connectedDevices.values()); 222 223 for (IDevice newDev : newDevices) 224 { 225 String serialNum = newDev.getSerialNumber(); 226 if (connectedDevices.containsKey(serialNum)) 227 { 228 IDevice oldDev = connectedDevices.get(serialNum); 229 oldDevList.remove(oldDev); 230 if (oldDev.getState().compareTo((newDev).getState()) != 0) 231 { 232 if ((newDev).getState() == DeviceState.OFFLINE) 233 { 234 deviceDisconnected(newDev); 235 } 236 else if ((newDev).getState() == DeviceState.ONLINE) 237 { 238 deviceConnected(newDev); 239 } 240 } 241 } 242 else 243 { 244 deviceConnected(newDev); 245 } 246 } 247 248 for (IDevice oldDev : oldDevList) 249 { 250 deviceDisconnected(oldDev); 251 } 252 } 253 254 } 255 }); 256 257 // Adds listener for the HOME application. It adds the serial number of the 258 // device to a collection when it identifies that the HOME application has 259 // loaded 260 AndroidDebugBridge.addClientChangeListener(new IClientChangeListener() 261 { 262 263 public void clientChanged(Client client, int changeMask) 264 { 265 if ((changeMask & Client.CHANGE_NAME) == Client.CHANGE_NAME) 266 { 267 final Client finalClient = client; 268 Thread t = new Thread() 269 { 270 271 @Override 272 public void run() 273 { 274 String applicationName = 275 finalClient.getClientData().getClientDescription(); 276 if (applicationName != null) 277 { 278 IPreferenceStore store = 279 AdtPlugin.getDefault().getPreferenceStore(); 280 String home = store.getString(AdtPrefs.PREFS_HOME_PACKAGE); 281 if (home.equals(applicationName)) 282 { 283 String serialNum = finalClient.getDevice().getSerialNumber(); 284 synchronized (completelyUpDevices) 285 { 286 StudioLogger.debug("Completely Up Device: " + serialNum); //$NON-NLS-1$ 287 completelyUpDevices.add(serialNum); 288 } 289 } 290 } 291 } 292 }; 293 t.start(); 294 } 295 } 296 }); 297 } 298 299 static void deviceStatusChanged(IDevice device) 300 { 301 StudioLogger.debug("Device changed: " + device.getSerialNumber()); //$NON-NLS-1$ 302 synchronized (connectedDevices) 303 { 304 connectedDevices.put(device.getSerialNumber(), device); 305 } 306 if ((device).getState() == DeviceState.ONLINE) 307 { 308 IPreferenceStore store = AdtPlugin.getDefault().getPreferenceStore(); 309 String home = store.getString(AdtPrefs.PREFS_HOME_PACKAGE); 310 if (device.getClient(home) != null) 311 { 312 synchronized (completelyUpDevices) 313 { 314 StudioLogger.debug("Completely Up Device: " + device.getSerialNumber()); //$NON-NLS-1$ 315 if (!completelyUpDevices.contains(device.getSerialNumber())) 316 { 317 completelyUpDevices.add(device.getSerialNumber()); 318 } 319 } 320 } 321 } 322 } 323 324 /** 325 * Registers a device as connected 326 * 327 * @param device 328 */ 329 static void deviceConnected(IDevice device) 330 { 331 final String serialNumber = device.getSerialNumber(); 332 StudioLogger.debug("Device connected: " + serialNumber); //$NON-NLS-1$ 333 synchronized (connectedDevices) 334 { 335 connectedDevices.put(serialNumber, device); 336 } 337 338 if (!device.isEmulator() && !device.hasClients()) 339 { 340 boolean timeout = false; 341 long startTime = System.currentTimeMillis(); 342 int maxInterval = 10000; 343 do 344 { 345 try 346 { 347 Thread.sleep(250); 348 } 349 catch (InterruptedException e) 350 { 351 //do nothing 352 } 353 long currentTime = System.currentTimeMillis(); 354 timeout = ((startTime + maxInterval) < currentTime); 355 356 } 357 while (!device.hasClients() && !timeout); 358 if (timeout) 359 { 360 synchronized (completelyUpDevices) 361 { 362 //put the device up anyway. 363 completelyUpDevices.add(serialNumber); 364 } 365 } 366 } 367 368 if (device.hasClients()) 369 { 370 // When a device is connected, look for the HOME application and add 371 // the device serial number to a collection if it is already running. 372 IPreferenceStore store = AdtPlugin.getDefault().getPreferenceStore(); 373 String home = store.getString(AdtPrefs.PREFS_HOME_PACKAGE); 374 if (device.getClient(home) != null) 375 { 376 StudioLogger.debug("Completely Up Device: " + serialNumber); //$NON-NLS-1$ 377 synchronized (completelyUpDevices) 378 { 379 completelyUpDevices.add(serialNumber); 380 } 381 } 382 } 383 384 StudioAndroidEventManager.fireEvent(EventType.DEVICE_CONNECTED, serialNumber); 385 } 386 387 /** 388 * Unregisters a device as connected 389 * 390 * @param device 391 */ 392 static void deviceDisconnected(IDevice device) 393 { 394 final String serialNumber = device.getSerialNumber(); 395 StudioLogger.debug("Device disconnected: " + serialNumber); //$NON-NLS-1$ 396 synchronized (completelyUpDevices) 397 { 398 completelyUpDevices.remove(serialNumber); 399 } 400 synchronized (connectedDevices) 401 { 402 connectedDevices.remove(serialNumber); 403 } 404 StudioAndroidEventManager.fireEvent(EventType.DEVICE_DISCONNECTED, serialNumber); 405 avdNameMap.remove(device.getSerialNumber()); 406 407 } 408 409 /** 410 * Get all connected device serial numbers 411 * 412 * @return 413 */ 414 public static Collection<String> getConnectedSerialNumbers() 415 { 416 return connectedDevices.keySet(); 417 } 418 419 /** 420 * Get the Device associated with the given serial number 421 * 422 * @param serialNumber Serial number of the device to retrieve 423 * @return Device associated with the given serial number 424 */ 425 public static IDevice getDeviceBySerialNumber(String serialNumber) 426 { 427 return connectedDevices.get(serialNumber); 428 } 429 430 /** 431 * Runs an activity at the given device 432 * 433 * @param serialNumber The serial number of the device to have the activity executed 434 * @param activityName The activity to execute 435 * @param debugMode Whether the activity shall be run in debug mode or not 436 * @param processOut The output stream of the process running "adb" 437 * 438 * @return An IStatus object with the result of the operation 439 */ 440 public static IStatus runActivity(String serialNumber, String activityName, boolean debugMode, 441 OutputStream processOut) 442 { 443 IStatus status = Status.OK_STATUS; 444 445 // Return if no instance is selected 446 if (serialNumber == null) 447 { 448 StudioLogger.error("Abort run operation. Serial number is null."); //$NON-NLS-1$ 449 return new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, 450 AndroidNLS.ERR_DDMSFacade_SerialNumberNullPointer); 451 } 452 453 // Return if instance is not started 454 if (!isDeviceOnline(serialNumber)) 455 { 456 StudioLogger.error("Abort run operation. Device is not online."); //$NON-NLS-1$ 457 return new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, ""); //$NON-NLS-1$ 458 } 459 460 try 461 { 462 String[] cmd = createRunCommand(serialNumber, activityName, debugMode); 463 executeCommand(cmd, processOut); 464 } 465 catch (IOException e) 466 { 467 StudioLogger.error("Deploy: Could not execute adb install command."); //$NON-NLS-1$ 468 status = new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, e.getMessage()); 469 } 470 471 return status; 472 } 473 474 static String executeCommand(String[] cmd, OutputStream out) throws IOException 475 { 476 return executeCommand(cmd, out, null); 477 } 478 479 /** 480 * DOCUMENT ME!! 481 * @param cmd 482 * @param out 483 * @param serialNumber 484 * @return 485 * @throws IOException 486 */ 487 static String executeCommand(String[] cmd, OutputStream out, String serialNumber) 488 throws IOException 489 { 490 String fullCmd = ""; //$NON-NLS-1$ 491 if (out != null) 492 { 493 for (String cmdArg : cmd) 494 { 495 fullCmd += cmdArg + " "; //$NON-NLS-1$ 496 } 497 out.write(fullCmd.getBytes()); 498 out.write("\n".getBytes()); //$NON-NLS-1$ 499 } 500 501 Runtime r = Runtime.getRuntime(); 502 Process p = r.exec(cmd); 503 504 String command_results = ""; //$NON-NLS-1$ 505 InputStream processIn = p.getInputStream(); 506 final BufferedReader br = new BufferedReader(new InputStreamReader(processIn)); 507 String line; 508 try 509 { 510 while ((line = br.readLine()) != null) 511 { 512 command_results += line; 513 command_results += "\n"; //$NON-NLS-1$ 514 if (out != null) 515 { 516 if (serialNumber != null) 517 { 518 out.write((serialNumber + ": ").getBytes()); //$NON-NLS-1$ 519 } 520 out.write(line.getBytes()); 521 out.write("\n".getBytes()); //$NON-NLS-1$ 522 } 523 } 524 } 525 finally 526 { 527 br.close(); 528 } 529 530 return command_results; 531 } 532 533 /** 534 * See http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html 535 * to understand how Process.exec works and its problems 536 * 537 * @param cmd Command to be executed. 538 * @param out Output Stream. 539 * @param timeout Timeout (secs.) 540 * @param monitor {@link IProgressMonitor} 541 * 542 * @return the {@link IStatus} of this process execution. 543 * 544 * @throws IOException Exception thrown in case there is any problem 545 * executing the command. 546 */ 547 private static IStatus executeRemoteDevicesCommand(String[] cmd, OutputStream out, int timeout, 548 String timeoutMsg, IStopCondition stopCondition, IProgressMonitor monitor) 549 throws IOException 550 { 551 552 IStatus status = Status.OK_STATUS; 553 554 long timeoutLimit = -1; 555 if (timeout != 0) 556 { 557 timeoutLimit = System.currentTimeMillis() + (timeout * 1000); 558 } 559 560 String fullCmd = ""; //$NON-NLS-1$ 561 for (String cmdArg : cmd) 562 { 563 fullCmd += cmdArg + " "; //$NON-NLS-1$ 564 } 565 if (out != null) 566 { 567 out.write(fullCmd.getBytes()); 568 out.write("\n".getBytes()); //$NON-NLS-1$ 569 } 570 571 Runtime r = Runtime.getRuntime(); 572 Process p = r.exec(cmd); 573 574 int errorCode = 0; 575 576 // inputStream / errorStream; 577 String[] commandResults = new String[] 578 { 579 "", "" //$NON-NLS-1$ //$NON-NLS-2$ 580 }; 581 582 commandResults = 583 readCmdOutputFromStreams(commandResults[0], commandResults[1], p.getInputStream(), 584 p.getErrorStream(), out); 585 586 while (!stopCondition.canStop()) 587 { 588 if ((monitor != null) && (monitor.isCanceled())) 589 { 590 p.destroy(); 591 return Status.CANCEL_STATUS; 592 } 593 594 try 595 { 596 errorCode = p.exitValue(); 597 if (errorCode != 0) 598 { 599 break; 600 } 601 602 } 603 catch (IllegalThreadStateException e) 604 { 605 // Process is still running... Proceed with loop 606 } 607 608 try 609 { 610 Thread.sleep(1000); 611 } 612 catch (InterruptedException e) 613 { 614 StudioLogger.error("Execute command: thread has been interrupted"); //$NON-NLS-1$ 615 } 616 617 if (timeout > 0) 618 { 619 try 620 { 621 testTimeout(timeoutLimit, ((timeoutMsg != null) ? timeoutMsg 622 : AndroidNLS.ERR_GenericTimeout)); 623 } 624 catch (TimeoutException e) 625 { 626 p.destroy(); 627 StudioLogger.debug("The timeout " + timeout //$NON-NLS-1$ 628 + " has been reached when executing the command " + fullCmd); //$NON-NLS-1$ 629 return new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, e.getMessage(), e); 630 } 631 } 632 633 } 634 635 commandResults = 636 readCmdOutputFromStreams(commandResults[0], commandResults[1], p.getInputStream(), 637 p.getErrorStream(), out); 638 639 if (errorCode != 0) 640 { 641 StudioLogger.debug("Command " + cmd + " returned an error code: " + errorCode); //$NON-NLS-1$ //$NON-NLS-2$ 642 status = 643 new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, NLS.bind( 644 AndroidNLS.ERR_CommandError, errorCode) + "\n" //$NON-NLS-1$ 645 + ((!commandResults[1].equals("")) ? commandResults[1] //$NON-NLS-1$ 646 : commandResults[0])); 647 } 648 else 649 { 650 status = new Status(IStatus.OK, AndroidPlugin.PLUGIN_ID, commandResults[0]); 651 } 652 653 return status; 654 } 655 656 /** 657 * Defines a stop condition 658 */ 659 interface IStopCondition 660 { 661 public boolean canStop(); 662 } 663 664 /** 665 * @param commandResults 666 * @param errorResults 667 * @param inputStream 668 * @param errorStream 669 * @param out 670 */ 671 private static String[] readCmdOutputFromStreams(String commandResults, String errorResults, 672 InputStream inputStream, InputStream errorStream, OutputStream out) 673 { 674 String[] results = new String[2]; 675 String line = ""; //$NON-NLS-1$ 676 677 BufferedReader brInput = new BufferedReader(new InputStreamReader(inputStream)); 678 BufferedReader brError = new BufferedReader(new InputStreamReader(errorStream)); 679 680 try 681 { 682 683 // input stream 684 if (brInput.ready()) 685 { 686 while ((line = brInput.readLine()) != null) 687 { 688 commandResults += line; 689 commandResults += "\n"; //$NON-NLS-1$ 690 if (out != null) 691 { 692 out.write(line.getBytes()); 693 out.write("\n".getBytes()); //$NON-NLS-1$ 694 } 695 } 696 } 697 698 // error stream 699 if (brError.ready()) 700 { 701 while ((line = brError.readLine()) != null) 702 { 703 errorResults += "\n"; //$NON-NLS-1$ 704 if (out != null) 705 { 706 out.write(line.getBytes()); 707 out.write("\n".getBytes()); //$NON-NLS-1$ 708 } 709 } 710 } 711 } 712 catch (IOException e) 713 { 714 StudioLogger.error("Cannot read command outputs"); //$NON-NLS-1$ 715 } 716 finally 717 { 718 try 719 { 720 brInput.close(); 721 brError.close(); 722 } 723 catch (IOException e) 724 { 725 StudioLogger.error("Could not close console stream: " + e.getMessage()); 726 } 727 } 728 729 results[0] = commandResults; 730 results[1] = errorResults; 731 732 return results; 733 734 } 735 736 /** 737 * Checks if the timeout limit has reached. 738 * 739 * @param timeoutLimit The system time limit that cannot be overtaken, in milliseconds. 740 * @throws StartTimeoutException When the system time limit is overtaken. 741 */ 742 private static void testTimeout(long timeoutLimit, String timeoutErrorMessage) 743 throws TimeoutException 744 { 745 if (System.currentTimeMillis() > timeoutLimit) 746 { 747 throw new TimeoutException(timeoutErrorMessage); 748 } 749 } 750 751 /** 752 * Creates a string with the command that should 753 * be called in order to run the application. 754 */ 755 private static String[] createRunCommand(String serialNumber, String activityName, 756 boolean debugMode) 757 { 758 String cmd[]; 759 String sdkPath = SdkUtils.getSdkPath(); 760 761 // The tools folder should exist and be here, but double-cheking 762 // once more wont kill 763 File f = new File(sdkPath + PLATFORM_TOOLS_FOLDER + File.separator); 764 if (!f.exists()) 765 { 766 StudioLogger 767 .error("Run: Could not find tools folder on " + sdkPath + PLATFORM_TOOLS_FOLDER //$NON-NLS-1$ 768 + File.separator); 769 } 770 else 771 { 772 if (!f.isDirectory()) 773 { 774 StudioLogger.error("Run: Invalid tools folder " + sdkPath + PLATFORM_TOOLS_FOLDER //$NON-NLS-1$ 775 + File.separator); 776 } 777 } 778 779 String completeAppPath = 780 activityName.substring(0, activityName.lastIndexOf(".")) + "/" + activityName; //$NON-NLS-1$ //$NON-NLS-2$ 781 if (debugMode) 782 { 783 // If debugMode option is checked, create command with the -D paramater 784 String cmdTemp[] = 785 { 786 sdkPath + PLATFORM_TOOLS_FOLDER + File.separator + ADB_COMMAND, 787 ADB_INSTANCE_PARAMETER, serialNumber, SHELL_CMD, AM_CMD, START_CMD, 788 ADB_AM_DEBUG, ADB_AM_NAME, completeAppPath 789 }; 790 cmd = cmdTemp; 791 } 792 else 793 { 794 // If debugMode option is unchecked, create command without the -D paramater 795 String cmdTemp[] = 796 { 797 sdkPath + PLATFORM_TOOLS_FOLDER + File.separator + ADB_COMMAND, 798 ADB_INSTANCE_PARAMETER, serialNumber, SHELL_CMD, AM_CMD, START_CMD, 799 ADB_AM_NAME, completeAppPath 800 }; 801 cmd = cmdTemp; 802 } 803 804 return cmd; 805 } 806 807 /** 808 * Check if the device is Online (i.e. if it's possible to communicate with it) 809 * Notice it is a verification of the status of the Device which may be different than the status of the Tml Instance. 810 * 811 * @param serialNumber 812 * @return true if the Device is online, false otherwise 813 */ 814 public static boolean isDeviceOnline(String serialNumber) 815 { 816 IDevice device = getDeviceBySerialNumber(serialNumber); 817 if ((device == null) || !device.isOnline()) 818 { 819 return false; 820 } 821 return true; 822 } 823 824 /** 825 * Return true if the Device is being shown on the OFFLINE state. 826 * 827 * @param serialNumber Devices serial number. 828 * 829 * @return <code>true</code> in case the Device if offline, 830 * <code>false</code> otherwise. 831 */ 832 public static boolean isDeviceOffline(String serialNumber) 833 { 834 835 IDevice device = getDeviceBySerialNumber(serialNumber); 836 return ((device == null) || ((device != null) && device.isOffline())); 837 } 838 839 /** 840 * Check if the device is completely loaded 841 * A device is completely loaded when it loads the HOME application 842 * 843 * @param serialNumber 844 * @return true if the Device has completely loaded; false otherwise 845 */ 846 public static boolean isDeviceCompletelyLoaded(String serialNumber) 847 { 848 return completelyUpDevices.contains(serialNumber); 849 } 850 851 /** 852 * Tests if the device represented by the serial number (if it exists) is an emulator 853 * 854 * @param serialNumber 855 * @return true if it is an emulator, false if not or non existent 856 */ 857 public static boolean isEmulator(String serialNumber) 858 { 859 IDevice device = getDeviceBySerialNumber(serialNumber); 860 if ((device != null) && device.isEmulator()) 861 { 862 return true; 863 } 864 return false; 865 } 866 867 public static boolean isRemote(String serialNumber) 868 { 869 // firstly, test if the serial number has the format "anything:digits" 870 Pattern p = Pattern.compile("(.)+:(\\d)+"); //$NON-NLS-1$ 871 Matcher m = p.matcher(serialNumber); 872 if (m.matches()) 873 { 874 IDevice device = getDeviceBySerialNumber(serialNumber); 875 if ((device != null) && !device.isEmulator()) 876 { 877 return true; 878 } 879 } 880 881 return false; 882 } 883 884 /** 885 * Execute an app in the Device 886 * 887 * @param serialNumber Serial number of the device where to execute the command 888 * @param remoteCommand command to be executed remotely on the Device 889 * @param monitor monitor associated with the operation 890 * 891 * @return The lines read from the command output 892 * 893 * @throws IOException 894 */ 895 public static Collection<String> execRemoteApp(String serialNumber, String remoteCommand, 896 final IProgressMonitor monitor) throws IOException 897 { 898 return executeShellCmd(serialNumber, remoteCommand, monitor); 899 } 900 901 /** 902 * Execute an app in the Device 903 * 904 * @param serialNumber Serial number of the device where to execute the command 905 * @param remoteCommands commands to be executed remotely on the Device 906 * @param monitor monitor associated with the operation 907 * 908 * @throws IOException 909 */ 910 public static Map<String, Collection<String>> execRemoteApp(String serialNumber, 911 Collection<String> remoteCommands, final IProgressMonitor monitor) throws IOException 912 { 913 Map<String, Collection<String>> cmdAnswers = 914 new LinkedHashMap<String, Collection<String>>(); 915 for (String remoteCommand : remoteCommands) 916 { 917 StudioLogger.debug(remoteCommand); 918 Collection<String> answers = executeShellCmd(serialNumber, remoteCommand, monitor); 919 cmdAnswers.put(remoteCommand, answers); 920 } 921 922 return cmdAnswers; 923 } 924 925 private static Collection<String> executeShellCmd(String serialNumber, final String cmd, 926 final IProgressMonitor monitor) 927 { 928 final Collection<String> results = new ArrayList<String>(); 929 IDevice d = getDeviceBySerialNumber(serialNumber); 930 if (d != null) 931 { 932 try 933 { 934 d.executeShellCommand(cmd, new MultiLineReceiver() 935 { 936 public boolean isCancelled() 937 { 938 return monitor.isCanceled(); 939 } 940 941 @Override 942 public void processNewLines(String[] lines) 943 { 944 for (String line : lines) 945 { 946 if ((!line.equals("")) && (!line.equals(cmd))) //$NON-NLS-1$ 947 { 948 results.add(line); 949 } 950 } 951 } 952 }, 0); 953 } 954 catch (Exception e) 955 { 956 StudioLogger.error(DDMSFacade.class, "Error executing shell command " + cmd //$NON-NLS-1$ 957 + " at device " + serialNumber, e); //$NON-NLS-1$ 958 } 959 } 960 return results; 961 } 962 963 /** 964 * Retrieves all properties from the device with provided serial number. 965 * @param serialNumber 966 * @return 967 */ 968 public static Properties getDeviceProperties(String serialNumber) 969 { 970 Properties instanceProperties = new Properties(); 971 if (serialNumber != null) 972 { 973 String key = ""; //$NON-NLS-1$ 974 String value = ""; //$NON-NLS-1$ 975 Collection<String> lines; 976 try 977 { 978 lines = execRemoteApp(serialNumber, "getprop", new NullProgressMonitor()); //$NON-NLS-1$ 979 980 for (String line : lines) 981 { 982 String[] split = line.split("]:"); //$NON-NLS-1$ 983 if (split.length >= 2) 984 { 985 if (!split[0].equals("")) //$NON-NLS-1$ 986 { 987 key = split[0].substring(1, split[0].length()); 988 if (!split[1].equals("")) //$NON-NLS-1$ 989 { 990 value = split[1].substring(2, split[1].length() - 1); 991 instanceProperties.setProperty(key, value); 992 } 993 } 994 995 } 996 } 997 } 998 catch (IOException e) 999 { 1000 StudioLogger.error("IOException while executing an app on device. " 1001 + e.getMessage()); 1002 } 1003 } 1004 return instanceProperties; 1005 } 1006 1007 /** 1008 * Retrieves a given property from the device with provided serial number. 1009 * 1010 * @param serialNumber 1011 * @param propertyName 1012 * 1013 * @return 1014 */ 1015 public static String getDeviceProperty(String serialNumber, String propertyName) 1016 { 1017 String result = null; 1018 IDevice device = DDMSFacade.getDeviceBySerialNumber(serialNumber); 1019 if (device != null) 1020 { 1021 result = device.getProperty(propertyName); 1022 } 1023 return result; 1024 } 1025 1026 /** 1027 * Get the name of the VM associated to the emulator running in the given deviceSerial identification. 1028 * 1029 * @param deviceSerial identification of the emulator whose vm name must be retrieved. 1030 * @return the name of the VM used by the emulator running with the given id, 1031 * or null if the vmname could be retrieved. 1032 */ 1033 public static String getVmName(final IDevice d) 1034 { 1035 String vmName = null; 1036 String serialNumber = d.getSerialNumber(); 1037 int MAX_TRIES = 120; 1038 int tries = 0; 1039 1040 while ((vmName == null) && (tries < MAX_TRIES)) 1041 { 1042 synchronized (avdNameMap) 1043 { 1044 vmName = avdNameMap.get(serialNumber); 1045 } 1046 1047 if (vmName == null) 1048 { 1049 vmName = d.getAvdName(); 1050 } 1051 1052 if (vmName == null) 1053 { 1054 try 1055 { 1056 Thread.sleep(1000); 1057 } 1058 catch (InterruptedException e) 1059 { 1060 //do nothing 1061 } 1062 finally 1063 { 1064 tries++; 1065 } 1066 } 1067 1068 //try to get vmname by telnet as last option 1069 if (vmName == null) 1070 { 1071 vmName = getVmNameByTelnet(serialNumber); 1072 } 1073 1074 } 1075 1076 if (vmName != null) 1077 { 1078 synchronized (avdNameMap) 1079 { 1080 if (avdNameMap.get(serialNumber) == null) 1081 { 1082 avdNameMap.put(serialNumber, vmName); 1083 } 1084 } 1085 1086 } 1087 1088 return vmName; 1089 } 1090 1091 private static String getVmNameByTelnet(String serialNumber) 1092 { 1093 Pattern pattern = Pattern.compile("emulator-([0-9]+)"); //$NON-NLS-1$ 1094 TelnetFrameworkAndroid telnet = new TelnetFrameworkAndroid(); 1095 Matcher matcher = pattern.matcher(serialNumber); 1096 matcher.matches(); 1097 String avdName = null; 1098 1099 try 1100 { 1101 Integer telnetPort = Integer.valueOf(matcher.group(1)); 1102 telnet.connect("localhost", telnetPort); //$NON-NLS-1$ 1103 String avdNameRaw = telnet.write("avd name\r\n", new String[] //$NON-NLS-1$ 1104 { 1105 "KO" //$NON-NLS-1$ 1106 }); 1107 1108 String split = avdNameRaw.contains("\r\n") ? "\r\n" : "\n"; 1109 1110 String[] outputArray = avdNameRaw.split(split); //$NON-NLS-1$ 1111 if (outputArray.length > 2) 1112 { 1113 avdName = outputArray[2]; 1114 } 1115 1116 if (avdName != null) 1117 { 1118 avdNameMap.put(serialNumber, avdName); 1119 } 1120 } 1121 catch (NumberFormatException e) 1122 { 1123 avdName = serialNumber; 1124 } 1125 catch (IOException e) 1126 { 1127 avdName = serialNumber; 1128 } 1129 finally 1130 { 1131 try 1132 { 1133 telnet.disconnect(); 1134 } 1135 catch (IOException e) 1136 { 1137 //Do nothing. 1138 } 1139 } 1140 return avdName; 1141 } 1142 1143 /** 1144 * Get the Device associated with the Android VM 1145 * 1146 * @param vmName Android VM name 1147 * @return Device associated with the given Android VM 1148 */ 1149 public static IDevice getDeviceWithVmName(String vmName) 1150 { 1151 IDevice toReturn = null; 1152 if (vmName != null) 1153 { 1154 Collection<IDevice> devices = connectedDevices.values(); 1155 for (IDevice d : devices) 1156 { 1157 if (d.isEmulator()) 1158 { 1159 String deviceVmName = DDMSFacade.getVmName(d); 1160 if (vmName.equals(deviceVmName)) 1161 { 1162 toReturn = d; 1163 break; 1164 } 1165 } 1166 } 1167 } 1168 return toReturn; 1169 } 1170 1171 /** 1172 * Securely get the name of the AVD associated to the given device. 1173 * 1174 * @param serialNumber The serialNumber of the device that we want the AVD name for 1175 * @return the name of the AVD used by the emulator running with the given device, 1176 * or null if the vmname could be retrieved. 1177 */ 1178 public static String getNameBySerialNumber(String serialNumber) 1179 { 1180 String avdName = null; 1181 IDevice d = getDeviceBySerialNumber(serialNumber); 1182 avdName = getNameByDevice(d); 1183 1184 return avdName; 1185 } 1186 1187 /** 1188 * Securely get the serial number of the given instance. 1189 * 1190 * @param instanceName The name of the instance we want the serial number of 1191 * @return the serial number of the given instance, or <code>null</code> if no instance with the 1192 * given name is online 1193 */ 1194 public static String getSerialNumberByName(String instanceName) 1195 { 1196 String serialNumber = null; 1197 if (instanceName != null) 1198 { 1199 List<IDevice> devices = null; 1200 synchronized (connectedDevices) 1201 { 1202 devices = new ArrayList<IDevice>(connectedDevices.size()); 1203 devices.addAll(connectedDevices.values()); 1204 } 1205 if (devices != null) 1206 { 1207 for (IDevice dev : devices) 1208 { 1209 if (instanceName.equals(getNameByDevice(dev))) 1210 { 1211 serialNumber = dev.getSerialNumber(); 1212 break; 1213 } 1214 } 1215 } 1216 } 1217 1218 return serialNumber; 1219 } 1220 1221 public static Collection<String> getRunningApplications(String serialNumber) 1222 { 1223 Collection<String> apps = new ArrayList<String>(); 1224 if (serialNumber != null) 1225 { 1226 IDevice dev = getDeviceBySerialNumber(serialNumber); 1227 if (dev != null) 1228 { 1229 Client[] clients = dev.getClients(); 1230 if ((clients != null) && (clients.length > 0)) 1231 { 1232 for (Client c : clients) 1233 { 1234 apps.add(c.getClientData().getClientDescription()); 1235 } 1236 } 1237 } 1238 } 1239 1240 return apps; 1241 } 1242 1243 /** 1244 * Dumps a HPROF file based on a client description and a device serial number 1245 * @param clientDescription A client description of a running application 1246 */ 1247 public static IStatus dumpHprofFile(String clientDescription, String serialNumber, 1248 IProgressMonitor monitor) 1249 { 1250 IStatus status = Status.OK_STATUS; 1251 monitor.beginTask(AndroidNLS.DumpHprofFile_GeneratingMemoryAnalysisOutput, 100); 1252 1253 // Retrive running apps 1254 monitor.setTaskName(AndroidNLS.DumpHprofFile_GettingRunningApplications); 1255 IDevice dev = getDeviceBySerialNumber(serialNumber); 1256 Client[] clients = dev.getClients(); 1257 monitor.worked(25); 1258 1259 // Store the shell 1260 final Shell[] shell = new Shell[1]; 1261 1262 PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() 1263 { 1264 public void run() 1265 { 1266 1267 shell[0] = new Shell(PlatformUI.getWorkbench().getDisplay()); 1268 1269 } 1270 }); 1271 1272 MotodevHProfDumpHandler hprofHandler = new MotodevHProfDumpHandler(shell[0], monitor); 1273 monitor.setTaskName(AndroidNLS.DumpHprofFile_SettingApplicationToAnalyse); 1274 hprofHandler.setSelectedApp(clientDescription); 1275 monitor.worked(25); 1276 1277 try 1278 { 1279 // Find a client with matching description and dum the HPROF file 1280 for (Client c : clients) 1281 { 1282 if (c.getClientData().getClientDescription().equals(clientDescription)) 1283 { 1284 // Set our handler as the HprofDumpHandler 1285 ClientData.setHprofDumpHandler(hprofHandler); 1286 monitor.setTaskName(AndroidNLS.DumpHprofFile_DumpingHprofFile); 1287 c.dumpHprof(); 1288 synchronized (DDMSFacade.class) 1289 { 1290 DDMSFacade.class.wait(); 1291 } 1292 1293 monitor.worked(50); 1294 } 1295 } 1296 } 1297 catch (Exception e) 1298 { 1299 // Status not ok 1300 status = Status.CANCEL_STATUS; 1301 } 1302 finally 1303 { 1304 monitor.done(); 1305 } 1306 1307 return status; 1308 1309 } 1310 1311 /** 1312 * Gets the AVD name of the device 1313 * 1314 * @param d The device to be searched for the AVD name 1315 * 1316 * @return The AVD name 1317 */ 1318 private static String getNameByDevice(final IDevice d) 1319 { 1320 String name = null; 1321 if (d != null) 1322 { 1323 if (d.isEmulator()) 1324 { 1325 1326 name = getVmName(d); 1327 } 1328 else 1329 { 1330 name = d.getSerialNumber(); 1331 } 1332 } 1333 1334 return name; 1335 } 1336 1337 /** 1338 * Create port forward for a given VM 1339 * 1340 * @param serialNumber Android serial number 1341 * @param from port number from 1342 * @param to port number to 1343 * @return true is the port forward was successful, false otherwise 1344 */ 1345 public static boolean createForward(String serialNumber, int from, int to) 1346 { 1347 boolean ok = true; 1348 IDevice device = getDeviceBySerialNumber(serialNumber); 1349 try 1350 { 1351 device.createForward(from, to); 1352 } 1353 catch (Exception e) 1354 { 1355 StudioLogger.error(DDMSFacade.class, "Error creating forward of device: " //$NON-NLS-1$ 1356 + serialNumber + " from " + from + " to " + to, e); //$NON-NLS-1$ //$NON-NLS-2$ 1357 ok = false; 1358 } 1359 return ok; 1360 } 1361 1362 /** 1363 * Kill the communication channel 1364 * 1365 * @param serialNumber The serial number of the device to kill 1366 */ 1367 public static void kill(String serialNumber) 1368 { 1369 if (isDeviceOnline(serialNumber)) 1370 { 1371 IDevice deviceToKill = getDeviceBySerialNumber(serialNumber); 1372 if (deviceToKill != null) 1373 { 1374 synchronized (consoleLock) 1375 { 1376 EmulatorConsole console = EmulatorConsole.getConsole(deviceToKill); 1377 if (console != null) 1378 { 1379 console.kill(); 1380 } 1381 } 1382 } 1383 } 1384 } 1385 1386 /** 1387 * Push files to device 1388 * 1389 * @param serialNumber Android device serial number 1390 * @param localDir local folder path 1391 * @param fileNames files to transfer 1392 * @param remoteDir destination folder path 1393 * @param timeout timeout for the operation 1394 * @param monitor monitor associated with the operation 1395 */ 1396 public static IStatus pushFiles(String serialNumber, String localDir, 1397 Collection<String> fileNames, String remoteDir, int timeout, 1398 final IProgressMonitor monitor, OutputStream outputStream) 1399 { 1400 return transferFiles(true, serialNumber, localDir, fileNames, remoteDir, timeout, monitor, 1401 outputStream); 1402 } 1403 1404 /** 1405 * Push files to device 1406 * 1407 * @param serialNumber Android device serial number 1408 * @param localFiles destination local files 1409 * @param remoteFiles remote files to transfer as localFiles to desktop 1410 * @param timeout timeout for the operation 1411 * @param monitor monitor associated with the operation 1412 */ 1413 public static IStatus pushFiles(String serialNumber, List<File> localFiles, 1414 List<String> remoteFiles, int timeout, final IProgressMonitor monitor, 1415 OutputStream outputStream) 1416 { 1417 return transferFiles(true, serialNumber, localFiles, remoteFiles, timeout, monitor, 1418 outputStream); 1419 } 1420 1421 /** 1422 * Pull files from device 1423 * 1424 * @param serialNumber Android device serial number 1425 * @param localDir local folder path 1426 * @param fileNames files to transfer 1427 * @param remoteDir destination folder path 1428 * @param timeout timeout for the operation 1429 * @param monitor monitor associated with the operation 1430 */ 1431 public static IStatus pullFiles(String serialNumber, String localDir, 1432 Collection<String> fileNames, String remoteDir, int timeout, 1433 final IProgressMonitor monitor, OutputStream outputStream) 1434 { 1435 return transferFiles(false, serialNumber, localDir, fileNames, remoteDir, timeout, monitor, 1436 outputStream); 1437 } 1438 1439 /** 1440 * Pull files from device 1441 * 1442 * @param serialNumber Android device serial number 1443 * @param localFiles local files to transfer as remoteFiles to device 1444 * @param remoteFiles destination remote files 1445 * @param timeout timeout for the operation 1446 * @param monitor monitor associated with the operation 1447 */ 1448 public static IStatus pullFiles(String serialNumber, List<File> localFiles, 1449 List<String> remoteFiles, int timeout, final IProgressMonitor monitor, 1450 OutputStream outputStream) 1451 { 1452 return transferFiles(false, serialNumber, localFiles, remoteFiles, timeout, monitor, 1453 outputStream); 1454 } 1455 1456 /** 1457 * Get the service used to transfer files to the Device 1458 * 1459 * @param device Device 1460 * @param timeout timeout for the operation 1461 * @param monitor monitor associated with the operation 1462 * @return The service used to transfer files to the Device 1463 * 1464 * @throws AndroidException 1465 */ 1466 private static SyncService getSyncService(IDevice device, int timeout, 1467 final IProgressMonitor monitor) throws AndroidException 1468 { 1469 1470 SyncService service = null; 1471 long timeoutLimit = System.currentTimeMillis() + timeout; 1472 do 1473 { 1474 if ((device != null) && (device.isOnline())) 1475 { 1476 try 1477 { 1478 service = device.getSyncService(); 1479 } 1480 catch (IOException e) 1481 { 1482 StudioLogger.debug("Couldn't get sync service; cause: " + e.getMessage()); //$NON-NLS-1$ 1483 } 1484 catch (com.android.ddmlib.TimeoutException e) 1485 { 1486 StudioLogger.debug("Couldn't get sync service; cause: " + e.getMessage()); //$NON-NLS-1$ 1487 } 1488 catch (AdbCommandRejectedException e) 1489 { 1490 StudioLogger.debug("Couldn't get sync service; cause: " + e.getMessage()); //$NON-NLS-1$ 1491 } 1492 } 1493 1494 try 1495 { 1496 Thread.sleep(100); 1497 } 1498 catch (InterruptedException e) 1499 { 1500 //do nothing 1501 } 1502 1503 if (monitor.isCanceled()) 1504 { 1505 StudioLogger.info("Operation canceled by the user"); //$NON-NLS-1$ 1506 return null; 1507 } 1508 1509 if (System.currentTimeMillis() > timeoutLimit) 1510 { 1511 StudioLogger.error("The emulator was not up within the set timeout"); //$NON-NLS-1$ 1512 throw new AndroidException( 1513 "Timeout while preparing to transfer files to the Device. " + device); //$NON-NLS-1$ 1514 } 1515 } 1516 while (service == null); 1517 1518 return service; 1519 } 1520 1521 private static IStatus transferFiles(boolean isPush, String serialNumber, String localDir, 1522 Collection<String> fileNames, String remoteDir, int timeout, 1523 final IProgressMonitor monitor, OutputStream outputStream) 1524 { 1525 List<File> localList = new ArrayList<File>(); 1526 List<String> remoteList = new ArrayList<String>(); 1527 for (String name : fileNames) 1528 { 1529 localList.add(new File(localDir, name)); 1530 remoteList.add(remoteDir + "/" + name); //$NON-NLS-1$ 1531 } 1532 return transferFiles(isPush, serialNumber, localList, remoteList, timeout, monitor, 1533 outputStream); 1534 } 1535 1536 private static IStatus transferFiles(boolean isPush, String serialNumber, 1537 List<File> localFiles, List<String> remoteFiles, int timeout, 1538 final IProgressMonitor monitor, OutputStream outputStream) 1539 { 1540 if (localFiles.size() != remoteFiles.size()) 1541 { 1542 return new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, 1543 AndroidNLS.ERR_DDMSFacade_IncompatibleFileLists); 1544 } 1545 1546 IStatus status = Status.OK_STATUS; 1547 IDevice device = DDMSFacade.getDeviceBySerialNumber(serialNumber); 1548 1549 SyncService service = null; 1550 try 1551 { 1552 service = getSyncService(device, timeout, monitor); 1553 if (service == null) 1554 { 1555 status = Status.CANCEL_STATUS; 1556 } 1557 else 1558 { 1559 final ISyncProgressMonitor syncMonitor = new ISyncProgressMonitor() 1560 { 1561 public void start(int i) 1562 { 1563 //do nothing 1564 } 1565 1566 public void stop() 1567 { 1568 //do nothing 1569 } 1570 1571 public void advance(int i) 1572 { 1573 //do nothing 1574 } 1575 1576 public boolean isCanceled() 1577 { 1578 return monitor.isCanceled(); 1579 } 1580 1581 public void startSubTask(String s) 1582 { 1583 //do nothing 1584 } 1585 }; 1586 1587 FileListingService flService = device.getFileListingService(); 1588 1589 for (int i = 0; i < localFiles.size(); i++) 1590 { 1591 File localFile = localFiles.get(i); 1592 String remotePath = remoteFiles.get(i); 1593 String absLocalFile = localFile.getAbsolutePath(); 1594 1595 String resultMessage = null; 1596 if (isPush) 1597 { 1598 StudioLogger.debug("Push " + absLocalFile + " to " + remotePath); //$NON-NLS-1$ //$NON-NLS-2$ 1599 try 1600 { 1601 service.pushFile(absLocalFile, remotePath, syncMonitor); 1602 } 1603 catch (SyncException e1) 1604 { 1605 StudioLogger 1606 .debug("Push result: " + "SyncException occured " + e1.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$ 1607 resultMessage = 1608 NLS.bind(AndroidNLS.CON_ConsolePush, absLocalFile, remotePath) 1609 + ": " + e1.getLocalizedMessage(); //$NON-NLS-1$ 1610 } 1611 catch (FileNotFoundException e1) 1612 { 1613 StudioLogger.debug("Push result: " + "FileNotFoundException occured " //$NON-NLS-1$ //$NON-NLS-2$ 1614 + e1.getMessage()); 1615 resultMessage = 1616 NLS.bind(AndroidNLS.CON_ConsolePush, absLocalFile, remotePath) 1617 + ": " + e1.getLocalizedMessage(); //$NON-NLS-1$ 1618 } 1619 catch (IOException e1) 1620 { 1621 StudioLogger 1622 .debug("Push result: " + "IOException occured " + e1.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$ 1623 resultMessage = 1624 NLS.bind(AndroidNLS.CON_ConsolePush, absLocalFile, remotePath) 1625 + ": " + e1.getLocalizedMessage(); //$NON-NLS-1$ 1626 } 1627 catch (com.android.ddmlib.TimeoutException e1) 1628 { 1629 StudioLogger 1630 .debug("Push result: " + "TimeoutException occured " + e1.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$ 1631 resultMessage = 1632 NLS.bind(AndroidNLS.CON_ConsolePush, absLocalFile, remotePath) 1633 + ": " + e1.getLocalizedMessage(); //$NON-NLS-1$ 1634 } 1635 1636 if ((outputStream != null) && (resultMessage != null)) 1637 { 1638 try 1639 { 1640 outputStream.write(resultMessage.getBytes()); 1641 outputStream.write('\n'); 1642 outputStream.flush(); 1643 } 1644 catch (Exception e) 1645 { 1646 // Do nothing 1647 } 1648 } 1649 1650 } 1651 else 1652 { 1653 FileEntry f1 = null; 1654 FileEntry f2 = null; 1655 1656 f2 = flService.getRoot(); 1657 flService.getChildren(f2, false, null); 1658 String[] dirs = remotePath.split("/"); //$NON-NLS-1$ 1659 1660 for (int j = 1; j < (dirs.length - 1); j++) 1661 { 1662 f1 = f2.findChild(dirs[j]); 1663 flService.getChildren(f1, false, null); 1664 f2 = f1; 1665 } 1666 1667 final FileEntry fileToPull = f2.findChild(dirs[dirs.length - 1]); 1668 1669 if (fileToPull != null) 1670 { 1671 try 1672 { 1673 service.pullFile(fileToPull, absLocalFile, syncMonitor); 1674 } 1675 catch (FileNotFoundException e) 1676 { 1677 resultMessage = e.getLocalizedMessage(); 1678 StudioLogger.debug("Pull result: " + e.getMessage()); //$NON-NLS-1$ 1679 } 1680 catch (SyncException e) 1681 { 1682 resultMessage = e.getLocalizedMessage(); 1683 StudioLogger.debug("Pull result: " + e.getMessage()); //$NON-NLS-1$ 1684 } 1685 catch (IOException e) 1686 { 1687 resultMessage = e.getLocalizedMessage(); 1688 StudioLogger.debug("Pull result: " + e.getMessage()); //$NON-NLS-1$ 1689 } 1690 catch (com.android.ddmlib.TimeoutException e) 1691 { 1692 resultMessage = e.getLocalizedMessage(); 1693 StudioLogger.debug("Pull result: " + e.getMessage()); //$NON-NLS-1$ 1694 } 1695 1696 if ((outputStream != null) && (resultMessage != null)) 1697 { 1698 String message = 1699 NLS.bind(AndroidNLS.CON_ConsolePull, 1700 fileToPull.getFullPath(), absLocalFile) 1701 + ": " + resultMessage; //$NON-NLS-1$ 1702 try 1703 { 1704 outputStream.write(message.getBytes()); 1705 outputStream.write('\n'); 1706 outputStream.flush(); 1707 } 1708 catch (IOException e) 1709 { 1710 //do nothing 1711 } 1712 } 1713 } 1714 else 1715 { 1716 resultMessage = 1717 NLS.bind(AndroidNLS.DDMSFacade_Remote_File_Not_Found, 1718 remotePath); 1719 StudioLogger.debug("Pull result: File not found " + remotePath); //$NON-NLS-1$ 1720 } 1721 } 1722 1723 if (resultMessage != null) 1724 { 1725 status = new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, resultMessage); 1726 } 1727 if (syncMonitor.isCanceled()) 1728 { 1729 status = Status.CANCEL_STATUS; 1730 break; 1731 } 1732 } 1733 } 1734 } 1735 catch (AndroidException e) 1736 { 1737 status = new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, e.getMessage()); 1738 } 1739 catch (NullPointerException e1) 1740 { 1741 status = 1742 new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, 1743 AndroidNLS.ERR_DDMSFacade_FileNotFound); 1744 } 1745 finally 1746 { 1747 if (service != null) 1748 { 1749 service.close(); 1750 } 1751 } 1752 1753 return status; 1754 } 1755 1756 /** 1757 * Check if the application is running in the device with specified serial number 1758 * @param serialNumber 1759 * @param applicationName 1760 * @return true if it is running, false otherwise 1761 */ 1762 public static boolean isApplicationRunning(String serialNumber, String applicationName) 1763 { 1764 IDevice dev = null; 1765 boolean running = false; 1766 dev = connectedDevices.get(serialNumber); 1767 if (dev != null) 1768 { 1769 running = dev.getClient(applicationName) != null; 1770 } 1771 return running; 1772 } 1773 1774 /** 1775 * Connect to a Remote Device given its IP/Port 1776 * 1777 * @param device the Remote Device Instance 1778 * @param host device host (IP) 1779 * @param port device port 1780 * @param timeout the maximum time allowed to successfully connect to the device 1781 * @param monitor the monitor of the operation 1782 * @return the status of the operation 1783 * @throws IOException 1784 */ 1785 public static IStatus connectTcpIp(final ISerialNumbered device, String host, String port, 1786 int timeout, IProgressMonitor monitor) throws IOException 1787 { 1788 SubMonitor subMonitor = SubMonitor.convert(monitor, 1000); 1789 1790 subMonitor.beginTask(AndroidNLS.DDMSFacade_MsgConnectingToDeviceViaTCPIP, 10); 1791 1792 IStatus status = Status.OK_STATUS; 1793 1794 String serialNumber = device.getSerialNumber(); 1795 if (!isDeviceOnline(serialNumber)) // check if it's already connected 1796 { 1797 String[] cmd = createConnectTcpIpCommand(host, port); 1798 1799 status = 1800 executeRemoteDevicesCommand( 1801 cmd, 1802 null, 1803 timeout, 1804 NLS.bind(AndroidNLS.ERR_RemoteDevice_TimeoutWhileConnecting, 1805 device.getDeviceName()), new IStopCondition() 1806 { 1807 1808 public boolean canStop() 1809 { 1810 String serialNumber = device.getSerialNumber(); 1811 if (serialNumber != null) 1812 { 1813 return isDeviceOnline(serialNumber); 1814 } 1815 else 1816 { 1817 return false; 1818 } 1819 } 1820 }, subMonitor.newChild(1000)); 1821 1822 } 1823 1824 subMonitor.worked(1000); 1825 1826 return status; 1827 } 1828 1829 /** 1830 * Method which switches the device connection mode from TCP/IP 1831 * to USB. 1832 * 1833 * @param device {@link ISerialNumbered} device to have its connection mode changed. 1834 * @param host The IP of the device. 1835 * @param port The port in which the TCP/IP connection is established. 1836 * @param timeout The maximum time which the switching operation is attempted. 1837 * @param monitor The {@link IProgressMonitor} which this operation is being computed. 1838 * 1839 * @return Returns the {@link IStatus} of this operation. 1840 * 1841 * @throws IOException Exception thrown in case something goes wrong while trying 1842 * to switch the device connection mode from TCP/IP to USB. 1843 */ 1844 public static IStatus switchFromTCPConnectionModeToUSBConnectionMode( 1845 final ISerialNumbered device, String host, String port, int timeout, 1846 IProgressMonitor monitor) throws IOException 1847 { 1848 SubMonitor subMonitor = SubMonitor.convert(monitor, 1000); 1849 1850 subMonitor.beginTask(AndroidNLS.DDMSFacade_MsgSwitchingDeviceFromTCPIPToUSB, 10); 1851 1852 IStatus status = Status.OK_STATUS; 1853 1854 String serialNumber = device.getSerialNumber(); 1855 if (isDeviceOnline(serialNumber)) // check if it's already connected 1856 { 1857 String[] cmd = createSwitchToUSBConnectionModeCommand(host, port); 1858 1859 status = 1860 executeRemoteDevicesCommand(cmd, null, timeout, NLS.bind( 1861 AndroidNLS.DDMSFacade_MsgTimeoutReachedSwitchingFromTCPToUSB, 1862 device.getDeviceName()), new IStopCondition() 1863 { 1864 1865 public boolean canStop() 1866 { 1867 String serialNumber = device.getSerialNumber(); 1868 if (serialNumber != null) 1869 { 1870 return isDeviceOnline(serialNumber); 1871 } 1872 else 1873 { 1874 return false; 1875 } 1876 } 1877 }, subMonitor.newChild(1000)); 1878 1879 } 1880 1881 subMonitor.worked(1000); 1882 1883 if (status.isOK()) 1884 { 1885 StudioLogger.collectUsageData(StudioLogger.WHAT_REMOTE_USB, 1886 StudioLogger.KIND_REMOTE_DEVICE, StudioLogger.DESCRIPTION_DEFAULT, 1887 AndroidPlugin.PLUGIN_ID, AndroidPlugin.getDefault().getBundle().getVersion() 1888 .toString()); 1889 } 1890 1891 return status; 1892 } 1893 1894 /** 1895 * Get the wireless ip from the connected handset 1896 * @param serialNumber 1897 * @param monitor 1898 * @return the ip or null if not possible to retrieve it 1899 */ 1900 public static String getWirelessIPfromHandset(String serialNumber, IProgressMonitor monitor) 1901 { 1902 String handset_wireless_ip = null; 1903 IDevice device = null; 1904 1905 device = connectedDevices.get(serialNumber); 1906 if (device != null) 1907 { 1908 // get the wi-fi name for executing the ipconfig command 1909 String wifiProperty = device.getProperty(WIFI_INTERFACE_DEVICE_PROPERTY); 1910 if (wifiProperty == null) 1911 { 1912 wifiProperty = DEFAULT_WIRELESS_DEVICE_PROPERTY; 1913 } 1914 // execute ipconfig command 1915 Collection<String> answers = 1916 executeShellCmd(serialNumber, IFCONFIG_CMD + " " + wifiProperty, monitor); //$NON-NLS-1$ 1917 1918 // Success message - for example 1919 // [tiwlan0: ip 192.168.0.174 mask 255.255.255.0 flags [up broadcast running multicast]] 1920 1921 if (answers != null) 1922 { 1923 String result = answers.toString(); 1924 if (result != null) 1925 { 1926 // splits the result of the shell command and gets the third position 1927 // that should be the IP number 1928 String[] result_splited = result.split(" "); //$NON-NLS-1$ 1929 if (result_splited.length >= 3) 1930 { 1931 // check whether there is an IP 1932 Pattern pattern = Pattern.compile(IP_PATTERN); 1933 Matcher matcher = pattern.matcher(result); 1934 if (matcher.find()) 1935 { 1936 handset_wireless_ip = result_splited[2]; 1937 } 1938 } 1939 } 1940 } 1941 } 1942 return handset_wireless_ip; 1943 } 1944 1945 /** 1946 * Switch adb connection mode of an specific device to TCPIP 1947 * 1948 * @param deviceName name of the handset instance 1949 * @param host wireless ip of the handset instance 1950 * @param port number of the port to be using during the connection 1951 * @param timeout the maximum time allowed to successfully connect to the device 1952 * @param monitor the monitor of the operation 1953 * @return the status of the operation 1954 * 1955 * @throws IOException Exception thrown in case there are problems switching the device. 1956 */ 1957 public static IStatus switchUSBtoTcpIp(String deviceName, final String serialNumber, 1958 String port, int timeout, IProgressMonitor monitor) throws IOException 1959 { 1960 SubMonitor subMonitor = SubMonitor.convert(monitor, 1000); 1961 1962 subMonitor.beginTask(AndroidNLS.DDMSFacade_MsgSwitchingFromUSBConnection, 10); 1963 1964 IStatus status = Status.OK_STATUS; 1965 1966 if (isDeviceOnline(serialNumber)) 1967 { 1968 String[] cmd = createSwitchToTcpIpCommand(serialNumber, port); 1969 1970 status = 1971 executeRemoteDevicesCommand(cmd, null, timeout, 1972 NLS.bind(AndroidNLS.ERR_WirelessRemoteDevice_TimeoutWhileConnecting, 1973 deviceName), new IStopCondition() 1974 { 1975 1976 public boolean canStop() 1977 { 1978 if (serialNumber != null) 1979 { 1980 return isDeviceOffline(serialNumber); 1981 } 1982 else 1983 { 1984 return false; 1985 } 1986 } 1987 }, subMonitor.newChild(1000)); 1988 1989 } 1990 1991 monitor.worked(1000); 1992 1993 return status; 1994 } 1995 1996 /** 1997 * Disconnect from a Remote Device given its IP/Port 1998 * 1999 * @param device the Remote Device Instance 2000 * @param host device host (IP) 2001 * @param port device port 2002 * @param timeout the maximum time allowed to successfully disconnect from the device 2003 * @param monitor the monitor of the operation 2004 * @return the status of the operation 2005 * @throws IOException 2006 */ 2007 public static IStatus disconnectTcpIp(final ISerialNumbered device, String host, String port, 2008 int timeout, IProgressMonitor monitor) throws IOException 2009 { 2010 IStatus status = Status.OK_STATUS; 2011 2012 String serialNumber = device.getSerialNumber(); 2013 if (isDeviceOnline(serialNumber)) // check if it's already disconnected 2014 { 2015 String[] cmd = createDisconnectTcpIpCommand(host, port); 2016 2017 status = 2018 executeRemoteDevicesCommand( 2019 cmd, 2020 null, 2021 timeout, 2022 NLS.bind(AndroidNLS.ERR_RemoteDevice_TimeoutWhileDisconnecting, 2023 device.getDeviceName()), new IStopCondition() 2024 { 2025 2026 public boolean canStop() 2027 { 2028 String serialNumber = device.getSerialNumber(); 2029 return !isDeviceOnline(serialNumber); 2030 } 2031 }, monitor); 2032 2033 } 2034 2035 return status; 2036 } 2037 2038 /** 2039 * Creates a string with the command that should 2040 * be called in order to connect to an IP/Port 2041 * 2042 * @param host device host (IP) 2043 * @param port device port 2044 * @return the command to be used to connect to an IP/Port 2045 */ 2046 private static String[] createConnectTcpIpCommand(String host, String port) 2047 { 2048 2049 String hostPort = host + ":" + port; //$NON-NLS-1$ 2050 2051 String sdkPath = SdkUtils.getSdkPath(); 2052 2053 String cmd[] = 2054 { 2055 sdkPath + PLATFORM_TOOLS_FOLDER + File.separator + ADB_COMMAND, 2056 CONNECT_TCPIP_CMD, hostPort 2057 }; 2058 2059 return cmd; 2060 } 2061 2062 /** 2063 * Creates a string with the command switches 2064 * a device from the TCP/IP connection mode 2065 * to the USB connection mode. 2066 * 2067 * @param host Device host (IP). 2068 * @param port Device port. 2069 * 2070 * @return The command to be used to switch back to USB connection mode. 2071 */ 2072 private static String[] createSwitchToUSBConnectionModeCommand(String host, String port) 2073 { 2074 2075 String hostPort = host + ":" + port; //$NON-NLS-1$ 2076 2077 String sdkPath = SdkUtils.getSdkPath(); 2078 2079 String cmd[] = 2080 { 2081 sdkPath + PLATFORM_TOOLS_FOLDER + File.separator + ADB_COMMAND, 2082 DEVICE_ID_INDICATOR, hostPort, USB_SWITCH_BACK_COMMAND 2083 }; 2084 2085 return cmd; 2086 } 2087 2088 /** 2089 * Creates a string with the command that should 2090 * be called in order to switch adb connection from USB to TPCIP mode 2091 * 2092 * @param serialNumber device serial number 2093 * @param port device port 2094 * @return the command to be used to switch adb connection to TCPIP mode 2095 */ 2096 private static String[] createSwitchToTcpIpCommand(String serialNumber, String port) 2097 { 2098 String sdkPath = SdkUtils.getSdkPath(); 2099 2100 String cmd[] = 2101 { 2102 sdkPath + PLATFORM_TOOLS_FOLDER + File.separator + ADB_COMMAND, 2103 ADB_INSTANCE_PARAMETER, serialNumber, TCPIP_CMD, port 2104 }; 2105 2106 return cmd; 2107 } 2108 2109 /** 2110 * Creates a string with the command that should 2111 * be called to delete a file from device 2112 * 2113 * @param serialNumber 2114 * @param file 2115 * @param folder 2116 * @return 2117 */ 2118 private static String[] createDeleteFileFromDeviceCommand(String serialNumber, String file, 2119 String folder) 2120 { 2121 String sdkPath = SdkUtils.getSdkPath(); 2122 2123 // The tools folder should exist and be here, but double-cheking 2124 // once more wont kill 2125 File f = new File(sdkPath + PLATFORM_TOOLS_FOLDER + File.separator); 2126 if (!f.exists()) 2127 { 2128 StudioLogger 2129 .error("Run: Could not find tools folder on " + sdkPath + PLATFORM_TOOLS_FOLDER //$NON-NLS-1$ 2130 + File.separator); 2131 } 2132 else 2133 { 2134 if (!f.isDirectory()) 2135 { 2136 StudioLogger.error("Run: Invalid tools folder " + sdkPath + PLATFORM_TOOLS_FOLDER //$NON-NLS-1$ 2137 + File.separator); 2138 } 2139 } 2140 2141 String cmd[] = 2142 { 2143 sdkPath + PLATFORM_TOOLS_FOLDER + File.separator + ADB_COMMAND, 2144 ADB_INSTANCE_PARAMETER, serialNumber, SHELL_CMD, 2145 "rm /" + folder + "/" + file //$NON-NLS-1$ //$NON-NLS-2$ 2146 }; 2147 2148 return cmd; 2149 } 2150 2151 /** 2152 * Uses the ADB shell command to remove a file from the device 2153 * 2154 * @param serialNumber 2155 * @param fileName 2156 * @param sdCardFolder 2157 * @return 2158 * @throws IOException 2159 */ 2160 private static boolean deleteFileFromDevice(String serialNumber, String fileName, String folder) 2161 throws IOException 2162 { 2163 2164 String command[] = createDeleteFileFromDeviceCommand(serialNumber, fileName, folder); 2165 IStatus status = executeRemoteDevicesCommand(command, null, 1000, "", new IStopCondition() //$NON-NLS-1$ 2166 { 2167 2168 public boolean canStop() 2169 { 2170 return true; 2171 } 2172 }, null); 2173 2174 return status.isOK(); 2175 } 2176 2177 /** 2178 * Check if a device identified by the serial number has a mounted SDCard 2179 * @param serialNumber the serial number 2180 * @return true if the device has a SDCard 2181 * @throws IOException 2182 */ 2183 public static boolean hasSDCard(String serialNumber) throws IOException 2184 { 2185 boolean hasSdCard = false; 2186 File tempSdCardFile = File.createTempFile("SDcheck", ".tmp"); //$NON-NLS-1$ //$NON-NLS-2$ 2187 boolean tempCopiedOnSdCardFile = 2188 pushFileToDevice(serialNumber, SDCARD_FOLDER, tempSdCardFile); 2189 2190 if (tempCopiedOnSdCardFile) 2191 { 2192 //trying to write on /sdcard folder (it works for phones previous from Platform 2.2) 2193 if (!deleteFileFromDevice(serialNumber, tempSdCardFile.getName(), SDCARD_FOLDER)) 2194 { 2195 StudioLogger 2196 .error("DDMSFacade: Could not delete tempfile from /sdcard when checking if card is enabled"); //$NON-NLS-1$ 2197 } 2198 hasSdCard = true; 2199 tempSdCardFile.delete(); 2200 } 2201 else 2202 { 2203 2204 File tempMntFile = File.createTempFile("SDcheck", ".tmp"); //$NON-NLS-1$ //$NON-NLS-2$ 2205 boolean tempCopiedOnMntFile = 2206 pushFileToDevice(serialNumber, MNT_SDCARD_FOLDER, tempSdCardFile); 2207 2208 if (tempCopiedOnMntFile) 2209 { 2210 //trying to write on /mnt/sdcard folder (it works for phones since Platform 2.2) 2211 if (!deleteFileFromDevice(serialNumber, tempMntFile.getName(), MNT_SDCARD_FOLDER)) 2212 { 2213 StudioLogger 2214 .error("DDMSFacade: Could not delete tempfile from /mnt/sdcard when checking if card is enabled"); //$NON-NLS-1$ 2215 } 2216 hasSdCard = true; 2217 tempMntFile.delete(); 2218 } 2219 2220 } 2221 2222 return hasSdCard; 2223 } 2224 2225 /** 2226 * 2227 * @param serialNumber 2228 * @param sdCardFolder 2229 * @param tempFile 2230 * @return true if manages to push file into the folder specified on device 2231 */ 2232 private static boolean pushFileToDevice(String serialNumber, String folder, File file) 2233 { 2234 Collection<String> files = new ArrayList<String>(); 2235 files.add(file.getName()); 2236 Path path = new Path(file.getAbsolutePath()); 2237 2238 IStatus status = 2239 pushFiles(serialNumber, path.removeLastSegments(1).toString(), files, folder, 2000, 2240 new NullProgressMonitor(), null); 2241 2242 return status.isOK(); 2243 } 2244 2245 /** 2246 * Creates a string with the command that should 2247 * be called in order to disconnect from an IP/Port 2248 * 2249 * @param host device host (IP) 2250 * @param port device port 2251 * @return the command to be used to disconnect from an IP/Port 2252 */ 2253 private static String[] createDisconnectTcpIpCommand(String host, String port) 2254 { 2255 2256 String hostPort = host + ":" + port; //$NON-NLS-1$ 2257 2258 String sdkPath = SdkUtils.getSdkPath(); 2259 String cmd[] = 2260 { 2261 sdkPath + PLATFORM_TOOLS_FOLDER + File.separator + ADB_COMMAND, 2262 DISCONNECT_TCPIP_CMD, hostPort 2263 }; 2264 2265 return cmd; 2266 } 2267 2268 public static void deleteFile(String serialNumber, String path) throws IOException 2269 { 2270 DDMSFacade.execRemoteApp(serialNumber, "rm " + path, //$NON-NLS-1$ 2271 new NullProgressMonitor()); 2272 } 2273 } 2274