Home | History | Annotate | Download | only in testtype
      1 /*
      2  * Copyright (C) 2011 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.tradefed.testtype;
     18 
     19 import com.android.ddmlib.IDevice;
     20 import com.android.tradefed.config.Option;
     21 import com.android.tradefed.config.OptionClass;
     22 import com.android.tradefed.device.DeviceNotAvailableException;
     23 import com.android.tradefed.device.DeviceSelectionOptions;
     24 import com.android.tradefed.device.ITestDevice;
     25 import com.android.tradefed.log.LogUtil.CLog;
     26 import com.android.tradefed.result.ITestInvocationListener;
     27 import com.android.tradefed.targetprep.ITargetPreparer;
     28 import com.android.tradefed.util.IRunUtil;
     29 import com.android.tradefed.util.RunUtil;
     30 
     31 import org.junit.Assert;
     32 
     33 import java.util.concurrent.ExecutionException;
     34 import java.util.concurrent.TimeUnit;
     35 
     36 /**
     37  * An {@link ITargetPreparer} that checks for a minimum battery charge, and waits for the battery
     38  * to reach a second charging threshold if the minimum charge isn't present.
     39  */
     40 @OptionClass(alias = "battery-checker")
     41 public class DeviceBatteryLevelChecker implements IDeviceTest, IRemoteTest {
     42 
     43     ITestDevice mTestDevice = null;
     44 
     45     /**
     46      * We use max-battery here to coincide with a {@link DeviceSelectionOptions} option of the same
     47      * name.  Thus, DeviceBatteryLevelChecker
     48      */
     49     @Option(name = "max-battery", description = "Charge level below which we force the device to " +
     50             "sit and charge.  Range: 0-100.")
     51     private Integer mMaxBattery = 20;
     52 
     53     @Option(name = "resume-level", description = "Charge level at which we release the device to " +
     54             "begin testing again. Range: 0-100.")
     55     private int mResumeLevel = 80;
     56 
     57     /**
     58      * This is decoupled from the log poll time below specifically to allow this invocation to be
     59      * killed without having to wait for the full log period to lapse.
     60      */
     61     @Option(name = "poll-time", description = "Time in minutes to wait between battery level " +
     62             "polls. Decimal times accepted.")
     63     private double mChargingPollTime = 1.0;
     64 
     65     @Option(name = "batt-log-period", description = "Min time in minutes to wait between " +
     66             "printing current battery level to log.  Decimal times accepted.")
     67     private double mLoggingPollTime = 10.0;
     68 
     69     @Option(name = "reboot-charging-devices", description = "Whether to reboot a device when we " +
     70             "detect that it should be held for charging.  This would hopefully kill any battery-" +
     71             "draining processes and allow the device to charge at its fastest rate.")
     72     private boolean mRebootChargeDevices = false;
     73 
     74     @Option(name = "stop-runtime", description = "Whether to stop runtime.")
     75     private boolean mStopRuntime = false;
     76 
     77     @Option(name = "stop-logcat", description = "Whether to stop logcat during the recharge. "
     78             + "this option is enabled by default.")
     79     private boolean mStopLogcat = true;
     80 
     81     Integer checkBatteryLevel(ITestDevice device) {
     82         try {
     83             IDevice idevice = device.getIDevice();
     84             // Force a synchronous check, which will also tell us if the device is still alive
     85             return idevice.getBattery(500, TimeUnit.MILLISECONDS).get();
     86         } catch (InterruptedException | ExecutionException e) {
     87             return null;
     88         }
     89     }
     90 
     91     private void stopDeviceRuntime() throws DeviceNotAvailableException {
     92         getDevice().executeShellCommand("stop");
     93     }
     94 
     95     /**
     96      * {@inheritDoc}
     97      */
     98     @Override
     99     public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
    100         Assert.assertNotNull(mTestDevice);
    101 
    102         Integer batteryLevel = checkBatteryLevel(mTestDevice);
    103 
    104         if (batteryLevel == null) {
    105             CLog.w("Failed to determine battery level for device %s.",
    106                     mTestDevice.getSerialNumber());
    107             return;
    108         } else if (batteryLevel < mMaxBattery) {
    109             // Time-out.  Send the device to the corner
    110             CLog.w("Battery level %d is below the min level %d; holding for device %s to charge " +
    111                     "to level %d", batteryLevel, mMaxBattery, mTestDevice.getSerialNumber(),
    112                     mResumeLevel);
    113         } else {
    114             // Good to go
    115             CLog.d("Battery level %d is above the minimum of %d; %s is good to go.", batteryLevel,
    116                     mMaxBattery, mTestDevice.getSerialNumber());
    117             return;
    118         }
    119 
    120         if (mRebootChargeDevices) {
    121             // reboot the device, in an attempt to kill any battery-draining processes
    122             CLog.d("Rebooting device %s prior to holding", mTestDevice.getSerialNumber());
    123             mTestDevice.reboot();
    124         }
    125 
    126         // turn screen off
    127         turnScreenOff(mTestDevice);
    128 
    129         if (mStopRuntime) {
    130             stopDeviceRuntime();
    131         }
    132         // Stop our logcat receiver
    133         if (mStopLogcat) {
    134             getDevice().stopLogcat();
    135         }
    136 
    137         // If we're down here, it's time to hold the device until it reaches mResumeLevel
    138         Long lastReportTime = System.currentTimeMillis();
    139         Integer newLevel = checkBatteryLevel(mTestDevice);
    140 
    141         while (batteryLevel != null && batteryLevel < mResumeLevel) {
    142             if (System.currentTimeMillis() - lastReportTime > mLoggingPollTime * 60 * 1000) {
    143                 // Log the battery level status every mLoggingPollTime minutes
    144                 CLog.w("Battery level for device %s is currently %d", mTestDevice.getSerialNumber(),
    145                         newLevel);
    146                 lastReportTime = System.currentTimeMillis();
    147             }
    148 
    149             getRunUtil().sleep((long) (mChargingPollTime * 60 * 1000));
    150             newLevel = checkBatteryLevel(mTestDevice);
    151             if (newLevel == null) {
    152                 // weird
    153                 CLog.w("Breaking out of wait loop because battery level read failed for device %s",
    154                         mTestDevice.getSerialNumber());
    155                 break;
    156             } else if (newLevel < batteryLevel) {
    157                 // also weird
    158                 CLog.w("Warning: battery discharged from %d to %d on device %s during the last " +
    159                         "%.02f minutes.", batteryLevel, newLevel, mTestDevice.getSerialNumber(),
    160                         mChargingPollTime);
    161             } else {
    162                 CLog.v("Battery level for device %s is currently %d", mTestDevice.getSerialNumber(),
    163                         newLevel);
    164             }
    165             batteryLevel = newLevel;
    166         }
    167         CLog.w("Device %s is now charged to battery level %d; releasing.",
    168                 mTestDevice.getSerialNumber(), batteryLevel);
    169     }
    170 
    171     private void turnScreenOff(ITestDevice device) throws DeviceNotAvailableException {
    172         // disable always on
    173         device.executeShellCommand("svc power stayon false");
    174         // set screen timeout to 1s
    175         device.executeShellCommand("settings put system screen_off_timeout 1000");
    176         // pause for 5s to ensure that screen would be off
    177         getRunUtil().sleep(5000);
    178     }
    179 
    180     /**
    181      * Get a RunUtil instance
    182      * <p />
    183      * Exposed for unit testing
    184      */
    185     IRunUtil getRunUtil() {
    186         return RunUtil.getDefault();
    187     }
    188 
    189     @Override
    190     public void setDevice(ITestDevice device) {
    191         mTestDevice = device;
    192     }
    193 
    194     @Override
    195     public ITestDevice getDevice() {
    196         return mTestDevice;
    197     }
    198 
    199     protected void setResumeLevel(int level) {
    200         mResumeLevel = level;
    201     }
    202 }
    203 
    204