blob: 720577eadd193db9ff9b7fb6690ed1eccc369c3d [file] [log] [blame]
Aurimas Liutikasdc3f8852024-07-11 10:07:48 -07001/*
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
17package com.android.internal.os;
18
19import android.annotation.NonNull;
20import android.os.FileUtils;
21import android.util.IntArray;
22import android.util.Slog;
23import android.util.SparseArray;
24
25import com.android.internal.annotations.VisibleForTesting;
26
27import libcore.util.EmptyArray;
28
29import java.io.File;
30import java.io.IOException;
31import java.util.Arrays;
32import java.util.regex.Matcher;
33import 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
44public 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
56 public CpuScalingPolicyReader() {
57 this(CPUFREQ_DIR);
58 }
59
60 @VisibleForTesting
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
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
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}