blob: 15018baab41c8df1a631b27bc7cbad1a09c47d0a [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tradefed.testtype;
import com.android.ddmlib.IDevice;
import com.android.tradefed.config.Option;
import com.android.tradefed.config.OptionClass;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.DeviceSelectionOptions;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.result.ITestInvocationListener;
import com.android.tradefed.targetprep.ITargetPreparer;
import com.android.tradefed.util.IRunUtil;
import com.android.tradefed.util.RunUtil;
import junit.framework.Assert;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/**
* An {@link ITargetPreparer} that checks for a minimum battery charge, and waits for the battery
* to reach a second charging threshold if the minimum charge isn't present.
*/
@OptionClass(alias = "battery-checker")
public class DeviceBatteryLevelChecker implements IDeviceTest, IRemoteTest {
ITestDevice mTestDevice = null;
/**
* We use max-battery here to coincide with a {@link DeviceSelectionOptions} option of the same
* name. Thus, DeviceBatteryLevelChecker
*/
@Option(name = "max-battery", description = "Charge level below which we force the device to " +
"sit and charge. Range: 0-100.")
private Integer mMaxBattery = 20;
@Option(name = "resume-level", description = "Charge level at which we release the device to " +
"begin testing again. Range: 0-100.")
private int mResumeLevel = 80;
/**
* This is decoupled from the log poll time below specifically to allow this invocation to be
* killed without having to wait for the full log period to lapse.
*/
@Option(name = "poll-time", description = "Time in minutes to wait between battery level " +
"polls. Decimal times accepted.")
private double mChargingPollTime = 1.0;
@Option(name = "batt-log-period", description = "Min time in minutes to wait between " +
"printing current battery level to log. Decimal times accepted.")
private double mLoggingPollTime = 10.0;
@Option(name = "reboot-charging-devices", description = "Whether to reboot a device when we " +
"detect that it should be held for charging. This would hopefully kill any battery-" +
"draining processes and allow the device to charge at its fastest rate.")
private boolean mRebootChargeDevices = false;
Integer checkBatteryLevel(ITestDevice device) throws DeviceNotAvailableException {
try {
IDevice idevice = device.getIDevice();
// Force a synchronous check, which will also tell us if the device is still alive
return idevice.getBattery(0, TimeUnit.MILLISECONDS).get();
} catch (InterruptedException | ExecutionException e) {
return null;
}
}
private void turnScreenOffOrStopRuntime(ITestDevice device) throws DeviceNotAvailableException {
String output = getDevice().executeShellCommand("pm path android");
if (output == null || !output.contains("package:")) {
CLog.d("framework does not seem to be running, trying to stop it.");
// stop framework in case it's running some sort of runtime restart loop, and we can
// still charge the device
getDevice().executeShellCommand("stop");
} else {
output = getDevice().executeShellCommand("dumpsys power");
if (output.contains("mScreenOn=true")) {
// KEYCODE_POWER = 26
getDevice().executeShellCommand("input keyevent 26");
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
Assert.assertNotNull(mTestDevice);
Integer batteryLevel = checkBatteryLevel(mTestDevice);
if (batteryLevel == null) {
CLog.w("Failed to determine battery level for device %s.",
mTestDevice.getSerialNumber());
return;
} else if (batteryLevel < mMaxBattery) {
// Time-out. Send the device to the corner
CLog.w("Battery level %d is below the min level %d; holding for device %s to charge " +
"to level %d", batteryLevel, mMaxBattery, mTestDevice.getSerialNumber(),
mResumeLevel);
} else {
// Good to go
CLog.d("Battery level %d is above the minimum of %d; %s is good to go.", batteryLevel,
mMaxBattery, mTestDevice.getSerialNumber());
return;
}
if (mRebootChargeDevices) {
// reboot the device, in an attempt to kill any battery-draining processes
CLog.d("Rebooting device %s prior to holding", mTestDevice.getSerialNumber());
mTestDevice.reboot();
}
turnScreenOffOrStopRuntime(mTestDevice);
// If we're down here, it's time to hold the device until it reaches mResumeLevel
Long lastReportTime = System.currentTimeMillis();
Integer newLevel = checkBatteryLevel(mTestDevice);
while (batteryLevel != null && batteryLevel < mResumeLevel) {
if (System.currentTimeMillis() - lastReportTime > mLoggingPollTime * 60 * 1000) {
// Log the battery level status every mLoggingPollTime minutes
CLog.w("Battery level for device %s is currently %d", mTestDevice.getSerialNumber(),
newLevel);
lastReportTime = System.currentTimeMillis();
}
getRunUtil().sleep((long) (mChargingPollTime * 60 * 1000));
newLevel = checkBatteryLevel(mTestDevice);
if (newLevel == null) {
// weird
CLog.w("Breaking out of wait loop because battery level read failed for device %s",
mTestDevice.getSerialNumber());
break;
} else if (newLevel < batteryLevel) {
// also weird
CLog.w("Warning: battery discharged from %d to %d on device %s during the last " +
"%.02f minutes.", batteryLevel, newLevel, mTestDevice.getSerialNumber(),
mChargingPollTime);
} else {
CLog.v("Battery level for device %s is currently %d", mTestDevice.getSerialNumber(),
newLevel);
}
batteryLevel = newLevel;
}
CLog.w("Device %s is now charged to battery level %d; releasing.",
mTestDevice.getSerialNumber(), batteryLevel);
}
/**
* Get a RunUtil instance
* <p />
* Exposed for unit testing
*/
IRunUtil getRunUtil() {
return RunUtil.getDefault();
}
@Override
public void setDevice(ITestDevice device) {
mTestDevice = device;
}
@Override
public ITestDevice getDevice() {
return mTestDevice;
}
}