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