| /* |
| * 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.targetprep; |
| |
| import com.android.tradefed.build.IBuildInfo; |
| import com.android.tradefed.config.Option; |
| import com.android.tradefed.config.OptionClass; |
| import com.android.tradefed.device.DeviceNotAvailableException; |
| import com.android.tradefed.device.ITestDevice; |
| import com.android.tradefed.log.LogUtil.CLog; |
| import com.android.tradefed.util.IRunUtil; |
| import com.android.tradefed.util.RunUtil; |
| |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| |
| /** |
| * 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 ITargetPreparer { |
| |
| @Option(name="min-level", description="Charge level below which we force the device to sit " + |
| "and charge. Range: 0-100.") |
| private int mMinChargeLevel = 10; |
| |
| @Option(name="resume-level", description="Charge level at which we release the device to " + |
| "begin testing again. Range: 0-100.") |
| private int mResumeLevel = 80; |
| |
| @Option(name="poll-time", description="Time in minutes to wait between battery level polls. " + |
| "Decimal times accepted.") |
| private double mChargingPollTime = 5.0; |
| |
| private static final Pattern BATTERY_LEVEL = Pattern.compile("\\s*level: (\\d+)"); |
| |
| private Integer checkBatteryLevel(ITestDevice device) throws DeviceNotAvailableException { |
| // FIXME: scale the battery level by "scale" instead of assuming 100 |
| String dumpsys = device.executeShellCommand("dumpsys battery"); |
| if (dumpsys != null) { |
| String[] lines = dumpsys.split("\r?\n"); |
| for (String line : lines) { |
| Matcher m = BATTERY_LEVEL.matcher(line); |
| if (m.matches()) { |
| try { |
| return Integer.parseInt(m.group(1)); |
| } catch (NumberFormatException e) { |
| CLog.w("Failed to parse %s as an integer", m.group(1)); |
| } |
| } |
| } |
| } |
| CLog.w("Failed to determine battery level for device %s. `dumpsys battery` was: %s", |
| device.getSerialNumber(), dumpsys); |
| return null; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void setUp(ITestDevice device, IBuildInfo buildInfo) throws TargetSetupError, BuildError, |
| DeviceNotAvailableException { |
| Integer batteryLevel = checkBatteryLevel(device); |
| if (batteryLevel == null) { |
| // we already logged a warning |
| return; |
| } else if (batteryLevel < mMinChargeLevel) { |
| // 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, mMinChargeLevel, device.getSerialNumber(), |
| mResumeLevel); |
| } else { |
| // Good to go |
| CLog.d("Battery level %d is above the minimum of %d; %s is good to go.", batteryLevel, |
| mMinChargeLevel, device.getSerialNumber()); |
| return; |
| } |
| |
| // If we're down here, it's time to hold the device until it reaches mResumeLevel |
| while (batteryLevel != null && batteryLevel < mResumeLevel) { |
| // FIXME show periodic status messages with "w" log level |
| getRunUtil().sleep((long) (mChargingPollTime * 60 * 1000)); |
| Integer newLevel = checkBatteryLevel(device); |
| if (newLevel == null) { |
| // weird |
| CLog.w("Breaking out of wait loop because battery level read failed for device %s", |
| device.getSerialNumber()); |
| break; |
| } else if (newLevel < batteryLevel) { |
| // also weird |
| CLog.w("Warning: battery discharged from %d to %d on device %s during the last " + |
| "%d minutes.", batteryLevel, newLevel, device.getSerialNumber(), |
| mChargingPollTime); |
| } else { |
| CLog.d("Battery level for device %s is now %d", device.getSerialNumber(), newLevel); |
| } |
| batteryLevel = newLevel; |
| } |
| CLog.w("Device %s is now charged to battery level %d; releasing.", device.getSerialNumber(), |
| batteryLevel); |
| } |
| |
| /** |
| * Get a RunUtil instance |
| * <p /> |
| * Exposed for unit testing |
| */ |
| IRunUtil getRunUtil() { |
| return RunUtil.getDefault(); |
| } |
| } |
| |