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