• 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     private static final int BATTERY_TIMEOUT = 2*1000; //2 seconds
40     private static final int GETPROP_TIMEOUT = 2*1000; //2 seconds
41 
42     /** Emulator Serial Number regexp. */
43     final static String RE_EMULATOR_SN = "emulator-(\\d+)"; //$NON-NLS-1$
44 
45     /** Serial number of the device */
46     private String mSerialNumber = null;
47 
48     /** Name of the AVD */
49     private String mAvdName = null;
50 
51     /** State of the device. */
52     private DeviceState mState = null;
53 
54     /** Device properties. */
55     private final Map<String, String> mProperties = new HashMap<String, String>();
56     private final Map<String, String> mMountPoints = new HashMap<String, String>();
57 
58     private final ArrayList<Client> mClients = new ArrayList<Client>();
59     private DeviceMonitor mMonitor;
60 
61     private static final String LOG_TAG = "Device";
62 
63     /**
64      * Socket for the connection monitoring client connection/disconnection.
65      */
66     private SocketChannel mSocketChannel;
67 
68     private boolean mArePropertiesSet = false;
69 
70     private Integer mLastBatteryLevel = null;
71     private long mLastBatteryCheckTime = 0;
72 
73     /**
74      * Output receiver for "pm install package.apk" command line.
75      */
76     private static final class InstallReceiver extends MultiLineReceiver {
77 
78         private static final String SUCCESS_OUTPUT = "Success"; //$NON-NLS-1$
79         private static final Pattern FAILURE_PATTERN = Pattern.compile("Failure\\s+\\[(.*)\\]"); //$NON-NLS-1$
80 
81         private String mErrorMessage = null;
82 
InstallReceiver()83         public InstallReceiver() {
84         }
85 
86         @Override
processNewLines(String[] lines)87         public void processNewLines(String[] lines) {
88             for (String line : lines) {
89                 if (line.length() > 0) {
90                     if (line.startsWith(SUCCESS_OUTPUT)) {
91                         mErrorMessage = null;
92                     } else {
93                         Matcher m = FAILURE_PATTERN.matcher(line);
94                         if (m.matches()) {
95                             mErrorMessage = m.group(1);
96                         }
97                     }
98                 }
99             }
100         }
101 
102         @Override
isCancelled()103         public boolean isCancelled() {
104             return false;
105         }
106 
getErrorMessage()107         public String getErrorMessage() {
108             return mErrorMessage;
109         }
110     }
111 
112     /**
113      * Output receiver for "dumpsys battery" command line.
114      */
115     private static final class BatteryReceiver extends MultiLineReceiver {
116         private static final Pattern BATTERY_LEVEL = Pattern.compile("\\s*level: (\\d+)");
117         private static final Pattern SCALE = Pattern.compile("\\s*scale: (\\d+)");
118 
119         private Integer mBatteryLevel = null;
120         private Integer mBatteryScale = null;
121 
122         /**
123          * Get the parsed percent battery level.
124          * @return
125          */
getBatteryLevel()126         public Integer getBatteryLevel() {
127             if (mBatteryLevel != null && mBatteryScale != null) {
128                 return (mBatteryLevel * 100) / mBatteryScale;
129             }
130             return null;
131         }
132 
133         @Override
processNewLines(String[] lines)134         public void processNewLines(String[] lines) {
135             for (String line : lines) {
136                 Matcher batteryMatch = BATTERY_LEVEL.matcher(line);
137                 if (batteryMatch.matches()) {
138                     try {
139                         mBatteryLevel = Integer.parseInt(batteryMatch.group(1));
140                     } catch (NumberFormatException e) {
141                         Log.w(LOG_TAG, String.format("Failed to parse %s as an integer",
142                                 batteryMatch.group(1)));
143                     }
144                 }
145                 Matcher scaleMatch = SCALE.matcher(line);
146                 if (scaleMatch.matches()) {
147                     try {
148                         mBatteryScale = Integer.parseInt(scaleMatch.group(1));
149                     } catch (NumberFormatException e) {
150                         Log.w(LOG_TAG, String.format("Failed to parse %s as an integer",
151                                 batteryMatch.group(1)));
152                     }
153                 }
154             }
155         }
156 
157         @Override
isCancelled()158         public boolean isCancelled() {
159             return false;
160         }
161     }
162 
163     /*
164      * (non-Javadoc)
165      * @see com.android.ddmlib.IDevice#getSerialNumber()
166      */
167     @Override
getSerialNumber()168     public String getSerialNumber() {
169         return mSerialNumber;
170     }
171 
172     /** {@inheritDoc} */
173     @Override
getAvdName()174     public String getAvdName() {
175         return mAvdName;
176     }
177 
178     /**
179      * Sets the name of the AVD
180      */
setAvdName(String avdName)181     void setAvdName(String avdName) {
182         if (isEmulator() == false) {
183             throw new IllegalArgumentException(
184                     "Cannot set the AVD name of the device is not an emulator");
185         }
186 
187         mAvdName = avdName;
188     }
189 
190     /*
191      * (non-Javadoc)
192      * @see com.android.ddmlib.IDevice#getState()
193      */
194     @Override
getState()195     public DeviceState getState() {
196         return mState;
197     }
198 
199     /**
200      * Changes the state of the device.
201      */
setState(DeviceState state)202     void setState(DeviceState state) {
203         mState = state;
204     }
205 
206 
207     /*
208      * (non-Javadoc)
209      * @see com.android.ddmlib.IDevice#getProperties()
210      */
211     @Override
getProperties()212     public Map<String, String> getProperties() {
213         return Collections.unmodifiableMap(mProperties);
214     }
215 
216     /*
217      * (non-Javadoc)
218      * @see com.android.ddmlib.IDevice#getPropertyCount()
219      */
220     @Override
getPropertyCount()221     public int getPropertyCount() {
222         return mProperties.size();
223     }
224 
225     /*
226      * (non-Javadoc)
227      * @see com.android.ddmlib.IDevice#getProperty(java.lang.String)
228      */
229     @Override
getProperty(String name)230     public String getProperty(String name) {
231         return mProperties.get(name);
232     }
233 
234     /**
235      * {@inheritDoc}
236      */
237     @Override
arePropertiesSet()238     public boolean arePropertiesSet() {
239         return mArePropertiesSet;
240     }
241 
242     /**
243      * {@inheritDoc}
244      */
245     @Override
getPropertyCacheOrSync(String name)246     public String getPropertyCacheOrSync(String name) throws TimeoutException,
247             AdbCommandRejectedException, ShellCommandUnresponsiveException, IOException {
248         if (mArePropertiesSet) {
249             return getProperty(name);
250         } else {
251             return getPropertySync(name);
252         }
253     }
254 
255     /**
256      * {@inheritDoc}
257      */
258     @Override
getPropertySync(String name)259     public String getPropertySync(String name) throws TimeoutException,
260             AdbCommandRejectedException, ShellCommandUnresponsiveException, IOException {
261         CollectingOutputReceiver receiver = new CollectingOutputReceiver();
262         executeShellCommand(String.format("getprop '%s'", name), receiver, GETPROP_TIMEOUT);
263         String value = receiver.getOutput().trim();
264         if (value.isEmpty()) {
265             return null;
266         }
267         return value;
268     }
269 
270     @Override
getMountPoint(String name)271     public String getMountPoint(String name) {
272         return mMountPoints.get(name);
273     }
274 
275 
276     @Override
toString()277     public String toString() {
278         return mSerialNumber;
279     }
280 
281     /*
282      * (non-Javadoc)
283      * @see com.android.ddmlib.IDevice#isOnline()
284      */
285     @Override
isOnline()286     public boolean isOnline() {
287         return mState == DeviceState.ONLINE;
288     }
289 
290     /*
291      * (non-Javadoc)
292      * @see com.android.ddmlib.IDevice#isEmulator()
293      */
294     @Override
isEmulator()295     public boolean isEmulator() {
296         return mSerialNumber.matches(RE_EMULATOR_SN);
297     }
298 
299     /*
300      * (non-Javadoc)
301      * @see com.android.ddmlib.IDevice#isOffline()
302      */
303     @Override
isOffline()304     public boolean isOffline() {
305         return mState == DeviceState.OFFLINE;
306     }
307 
308     /*
309      * (non-Javadoc)
310      * @see com.android.ddmlib.IDevice#isBootLoader()
311      */
312     @Override
isBootLoader()313     public boolean isBootLoader() {
314         return mState == DeviceState.BOOTLOADER;
315     }
316 
317     /*
318      * (non-Javadoc)
319      * @see com.android.ddmlib.IDevice#hasClients()
320      */
321     @Override
hasClients()322     public boolean hasClients() {
323         return mClients.size() > 0;
324     }
325 
326     /*
327      * (non-Javadoc)
328      * @see com.android.ddmlib.IDevice#getClients()
329      */
330     @Override
getClients()331     public Client[] getClients() {
332         synchronized (mClients) {
333             return mClients.toArray(new Client[mClients.size()]);
334         }
335     }
336 
337     /*
338      * (non-Javadoc)
339      * @see com.android.ddmlib.IDevice#getClient(java.lang.String)
340      */
341     @Override
getClient(String applicationName)342     public Client getClient(String applicationName) {
343         synchronized (mClients) {
344             for (Client c : mClients) {
345                 if (applicationName.equals(c.getClientData().getClientDescription())) {
346                     return c;
347                 }
348             }
349 
350         }
351 
352         return null;
353     }
354 
355     /*
356      * (non-Javadoc)
357      * @see com.android.ddmlib.IDevice#getSyncService()
358      */
359     @Override
getSyncService()360     public SyncService getSyncService()
361             throws TimeoutException, AdbCommandRejectedException, IOException {
362         SyncService syncService = new SyncService(AndroidDebugBridge.getSocketAddress(), this);
363         if (syncService.openSync()) {
364             return syncService;
365          }
366 
367         return null;
368     }
369 
370     /*
371      * (non-Javadoc)
372      * @see com.android.ddmlib.IDevice#getFileListingService()
373      */
374     @Override
getFileListingService()375     public FileListingService getFileListingService() {
376         return new FileListingService(this);
377     }
378 
379     @Override
getScreenshot()380     public RawImage getScreenshot()
381             throws TimeoutException, AdbCommandRejectedException, IOException {
382         return AdbHelper.getFrameBuffer(AndroidDebugBridge.getSocketAddress(), this);
383     }
384 
385     @Override
executeShellCommand(String command, IShellOutputReceiver receiver)386     public void executeShellCommand(String command, IShellOutputReceiver receiver)
387             throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
388             IOException {
389         AdbHelper.executeRemoteCommand(AndroidDebugBridge.getSocketAddress(), command, this,
390                 receiver, DdmPreferences.getTimeOut());
391     }
392 
393     @Override
executeShellCommand(String command, IShellOutputReceiver receiver, int maxTimeToOutputResponse)394     public void executeShellCommand(String command, IShellOutputReceiver receiver,
395             int maxTimeToOutputResponse)
396             throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
397             IOException {
398         AdbHelper.executeRemoteCommand(AndroidDebugBridge.getSocketAddress(), command, this,
399                 receiver, maxTimeToOutputResponse);
400     }
401 
402     @Override
runEventLogService(LogReceiver receiver)403     public void runEventLogService(LogReceiver receiver)
404             throws TimeoutException, AdbCommandRejectedException, IOException {
405         AdbHelper.runEventLogService(AndroidDebugBridge.getSocketAddress(), this, receiver);
406     }
407 
408     @Override
runLogService(String logname, LogReceiver receiver)409     public void runLogService(String logname, LogReceiver receiver)
410             throws TimeoutException, AdbCommandRejectedException, IOException {
411         AdbHelper.runLogService(AndroidDebugBridge.getSocketAddress(), this, logname, receiver);
412     }
413 
414     @Override
createForward(int localPort, int remotePort)415     public void createForward(int localPort, int remotePort)
416             throws TimeoutException, AdbCommandRejectedException, IOException {
417         AdbHelper.createForward(AndroidDebugBridge.getSocketAddress(), this,
418                 String.format("tcp:%d", localPort),     //$NON-NLS-1$
419                 String.format("tcp:%d", remotePort));   //$NON-NLS-1$
420     }
421 
422     @Override
createForward(int localPort, String remoteSocketName, DeviceUnixSocketNamespace namespace)423     public void createForward(int localPort, String remoteSocketName,
424             DeviceUnixSocketNamespace namespace) throws TimeoutException,
425             AdbCommandRejectedException, IOException {
426         AdbHelper.createForward(AndroidDebugBridge.getSocketAddress(), this,
427                 String.format("tcp:%d", localPort),     //$NON-NLS-1$
428                 String.format("%s:%s", namespace.getType(), remoteSocketName));   //$NON-NLS-1$
429     }
430 
431     @Override
removeForward(int localPort, int remotePort)432     public void removeForward(int localPort, int remotePort)
433             throws TimeoutException, AdbCommandRejectedException, IOException {
434         AdbHelper.removeForward(AndroidDebugBridge.getSocketAddress(), this,
435                 String.format("tcp:%d", localPort),     //$NON-NLS-1$
436                 String.format("tcp:%d", remotePort));   //$NON-NLS-1$
437     }
438 
439     @Override
removeForward(int localPort, String remoteSocketName, DeviceUnixSocketNamespace namespace)440     public void removeForward(int localPort, String remoteSocketName,
441             DeviceUnixSocketNamespace namespace) throws TimeoutException,
442             AdbCommandRejectedException, IOException {
443         AdbHelper.removeForward(AndroidDebugBridge.getSocketAddress(), this,
444                 String.format("tcp:%d", localPort),     //$NON-NLS-1$
445                 String.format("%s:%s", namespace.getType(), remoteSocketName));   //$NON-NLS-1$
446     }
447 
448     /*
449      * (non-Javadoc)
450      * @see com.android.ddmlib.IDevice#getClientName(int)
451      */
452     @Override
getClientName(int pid)453     public String getClientName(int pid) {
454         synchronized (mClients) {
455             for (Client c : mClients) {
456                 if (c.getClientData().getPid() == pid) {
457                     return c.getClientData().getClientDescription();
458                 }
459             }
460         }
461 
462         return null;
463     }
464 
465 
Device(DeviceMonitor monitor, String serialNumber, DeviceState deviceState)466     Device(DeviceMonitor monitor, String serialNumber, DeviceState deviceState) {
467         mMonitor = monitor;
468         mSerialNumber = serialNumber;
469         mState = deviceState;
470     }
471 
getMonitor()472     DeviceMonitor getMonitor() {
473         return mMonitor;
474     }
475 
addClient(Client client)476     void addClient(Client client) {
477         synchronized (mClients) {
478             mClients.add(client);
479         }
480     }
481 
getClientList()482     List<Client> getClientList() {
483         return mClients;
484     }
485 
hasClient(int pid)486     boolean hasClient(int pid) {
487         synchronized (mClients) {
488             for (Client client : mClients) {
489                 if (client.getClientData().getPid() == pid) {
490                     return true;
491                 }
492             }
493         }
494 
495         return false;
496     }
497 
clearClientList()498     void clearClientList() {
499         synchronized (mClients) {
500             mClients.clear();
501         }
502     }
503 
504     /**
505      * Sets the client monitoring socket.
506      * @param socketChannel the sockets
507      */
setClientMonitoringSocket(SocketChannel socketChannel)508     void setClientMonitoringSocket(SocketChannel socketChannel) {
509         mSocketChannel = socketChannel;
510     }
511 
512     /**
513      * Returns the client monitoring socket.
514      */
getClientMonitoringSocket()515     SocketChannel getClientMonitoringSocket() {
516         return mSocketChannel;
517     }
518 
519     /**
520      * Removes a {@link Client} from the list.
521      * @param client the client to remove.
522      * @param notify Whether or not to notify the listeners of a change.
523      */
removeClient(Client client, boolean notify)524     void removeClient(Client client, boolean notify) {
525         mMonitor.addPortToAvailableList(client.getDebuggerListenPort());
526         synchronized (mClients) {
527             mClients.remove(client);
528         }
529         if (notify) {
530             mMonitor.getServer().deviceChanged(this, CHANGE_CLIENT_LIST);
531         }
532     }
533 
update(int changeMask)534     void update(int changeMask) {
535         if ((changeMask & CHANGE_BUILD_INFO) != 0) {
536             mArePropertiesSet = true;
537         }
538         mMonitor.getServer().deviceChanged(this, changeMask);
539     }
540 
update(Client client, int changeMask)541     void update(Client client, int changeMask) {
542         mMonitor.getServer().clientChanged(client, changeMask);
543     }
544 
addProperty(String label, String value)545     void addProperty(String label, String value) {
546         mProperties.put(label, value);
547     }
548 
setMountingPoint(String name, String value)549     void setMountingPoint(String name, String value) {
550         mMountPoints.put(name, value);
551     }
552 
553     @Override
pushFile(String local, String remote)554     public void pushFile(String local, String remote)
555             throws IOException, AdbCommandRejectedException, TimeoutException, SyncException {
556         SyncService sync = null;
557         try {
558             String targetFileName = getFileName(local);
559 
560             Log.d(targetFileName, String.format("Uploading %1$s onto device '%2$s'",
561                     targetFileName, getSerialNumber()));
562 
563             sync = getSyncService();
564             if (sync != null) {
565                 String message = String.format("Uploading file onto device '%1$s'",
566                         getSerialNumber());
567                 Log.d(LOG_TAG, message);
568                 sync.pushFile(local, remote, SyncService.getNullProgressMonitor());
569             } else {
570                 throw new IOException("Unable to open sync connection!");
571             }
572         } catch (TimeoutException e) {
573             Log.e(LOG_TAG, "Error during Sync: timeout.");
574             throw e;
575 
576         } catch (SyncException e) {
577             Log.e(LOG_TAG, String.format("Error during Sync: %1$s", e.getMessage()));
578             throw e;
579 
580         } catch (IOException e) {
581             Log.e(LOG_TAG, String.format("Error during Sync: %1$s", e.getMessage()));
582             throw e;
583 
584         } finally {
585             if (sync != null) {
586                 sync.close();
587             }
588         }
589     }
590 
591     @Override
pullFile(String remote, String local)592     public void pullFile(String remote, String local)
593             throws IOException, AdbCommandRejectedException, TimeoutException, SyncException {
594         SyncService sync = null;
595         try {
596             String targetFileName = getFileName(remote);
597 
598             Log.d(targetFileName, String.format("Downloading %1$s from device '%2$s'",
599                     targetFileName, getSerialNumber()));
600 
601             sync = getSyncService();
602             if (sync != null) {
603                 String message = String.format("Downloding file from device '%1$s'",
604                         getSerialNumber());
605                 Log.d(LOG_TAG, message);
606                 sync.pullFile(remote, local, SyncService.getNullProgressMonitor());
607             } else {
608                 throw new IOException("Unable to open sync connection!");
609             }
610         } catch (TimeoutException e) {
611             Log.e(LOG_TAG, "Error during Sync: timeout.");
612             throw e;
613 
614         } catch (SyncException e) {
615             Log.e(LOG_TAG, String.format("Error during Sync: %1$s", e.getMessage()));
616             throw e;
617 
618         } catch (IOException e) {
619             Log.e(LOG_TAG, String.format("Error during Sync: %1$s", e.getMessage()));
620             throw e;
621 
622         } finally {
623             if (sync != null) {
624                 sync.close();
625             }
626         }
627     }
628 
629     @Override
installPackage(String packageFilePath, boolean reinstall, String... extraArgs)630     public String installPackage(String packageFilePath, boolean reinstall, String... extraArgs)
631             throws InstallException {
632         try {
633             String remoteFilePath = syncPackageToDevice(packageFilePath);
634             String result = installRemotePackage(remoteFilePath, reinstall, extraArgs);
635             removeRemotePackage(remoteFilePath);
636             return result;
637         } catch (IOException e) {
638             throw new InstallException(e);
639         } catch (AdbCommandRejectedException e) {
640             throw new InstallException(e);
641         } catch (TimeoutException e) {
642             throw new InstallException(e);
643         } catch (SyncException e) {
644             throw new InstallException(e);
645         }
646     }
647 
648     @Override
syncPackageToDevice(String localFilePath)649     public String syncPackageToDevice(String localFilePath)
650             throws IOException, AdbCommandRejectedException, TimeoutException, SyncException {
651         SyncService sync = null;
652         try {
653             String packageFileName = getFileName(localFilePath);
654             String remoteFilePath = String.format("/data/local/tmp/%1$s", packageFileName); //$NON-NLS-1$
655 
656             Log.d(packageFileName, String.format("Uploading %1$s onto device '%2$s'",
657                     packageFileName, getSerialNumber()));
658 
659             sync = getSyncService();
660             if (sync != null) {
661                 String message = String.format("Uploading file onto device '%1$s'",
662                         getSerialNumber());
663                 Log.d(LOG_TAG, message);
664                 sync.pushFile(localFilePath, remoteFilePath, SyncService.getNullProgressMonitor());
665             } else {
666                 throw new IOException("Unable to open sync connection!");
667             }
668             return remoteFilePath;
669         } catch (TimeoutException e) {
670             Log.e(LOG_TAG, "Error during Sync: timeout.");
671             throw e;
672 
673         } catch (SyncException e) {
674             Log.e(LOG_TAG, String.format("Error during Sync: %1$s", e.getMessage()));
675             throw e;
676 
677         } catch (IOException e) {
678             Log.e(LOG_TAG, String.format("Error during Sync: %1$s", e.getMessage()));
679             throw e;
680 
681         } finally {
682             if (sync != null) {
683                 sync.close();
684             }
685         }
686     }
687 
688     /**
689      * Helper method to retrieve the file name given a local file path
690      * @param filePath full directory path to file
691      * @return {@link String} file name
692      */
getFileName(String filePath)693     private String getFileName(String filePath) {
694         return new File(filePath).getName();
695     }
696 
697     @Override
installRemotePackage(String remoteFilePath, boolean reinstall, String... extraArgs)698     public String installRemotePackage(String remoteFilePath, boolean reinstall,
699             String... extraArgs) throws InstallException {
700         try {
701             InstallReceiver receiver = new InstallReceiver();
702             StringBuilder optionString = new StringBuilder();
703             if (reinstall) {
704                 optionString.append("-r ");
705             }
706             for (String arg : extraArgs) {
707                 optionString.append(arg);
708                 optionString.append(' ');
709             }
710             String cmd = String.format("pm install %1$s \"%2$s\"", optionString.toString(),
711                     remoteFilePath);
712             executeShellCommand(cmd, receiver, INSTALL_TIMEOUT);
713             return receiver.getErrorMessage();
714         } catch (TimeoutException e) {
715             throw new InstallException(e);
716         } catch (AdbCommandRejectedException e) {
717             throw new InstallException(e);
718         } catch (ShellCommandUnresponsiveException e) {
719             throw new InstallException(e);
720         } catch (IOException e) {
721             throw new InstallException(e);
722         }
723     }
724 
725     @Override
removeRemotePackage(String remoteFilePath)726     public void removeRemotePackage(String remoteFilePath) throws InstallException {
727         try {
728             executeShellCommand("rm " + remoteFilePath, new NullOutputReceiver(), INSTALL_TIMEOUT);
729         } catch (IOException e) {
730             throw new InstallException(e);
731         } catch (TimeoutException e) {
732             throw new InstallException(e);
733         } catch (AdbCommandRejectedException e) {
734             throw new InstallException(e);
735         } catch (ShellCommandUnresponsiveException e) {
736             throw new InstallException(e);
737         }
738     }
739 
740     @Override
uninstallPackage(String packageName)741     public String uninstallPackage(String packageName) throws InstallException {
742         try {
743             InstallReceiver receiver = new InstallReceiver();
744             executeShellCommand("pm uninstall " + packageName, receiver, INSTALL_TIMEOUT);
745             return receiver.getErrorMessage();
746         } catch (TimeoutException e) {
747             throw new InstallException(e);
748         } catch (AdbCommandRejectedException e) {
749             throw new InstallException(e);
750         } catch (ShellCommandUnresponsiveException e) {
751             throw new InstallException(e);
752         } catch (IOException e) {
753             throw new InstallException(e);
754         }
755     }
756 
757     /*
758      * (non-Javadoc)
759      * @see com.android.ddmlib.IDevice#reboot()
760      */
761     @Override
reboot(String into)762     public void reboot(String into)
763             throws TimeoutException, AdbCommandRejectedException, IOException {
764         AdbHelper.reboot(into, AndroidDebugBridge.getSocketAddress(), this);
765     }
766 
767     @Override
getBatteryLevel()768     public Integer getBatteryLevel() throws TimeoutException, AdbCommandRejectedException,
769             IOException, ShellCommandUnresponsiveException {
770         // use default of 5 minutes
771         return getBatteryLevel(5 * 60 * 1000);
772     }
773 
774     @Override
getBatteryLevel(long freshnessMs)775     public Integer getBatteryLevel(long freshnessMs) throws TimeoutException,
776             AdbCommandRejectedException, IOException, ShellCommandUnresponsiveException {
777         if (mLastBatteryLevel != null
778                 && mLastBatteryCheckTime > (System.currentTimeMillis() - freshnessMs)) {
779             return mLastBatteryLevel;
780         }
781         BatteryReceiver receiver = new BatteryReceiver();
782         executeShellCommand("dumpsys battery", receiver, BATTERY_TIMEOUT);
783         mLastBatteryLevel = receiver.getBatteryLevel();
784         mLastBatteryCheckTime = System.currentTimeMillis();
785         return mLastBatteryLevel;
786     }
787 }
788