• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 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.cts;
18 
19 import com.android.ddmlib.Client;
20 import com.android.ddmlib.ClientData;
21 import com.android.ddmlib.IDevice;
22 import com.android.ddmlib.IShellOutputReceiver;
23 import com.android.ddmlib.MultiLineReceiver;
24 import com.android.ddmlib.NullOutputReceiver;
25 import com.android.ddmlib.RawImage;
26 import com.android.ddmlib.SyncService;
27 import com.android.ddmlib.SyncService.ISyncProgressMonitor;
28 import com.android.ddmlib.SyncService.SyncResult;
29 import com.android.ddmlib.log.LogReceiver;
30 import com.android.ddmlib.log.LogReceiver.ILogListener;
31 
32 import java.io.BufferedReader;
33 import java.io.IOException;
34 import java.io.InputStream;
35 import java.io.InputStreamReader;
36 import java.util.ArrayList;
37 import java.util.Collection;
38 import java.util.HashMap;
39 import java.util.Iterator;
40 import java.util.Timer;
41 import java.util.TimerTask;
42 import java.util.regex.Matcher;
43 import java.util.regex.Pattern;
44 
45 /**
46  * Manage the testing target device for<br>
47  * <ul>
48  *    <li> install/uninstall test package, and
49  *    <li> execute command on device
50  *    <li> get command feedback from standard output
51  * </ul>
52  */
53 public class TestDevice implements DeviceObserver {
54     private static final String DEVICE_SETUP_APK = "TestDeviceSetup";
55     private static final String DEVICE_SETUP_APP_PACKAGE_NAME = "android.tests.devicesetup";
56     private static final String DEFAULT_TEST_RUNNER_NAME =
57                                   "android.test.InstrumentationTestRunner";
58     private static final String ACTION_INSTALL = "install";
59     private static final String ACTION_UNINSTALL = "uninstall";
60     private static final String ACTION_GET_DEV_INFO = "getDeviceInfo";
61     private static final String sInstrumentResultExpr = "INSTRUMENTATION_RESULT: (\\S+)=(.+)";
62 
63     public static final int STATUS_IDLE = 0;
64     public static final int STATUS_BUSY = STATUS_IDLE + 1;
65     public static final int STATUS_OFFLINE = STATUS_IDLE + 2;
66     private static final String STATUS_STR_IDLE = "idle";
67     private static final String STATUS_STR_IN_USE = "in use";
68     private static final String STATUS_STR_OFFLINE = "offline";
69 
70     /** Interval [ms] for polling a device until boot is completed. */
71     private static final int REBOOT_POLL_INTERVAL = 5000;
72     /** Number of times a booting device should be polled before we give up. */
73     private static final int REBOOT_POLL_COUNT = 10 * 60 * 1000 / REBOOT_POLL_INTERVAL;
74     /** Max time [ms] to wait for <code>adb shell getprop</code> to return a result. */
75     private static final int GETPROP_TIMEOUT = 5000;
76 
77     public static final Pattern INSTRUMENT_RESULT_PATTERN;
78 
79     private BatchModeResultParser mBatchModeResultParser;
80 
81     private DeviceObserver mDeviceObserver;
82     private IDevice mDevice;
83     private DeviceParameterCollector mDeviceInfo;
84 
85     private SyncService mSyncService;
86 
87     private PackageActionObserver mUninstallObserver;
88 
89     private int mStatus;
90     private static HashMap<Integer, String> mStatusMap;
91     private PackageActionTimer mPackageActionTimer;
92 
93     private ObjectSync mObjectSync;
94 
95     private MultiplexingLogListener logListener = new MultiplexingLogListener();
96     private LogReceiver logReceiver = new LogReceiver(logListener);
97 
98     private class LogServiceThread extends Thread {
99         @Override
run()100         public void run() {
101             try {
102                 mDevice.runLogService("main", logReceiver);
103             } catch (IOException e) {
104             }
105         }
106 
107         /**
108          * Cancel logging and exit this thread.
109          */
cancelLogService()110         public void cancelLogService() {
111             // this will cause the loop in our run method to
112             // exit, terminating this thread.
113             logReceiver.cancel();
114         }
115     }
116 
117     private LogServiceThread logServiceThread;
118 
119     static {
120         INSTRUMENT_RESULT_PATTERN = Pattern.compile(sInstrumentResultExpr);
121         mStatusMap = new HashMap<Integer, String>();
mStatusMap.put(STATUS_IDLE, STATUS_STR_IDLE)122         mStatusMap.put(STATUS_IDLE, STATUS_STR_IDLE);
mStatusMap.put(STATUS_BUSY, STATUS_STR_IN_USE)123         mStatusMap.put(STATUS_BUSY, STATUS_STR_IN_USE);
mStatusMap.put(STATUS_OFFLINE, STATUS_STR_OFFLINE)124         mStatusMap.put(STATUS_OFFLINE, STATUS_STR_OFFLINE);
125     }
126 
127     // This constructor just for unit test
TestDevice(final String serialNumber)128     TestDevice(final String serialNumber) {
129         mDeviceInfo = new DeviceParameterCollector();
130         mDeviceInfo.setSerialNumber(serialNumber);
131     }
132 
TestDevice(IDevice device)133     public TestDevice(IDevice device) {
134         mDevice = device;
135         try {
136             mSyncService = mDevice.getSyncService();
137         } catch (IOException e) {
138             // FIXME: handle failed connection.
139         }
140         mBatchModeResultParser = null;
141         mUninstallObserver = new PackageActionObserver(ACTION_UNINSTALL);
142         mStatus = STATUS_IDLE;
143         mDeviceInfo = new DeviceParameterCollector();
144         mPackageActionTimer = new PackageActionTimer();
145         mObjectSync = new ObjectSync();
146     }
147 
148     /**
149      * Gets this device's information.
150      *
151      * Assumes that the test device setup apk is already installed.
152      * See {@link #installDeviceSetupApp()}.
153      *
154      * @return information of this device.
155      */
getDeviceInfo()156     public DeviceParameterCollector getDeviceInfo()
157                 throws DeviceDisconnectedException, InvalidNameSpaceException,
158                 InvalidApkPathException {
159         if (mDeviceInfo.size() == 0) {
160             logServiceThread = new LogServiceThread();
161             logServiceThread.start();
162             genDeviceInfo();
163         }
164         return mDeviceInfo;
165     }
166 
167     /**
168      * Attempt to disable the screen guard on device.
169      *
170      * Assumes the test device setup apk is already installed.
171      * See {@link #installDeviceSetupApp()}.
172      *
173      * Note: uninstalling the device setup app {@link #uninstallDeviceSetupApp()} will re-enable
174      * keyguard.
175      *
176      * @throws DeviceDisconnectedException
177      */
disableKeyguard()178     public void disableKeyguard () throws DeviceDisconnectedException {
179         final String commandStr = "am broadcast -a android.tests.util.disablekeyguard";
180         Log.d(commandStr);
181 
182         executeShellCommand(commandStr, new NullOutputReceiver());
183     }
184 
185     /**
186      * Return the Device instance associated with this TestDevice.
187      */
getDevice()188     public IDevice getDevice() {
189         return mDevice;
190     }
191 
192     class RestartPropReceiver extends MultiLineReceiver {
193         private boolean mRestarted;
194         private boolean mCancelled;
195         private boolean mDone;
196 
197         @Override
processNewLines(String[] lines)198         public void processNewLines(String[] lines) {
199             for (String line : lines) {
200                 if (line.trim().equals("1")) {
201                     mRestarted = true;
202                 }
203             }
204         }
205         @Override
done()206         public void done() {
207             synchronized(this) {
208                 mDone = true;
209                 this.notifyAll();
210             }
211         }
212 
isCancelled()213         public boolean isCancelled() {
214             return mCancelled;
215         }
216 
hasRestarted(long timeout)217         boolean hasRestarted(long timeout) {
218             try {
219                 synchronized (this) {
220                     if (!mDone) {
221                         this.wait(timeout);
222                     }
223                 }
224             } catch (InterruptedException e) {
225                 // ignore
226             }
227             mCancelled = true;
228             return mRestarted;
229         }
230     }
231 
232     /**
233      * Wait until device indicates that boot is complete.
234      *
235      * @return true if the device has completed the boot process, false if it does not, or the
236      * device does not respond.
237      */
waitForBootComplete()238     public boolean waitForBootComplete() throws DeviceDisconnectedException {
239         Log.d("probe device status...");
240 
241         mDeviceInfo.set(DeviceParameterCollector.SERIAL_NUMBER, getSerialNumber());
242         mObjectSync = new ObjectSync();
243 
244         // reset device observer
245         DeviceObserver tmpDeviceObserver = mDeviceObserver;
246         mDeviceObserver = this;
247 
248         int retries = 0;
249         boolean success = false;
250         while (!success && (retries < REBOOT_POLL_COUNT)) {
251             Log.d("Waiting for device to complete boot");
252             RestartPropReceiver rpr = new RestartPropReceiver();
253             this.executeShellCommand("getprop dev.bootcomplete", rpr);
254             success = rpr.hasRestarted(GETPROP_TIMEOUT);
255             if (!success) {
256                 try {
257                     Thread.sleep(REBOOT_POLL_INTERVAL);
258                 } catch (InterruptedException e) {
259                     // ignore and retry
260                 }
261                 retries += 1;
262             }
263         }
264         mDeviceObserver = tmpDeviceObserver;
265         if (success) {
266             Log.d("Device boot complete");
267         }
268         return success;
269     }
270 
271     /**
272      * Run device information collector command to got the device info.
273      */
genDeviceInfo()274     private void genDeviceInfo() throws DeviceDisconnectedException,
275                 InvalidNameSpaceException, InvalidApkPathException {
276         mDeviceInfo.set(DeviceParameterCollector.SERIAL_NUMBER, getSerialNumber());
277         // run shell command to run device information collector
278         Log.d("run device information collector");
279         runDeviceInfoCollectorCommand();
280         waitForCommandFinish();
281     }
282 
283     /**
284      * Uninstall the device setup apk from device.
285      *
286      * See {@link #installDeviceSetupApp}
287      *
288      * @throws DeviceDisconnectedException
289      * @throws InvalidNameSpaceException
290      */
uninstallDeviceSetupApp()291     public void uninstallDeviceSetupApp() throws DeviceDisconnectedException,
292             InvalidNameSpaceException {
293         // reset device observer
294         DeviceObserver tmpDeviceObserver = mDeviceObserver;
295         mDeviceObserver = this;
296         Log.d("uninstall get info ...");
297         uninstallAPK(DEVICE_SETUP_APP_PACKAGE_NAME);
298         waitForCommandFinish();
299         Log.d("uninstall device information collector successfully");
300         mDeviceObserver = tmpDeviceObserver;
301     }
302 
303     /**
304      * Install the device setup apk on the device.
305      *
306      * @throws DeviceDisconnectedException
307      * @throws InvalidApkPathException
308      */
installDeviceSetupApp()309     public void installDeviceSetupApp() throws DeviceDisconnectedException, InvalidApkPathException {
310         String apkPath = HostConfig.getInstance().getCaseRepository().getApkPath(DEVICE_SETUP_APK);
311         if (!HostUtils.isFileExist(apkPath)) {
312             Log.e("File doesn't exist: " + apkPath, null);
313             return;
314         }
315 
316         Log.d("installing " + DEVICE_SETUP_APK + " apk");
317         mObjectSync = new ObjectSync();
318 
319         // reset device observer
320         DeviceObserver tmpDeviceObserver = mDeviceObserver;
321         mDeviceObserver = this;
322 
323         Log.d("install get info ...");
324         installAPK(apkPath);
325         waitForCommandFinish();
326         mDeviceObserver = tmpDeviceObserver;
327     }
328 
329     /**
330      * Run command to collect device info.
331      */
runDeviceInfoCollectorCommand()332     private void runDeviceInfoCollectorCommand() throws DeviceDisconnectedException {
333         final String commandStr = "am instrument -w -e bundle true "
334             + String.format("%s/android.tests.getinfo.DeviceInfoInstrument",
335                     DEVICE_SETUP_APP_PACKAGE_NAME);
336         Log.d(commandStr);
337 
338         mPackageActionTimer.start(ACTION_GET_DEV_INFO, this);
339         executeShellCommand(commandStr, new DeviceInfoReceiver(mDeviceInfo));
340     }
341 
342     /**
343      * Receiver which receives and parses the device information.
344      */
345     final class DeviceInfoReceiver extends MultiLineReceiver {
346 
347         private ArrayList<String> mResultLines = new ArrayList<String>();
348         private DeviceParameterCollector mDeviceParamCollector;
349 
DeviceInfoReceiver(DeviceParameterCollector paramCollector)350         public DeviceInfoReceiver(DeviceParameterCollector paramCollector) {
351             super();
352             mDeviceParamCollector = paramCollector;
353             setTrimLine(false);
354         }
355 
356         /** {@inheritDoc} */
357         @Override
processNewLines(String[] lines)358         public void processNewLines(String[] lines) {
359             for (String line : lines) {
360                 mResultLines.add(line);
361             }
362         }
363 
364         /** {@inheritDoc} */
isCancelled()365         public boolean isCancelled() {
366             return false;
367         }
368 
369         /** {@inheritDoc} */
370         @Override
done()371         public void done() {
372             super.done();
373             String key, value;
374             for (String line : mResultLines) {
375                 Matcher matcher = INSTRUMENT_RESULT_PATTERN.matcher(line);
376                 if (matcher.matches()) {
377                     key = matcher.group(1);
378                     value = matcher.group(2);
379                     mDeviceParamCollector.set(key, value);
380                 }
381             }
382 
383             synchronized(mObjectSync) {
384                 mObjectSync.sendNotify();
385             }
386         }
387 
388     }
389 
390     /**
391      * Store the build information of a device
392      */
393     public static final class DeviceParameterCollector{
394         public static final String PRODUCT_NAME = "product_name";
395         public static final String BUILD_VERSION = "version_release";
396         public static final String BUILD_ID = "build_id";
397         public static final String BUILD_FINGERPRINT = "build_fingerprint";
398         public static final String BUILD_TAGS = "build_tags";
399         public static final String BUILD_TYPE = "build_type";
400         public static final String BUILD_MODEL = "build_model";
401         public static final String BUILD_BRAND = "build_brand";
402         public static final String BUILD_BOARD = "build_board";
403         public static final String BUILD_DEVICE = "build_device";
404         public static final String SCREEN_HEIGHT = "screen_height";
405         public static final String SCREEN_WIDTH = "screen_width";
406         public static final String SCREEN_DENSITY = "screen_density";
407         public static final String SERIAL_NUMBER = "serialNumber";
408         public static final String VERSION_SDK = "version_sdk";
409         public static final String LOCALES = "locales";
410         public static final String SCREEN_Y_DENSITY = "screen_Y_density";
411         public static final String SCREEN_X_DENSITY = "screen_X_density";
412         public static final String TOUCH_SCREEN = "touch_screen";
413         public static final String NAVIGATION = "navigation";
414         public static final String KEYPAD = "keypad";
415         public static final String NETWORK = "network";
416         public static final String IMEI = "imei";
417         public static final String IMSI = "imsi";
418         public static final String PHONE_NUMBER = "phoneNumber";
419 
420         private HashMap<String, String> mInfoMap;
421 
DeviceParameterCollector()422         public DeviceParameterCollector() {
423             mInfoMap = new HashMap<String, String>();
424         }
425 
426         /**
427          * Set the pair of key and value of device information.
428          *
429          * @param key The key of the pair.
430          * @param value The value of the pair.
431          */
set(final String key, final String value)432         public void set(final String key, final String value) {
433             mInfoMap.put(key, value);
434         }
435 
436         /**
437          * Return the number of device info items which stored in.
438          *
439          * @return the number of device info items which stored in.
440          */
size()441         public int size() {
442             return mInfoMap.size();
443         }
444 
445         /**
446          * Set the build finger print.
447          *
448          * @param buildFingerPrint The build finger print.
449          */
setBuildFingerPrint(final String buildFingerPrint)450         public void setBuildFingerPrint(final String buildFingerPrint) {
451             mInfoMap.put(BUILD_FINGERPRINT, buildFingerPrint);
452         }
453 
454         /**
455          * Set the build tags.
456          *
457          * @param buildTags The build tags.
458          */
setBuildTags(final String buildTags)459         public void setBuildTags(final String buildTags) {
460             mInfoMap.put(BUILD_TAGS, buildTags);
461         }
462 
463         /**
464          * Set build type.
465          *
466          * @param buildType The build type.
467          */
setBuildType(final String buildType)468         public void setBuildType(final String buildType) {
469             mInfoMap.put(BUILD_TYPE, buildType);
470         }
471 
472         /**
473          * Set the build model.
474          *
475          * @param buildModel The build model.
476          */
setBuildModel(final String buildModel)477         public void setBuildModel(final String buildModel) {
478             mInfoMap.put(BUILD_MODEL, buildModel);
479         }
480 
481         /**
482          * Set the build brand.
483          *
484          * @param buildBrand The build brand.
485          */
setBuildBrand(final String buildBrand)486         public void setBuildBrand(final String buildBrand) {
487             mInfoMap.put(BUILD_BRAND, buildBrand);
488         }
489 
490         /**
491          * Set the build board.
492          *
493          * @param buildBoard The build board.
494          */
setBuildBoard(final String buildBoard)495         public void setBuildBoard(final String buildBoard) {
496             mInfoMap.put(BUILD_BOARD, buildBoard);
497         }
498 
499         /**
500          * Set the build device.
501          *
502          * @param buildDevice The build device.
503          */
setBuildDevice(final String buildDevice)504         public void setBuildDevice(final String buildDevice) {
505             mInfoMap.put(BUILD_DEVICE, buildDevice);
506         }
507 
508         /**
509          * set the serialNumber of this device
510          *
511          * @param serialNumber The serial number.
512          */
setSerialNumber(final String serialNumber)513         public void setSerialNumber(final String serialNumber) {
514             mInfoMap.put(SERIAL_NUMBER, serialNumber);
515         }
516 
517         /**
518          * set the build id
519          *
520          * @param bldId The build ID.
521          */
setBuildId(final String bldId)522         public void setBuildId(final String bldId) {
523             mInfoMap.put(BUILD_ID, bldId);
524         }
525 
526         /**
527          * set the build version
528          *
529          * @param bldVer The build version.
530          */
setBuildVersion(final String bldVer)531         public void setBuildVersion(final String bldVer) {
532             mInfoMap.put(BUILD_VERSION, bldVer);
533         }
534 
535         /**
536          * set the product name
537          **
538          * @param productName The product name.
539          */
setProductName(final String productName)540         public void setProductName(final String productName) {
541             mInfoMap.put(PRODUCT_NAME, productName);
542         }
543 
544         /**
545          * Get the build finger print.
546          *
547          * @return The build finger print.
548          */
getBuildFingerPrint()549         public String getBuildFingerPrint() {
550             return mInfoMap.get(BUILD_FINGERPRINT);
551         }
552 
553         /**
554          * Get the build tags.
555          *
556          * @return The build tags.
557          */
getBuildTags()558         public String getBuildTags() {
559             return mInfoMap.get(BUILD_TAGS);
560         }
561 
562         /**
563          * Get build type.
564          *
565          * @return The build type.
566          */
getBuildType()567         public String getBuildType() {
568             return mInfoMap.get(BUILD_TYPE);
569         }
570 
571         /**
572          * Get the build model.
573          *
574          * @return The build model.
575          */
getBuildModel()576         public String getBuildModel() {
577             return mInfoMap.get(BUILD_MODEL);
578         }
579 
580         /**
581          * Get the build brand.
582          *
583          * @return The build brand.
584          */
getBuildBrand()585         public String getBuildBrand() {
586             return mInfoMap.get(BUILD_BRAND);
587         }
588 
589         /**
590          * Get the build board.
591          *
592          * @return The build board.
593          */
getBuildBoard()594         public String getBuildBoard() {
595             return mInfoMap.get(BUILD_BOARD);
596         }
597 
598         /**
599          * Get the build device.
600          *
601          * @return The build device.
602          */
getBuildDevice()603         public String getBuildDevice() {
604             return mInfoMap.get(BUILD_DEVICE);
605         }
606 
607         /**
608          * get the build id
609          **
610          * @return The build ID.
611          */
getBuildId()612         public String getBuildId() {
613             return mInfoMap.get(BUILD_ID);
614         }
615 
616         /**
617          * get the build version
618          **
619          * @return The build version.
620          */
getBuildVersion()621         public String getBuildVersion() {
622             return mInfoMap.get(BUILD_VERSION);
623         }
624 
625         /**
626          * get the product name
627          *
628          * @return The product name.
629          */
getProductName()630         public String getProductName() {
631             return mInfoMap.get(PRODUCT_NAME);
632         }
633 
634         /**
635          * get the serial number
636          *
637          * @return The serial number.
638          */
getSerialNumber()639         public String getSerialNumber() {
640             return mInfoMap.get(SERIAL_NUMBER);
641         }
642 
643         /**
644          * Return screen resolution(width x height)
645          *
646          * @return The screen resolution.
647          */
getScreenResolution()648         public String getScreenResolution() {
649             return mInfoMap.get(SCREEN_WIDTH) + "x" + mInfoMap.get(SCREEN_HEIGHT);
650         }
651 
652         /**
653          * Get Android platform version.
654          *
655          * @return The Android platform version.
656          */
getAndroidPlatformVersion()657         public String getAndroidPlatformVersion() {
658             return mInfoMap.get(VERSION_SDK);
659         }
660 
661         /**
662          * Get supported locales.
663          *
664          * @return The supported locales.
665          */
getLocales()666         public String getLocales() {
667             return mInfoMap.get(LOCALES);
668         }
669 
670         /**
671          * Get x dip
672          *
673          * @return The X dip.
674          */
getXdpi()675         public String getXdpi() {
676             return mInfoMap.get(SCREEN_X_DENSITY);
677         }
678 
679         /**
680          * Get y dip
681          *
682          * @return The y dip.
683          */
getYdpi()684         public String getYdpi() {
685             return mInfoMap.get(SCREEN_Y_DENSITY);
686         }
687 
688         /**
689          * Get touch information
690          *
691          * @return The touch screen information.
692          */
getTouchInfo()693         public String getTouchInfo() {
694             return mInfoMap.get(TOUCH_SCREEN);
695         }
696 
697         /**
698          * Get navigation information
699          *
700          * @return The navigation information.
701          */
getNavigation()702         public String getNavigation() {
703             return mInfoMap.get(NAVIGATION);
704         }
705 
706         /**
707          * Get keypad information
708          *
709          * @return The keypad information.
710          */
getKeypad()711         public String getKeypad() {
712             return mInfoMap.get(KEYPAD);
713         }
714 
715         /**
716          * Get network information
717          *
718          * @return The network information.
719          */
getNetwork()720         public String getNetwork() {
721             return mInfoMap.get(NETWORK);
722         }
723 
724         /**
725          * Get IMEI
726          *
727          * @return IMEI.
728          */
getIMEI()729         public String getIMEI() {
730             return mInfoMap.get(IMEI);
731         }
732 
733         /**
734          * Get IMSI
735          *
736          * @return IMSI.
737          */
getIMSI()738         public String getIMSI() {
739             return mInfoMap.get(IMSI);
740         }
741 
742         /**
743          * Get phone number
744          *
745          * @return Phone number.
746          */
getPhoneNumber()747         public String getPhoneNumber() {
748             return mInfoMap.get(PHONE_NUMBER);
749         }
750     }
751 
752     /**
753      * Get the serial number of the  {@link TestDevice}.
754      *
755      * @return the serial number of the {@link TestDevice}
756      */
getSerialNumber()757     public String getSerialNumber() {
758         if (mDevice == null) {
759             return mDeviceInfo.getSerialNumber();
760         }
761         return mDevice.getSerialNumber();
762     }
763 
764     /**
765      * Run a specified test.
766      *
767      * @param test The test to be run.
768      */
runTest(Test test)769     public void runTest(Test test) throws DeviceDisconnectedException {
770 
771         final String appNameSpace = test.getAppNameSpace();
772         String runner = test.getInstrumentationRunner();
773         if (runner == null) {
774             runner = DEFAULT_TEST_RUNNER_NAME;
775         }
776 
777         // need to doubly escape any '$' chars in the name since this string is
778         // passed through two shells \\\$ -> \$ -> $
779         final String testName = test.getFullName().replaceAll("\\$", "\\\\\\$");
780 
781         final String commandStr = "am instrument -w -r -e class "
782                 + testName + " " + appNameSpace + "/" + runner;
783         Log.d(commandStr);
784         executeShellCommand(commandStr, new IndividualModeResultParser(test));
785     }
786 
787     /**
788      * Run a test package in batch mode.
789      *
790      * @param testPackage The testPackage to be run.
791      * @param javaPkgName The java package name. If null, run the whole test package;
792      *              else, run the specified java package contained in the test package
793      */
runInBatchMode(TestPackage testPackage, final String javaPkgName)794     public void runInBatchMode(TestPackage testPackage, final String javaPkgName)
795                 throws DeviceDisconnectedException {
796         String appNameSpace = testPackage.getAppNameSpace();
797         String runner = testPackage.getInstrumentationRunner();
798         if (runner == null) {
799             runner = DEFAULT_TEST_RUNNER_NAME;
800         }
801 
802         String name = testPackage.getAppPackageName();
803         if ((javaPkgName != null) && (javaPkgName.length() != 0)) {
804             name = javaPkgName;
805         }
806 
807         String cmdHeader = "am instrument -w -r -e package " + name + " ";
808         final String commandStr = cmdHeader + appNameSpace + "/" + runner;
809         Log.d(commandStr);
810 
811         mBatchModeResultParser = new BatchModeResultParser(testPackage);
812         executeShellCommand(commandStr, mBatchModeResultParser);
813     }
814 
815     /**
816      * Run a in batch mode of a TestPackage.
817      *
818      * @param testPackage The testPackage to be run.
819      * @param javaClassName The java class name.
820      */
runTestCaseInBatchMode(TestPackage testPackage, final String javaClassName)821     public void runTestCaseInBatchMode(TestPackage testPackage, final String javaClassName)
822                 throws DeviceDisconnectedException {
823         if (javaClassName == null) {
824             return;
825         }
826 
827         String appNameSpace = testPackage.getAppNameSpace();
828         String runner = testPackage.getInstrumentationRunner();
829         if (runner == null) {
830             runner = DEFAULT_TEST_RUNNER_NAME;
831         }
832 
833         String cmdHeader = "am instrument -w -r -e class " + javaClassName + " ";
834         final String commandStr = cmdHeader + appNameSpace + "/" + runner;
835         Log.d(commandStr);
836 
837         mBatchModeResultParser = new BatchModeResultParser(testPackage);
838         executeShellCommand(commandStr, mBatchModeResultParser);
839     }
840 
841     /**
842      * Get clients.
843      *
844      * @return The clients.
845      */
getClients()846     public Client[] getClients() {
847         return mDevice.getClients();
848     }
849 
850     /**
851      * Push a file to a given path.
852      *
853      * @param localPath The local path.
854      * @param remotePath The remote path.
855      */
pushFile(String localPath, String remotePath)856     public void pushFile(String localPath, String remotePath) {
857         SyncResult result = mSyncService.pushFile(localPath, remotePath,
858                 new PushMonitor());
859         if (result.getCode() != SyncService.RESULT_OK) {
860             Log.e("Uploading file failed: " + result.getMessage(), null);
861         }
862     }
863 
864     /**
865      * Install a specified APK using adb command install.
866      *
867      * @param apkPath Name of the package to be installed.
868      */
installAPK(final String apkPath)869     public void installAPK(final String apkPath) throws DeviceDisconnectedException,
870                 InvalidApkPathException {
871         if ((apkPath == null) || (apkPath.length() == 0) || (!HostUtils.isFileExist(apkPath))) {
872             throw new InvalidApkPathException(apkPath);
873         }
874 
875         // Use re-install directly
876         final String cmd = DeviceManager.getAdbLocation() + " -s "
877                 + getSerialNumber() + " install -r " + apkPath;
878         Log.d(cmd);
879 
880         mPackageActionTimer.start(ACTION_INSTALL, this);
881         executeCommand(cmd, new PackageActionObserver(ACTION_INSTALL));
882     }
883 
884     /**
885      * Execute the given command.
886      *
887      * @param command The command to be executed.
888      * @param stdOutReceiver The receiver for handling the output from the device.
889      */
executeCommand(String command, StdOutObserver stdOutReceiver)890     private void executeCommand(String command, StdOutObserver stdOutReceiver)
891                     throws DeviceDisconnectedException {
892         if (mStatus != STATUS_OFFLINE) {
893             try {
894                 Process proc = Runtime.getRuntime().exec(command);
895 
896                 if (stdOutReceiver != null) {
897                     stdOutReceiver.setInputStream(proc.getInputStream());
898                 }
899             } catch (IOException e) {
900                 e.printStackTrace();
901             }
902         } else {
903             throw new DeviceDisconnectedException(getSerialNumber());
904         }
905     }
906 
907     /**
908      * Standard output observer.
909      *
910      */
911     interface StdOutObserver {
912         /**
913          * set the input Stream.
914          */
setInputStream(InputStream is)915         public void setInputStream(InputStream is);
916 
917         /**
918          * Process lines.
919          */
processLines()920         public void processLines() throws IOException;
921     }
922 
923     /**
924      * Un-install APK.
925      *
926      * @param packageName The package to be un-installed.
927      */
uninstallAPK(String packageName)928     public void uninstallAPK(String packageName) throws DeviceDisconnectedException,
929                 InvalidNameSpaceException {
930         if ((packageName == null) || (packageName.length() == 0)) {
931             throw new InvalidNameSpaceException(packageName);
932         }
933 
934         uninstallAPKImpl(packageName, mUninstallObserver);
935     }
936 
937     /**
938      * The implementation of uninstalling APK.
939      *
940      * @param packageName The package to be uninstalled.
941      * @param observer The uninstall observer
942      */
uninstallAPKImpl(final String packageName, final PackageActionObserver observer)943     private void uninstallAPKImpl(final String packageName, final PackageActionObserver observer)
944                 throws DeviceDisconnectedException {
945         final String cmdStr = DeviceManager.getAdbLocation() + " -s "
946                       + getSerialNumber() + " uninstall " + packageName;
947         Log.d(cmdStr);
948         mPackageActionTimer.start(ACTION_UNINSTALL, this);
949         executeCommand(cmdStr, observer);
950     }
951 
952     /**
953      * Package action(install/uninstall) timeout task
954      */
955     class PackageActionTimeoutTask extends TimerTask {
956 
957         private String mAction;
958         private TestDevice mTargetDevice;
959 
960         /**
961          * Task of package action timeout.
962          *
963          * @param action string of action
964          * @param testDevice the {@TestDevice} which got the timeout.
965          */
PackageActionTimeoutTask(final String action, TestDevice testDevice)966         public PackageActionTimeoutTask(final String action,
967                 TestDevice testDevice) {
968             mAction = action;
969             mTargetDevice = testDevice;
970         }
971 
972         /** {@inheritDoc}*/
973         @Override
run()974         public void run() {
975             Log.d("PackageActionTimeoutTask.run(): mAction=" + mAction);
976             synchronized (mObjectSync) {
977                 mObjectSync.sendNotify();
978             }
979 
980             if (mAction.toLowerCase().equals(ACTION_INSTALL)) {
981                 mDeviceObserver.notifyInstallingTimeout(mTargetDevice);
982             } else if (mAction.toLowerCase().equals(ACTION_UNINSTALL)) {
983                 mDeviceObserver.notifyUninstallingTimeout(mTargetDevice);
984             } else if (mAction.toLowerCase().equals(ACTION_GET_DEV_INFO)) {
985                 Log.e("Get device information timeout", null);
986             } else {
987                 Log.e("Timeout: " + mAction, null);
988             }
989         }
990     }
991 
992     /**
993      * Package action timer monitors the package action.
994      *
995      */
996     class PackageActionTimer {
997         private Timer mTimer;
998 
999         /**
1000          * Start the timer while package install/uninstall/getDeviceInfo/checkAPI.
1001          *
1002          * @param action The action of package.
1003          * @param device The TestDevice the action is taken over.
1004          */
start(final String action, final TestDevice device)1005         private void start(final String action, final TestDevice device) {
1006             start(action, HostConfig.Ints.packageInstallTimeoutMs.value(), device);
1007         }
1008 
1009         /**
1010          * Start the timer while package install/uninstall/getDeviceInfo/checkAPI with specific
1011          * timeout.
1012          *
1013          * @param action The action of package
1014          * @param timeout The specific timeout
1015          * @param device The TestDevice under operation
1016          */
start(final String action, final int timeout, final TestDevice device)1017         private void start(final String action, final int timeout, final TestDevice device) {
1018             Log.d("start(), action=" + action + ",mTimer=" + mTimer + ",timeout=" + timeout);
1019             synchronized (this) {
1020                 if (mTimer != null) {
1021                     mTimer.cancel();
1022                 }
1023 
1024                 mTimer = new Timer();
1025                 mTimer.schedule(new PackageActionTimeoutTask(action, device), timeout);
1026             }
1027         }
1028 
1029         /**
1030          * Stop the action timer.
1031          */
stop()1032         private void stop() {
1033             synchronized (this) {
1034                 Log.d("stop() , mTimer=" + mTimer);
1035                 if (mTimer != null) {
1036                     mTimer.cancel();
1037                     mTimer = null;
1038                 }
1039             }
1040         }
1041     }
1042 
1043     /**
1044      * The observer of package action, currently including installing and uninstalling.
1045      */
1046     final class PackageActionObserver implements StdOutObserver, Runnable {
1047 
1048         private BufferedReader mReader;
1049         private String mAction;
1050 
PackageActionObserver(final String action)1051         public PackageActionObserver(final String action) {
1052             mAction = action;
1053         }
1054 
1055         /** {@inheritDoc} */
run()1056         public void run() {
1057             try {
1058                 processLines();
1059             } catch (IOException e) {
1060                 e.printStackTrace();
1061             } finally {
1062                 try {
1063                     mReader.close();
1064                 } catch (IOException e) {
1065                     e.printStackTrace();
1066                 }
1067             }
1068         }
1069 
1070         /**
1071          * Parse the standard out to judge where the installation is complete.
1072          */
processLines()1073         public void processLines() throws IOException {
1074             String line = mReader.readLine();
1075             int statusCode = DeviceObserver.FAIL;
1076             boolean gotResult = false;
1077 
1078             while (line != null) {
1079                 line = line.toLowerCase();
1080                 if (line.indexOf("success") != -1) {
1081                     statusCode = DeviceObserver.SUCCESS;
1082                     gotResult = true;
1083                 } else if (line.indexOf("failure") != -1) {
1084                     statusCode = DeviceObserver.FAIL;
1085                     CUIOutputStream.println(mAction.toLowerCase() + " met " + line);
1086                     gotResult = true;
1087                 } else if (line.indexOf("error") != -1) {
1088                     CUIOutputStream.println(mAction.toLowerCase() + " met " + line);
1089                     statusCode = DeviceObserver.FAIL;
1090                     gotResult = true;
1091                 }
1092 
1093                 if (gotResult) {
1094                     Log.d(mAction + " calls stopPackageActionTimer()");
1095                     mPackageActionTimer.stop();
1096 
1097                     if (mDeviceObserver != null) {
1098                         mDeviceObserver.notifyInstallingComplete(statusCode);
1099                     }
1100                     break;
1101                 }
1102                 line = mReader.readLine();
1103             }
1104         }
1105 
1106         /** {@inheritDoc} */
setInputStream(InputStream is)1107         public void setInputStream(InputStream is) {
1108             mReader = new BufferedReader(new InputStreamReader(is));
1109             new Thread(this).start();
1110         }
1111     }
1112 
1113     /**
1114      * Raw mode result parser.
1115      */
1116     abstract class RawModeResultParser extends MultiLineReceiver {
1117         public final static String EQ_MARK = "=";
1118         public final static String COMMA_MARK = ":";
1119         public final static String AT_MARK = "at ";
1120 
1121         public final static String STATUS_STREAM = "INSTRUMENTATION_STATUS: stream=";
1122         public final static String STATUS_TEST = "INSTRUMENTATION_STATUS: test=";
1123         public final static String STATUS_CLASS = "INSTRUMENTATION_STATUS: class=";
1124         public final static String STATUS_CODE = "INSTRUMENTATION_STATUS_CODE:";
1125         public final static String STATUS_STACK = "INSTRUMENTATION_STATUS: stack=";
1126         public final static String STATUS_CURRENT = "INSTRUMENTATION_STATUS: current=";
1127         public final static String STATUS_NUM = "INSTRUMENTATION_STATUS: numtests=";
1128         public final static String STATUS_ERROR_STR = "INSTRUMENTATION_STATUS: Error=";
1129 
1130         public final static String FAILURE = "Failure in ";
1131         public final static String ASSERTION = "junit.framework.Assertion";
1132 
1133         public final static String RESULT_STREAM = "INSTRUMENTATION_RESULT: stream=";
1134         public final static String RESULT_CODE = "INSTRUMENTATION_CODE:";
1135         public final static String RESULT = "Test results";
1136         public final static String RESULT_TIME = "Time:";
1137         public final static String RESULT_SUMMARY = "Tests run:";
1138 
1139         public final static int STATUS_STARTING = 1;
1140         public final static int STATUS_PASS = 0;
1141         public final static int STATUS_FAIL = -1;
1142         public final static int STATUS_ERROR = -2;
1143 
1144         private ArrayList<String> mResultLines;
1145 
1146         public String mStackTrace;
1147         public String mFailedMsg;
1148         public int mResultCode;
1149 
1150         public Test mTest;
1151 
RawModeResultParser(Test test)1152         public RawModeResultParser(Test test) {
1153             super();
1154 
1155             setTrimLine(false);
1156 
1157             mTest = test;
1158             mResultLines = new ArrayList<String>();
1159             mStackTrace = null;
1160             mFailedMsg = null;
1161             mResultCode = CtsTestResult.CODE_PASS;
1162         }
1163 
1164         /** {@inheritDoc} */
1165         @Override
processNewLines(String[] lines)1166         public void processNewLines(String[] lines) {
1167             for (String line : lines) {
1168                 processNewLine(line.trim());
1169             }
1170         }
1171 
1172         /**
1173          * Process the new line.
1174          *
1175          * @param line The new line.
1176          */
processNewLine(final String line)1177         abstract public void processNewLine(final String line);
1178 
1179         /**
1180          * Get the result lines.
1181          *
1182          * @return The result lines.
1183          */
getResultLines()1184         public ArrayList<String> getResultLines() {
1185             return mResultLines;
1186         }
1187 
1188         /**
1189          * Get the named string from the string containing the mark.
1190          *
1191          * @param mark The mark to search against the results.
1192          * @return The test name.
1193          */
getNamedString(String mark)1194         public String getNamedString(String mark) {
1195             for (String line : mResultLines) {
1196                 if (line.startsWith(mark)) {
1197                     String name = line.substring(line.indexOf(EQ_MARK) + 1);
1198                     return name.trim();
1199                 }
1200             }
1201 
1202             return null;
1203         }
1204 
1205         /**
1206          * Parse the int from the string containing the mark.
1207          *
1208          * @param mark The mark to search against the results.
1209          * @return The number.
1210          */
parseIntWithMark(String mark)1211         public int parseIntWithMark(String mark) {
1212             for (String line : mResultLines) {
1213                 if (line.startsWith(mark)) {
1214                     String code = line.substring(line.indexOf(EQ_MARK) + 1);
1215                     return Integer.parseInt(code.trim());
1216                 }
1217             }
1218 
1219             return 0;
1220         }
1221 
1222         /**
1223          * Get failed message.
1224          *
1225          * @return The failed message.
1226          */
getFailedMessage()1227         public String getFailedMessage() {
1228             Iterator<String> iterator = mResultLines.iterator();
1229             while (iterator.hasNext()) {
1230                 String line = iterator.next();
1231                 if (line.startsWith(STATUS_STACK)) {
1232                     String failedMsg = line.substring(STATUS_STACK.length());
1233                     if (iterator.hasNext()) {
1234                         failedMsg += " " + iterator.next();
1235                     }
1236                     return failedMsg;
1237                 }
1238             }
1239             return null;
1240         }
1241 
1242         /**
1243          * Get stack trace from output result.
1244          *
1245          * @return The stack trace message.
1246          */
getStackTrace()1247         public String getStackTrace() {
1248             StringBuilder sb = new StringBuilder();
1249             for (String line : mResultLines) {
1250                 line = line.trim();
1251                 if (line.startsWith(AT_MARK) && line.endsWith(")")) {
1252                     sb.append(line + "\n");
1253                 }
1254             }
1255             return sb.toString();
1256         }
1257 
1258         /**
1259          * Get the status code of the test result.
1260          *
1261          * @param line The string contains the status code of the test result.
1262          * @return The status code of the test result.
1263          */
getStatusCode(String line)1264         public int getStatusCode(String line) {
1265             String codeStr = line.substring(line.indexOf(COMMA_MARK) + 1);
1266             return Integer.parseInt(codeStr.trim());
1267         }
1268 
1269         /** {@inheritDoc} */
isCancelled()1270         public boolean isCancelled() {
1271             return false;
1272         }
1273 
1274         /** {@inheritDoc} */
1275         @Override
done()1276         public void done() {
1277             super.done();
1278         }
1279     }
1280 
1281     /**
1282      * Individual mode result parser. <br>
1283      * Individual mode means that the host sends request
1284      * to the device method by method. And the device
1285      * reactions and outputs the result to each request.
1286      */
1287     final class IndividualModeResultParser extends RawModeResultParser {
1288 
IndividualModeResultParser(Test test)1289         public IndividualModeResultParser(Test test) {
1290             super(test);
1291         }
1292 
1293         /**
1294          * Process a new line.
1295          *
1296          * @param line The new line.
1297          */
1298         @Override
processNewLine(final String line)1299         public void processNewLine(final String line) {
1300             if ((line == null) || (line.trim().length() == 0)) {
1301                 return;
1302             }
1303 
1304             ArrayList<String> resultLines = getResultLines();
1305             resultLines.add(line);
1306 
1307             if (line.startsWith(STATUS_CODE)) {
1308                 int statusCode = getStatusCode(line);
1309                 processTestResult(statusCode);
1310                 resultLines.removeAll(resultLines);
1311             }
1312         }
1313 
1314         /**
1315          * Process the test result of a single test.
1316          *
1317          * @param statusCode The status code of a single test's test result.
1318          */
processTestResult(int statusCode)1319         public void processTestResult(int statusCode) {
1320             String testName = getNamedString(STATUS_TEST);
1321             String className = getNamedString(STATUS_CLASS);
1322             String testFullName = className + Test.METHOD_SEPARATOR + testName;
1323             String errorMessage = getNamedString(STATUS_ERROR_STR);
1324 
1325             mFailedMsg = null;
1326             mStackTrace = null;
1327             if ((statusCode == STATUS_FAIL) || (statusCode == STATUS_ERROR)) {
1328                 mFailedMsg = getFailedMessage();
1329                 mStackTrace = getStackTrace();
1330             }
1331 
1332             if ((errorMessage != null) && (errorMessage.length() != 0)) {
1333                 if (mFailedMsg == null) {
1334                     mFailedMsg = errorMessage;
1335                 } else {
1336                     mFailedMsg += " : " + errorMessage;
1337                 }
1338             }
1339 
1340             Log.d(testFullName + "...(" + statusCode + ")");
1341             Log.d("errorMessage= " + errorMessage);
1342             Log.d("mFailedMsg=" + mFailedMsg);
1343             Log.d("mStackTrace=" + mStackTrace);
1344 
1345             switch (statusCode) {
1346             case STATUS_STARTING:
1347                 break;
1348 
1349             case STATUS_PASS:
1350                 mResultCode = CtsTestResult.CODE_PASS;
1351                 break;
1352 
1353             case STATUS_FAIL:
1354             case STATUS_ERROR:
1355                 mResultCode = CtsTestResult.CODE_FAIL;
1356                 break;
1357             }
1358         }
1359 
1360         /** {@inheritDoc} */
1361         @Override
done()1362         public void done() {
1363             mTest.notifyResult(new CtsTestResult(mResultCode, mFailedMsg, mStackTrace));
1364             super.done();
1365         }
1366     }
1367 
1368     /**
1369      * Batch mode result parser.
1370      * Batch mode means that the host sends only one request
1371      * for all of the methods contained in the package to the
1372      * device. And then, the device runs the method one by one
1373      * and outputs the result method by method.
1374      */
1375     final class BatchModeResultParser extends RawModeResultParser {
1376         private TestPackage mTestPackage;
1377         private Collection<Test> mTests;
1378         public int mCurrentTestNum;
1379         public int mTotalNum;
1380 
BatchModeResultParser(TestPackage testPackage)1381         public BatchModeResultParser(TestPackage testPackage) {
1382             super(null);
1383 
1384             mTestPackage = testPackage;
1385             if (mTestPackage != null) {
1386                 mTests = mTestPackage.getTests();
1387             }
1388         }
1389 
1390         /**
1391          * Process a new line.
1392          *
1393          * @param line The new line.
1394          */
1395         @Override
processNewLine(final String line)1396         public void processNewLine(final String line) {
1397             if ((line == null) || (line.trim().length() == 0)) {
1398                 return;
1399             }
1400 
1401             ArrayList<String> resultLines = getResultLines();
1402             resultLines.add(line);
1403 
1404             if (line.startsWith(STATUS_CODE)) {
1405                 int statusCode = getStatusCode(line);
1406                 processTestResult(statusCode);
1407                 resultLines.removeAll(resultLines);
1408             } else if (line.startsWith(RESULT_CODE)) {
1409                 int resultCode = getStatusCode(line);
1410                 switch(resultCode) {
1411                 case STATUS_STARTING:
1412                     break;
1413 
1414                 case STATUS_FAIL:
1415                 case STATUS_ERROR:
1416                     mResultCode = CtsTestResult.CODE_FAIL;
1417                     break;
1418 
1419                 case STATUS_PASS:
1420                     mResultCode = CtsTestResult.CODE_PASS;
1421                     break;
1422                 }
1423                 resultLines.removeAll(resultLines);
1424             }
1425         }
1426 
1427         /**
1428          * Process the test result of a single test.
1429          *
1430          * @param statusCode The status code of a single test's test result.
1431          */
processTestResult(int statusCode)1432         public void processTestResult(int statusCode) {
1433             String testName = getNamedString(STATUS_TEST);
1434             String className = getNamedString(STATUS_CLASS);
1435             String testFullName = className + Test.METHOD_SEPARATOR + testName;
1436             mCurrentTestNum = parseIntWithMark(STATUS_CURRENT);
1437             mTotalNum = parseIntWithMark(STATUS_NUM);
1438 
1439             mFailedMsg = null;
1440             mStackTrace = null;
1441             if ((statusCode == STATUS_FAIL) || ((statusCode == STATUS_ERROR))) {
1442                 mFailedMsg = getFailedMessage();
1443                 mStackTrace = getStackTrace();
1444             }
1445 
1446             Log.d(testFullName + "...(" + statusCode + ")");
1447             Log.d("mFailedMsg=" + mFailedMsg);
1448             Log.d("mStackTrace=" + mStackTrace);
1449 
1450             String status = TestPackage.FINISH;
1451 
1452             if (statusCode == STATUS_STARTING) {
1453                 status = TestPackage.START;
1454             }
1455 
1456             mTest = searchTest(testFullName);
1457             if (mTest != null) {
1458                 switch(statusCode) {
1459                 case STATUS_STARTING:
1460                     status = TestPackage.START;
1461                     break;
1462 
1463                 case STATUS_PASS:
1464                     mTest.setResult(new CtsTestResult(
1465                             CtsTestResult.CODE_PASS, null, null));
1466                     break;
1467 
1468                 case STATUS_ERROR:
1469                 case STATUS_FAIL:
1470                     mTest.setResult(new CtsTestResult(
1471                             CtsTestResult.CODE_FAIL, mFailedMsg, mStackTrace));
1472                     break;
1473                 }
1474             }
1475             // report status even if no matching test was found
1476             mTestPackage.notifyTestStatus(mTest, status);
1477         }
1478 
1479         /**
1480          * Search Test with given test full name.
1481          *
1482          * @param testFullName The test full name.
1483          * @return The Test matches the test full name given.
1484          */
searchTest(String testFullName)1485         private Test searchTest(String testFullName) {
1486             for (Test test : mTests) {
1487                 if (testFullName.equals(test.getFullName())) {
1488                     return test;
1489                 }
1490             }
1491             return null;
1492         }
1493 
1494         /** {@inheritDoc} */
1495         @Override
done()1496         public void done() {
1497             mTestPackage.notifyBatchModeFinish();
1498             super.done();
1499         }
1500     }
1501 
1502     /**
1503      * Remove the run time listener.
1504      */
removeRuntimeListener()1505     public void removeRuntimeListener() {
1506         mDeviceObserver = null;
1507     }
1508 
1509     /**
1510      * Set the run time listener.
1511      *
1512      * @param listener The run time listener.
1513      */
setRuntimeListener(DeviceObserver listener)1514     public void setRuntimeListener(DeviceObserver listener) {
1515         mDeviceObserver = listener;
1516     }
1517 
1518     /**
1519      * Push monitor monitoring the status of pushing a file.
1520      */
1521     class PushMonitor implements ISyncProgressMonitor {
1522 
PushMonitor()1523         public PushMonitor() {
1524         }
1525 
1526         /** {@inheritDoc} */
advance(int arg0)1527         public void advance(int arg0) {
1528         }
1529 
1530         /** {@inheritDoc} */
isCanceled()1531         public boolean isCanceled() {
1532             return false;
1533         }
1534 
1535         /** {@inheritDoc} */
start(int arg0)1536         public void start(int arg0) {
1537         }
1538 
1539         /** {@inheritDoc} */
startSubTask(String arg0)1540         public void startSubTask(String arg0) {
1541         }
1542 
1543         /** {@inheritDoc} */
stop()1544         public void stop() {
1545         }
1546     }
1547 
1548     /**
1549      * Add a new log listener.
1550      *
1551      * @param listener the listener
1552      */
addMainLogListener(ILogListener listener)1553     public void addMainLogListener(ILogListener listener) {
1554         logListener.addListener(listener);
1555     }
1556 
1557     /**
1558      * Remove an existing log listener.
1559      *
1560      * @param listener the listener to remove.
1561      */
removeMainLogListener(ILogListener listener)1562     public void removeMainLogListener(ILogListener listener) {
1563         logListener.removeListener(listener);
1564     }
1565 
1566     /**
1567      * Execute Adb shell command on {@link IDevice}
1568      *
1569      * @param cmd the string of command.
1570      * @param receiver {@link IShellOutputReceiver}
1571      * @throws DeviceDisconnectedException if the device disconnects during the command
1572      */
executeShellCommand(final String cmd, final IShellOutputReceiver receiver)1573     public void executeShellCommand(final String cmd,
1574             final IShellOutputReceiver receiver) throws DeviceDisconnectedException {
1575         executeShellCommand(cmd, receiver, null);
1576     }
1577 
1578     /**
1579      * Execute Adb shell command on {@link IDevice}
1580      *
1581      * Note that the receivers run in a different thread than the caller.
1582      *
1583      * @param cmd the string of command.
1584      * @param receiver {@link IShellOutputReceiver}
1585      * @param logReceiver {@link LogReceiver}
1586      * @throws DeviceDisconnectedException if the device disconnects during the command
1587      */
executeShellCommand(final String cmd, final IShellOutputReceiver receiver, final LogReceiver logReceiver)1588     public void executeShellCommand(final String cmd,
1589             final IShellOutputReceiver receiver,
1590             final LogReceiver logReceiver)
1591             throws DeviceDisconnectedException {
1592         if (mStatus == STATUS_OFFLINE) {
1593             Log.d(String.format("device %s is offline when attempting to execute %s",
1594                     getSerialNumber(), cmd));
1595             throw new DeviceDisconnectedException(getSerialNumber());
1596         }
1597 
1598         new Thread() {
1599             @Override
1600             public void run() {
1601                 try {
1602                     mDevice.executeShellCommand(cmd, receiver);
1603                 } catch (IOException e) {
1604                     Log.e("", e);
1605                 }
1606             }
1607         }.start();
1608     }
1609 
1610     /**
1611      * Kill {@link Client} which running the test on the {@link IDevice}
1612      *
1613      * @param packageName the test package name
1614      */
killProcess(String packageName)1615     public void killProcess(String packageName) {
1616         if (mStatus == STATUS_OFFLINE) {
1617             return;
1618         }
1619         Client[] clients = mDevice.getClients();
1620 
1621         for (Client c : clients) {
1622             ClientData cd = c.getClientData();
1623             if (cd.getClientDescription() == null) {
1624                 continue;
1625             }
1626             if (cd.getClientDescription().equals(packageName)) {
1627                 c.kill();
1628                 break;
1629             }
1630         }
1631     }
1632 
1633     /**
1634      * Called when the {@link TestDevice} disconnected.
1635      */
disconnected()1636     public void disconnected() {
1637         CUIOutputStream.println("Device(" + getSerialNumber() + ") disconnected");
1638         mDevice = null;
1639         mSyncService = null;
1640 
1641         synchronized (mObjectSync) {
1642             mObjectSync.sendNotify();
1643             mPackageActionTimer.stop();
1644         }
1645 
1646         if (mStatus == STATUS_BUSY) {
1647             Log.d("TestDevice.disconnected calls notifyTestingDeviceDisconnected");
1648             mDeviceObserver.notifyTestingDeviceDisconnected();
1649         } else {
1650             if (!TestSession.isADBServerRestartedMode()) {
1651                 CUIOutputStream.printPrompt();
1652             }
1653         }
1654         setStatus(STATUS_OFFLINE);
1655         if (logServiceThread != null) {
1656             logServiceThread.cancelLogService();
1657         }
1658     }
1659 
1660     /**
1661      * Set the status of the {@link TestDevice}
1662      *
1663      * @param statusCode the status code of {@link TestDevice}
1664      */
setStatus(final int statusCode)1665     public void setStatus(final int statusCode) {
1666         if (statusCode != STATUS_IDLE && statusCode != STATUS_BUSY
1667                 && statusCode != STATUS_OFFLINE) {
1668             throw new IllegalArgumentException("Invalid status code");
1669         }
1670         mStatus = statusCode;
1671     }
1672 
1673     /**
1674      * Get the status code of the {@link TestDevice}.
1675      *
1676      * @return get the status code of the {@link TestDevice}
1677      */
getStatus()1678     public int getStatus() {
1679         return mStatus;
1680     }
1681 
1682     /**
1683      * Get the status of the {@link TestDevice} as string.
1684      *
1685      * @return the status of the {@link TestDevice} as string.
1686      */
getStatusAsString()1687     public String getStatusAsString() {
1688         return mStatusMap.get(mStatus);
1689     }
1690 
1691     /**
1692      * Wait for command finish.
1693      */
waitForCommandFinish()1694     public void waitForCommandFinish() {
1695         synchronized (mObjectSync) {
1696             try {
1697                 mObjectSync.waitOn();
1698             } catch (InterruptedException e) {
1699             }
1700         }
1701     }
1702 
1703     /**
1704      * Start the action timer with specific timeout
1705      *
1706      * @param action the action to start the timer
1707      * @param timeout the specific timeout
1708      */
startActionTimer(final String action, final int timeout)1709     void startActionTimer(final String action, final int timeout) {
1710         mPackageActionTimer.start(action, timeout, this);
1711     }
1712 
1713     /**
1714      * Start the action timer.
1715      *
1716      * @param action the action to start the timer.
1717      */
startActionTimer(String action)1718     void startActionTimer(String action) {
1719        mPackageActionTimer.start(action, this);
1720     }
1721 
1722     /**
1723      * Stop the action timer.
1724      */
stopActionTimer()1725     void stopActionTimer() {
1726         mPackageActionTimer.stop();
1727     }
1728 
1729     /**
1730      * Allows an external test to signal that it's command is complete.
1731      */
notifyExternalTestComplete()1732     void notifyExternalTestComplete() {
1733         synchronized (mObjectSync) {
1734             mObjectSync.sendNotify();
1735         }
1736     }
1737 
1738     /**
1739      * Notify install complete.
1740      */
notifyInstallingComplete(int resultCode)1741     public void notifyInstallingComplete(int resultCode) {
1742         synchronized (mObjectSync) {
1743             mObjectSync.sendNotify();
1744             mPackageActionTimer.stop();
1745         }
1746     }
1747 
1748     /** {@inheritDoc} */
notifyInstallingTimeout(TestDevice testDevice)1749     public void notifyInstallingTimeout(TestDevice testDevice) {
1750         synchronized (mObjectSync) {
1751             mObjectSync.sendNotify();
1752         }
1753     }
1754 
1755     /** {@inheritDoc} */
notifyTestingDeviceDisconnected()1756     public void notifyTestingDeviceDisconnected() {
1757         synchronized (mObjectSync) {
1758             mObjectSync.sendNotify();
1759             if (mPackageActionTimer != null) {
1760                 mPackageActionTimer.stop();
1761             }
1762         }
1763     }
1764 
1765     /** {@inheritDoc} */
notifyUninstallingComplete(int resultCode)1766     public void notifyUninstallingComplete(int resultCode) {
1767         synchronized (mObjectSync) {
1768             mObjectSync.sendNotify();
1769             mPackageActionTimer.stop();
1770         }
1771     }
1772 
1773     /** {@inheritDoc} */
notifyUninstallingTimeout(TestDevice testDevice)1774     public void notifyUninstallingTimeout(TestDevice testDevice) {
1775         synchronized (mObjectSync) {
1776             mObjectSync.sendNotify();
1777         }
1778     }
1779 
1780     /**
1781      * Synchronization object for communication between threads.
1782      */
1783     class ObjectSync {
1784         private boolean mNotifySent = false;
1785 
1786         /**
1787          * Send notify to the waiting thread.
1788          */
sendNotify()1789         public void sendNotify() {
1790             Log.d("ObjectSync.sendNotify() is called, mNotifySent=" + mNotifySent);
1791             mNotifySent = true;
1792             notify();
1793         }
1794 
1795         /**
1796          * Wait on.
1797          */
waitOn()1798         public void waitOn() throws InterruptedException {
1799             Log.d("ObjectSync.waitOn() is called, mNotifySent=" + mNotifySent);
1800             if (!mNotifySent) {
1801                 wait();
1802             }
1803 
1804             mNotifySent = false;
1805         }
1806 
1807         /**
1808          * Check if notify has been sent to the waiting thread.
1809          *
1810          * @return If sent, return true; else, return false.
1811          */
isNotified()1812         public boolean isNotified() {
1813             return mNotifySent;
1814         }
1815     }
1816 
1817     /**
1818      * Take a screenshot of the device under test.
1819      *
1820      * @return the screenshot
1821      * @throws IOException
1822      */
getScreenshot()1823     public RawImage getScreenshot() throws IOException {
1824         return mDevice.getScreenshot();
1825     }
1826 }
1827