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