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