1 /* 2 * Copyright (C) 2007 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.android.ddmlib; 18 19 import com.android.ddmlib.log.LogReceiver; 20 21 import java.io.File; 22 import java.io.IOException; 23 import java.nio.channels.SocketChannel; 24 import java.util.ArrayList; 25 import java.util.Collections; 26 import java.util.HashMap; 27 import java.util.List; 28 import java.util.Map; 29 import java.util.regex.Matcher; 30 import java.util.regex.Pattern; 31 32 33 /** 34 * A Device. It can be a physical device or an emulator. 35 */ 36 final class Device implements IDevice { 37 38 private final static int INSTALL_TIMEOUT = 2*60*1000; //2min 39 40 /** Emulator Serial Number regexp. */ 41 final static String RE_EMULATOR_SN = "emulator-(\\d+)"; //$NON-NLS-1$ 42 43 /** Serial number of the device */ 44 private String mSerialNumber = null; 45 46 /** Name of the AVD */ 47 private String mAvdName = null; 48 49 /** State of the device. */ 50 private DeviceState mState = null; 51 52 /** Device properties. */ 53 private final Map<String, String> mProperties = new HashMap<String, String>(); 54 private final Map<String, String> mMountPoints = new HashMap<String, String>(); 55 56 private final ArrayList<Client> mClients = new ArrayList<Client>(); 57 private DeviceMonitor mMonitor; 58 59 private static final String LOG_TAG = "Device"; 60 61 /** 62 * Socket for the connection monitoring client connection/disconnection. 63 */ 64 private SocketChannel mSocketChannel; 65 66 private boolean mArePropertiesSet = false; 67 68 /** 69 * Output receiver for "pm install package.apk" command line. 70 */ 71 private static final class InstallReceiver extends MultiLineReceiver { 72 73 private static final String SUCCESS_OUTPUT = "Success"; //$NON-NLS-1$ 74 private static final Pattern FAILURE_PATTERN = Pattern.compile("Failure\\s+\\[(.*)\\]"); //$NON-NLS-1$ 75 76 private String mErrorMessage = null; 77 InstallReceiver()78 public InstallReceiver() { 79 } 80 81 @Override processNewLines(String[] lines)82 public void processNewLines(String[] lines) { 83 for (String line : lines) { 84 if (line.length() > 0) { 85 if (line.startsWith(SUCCESS_OUTPUT)) { 86 mErrorMessage = null; 87 } else { 88 Matcher m = FAILURE_PATTERN.matcher(line); 89 if (m.matches()) { 90 mErrorMessage = m.group(1); 91 } 92 } 93 } 94 } 95 } 96 isCancelled()97 public boolean isCancelled() { 98 return false; 99 } 100 getErrorMessage()101 public String getErrorMessage() { 102 return mErrorMessage; 103 } 104 } 105 106 /* 107 * (non-Javadoc) 108 * @see com.android.ddmlib.IDevice#getSerialNumber() 109 */ getSerialNumber()110 public String getSerialNumber() { 111 return mSerialNumber; 112 } 113 114 /** {@inheritDoc} */ getAvdName()115 public String getAvdName() { 116 return mAvdName; 117 } 118 119 /** 120 * Sets the name of the AVD 121 */ setAvdName(String avdName)122 void setAvdName(String avdName) { 123 if (isEmulator() == false) { 124 throw new IllegalArgumentException( 125 "Cannot set the AVD name of the device is not an emulator"); 126 } 127 128 mAvdName = avdName; 129 } 130 131 /* 132 * (non-Javadoc) 133 * @see com.android.ddmlib.IDevice#getState() 134 */ getState()135 public DeviceState getState() { 136 return mState; 137 } 138 139 /** 140 * Changes the state of the device. 141 */ setState(DeviceState state)142 void setState(DeviceState state) { 143 mState = state; 144 } 145 146 147 /* 148 * (non-Javadoc) 149 * @see com.android.ddmlib.IDevice#getProperties() 150 */ getProperties()151 public Map<String, String> getProperties() { 152 return Collections.unmodifiableMap(mProperties); 153 } 154 155 /* 156 * (non-Javadoc) 157 * @see com.android.ddmlib.IDevice#getPropertyCount() 158 */ getPropertyCount()159 public int getPropertyCount() { 160 return mProperties.size(); 161 } 162 163 /* 164 * (non-Javadoc) 165 * @see com.android.ddmlib.IDevice#getProperty(java.lang.String) 166 */ getProperty(String name)167 public String getProperty(String name) { 168 return mProperties.get(name); 169 } 170 171 /** 172 * {@inheritDoc} 173 */ arePropertiesSet()174 public boolean arePropertiesSet() { 175 return mArePropertiesSet; 176 } 177 178 /** 179 * {@inheritDoc} 180 */ getPropertyCacheOrSync(String name)181 public String getPropertyCacheOrSync(String name) throws TimeoutException, 182 AdbCommandRejectedException, ShellCommandUnresponsiveException, IOException { 183 if (mArePropertiesSet) { 184 return getProperty(name); 185 } else { 186 return getPropertySync(name); 187 } 188 } 189 190 /** 191 * {@inheritDoc} 192 */ getPropertySync(String name)193 public String getPropertySync(String name) throws TimeoutException, 194 AdbCommandRejectedException, ShellCommandUnresponsiveException, IOException { 195 CollectingOutputReceiver receiver = new CollectingOutputReceiver(); 196 executeShellCommand(String.format("getprop '%s'", name), receiver); 197 String value = receiver.getOutput().trim(); 198 if (value.isEmpty()) { 199 return null; 200 } 201 return value; 202 } 203 getMountPoint(String name)204 public String getMountPoint(String name) { 205 return mMountPoints.get(name); 206 } 207 208 209 @Override toString()210 public String toString() { 211 return mSerialNumber; 212 } 213 214 /* 215 * (non-Javadoc) 216 * @see com.android.ddmlib.IDevice#isOnline() 217 */ isOnline()218 public boolean isOnline() { 219 return mState == DeviceState.ONLINE; 220 } 221 222 /* 223 * (non-Javadoc) 224 * @see com.android.ddmlib.IDevice#isEmulator() 225 */ isEmulator()226 public boolean isEmulator() { 227 return mSerialNumber.matches(RE_EMULATOR_SN); 228 } 229 230 /* 231 * (non-Javadoc) 232 * @see com.android.ddmlib.IDevice#isOffline() 233 */ isOffline()234 public boolean isOffline() { 235 return mState == DeviceState.OFFLINE; 236 } 237 238 /* 239 * (non-Javadoc) 240 * @see com.android.ddmlib.IDevice#isBootLoader() 241 */ isBootLoader()242 public boolean isBootLoader() { 243 return mState == DeviceState.BOOTLOADER; 244 } 245 246 /* 247 * (non-Javadoc) 248 * @see com.android.ddmlib.IDevice#hasClients() 249 */ hasClients()250 public boolean hasClients() { 251 return mClients.size() > 0; 252 } 253 254 /* 255 * (non-Javadoc) 256 * @see com.android.ddmlib.IDevice#getClients() 257 */ getClients()258 public Client[] getClients() { 259 synchronized (mClients) { 260 return mClients.toArray(new Client[mClients.size()]); 261 } 262 } 263 264 /* 265 * (non-Javadoc) 266 * @see com.android.ddmlib.IDevice#getClient(java.lang.String) 267 */ getClient(String applicationName)268 public Client getClient(String applicationName) { 269 synchronized (mClients) { 270 for (Client c : mClients) { 271 if (applicationName.equals(c.getClientData().getClientDescription())) { 272 return c; 273 } 274 } 275 276 } 277 278 return null; 279 } 280 281 /* 282 * (non-Javadoc) 283 * @see com.android.ddmlib.IDevice#getSyncService() 284 */ getSyncService()285 public SyncService getSyncService() 286 throws TimeoutException, AdbCommandRejectedException, IOException { 287 SyncService syncService = new SyncService(AndroidDebugBridge.getSocketAddress(), this); 288 if (syncService.openSync()) { 289 return syncService; 290 } 291 292 return null; 293 } 294 295 /* 296 * (non-Javadoc) 297 * @see com.android.ddmlib.IDevice#getFileListingService() 298 */ getFileListingService()299 public FileListingService getFileListingService() { 300 return new FileListingService(this); 301 } 302 getScreenshot()303 public RawImage getScreenshot() 304 throws TimeoutException, AdbCommandRejectedException, IOException { 305 return AdbHelper.getFrameBuffer(AndroidDebugBridge.getSocketAddress(), this); 306 } 307 executeShellCommand(String command, IShellOutputReceiver receiver)308 public void executeShellCommand(String command, IShellOutputReceiver receiver) 309 throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException, 310 IOException { 311 AdbHelper.executeRemoteCommand(AndroidDebugBridge.getSocketAddress(), command, this, 312 receiver, DdmPreferences.getTimeOut()); 313 } 314 executeShellCommand(String command, IShellOutputReceiver receiver, int maxTimeToOutputResponse)315 public void executeShellCommand(String command, IShellOutputReceiver receiver, 316 int maxTimeToOutputResponse) 317 throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException, 318 IOException { 319 AdbHelper.executeRemoteCommand(AndroidDebugBridge.getSocketAddress(), command, this, 320 receiver, maxTimeToOutputResponse); 321 } 322 runEventLogService(LogReceiver receiver)323 public void runEventLogService(LogReceiver receiver) 324 throws TimeoutException, AdbCommandRejectedException, IOException { 325 AdbHelper.runEventLogService(AndroidDebugBridge.getSocketAddress(), this, receiver); 326 } 327 runLogService(String logname, LogReceiver receiver)328 public void runLogService(String logname, LogReceiver receiver) 329 throws TimeoutException, AdbCommandRejectedException, IOException { 330 AdbHelper.runLogService(AndroidDebugBridge.getSocketAddress(), this, logname, receiver); 331 } 332 createForward(int localPort, int remotePort)333 public void createForward(int localPort, int remotePort) 334 throws TimeoutException, AdbCommandRejectedException, IOException { 335 AdbHelper.createForward(AndroidDebugBridge.getSocketAddress(), this, localPort, remotePort); 336 } 337 removeForward(int localPort, int remotePort)338 public void removeForward(int localPort, int remotePort) 339 throws TimeoutException, AdbCommandRejectedException, IOException { 340 AdbHelper.removeForward(AndroidDebugBridge.getSocketAddress(), this, localPort, remotePort); 341 } 342 343 /* 344 * (non-Javadoc) 345 * @see com.android.ddmlib.IDevice#getClientName(int) 346 */ getClientName(int pid)347 public String getClientName(int pid) { 348 synchronized (mClients) { 349 for (Client c : mClients) { 350 if (c.getClientData().getPid() == pid) { 351 return c.getClientData().getClientDescription(); 352 } 353 } 354 } 355 356 return null; 357 } 358 359 Device(DeviceMonitor monitor, String serialNumber, DeviceState deviceState)360 Device(DeviceMonitor monitor, String serialNumber, DeviceState deviceState) { 361 mMonitor = monitor; 362 mSerialNumber = serialNumber; 363 mState = deviceState; 364 } 365 getMonitor()366 DeviceMonitor getMonitor() { 367 return mMonitor; 368 } 369 addClient(Client client)370 void addClient(Client client) { 371 synchronized (mClients) { 372 mClients.add(client); 373 } 374 } 375 getClientList()376 List<Client> getClientList() { 377 return mClients; 378 } 379 hasClient(int pid)380 boolean hasClient(int pid) { 381 synchronized (mClients) { 382 for (Client client : mClients) { 383 if (client.getClientData().getPid() == pid) { 384 return true; 385 } 386 } 387 } 388 389 return false; 390 } 391 clearClientList()392 void clearClientList() { 393 synchronized (mClients) { 394 mClients.clear(); 395 } 396 } 397 398 /** 399 * Sets the client monitoring socket. 400 * @param socketChannel the sockets 401 */ setClientMonitoringSocket(SocketChannel socketChannel)402 void setClientMonitoringSocket(SocketChannel socketChannel) { 403 mSocketChannel = socketChannel; 404 } 405 406 /** 407 * Returns the client monitoring socket. 408 */ getClientMonitoringSocket()409 SocketChannel getClientMonitoringSocket() { 410 return mSocketChannel; 411 } 412 413 /** 414 * Removes a {@link Client} from the list. 415 * @param client the client to remove. 416 * @param notify Whether or not to notify the listeners of a change. 417 */ removeClient(Client client, boolean notify)418 void removeClient(Client client, boolean notify) { 419 mMonitor.addPortToAvailableList(client.getDebuggerListenPort()); 420 synchronized (mClients) { 421 mClients.remove(client); 422 } 423 if (notify) { 424 mMonitor.getServer().deviceChanged(this, CHANGE_CLIENT_LIST); 425 } 426 } 427 update(int changeMask)428 void update(int changeMask) { 429 if ((changeMask & CHANGE_BUILD_INFO) != 0) { 430 mArePropertiesSet = true; 431 } 432 mMonitor.getServer().deviceChanged(this, changeMask); 433 } 434 update(Client client, int changeMask)435 void update(Client client, int changeMask) { 436 mMonitor.getServer().clientChanged(client, changeMask); 437 } 438 addProperty(String label, String value)439 void addProperty(String label, String value) { 440 mProperties.put(label, value); 441 } 442 setMountingPoint(String name, String value)443 void setMountingPoint(String name, String value) { 444 mMountPoints.put(name, value); 445 } 446 pushFile(String local, String remote)447 public void pushFile(String local, String remote) 448 throws IOException, AdbCommandRejectedException, TimeoutException, SyncException { 449 try { 450 String targetFileName = getFileName(local); 451 452 Log.d(targetFileName, String.format("Uploading %1$s onto device '%2$s'", 453 targetFileName, getSerialNumber())); 454 455 SyncService sync = getSyncService(); 456 if (sync != null) { 457 String message = String.format("Uploading file onto device '%1$s'", 458 getSerialNumber()); 459 Log.d(LOG_TAG, message); 460 sync.pushFile(local, remote, SyncService.getNullProgressMonitor()); 461 } else { 462 throw new IOException("Unable to open sync connection!"); 463 } 464 } catch (TimeoutException e) { 465 Log.e(LOG_TAG, "Error during Sync: timeout."); 466 throw e; 467 468 } catch (SyncException e) { 469 Log.e(LOG_TAG, String.format("Error during Sync: %1$s", e.getMessage())); 470 throw e; 471 472 } catch (IOException e) { 473 Log.e(LOG_TAG, String.format("Error during Sync: %1$s", e.getMessage())); 474 throw e; 475 476 } 477 } 478 pullFile(String remote, String local)479 public void pullFile(String remote, String local) 480 throws IOException, AdbCommandRejectedException, TimeoutException, SyncException { 481 try { 482 String targetFileName = getFileName(remote); 483 484 Log.d(targetFileName, String.format("Downloading %1$s from device '%2$s'", 485 targetFileName, getSerialNumber())); 486 487 SyncService sync = getSyncService(); 488 if (sync != null) { 489 String message = String.format("Downloding file from device '%1$s'", 490 getSerialNumber()); 491 Log.d(LOG_TAG, message); 492 sync.pullFile(remote, local, SyncService.getNullProgressMonitor()); 493 } else { 494 throw new IOException("Unable to open sync connection!"); 495 } 496 } catch (TimeoutException e) { 497 Log.e(LOG_TAG, "Error during Sync: timeout."); 498 throw e; 499 500 } catch (SyncException e) { 501 Log.e(LOG_TAG, String.format("Error during Sync: %1$s", e.getMessage())); 502 throw e; 503 504 } catch (IOException e) { 505 Log.e(LOG_TAG, String.format("Error during Sync: %1$s", e.getMessage())); 506 throw e; 507 508 } 509 } 510 installPackage(String packageFilePath, boolean reinstall, String... extraArgs)511 public String installPackage(String packageFilePath, boolean reinstall, String... extraArgs) 512 throws InstallException { 513 try { 514 String remoteFilePath = syncPackageToDevice(packageFilePath); 515 String result = installRemotePackage(remoteFilePath, reinstall, extraArgs); 516 removeRemotePackage(remoteFilePath); 517 return result; 518 } catch (IOException e) { 519 throw new InstallException(e); 520 } catch (AdbCommandRejectedException e) { 521 throw new InstallException(e); 522 } catch (TimeoutException e) { 523 throw new InstallException(e); 524 } catch (SyncException e) { 525 throw new InstallException(e); 526 } 527 } 528 syncPackageToDevice(String localFilePath)529 public String syncPackageToDevice(String localFilePath) 530 throws IOException, AdbCommandRejectedException, TimeoutException, SyncException { 531 try { 532 String packageFileName = getFileName(localFilePath); 533 String remoteFilePath = String.format("/data/local/tmp/%1$s", packageFileName); //$NON-NLS-1$ 534 535 Log.d(packageFileName, String.format("Uploading %1$s onto device '%2$s'", 536 packageFileName, getSerialNumber())); 537 538 SyncService sync = getSyncService(); 539 if (sync != null) { 540 String message = String.format("Uploading file onto device '%1$s'", 541 getSerialNumber()); 542 Log.d(LOG_TAG, message); 543 sync.pushFile(localFilePath, remoteFilePath, SyncService.getNullProgressMonitor()); 544 } else { 545 throw new IOException("Unable to open sync connection!"); 546 } 547 return remoteFilePath; 548 } catch (TimeoutException e) { 549 Log.e(LOG_TAG, "Error during Sync: timeout."); 550 throw e; 551 552 } catch (SyncException e) { 553 Log.e(LOG_TAG, String.format("Error during Sync: %1$s", e.getMessage())); 554 throw e; 555 556 } catch (IOException e) { 557 Log.e(LOG_TAG, String.format("Error during Sync: %1$s", e.getMessage())); 558 throw e; 559 560 } 561 } 562 563 /** 564 * Helper method to retrieve the file name given a local file path 565 * @param filePath full directory path to file 566 * @return {@link String} file name 567 */ getFileName(String filePath)568 private String getFileName(String filePath) { 569 return new File(filePath).getName(); 570 } 571 installRemotePackage(String remoteFilePath, boolean reinstall, String... extraArgs)572 public String installRemotePackage(String remoteFilePath, boolean reinstall, 573 String... extraArgs) throws InstallException { 574 try { 575 InstallReceiver receiver = new InstallReceiver(); 576 StringBuilder optionString = new StringBuilder(); 577 if (reinstall) { 578 optionString.append("-r "); 579 } 580 for (String arg : extraArgs) { 581 optionString.append(arg); 582 optionString.append(' '); 583 } 584 String cmd = String.format("pm install %1$s \"%2$s\"", optionString.toString(), 585 remoteFilePath); 586 executeShellCommand(cmd, receiver, INSTALL_TIMEOUT); 587 return receiver.getErrorMessage(); 588 } catch (TimeoutException e) { 589 throw new InstallException(e); 590 } catch (AdbCommandRejectedException e) { 591 throw new InstallException(e); 592 } catch (ShellCommandUnresponsiveException e) { 593 throw new InstallException(e); 594 } catch (IOException e) { 595 throw new InstallException(e); 596 } 597 } 598 removeRemotePackage(String remoteFilePath)599 public void removeRemotePackage(String remoteFilePath) throws InstallException { 600 try { 601 executeShellCommand("rm " + remoteFilePath, new NullOutputReceiver(), INSTALL_TIMEOUT); 602 } catch (IOException e) { 603 throw new InstallException(e); 604 } catch (TimeoutException e) { 605 throw new InstallException(e); 606 } catch (AdbCommandRejectedException e) { 607 throw new InstallException(e); 608 } catch (ShellCommandUnresponsiveException e) { 609 throw new InstallException(e); 610 } 611 } 612 uninstallPackage(String packageName)613 public String uninstallPackage(String packageName) throws InstallException { 614 try { 615 InstallReceiver receiver = new InstallReceiver(); 616 executeShellCommand("pm uninstall " + packageName, receiver, INSTALL_TIMEOUT); 617 return receiver.getErrorMessage(); 618 } catch (TimeoutException e) { 619 throw new InstallException(e); 620 } catch (AdbCommandRejectedException e) { 621 throw new InstallException(e); 622 } catch (ShellCommandUnresponsiveException e) { 623 throw new InstallException(e); 624 } catch (IOException e) { 625 throw new InstallException(e); 626 } 627 } 628 629 /* 630 * (non-Javadoc) 631 * @see com.android.ddmlib.IDevice#reboot() 632 */ reboot(String into)633 public void reboot(String into) 634 throws TimeoutException, AdbCommandRejectedException, IOException { 635 AdbHelper.reboot(into, AndroidDebugBridge.getSocketAddress(), this); 636 } 637 } 638