1 /* 2 * Copyright (C) 2023 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.internal.os; 18 19 import android.annotation.NonNull; 20 import android.os.FileUtils; 21 import android.util.IntArray; 22 import android.util.Slog; 23 import android.util.SparseArray; 24 25 import com.android.internal.annotations.VisibleForTesting; 26 27 import libcore.util.EmptyArray; 28 29 import java.io.File; 30 import java.io.IOException; 31 import java.util.Arrays; 32 import java.util.regex.Matcher; 33 import java.util.regex.Pattern; 34 35 /** 36 * Captures a CPU scaling policies such as available scaling frequencies as well as 37 * CPUs (cores) for each policy. 38 * 39 * See <a 40 * href="https://www.kernel.org/doc/html/latest/admin-guide/pm/cpufreq.html 41 * #policy-interface-in-sysfs">Policy Interface in sysfs</a> 42 */ 43 @android.ravenwood.annotation.RavenwoodKeepWholeClass 44 public class CpuScalingPolicyReader { 45 private static final String TAG = "CpuScalingPolicyReader"; 46 private static final String CPUFREQ_DIR = "/sys/devices/system/cpu/cpufreq"; 47 private static final Pattern POLICY_PATTERN = Pattern.compile("policy(\\d+)"); 48 private static final String FILE_NAME_RELATED_CPUS = "related_cpus"; 49 private static final String FILE_NAME_SCALING_AVAILABLE_FREQUENCIES = 50 "scaling_available_frequencies"; 51 private static final String FILE_NAME_SCALING_BOOST_FREQUENCIES = "scaling_boost_frequencies"; 52 private static final String FILE_NAME_CPUINFO_CUR_FREQ = "cpuinfo_cur_freq"; 53 54 private final String mCpuFreqDir; 55 CpuScalingPolicyReader()56 public CpuScalingPolicyReader() { 57 this(CPUFREQ_DIR); 58 } 59 60 @VisibleForTesting CpuScalingPolicyReader(String cpuFreqDir)61 public CpuScalingPolicyReader(String cpuFreqDir) { 62 mCpuFreqDir = cpuFreqDir; 63 } 64 65 /** 66 * Reads scaling policy info from sysfs files in /sys/devices/system/cpu/cpufreq 67 */ 68 @NonNull read()69 public CpuScalingPolicies read() { 70 SparseArray<int[]> cpusByPolicy = new SparseArray<>(); 71 SparseArray<int[]> freqsByPolicy = new SparseArray<>(); 72 73 File cpuFreqDir = new File(mCpuFreqDir); 74 File[] policyDirs = cpuFreqDir.listFiles(); 75 if (policyDirs != null) { 76 for (File policyDir : policyDirs) { 77 Matcher matcher = POLICY_PATTERN.matcher(policyDir.getName()); 78 if (matcher.matches()) { 79 int[] relatedCpus = readIntsFromFile( 80 new File(policyDir, FILE_NAME_RELATED_CPUS)); 81 if (relatedCpus.length == 0) { 82 continue; 83 } 84 85 int[] availableFreqs = readIntsFromFile( 86 new File(policyDir, FILE_NAME_SCALING_AVAILABLE_FREQUENCIES)); 87 int[] boostFreqs = readIntsFromFile( 88 new File(policyDir, FILE_NAME_SCALING_BOOST_FREQUENCIES)); 89 int[] freqs; 90 if (boostFreqs.length == 0) { 91 freqs = availableFreqs; 92 } else { 93 freqs = Arrays.copyOf(availableFreqs, 94 availableFreqs.length + boostFreqs.length); 95 System.arraycopy(boostFreqs, 0, freqs, availableFreqs.length, 96 boostFreqs.length); 97 } 98 if (freqs.length == 0) { 99 freqs = readIntsFromFile(new File(policyDir, FILE_NAME_CPUINFO_CUR_FREQ)); 100 if (freqs.length == 0) { 101 freqs = new int[]{0}; // Unknown frequency 102 } 103 } 104 int policy = Integer.parseInt(matcher.group(1)); 105 cpusByPolicy.put(policy, relatedCpus); 106 freqsByPolicy.put(policy, freqs); 107 } 108 } 109 } 110 111 if (cpusByPolicy.size() == 0) { 112 // There just has to be at least one CPU - otherwise, what's executing this code? 113 cpusByPolicy.put(0, new int[]{0}); 114 freqsByPolicy.put(0, new int[]{0}); 115 } 116 117 return new CpuScalingPolicies(cpusByPolicy, freqsByPolicy); 118 } 119 120 @NonNull readIntsFromFile(File file)121 private static int[] readIntsFromFile(File file) { 122 if (!file.exists()) { 123 return EmptyArray.INT; 124 } 125 126 IntArray intArray = new IntArray(16); 127 try { 128 String contents = FileUtils.readTextFile(file, 0, null).trim(); 129 String[] strings = contents.split(" "); 130 intArray.clear(); 131 for (String s : strings) { 132 if (s.isBlank()) { 133 continue; 134 } 135 try { 136 intArray.add(Integer.parseInt(s)); 137 } catch (NumberFormatException e) { 138 Slog.e(TAG, "Unexpected file format " + file 139 + ": " + contents, e); 140 } 141 } 142 return intArray.toArray(); 143 } catch (IOException e) { 144 Slog.e(TAG, "Cannot read " + file, e); 145 return EmptyArray.INT; 146 } 147 } 148 } 149