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