1 /* 2 * Copyright (C) 2015 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 package com.android.tradefed.targetprep; 17 18 import com.android.annotations.VisibleForTesting; 19 import com.android.tradefed.config.Option; 20 import com.android.tradefed.config.OptionClass; 21 import com.android.tradefed.device.DeviceNotAvailableException; 22 import com.android.tradefed.device.ITestDevice; 23 import com.android.tradefed.invoker.TestInformation; 24 import com.android.tradefed.log.LogUtil.CLog; 25 import com.android.tradefed.result.error.InfraErrorIdentifier; 26 import com.android.tradefed.util.IRunUtil; 27 import com.android.tradefed.util.RunUtil; 28 29 import java.util.regex.Matcher; 30 import java.util.regex.Pattern; 31 32 /** An {@link ITargetPreparer} that waits until device's temperature gets down to target */ 33 @OptionClass(alias = "temperature-throttle-waiter") 34 public class TemperatureThrottlingWaiter extends BaseTargetPreparer { 35 // temperature raw format example output : Result:30 Raw:7f6f 36 private static final Pattern TEMPERATURE_RAW_FORMAT_REGEX = 37 Pattern.compile("Result:(\\d+)\\sRaw:(\\w+)"); 38 39 // temperature format example output : 30 40 private static final Pattern TEMPERATURE_FORMAT_REGEX = Pattern.compile("\\d+"); 41 42 // temperature format example output: 43 // Temperature{mValue=28.787504, mType=3, mName=VIRTUAL-SKIN, mStatus=0} 44 private static final Pattern TEMPERATURE_SKIN_FORMAT_PATTERN = 45 Pattern.compile("mValue=([\\d]+.[\\d]+).*mType=3"); 46 47 @Option(name = "poll-interval", 48 description = "Interval in seconds, to poll for device temperature; defaults to 30s") 49 private long mPollIntervalSecs = 30; 50 51 @Option(name = "max-wait", description = "Max wait time in seconds, for device cool down " 52 + "to target temperature; defaults to 20 minutes") 53 private long mMaxWaitSecs = 60 * 20; 54 55 @Option(name = "abort-on-timeout", description = "If test should be aborted if device is still " 56 + " above expected temperature; defaults to false") 57 private boolean mAbortOnTimeout = false; 58 59 @Option(name = "post-idle-wait", description = "Additional time to wait in seconds, after " 60 + "temperature has reached to target; defaults to 120s") 61 private long mPostIdleWaitSecs = 120; 62 63 public static final String DEVICE_TEMPERATURE_FILE_PATH_NAME = "device-temperature-file-path"; 64 65 @Option( 66 name = DEVICE_TEMPERATURE_FILE_PATH_NAME, 67 description = 68 "Name of file that contains device" 69 + "temperature. Example: /sys/class/hwmon/hwmon1/device/msm_therm") 70 private String mDeviceTemperatureFilePath = null; 71 72 public static final String DEVICE_TEMPERATURE_COMMAND_NAME = "device-temperature-command"; 73 74 @Option( 75 name = DEVICE_TEMPERATURE_COMMAND_NAME, 76 description = 77 "Command for getting device temperature. If both device-temperature-file-path" 78 + " and device-temperature-command are set," 79 + " only device-temperature-file-path will be actived." 80 + " Example: dumpsys thermalservice" 81 + " | grep 'mType=3' | grep Temperature | awk 'END{print}'") 82 private String mDeviceTemperatureCommand = 83 "dumpsys thermalservice | grep 'mType=3' | grep Temperature | awk 'END{print}'"; 84 85 @Option(name = "target-temperature", description = "Target Temperature that device should have;" 86 + "defaults to 30c") 87 private int mTargetTemperature = 30; 88 89 /** {@inheritDoc} */ 90 @Override setUp(TestInformation testInfo)91 public void setUp(TestInformation testInfo) 92 throws TargetSetupError, BuildError, DeviceNotAvailableException { 93 if (mDeviceTemperatureFilePath == null && mDeviceTemperatureCommand == null) { 94 return; 95 } 96 ITestDevice device = testInfo.getDevice(); 97 long start = System.currentTimeMillis(); 98 long maxWaitMs = mMaxWaitSecs * 1000; 99 long intervalMs = mPollIntervalSecs * 1000; 100 int deviceTemperature = Integer.MAX_VALUE; 101 while (true) { 102 // get device temperature 103 deviceTemperature = getDeviceTemperature(device); 104 if (deviceTemperature > mTargetTemperature) { 105 CLog.d("Temperature is still high actual %d/expected %d", 106 deviceTemperature, mTargetTemperature); 107 } else { 108 CLog.i("Total time elapsed to get to %dc : %ds", mTargetTemperature, 109 (System.currentTimeMillis() - start) / 1000); 110 break; // while loop 111 } 112 if ((System.currentTimeMillis() - start) > maxWaitMs) { 113 CLog.w("Temperature is still high, actual %d/expected %d; waiting after %ds", 114 deviceTemperature, mTargetTemperature, maxWaitMs); 115 if (mAbortOnTimeout) { 116 throw new TargetSetupError(String.format("Temperature is still high after wait " 117 + "timeout; actual %d/expected %d", deviceTemperature, 118 mTargetTemperature), device.getDeviceDescriptor()); 119 } 120 break; // while loop 121 } 122 getRunUtil().sleep(intervalMs); 123 } 124 // extra idle time after reaching the targetl to stable the system 125 getRunUtil().sleep(mPostIdleWaitSecs * 1000); 126 CLog.d("Done waiting, total time elapsed: %ds", 127 (System.currentTimeMillis() - start) / 1000); 128 } 129 130 /** 131 * @param device 132 * @throws DeviceNotAvailableException 133 */ getDeviceTemperature(ITestDevice device)134 protected int getDeviceTemperature (ITestDevice device) 135 throws DeviceNotAvailableException, TargetSetupError { 136 String result = executeDeviceTemperatureCommand(device); 137 CLog.i(String.format("Temperature command output : %s", result)); 138 139 if (result == null || result.contains("No such file or directory")) { 140 throw new TargetSetupError( 141 String.format("File %s doesn't exist", mDeviceTemperatureFilePath), 142 device.getDeviceDescriptor(), 143 InfraErrorIdentifier.OPTION_CONFIGURATION_ERROR); 144 } 145 return parseResult(result, device); 146 } 147 executeDeviceTemperatureCommand(ITestDevice device)148 private String executeDeviceTemperatureCommand(ITestDevice device) 149 throws DeviceNotAvailableException { 150 if (mDeviceTemperatureFilePath != null) { 151 return device.executeShellCommand( 152 String.format("cat %s", mDeviceTemperatureFilePath)).trim(); 153 } 154 if (mDeviceTemperatureCommand != null) { 155 return device.executeShellCommand(mDeviceTemperatureCommand).trim(); 156 } 157 return "UNKNOWN"; 158 } 159 parseResult(String result, ITestDevice device)160 private int parseResult(String result, ITestDevice device) throws TargetSetupError { 161 Matcher tempRawFormatMatcher = TEMPERATURE_RAW_FORMAT_REGEX.matcher(result); 162 if (tempRawFormatMatcher.find()) { 163 return Integer.parseInt(tempRawFormatMatcher.group(1)); 164 } 165 Matcher tempFormatMatcher = TEMPERATURE_FORMAT_REGEX.matcher(result); 166 if (tempFormatMatcher.find()) { 167 return Integer.parseInt(tempFormatMatcher.group()); 168 } 169 Matcher skinFormatMatcher = TEMPERATURE_SKIN_FORMAT_PATTERN.matcher(result); 170 if (skinFormatMatcher.find()) { 171 return Integer.parseInt(skinFormatMatcher.group(1)); 172 } 173 throw new TargetSetupError( 174 String.format("result content is not as expected. Content : %s", result), 175 device.getDeviceDescriptor()); 176 } 177 178 @VisibleForTesting getRunUtil()179 protected IRunUtil getRunUtil() { 180 return RunUtil.getDefault(); 181 } 182 } 183