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