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