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