2 * Copyright (C) 2007 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.ddmlib;
19 import com.android.ddmlib.SyncService.SyncResult;
20 import com.android.ddmlib.log.LogReceiver;
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;
30 import java.util.regex.Matcher;
31 import java.util.regex.Pattern;
35 * A Device. It can be a physical device or an emulator.
37 final class Device implements IDevice {
39 private final static int INSTALL_TIMEOUT = 2*60*1000; //2min
41 /** Emulator Serial Number regexp. */
42 final static String RE_EMULATOR_SN = "emulator-(\\d+)"; //$NON-NLS-1$
44 /** Serial number of the device */
45 private String mSerialNumber = null;
47 /** Name of the AVD */
48 private String mAvdName = null;
50 /** State of the device. */
51 private DeviceState mState = null;
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>();
57 private final ArrayList<Client> mClients = new ArrayList<Client>();
58 private DeviceMonitor mMonitor;
60 private static final String LOG_TAG = "Device";
63 * Socket for the connection monitoring client connection/disconnection.
65 private SocketChannel mSocketChannel;
68 * Output receiver for "pm install package.apk" command line.
70 private static final class InstallReceiver extends MultiLineReceiver {
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$
75 private String mErrorMessage = null;
77 public InstallReceiver() {
81 public void processNewLines(String[] lines) {
82 for (String line : lines) {
83 if (line.length() > 0) {
84 if (line.startsWith(SUCCESS_OUTPUT)) {
87 Matcher m = FAILURE_PATTERN.matcher(line);
89 mErrorMessage = m.group(1);
96 public boolean isCancelled() {
100 public String getErrorMessage() {
101 return mErrorMessage;
107 * @see com.android.ddmlib.IDevice#getSerialNumber()
109 public String getSerialNumber() {
110 return mSerialNumber;
114 public String getAvdName() {
119 * Sets the name of the AVD
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");
132 * @see com.android.ddmlib.IDevice#getState()
134 public DeviceState getState() {
139 * Changes the state of the device.
141 void setState(DeviceState state) {
148 * @see com.android.ddmlib.IDevice#getProperties()
150 public Map<String, String> getProperties() {
151 return Collections.unmodifiableMap(mProperties);
156 * @see com.android.ddmlib.IDevice#getPropertyCount()
158 public int getPropertyCount() {
159 return mProperties.size();
164 * @see com.android.ddmlib.IDevice#getProperty(java.lang.String)
166 public String getProperty(String name) {
167 return mProperties.get(name);
170 public String getMountPoint(String name) {
171 return mMountPoints.get(name);
176 public String toString() {
177 return mSerialNumber;
182 * @see com.android.ddmlib.IDevice#isOnline()
184 public boolean isOnline() {
185 return mState == DeviceState.ONLINE;
190 * @see com.android.ddmlib.IDevice#isEmulator()
192 public boolean isEmulator() {
193 return mSerialNumber.matches(RE_EMULATOR_SN);
198 * @see com.android.ddmlib.IDevice#isOffline()
200 public boolean isOffline() {
201 return mState == DeviceState.OFFLINE;
206 * @see com.android.ddmlib.IDevice#isBootLoader()
208 public boolean isBootLoader() {
209 return mState == DeviceState.BOOTLOADER;
214 * @see com.android.ddmlib.IDevice#hasClients()
216 public boolean hasClients() {
217 return mClients.size() > 0;
222 * @see com.android.ddmlib.IDevice#getClients()
224 public Client[] getClients() {
225 synchronized (mClients) {
226 return mClients.toArray(new Client[mClients.size()]);
232 * @see com.android.ddmlib.IDevice#getClient(java.lang.String)
234 public Client getClient(String applicationName) {
235 synchronized (mClients) {
236 for (Client c : mClients) {
237 if (applicationName.equals(c.getClientData().getClientDescription())) {
249 * @see com.android.ddmlib.IDevice#getSyncService()
251 public SyncService getSyncService()
252 throws TimeoutException, AdbCommandRejectedException, IOException {
253 SyncService syncService = new SyncService(AndroidDebugBridge.getSocketAddress(), this);
254 if (syncService.openSync()) {
263 * @see com.android.ddmlib.IDevice#getFileListingService()
265 public FileListingService getFileListingService() {
266 return new FileListingService(this);
269 public RawImage getScreenshot()
270 throws TimeoutException, AdbCommandRejectedException, IOException {
271 return AdbHelper.getFrameBuffer(AndroidDebugBridge.getSocketAddress(), this);
274 public void executeShellCommand(String command, IShellOutputReceiver receiver)
275 throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
277 AdbHelper.executeRemoteCommand(AndroidDebugBridge.getSocketAddress(), command, this,
278 receiver, DdmPreferences.getTimeOut());
281 public void executeShellCommand(String command, IShellOutputReceiver receiver,
282 int maxTimeToOutputResponse)
283 throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
285 AdbHelper.executeRemoteCommand(AndroidDebugBridge.getSocketAddress(), command, this,
286 receiver, maxTimeToOutputResponse);
289 public void runEventLogService(LogReceiver receiver)
290 throws TimeoutException, AdbCommandRejectedException, IOException {
291 AdbHelper.runEventLogService(AndroidDebugBridge.getSocketAddress(), this, receiver);
294 public void runLogService(String logname, LogReceiver receiver)
295 throws TimeoutException, AdbCommandRejectedException, IOException {
296 AdbHelper.runLogService(AndroidDebugBridge.getSocketAddress(), this, logname, receiver);
299 public void createForward(int localPort, int remotePort)
300 throws TimeoutException, AdbCommandRejectedException, IOException {
301 AdbHelper.createForward(AndroidDebugBridge.getSocketAddress(), this, localPort, remotePort);
304 public void removeForward(int localPort, int remotePort)
305 throws TimeoutException, AdbCommandRejectedException, IOException {
306 AdbHelper.removeForward(AndroidDebugBridge.getSocketAddress(), this, localPort, remotePort);
311 * @see com.android.ddmlib.IDevice#getClientName(int)
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();
326 Device(DeviceMonitor monitor, String serialNumber, DeviceState deviceState) {
328 mSerialNumber = serialNumber;
329 mState = deviceState;
332 DeviceMonitor getMonitor() {
336 void addClient(Client client) {
337 synchronized (mClients) {
338 mClients.add(client);
342 List<Client> getClientList() {
346 boolean hasClient(int pid) {
347 synchronized (mClients) {
348 for (Client client : mClients) {
349 if (client.getClientData().getPid() == pid) {
358 void clearClientList() {
359 synchronized (mClients) {
365 * Sets the client monitoring socket.
366 * @param socketChannel the sockets
368 void setClientMonitoringSocket(SocketChannel socketChannel) {
369 mSocketChannel = socketChannel;
373 * Returns the client monitoring socket.
375 SocketChannel getClientMonitoringSocket() {
376 return mSocketChannel;
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.
384 void removeClient(Client client, boolean notify) {
385 mMonitor.addPortToAvailableList(client.getDebuggerListenPort());
386 synchronized (mClients) {
387 mClients.remove(client);
390 mMonitor.getServer().deviceChanged(this, CHANGE_CLIENT_LIST);
394 void update(int changeMask) {
395 mMonitor.getServer().deviceChanged(this, changeMask);
398 void update(Client client, int changeMask) {
399 mMonitor.getServer().clientChanged(client, changeMask);
402 void addProperty(String label, String value) {
403 mProperties.put(label, value);
406 void setMountingPoint(String name, String value) {
407 mMountPoints.put(name, value);
410 public String installPackage(String packageFilePath, boolean reinstall)
411 throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
413 String remoteFilePath = syncPackageToDevice(packageFilePath);
414 String result = installRemotePackage(remoteFilePath, reinstall);
415 removeRemotePackage(remoteFilePath);
419 public String syncPackageToDevice(String localFilePath)
420 throws IOException, AdbCommandRejectedException, TimeoutException {
422 String packageFileName = getFileName(localFilePath);
423 String remoteFilePath = String.format("/data/local/tmp/%1$s", packageFileName); //$NON-NLS-1$
425 Log.d(packageFileName, String.format("Uploading %1$s onto device '%2$s'",
426 packageFileName, getSerialNumber()));
428 SyncService sync = getSyncService();
430 String message = String.format("Uploading file onto device '%1$s'",
432 Log.d(LOG_TAG, message);
433 SyncResult result = sync.pushFile(localFilePath, remoteFilePath,
434 SyncService.getNullProgressMonitor());
436 if (result.getCode() != SyncService.RESULT_OK) {
437 throw new IOException(String.format("Unable to upload file: %1$s",
438 result.getMessage()));
441 throw new IOException("Unable to open sync connection!");
443 return remoteFilePath;
444 } catch (TimeoutException e) {
445 Log.e(LOG_TAG, "Unable to open sync connection! Timeout.");
447 } catch (IOException e) {
448 Log.e(LOG_TAG, String.format("Unable to open sync connection! reason: %1$s",
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
459 private String getFileName(String filePath) {
460 return new File(filePath).getName();
463 public String installRemotePackage(String remoteFilePath, boolean reinstall)
464 throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
466 InstallReceiver receiver = new InstallReceiver();
467 String cmd = String.format(reinstall ? "pm install -r \"%1$s\"" : "pm install \"%1$s\"",
469 executeShellCommand(cmd, receiver, INSTALL_TIMEOUT);
470 return receiver.getErrorMessage();
476 public void removeRemotePackage(String remoteFilePath)
477 throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
479 // now we delete the app we sync'ed
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",
492 public String uninstallPackage(String packageName)
493 throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
495 InstallReceiver receiver = new InstallReceiver();
496 executeShellCommand("pm uninstall " + packageName, receiver, INSTALL_TIMEOUT);
497 return receiver.getErrorMessage();
502 * @see com.android.ddmlib.IDevice#reboot()
504 public void reboot(String into)
505 throws TimeoutException, AdbCommandRejectedException, IOException {
506 AdbHelper.reboot(into, AndroidDebugBridge.getSocketAddress(), this);