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