1 /* 2 * Copyright (C) 2020 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.tests.sysfs; 17 18 import static org.junit.Assert.assertNotNull; 19 import static org.junit.Assert.assertTrue; 20 import static org.junit.Assert.fail; 21 22 import com.android.tradefed.device.ITestDevice; 23 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; 24 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; 25 import com.android.tradefed.util.FileUtil; 26 import com.android.tradefed.util.TargetFileUtils; 27 import com.android.tradefed.util.TargetFileUtils.FilePermission; 28 import com.google.common.base.Strings; 29 import java.util.ArrayList; 30 import java.util.Arrays; 31 import java.util.HashSet; 32 import java.util.List; 33 import java.util.UUID; 34 import java.util.regex.Matcher; 35 import java.util.regex.Pattern; 36 import org.junit.Test; 37 import org.junit.runner.RunWith; 38 39 /* A test to check check sysfs files. */ 40 @RunWith(DeviceJUnit4ClassRunner.class) 41 public class KernelApiSysfsTest extends BaseHostJUnit4Test { 42 /* Check for the existence of required files in /sys/class/android_usb. */ 43 @Test testAndroidUSB()44 public void testAndroidUSB() throws Exception { 45 String state = "/sys/class/android_usb/android0/state"; 46 assertTrue(TargetFileUtils.isReadOnly(state, getDevice())); 47 String content = getDevice().pullFileContents(state).trim(); 48 HashSet<String> possibles = 49 new HashSet<>(Arrays.asList("DISCONNECTED", "CONNECTED", "CONFIGURED")); 50 assertTrue(possibles.contains(content)); 51 } 52 53 /** 54 * Check the format of cpu online file. 55 * 56 * Confirm /sys/devices/system/cpu/online exists and is read-only. 57 * Parse contents to ensure it is a comma-separated series of ranges 58 * (%d-%d) and/or integers. 59 */ 60 @Test testCpuOnlineFormat()61 public void testCpuOnlineFormat() throws Exception { 62 String filePath = "/sys/devices/system/cpu/online"; 63 assertTrue(TargetFileUtils.isReadOnly(filePath, getDevice())); 64 String content = getDevice().pullFileContents(filePath).trim(); 65 assertTrue(content.matches("(\\d+(-\\d+)?)(,\\d+(-\\d+)?)*")); 66 } 67 isReadOnlyAndIntegerContent(String filePath)68 private void isReadOnlyAndIntegerContent(String filePath) throws Exception { 69 assertTrue("Failed readonly check of " + filePath, 70 TargetFileUtils.isReadOnly(filePath, getDevice())); 71 String content = getDevice().pullFileContents(filePath).trim(); 72 // Confirm the content is integer. 73 Integer.parseInt(content); 74 } 75 isReadWriteAndIntegerContent(String filePath)76 private void isReadWriteAndIntegerContent(String filePath) throws Exception { 77 assertTrue("Failed readwrite check of " + filePath, 78 TargetFileUtils.isReadWriteOnly(filePath, getDevice())); 79 String content = getDevice().pullFileContents(filePath).trim(); 80 // Confirm the content is integer. 81 Integer.parseInt(content); 82 } 83 84 /** 85 * Check each cpu's scaling_cur_freq, scaling_min_freq, scaling_max_freq, 86 * scaling_available_frequencies, and time_in_state files. 87 */ 88 @Test testPerCpuCpufreq()89 public void testPerCpuCpufreq() throws Exception { 90 String filePath = "/sys/devices/system/cpu/present"; 91 assertTrue(TargetFileUtils.isReadOnly(filePath, getDevice())); 92 String presentCpus = getDevice().pullFileContents(filePath).trim(); 93 String[] cpuRanges = presentCpus.split(","); 94 List<Integer> cpuList = new ArrayList<>(); 95 Pattern p = Pattern.compile("(\\d+)(-\\d+)?"); 96 for (String range : cpuRanges) { 97 Matcher m = p.matcher(range); 98 assertTrue("Malformatted range in " + filePath, m.find()); 99 int startCpu = Integer.parseInt(m.group(1)); 100 int endCpu = startCpu; 101 if (m.group(2) != null) 102 endCpu = Integer.parseInt(m.group(2).substring(1)); 103 for (int i = startCpu; i <= endCpu; i++) { 104 cpuList.add(i); 105 } 106 } 107 108 String f; 109 String content; 110 for (int cpu : cpuList) { 111 f = String.format("/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", cpu); 112 if (getDevice().doesFileExist(f)) { 113 isReadOnlyAndIntegerContent(f); 114 } 115 116 f = String.format("/sys/devices/system/cpu/cpu%d/cpufreq/scaling_min_freq", cpu); 117 if (getDevice().doesFileExist(f)) { 118 isReadWriteAndIntegerContent(f); 119 } 120 121 f = String.format("/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq", cpu); 122 if (getDevice().doesFileExist(f)) { 123 isReadWriteAndIntegerContent(f); 124 } 125 126 f = String.format( 127 "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_available_frequencies", cpu); 128 if (getDevice().doesFileExist(f)) { 129 assertTrue(TargetFileUtils.isReadOnly(f, getDevice())); 130 content = getDevice().pullFileContents(f).trim(); 131 if (!Strings.isNullOrEmpty(content)) { 132 String[] availFreqs = content.split(" "); 133 for (String freq : availFreqs) { 134 Integer.parseInt(freq); 135 } 136 } 137 } 138 139 f = String.format("/sys/devices/system/cpu/cpu%d/cpufreq/stats/time_in_state", cpu); 140 if (getDevice().doesFileExist(f)) { 141 assertTrue(TargetFileUtils.isReadOnly(f, getDevice())); 142 content = getDevice().pullFileContents(f).trim(); 143 if (!Strings.isNullOrEmpty(content)) { 144 for (String line : content.split("\\n")) { 145 String[] values = line.split(" "); 146 for (String value : values) { 147 Integer.parseInt(value); 148 } 149 } 150 } 151 } 152 } 153 } 154 155 /* Check /sys/kernel/wakeup_reasons/last_resume_reason. */ 156 @Test testLastResumeReason()157 public void testLastResumeReason() throws Exception { 158 String filePath = "/sys/kernel/wakeup_reasons/last_resume_reason"; 159 assertTrue(TargetFileUtils.isReadOnly(filePath, getDevice())); 160 } 161 162 /* Check the value of /sys/devices/system/cpu/kernel_max. */ 163 @Test testKernelMax()164 public void testKernelMax() throws Exception { 165 String filePath = "/sys/devices/system/cpu/kernel_max"; 166 isReadOnlyAndIntegerContent(filePath); 167 } 168 findFiles(String dir, String nameFilter)169 private String[] findFiles(String dir, String nameFilter) throws Exception { 170 String output = getDevice().executeShellCommand( 171 String.format("find %s -name \"%s\" -maxdepth 1 -type l", dir, nameFilter)); 172 if (output.trim().isEmpty()) { 173 return new String[0]; 174 } 175 return output.split("\r?\n"); 176 } 177 178 /* Check for /sys/class/net/?/mtu. */ 179 @Test testNetMTU()180 public void testNetMTU() throws Exception { 181 String[] dirList = findFiles("/sys/class/net", "*"); 182 for (String entry : dirList) { 183 isReadWriteAndIntegerContent(entry + "/mtu"); 184 } 185 } 186 187 /* Check that at least one rtc exists with hctosys = 1. */ 188 @Test testRtcHctosys()189 public void testRtcHctosys() throws Exception { 190 String[] rtcList = findFiles("/sys/class/rtc", "rtc*"); 191 for (String entry : rtcList) { 192 String content = getDevice().pullFileContents(entry + "/hctosys"); 193 if (Strings.isNullOrEmpty(content)) { 194 continue; 195 } 196 try { 197 int hctosys = Integer.parseInt(content.trim()); 198 if (hctosys == 1) { 199 return; 200 } 201 } catch (NumberFormatException e) { 202 continue; 203 } 204 } 205 fail("No RTC with hctosys=1 present"); 206 } 207 208 /* Check that locking and unlocking a wake lock works.. */ 209 @Test testWakeLock()210 public void testWakeLock() throws Exception { 211 String wakeLockPath = "/sys/power/wake_lock"; 212 String wakeUnLockPath = "/sys/power/wake_unlock"; 213 String lockName = "KernelApiSysfsTestWakeLock" + UUID.randomUUID().toString(); 214 215 // Enable wake lock 216 getDevice().executeShellCommand(String.format("echo %s > %s", lockName, wakeLockPath)); 217 218 // Confirm wake lock is enabled 219 String results = getDevice().pullFileContents(wakeLockPath).trim(); 220 HashSet<String> activeSources = new HashSet<>(Arrays.asList(results.split(" "))); 221 assertTrue(String.format("active wake lock not reported in %s", wakeLockPath), 222 activeSources.contains(lockName)); 223 224 // Disable wake lock 225 getDevice().executeShellCommand(String.format("echo %s > %s", lockName, wakeUnLockPath)); 226 227 // Confirm wake lock is no longer enabled 228 results = getDevice().pullFileContents(wakeLockPath).trim(); 229 activeSources = new HashSet<>(Arrays.asList(results.split(" "))); 230 assertTrue(String.format("inactive wake lock reported in %s", wakeLockPath), 231 !activeSources.contains(lockName)); 232 233 results = getDevice().pullFileContents(wakeUnLockPath).trim(); 234 activeSources = new HashSet<>(Arrays.asList(results.split(" "))); 235 assertTrue(String.format("inactive wake lock not reported in %s", wakeUnLockPath), 236 activeSources.contains(lockName)); 237 } 238 239 @Test testWakeupCount()240 public void testWakeupCount() throws Exception { 241 String filePath = "/sys/power/wakeup_count"; 242 assertTrue("Failed readwrite check of " + filePath, 243 TargetFileUtils.isReadWriteOnly(filePath, getDevice())); 244 } 245 246 /* /sys/power/state controls the system sleep states. */ 247 @Test testSysPowerState()248 public void testSysPowerState() throws Exception { 249 String filePath = "/sys/power/state"; 250 assertTrue("Failed readwrite check of " + filePath, 251 TargetFileUtils.isReadWriteOnly(filePath, getDevice())); 252 String content = getDevice().pullFileContents(filePath).trim(); 253 HashSet<String> allowedStates = 254 new HashSet<String>(Arrays.asList("freeze", "mem", "disk", "standby")); 255 for (String state : content.split(" ")) { 256 assertTrue(String.format("Invalid system power state: '%s'", state), 257 allowedStates.contains(state)); 258 } 259 } 260 } 261