blob: e9b1cb456682648f41dea1d751242922fd78111f [file] [log] [blame]
Howard Hao1f819812021-06-21 15:28:29 -07001/*
2 * Copyright (C) 2021 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.car.telemetry.systemmonitor;
18
Howard Hao2b11eb52021-08-06 16:43:24 -070019import android.annotation.Nullable;
20import android.app.ActivityManager;
21import android.app.ActivityManager.MemoryInfo;
22import android.content.Context;
23import android.os.Handler;
24import android.util.Slog;
25
26import com.android.car.CarLog;
27import com.android.internal.annotations.GuardedBy;
28import com.android.internal.annotations.VisibleForTesting;
29
30import java.io.BufferedReader;
31import java.io.FileReader;
32import java.io.IOException;
33
Howard Hao1f819812021-06-21 15:28:29 -070034/**
35 * SystemMonitor monitors system states and report to listeners when there are
36 * important changes.
37 */
38public class SystemMonitor {
39
Howard Hao2b11eb52021-08-06 16:43:24 -070040 private static final int NUM_LOADAVG_VALS = 3;
41 private static final float HI_CPU_LOAD_PER_CORE_BASE_LEVEL = 1.0f;
42 private static final float MED_CPU_LOAD_PER_CORE_BASE_LEVEL = 0.5f;
43 private static final float HI_MEM_LOAD_BASE_LEVEL = 0.95f;
44 private static final float MED_MEM_LOAD_BASE_LEVEL = 0.80f;
45 private static final String LOADAVG_PATH = "/proc/loadavg";
46
47 private static final int POLL_INTERVAL_MILLIS = 60000;
48
49 private final Handler mWorkerHandler;
50
51 private final Object mLock = new Object();
52
53 private final Context mContext;
54 private final ActivityManager mActivityManager;
55 private final String mLoadavgPath;
56
57 @GuardedBy("mLock")
58 @Nullable private SystemMonitorCallback mCallback;
59 @GuardedBy("mLock")
60 private boolean mSystemMonitorRunning = false;
61
Howard Hao1f819812021-06-21 15:28:29 -070062
63 /**
64 * Interface for receiving notifications about system monitor changes.
65 */
66 public interface SystemMonitorCallback {
67 /**
68 * Listens to system monitor event.
69 *
70 * @param event the system monitor event.
71 */
72 void onSystemMonitorEvent(SystemMonitorEvent event);
73 }
74
75 /**
Howard Hao2b11eb52021-08-06 16:43:24 -070076 * Creates a SystemMonitor instance set with default loadavg path.
77 *
78 * @param context the context this is running in.
79 * @param workerHandler a handler for running monitoring jobs.
80 * @return SystemMonitor instance.
81 */
82 public static SystemMonitor create(Context context, Handler workerHandler) {
83 return new SystemMonitor(context, workerHandler, LOADAVG_PATH);
84 }
85
86 @VisibleForTesting
87 SystemMonitor(Context context, Handler workerHandler, String loadavgPath) {
88 mContext = context;
89 mWorkerHandler = workerHandler;
90 mActivityManager = (ActivityManager)
91 mContext.getSystemService(Context.ACTIVITY_SERVICE);
92 mLoadavgPath = loadavgPath;
93 }
94
95 /**
96 * Sets the {@link SystemMonitorCallback} to notify of system state changes.
Howard Hao1f819812021-06-21 15:28:29 -070097 *
98 * @param callback the callback to nofify state changes on.
99 */
100 public void setSystemMonitorCallback(SystemMonitorCallback callback) {
Howard Hao2b11eb52021-08-06 16:43:24 -0700101 synchronized (mLock) {
102 mCallback = callback;
103 if (!mWorkerHandler.hasCallbacks(this::getSystemLoadRepeated)) {
104 startSystemLoadMonitoring();
105 }
106 }
107 }
108
109 /**
110 * Unsets the {@link SystemMonitorCallback}.
111 */
112 public void unsetSystemMonitorCallback() {
113 synchronized (mLock) {
114 stopSystemLoadMonitoringLocked();
115 mCallback = null;
116 }
117 }
118
119 /**
120 * Gets the loadavg data from /proc/loadavg, getting the first 3 averages,
121 * which are 1-min, 5-min and 15-min moving averages respectively.
122 *
123 * Requires Selinux permissions 'open', 'read, 'getattr' to proc_loadavg,
124 * which is set in Car/car_product/sepolicy/private/carservice_app.te.
125 *
126 * @return the {@link CpuLoadavg}.
127 */
128 @VisibleForTesting
129 @Nullable
130 CpuLoadavg getCpuLoad() {
131 try (BufferedReader reader = new BufferedReader(new FileReader(mLoadavgPath))) {
132 String line = reader.readLine();
133 String[] vals = line.split("\\s+", NUM_LOADAVG_VALS + 1);
134 if (vals.length < NUM_LOADAVG_VALS) {
135 Slog.w(CarLog.TAG_TELEMETRY, "Loadavg wrong format");
136 return null;
137 }
138 CpuLoadavg cpuLoadavg = new CpuLoadavg();
139 cpuLoadavg.mOneMinuteVal = Float.parseFloat(vals[0]);
140 cpuLoadavg.mFiveMinutesVal = Float.parseFloat(vals[1]);
141 cpuLoadavg.mFifteenMinutesVal = Float.parseFloat(vals[2]);
142 return cpuLoadavg;
143 } catch (IOException | NumberFormatException ex) {
144 Slog.w(CarLog.TAG_TELEMETRY, "Failed to read loadavg file.", ex);
145 return null;
146 }
147 }
148
149 /**
150 * Gets the {@link ActivityManager.MemoryInfo} for system memory pressure.
151 *
152 * Of the MemoryInfo fields, we will only be using availMem and totalMem,
153 * since lowMemory and threshold are likely deprecated.
154 *
155 * @return {@link MemoryInfo} for the system.
156 */
157 private MemoryInfo getMemoryLoad() {
158 MemoryInfo mi = new ActivityManager.MemoryInfo();
159 mActivityManager.getMemoryInfo(mi);
160 return mi;
161 }
162
163 /**
164 * Sets the CPU usage level for a {@link SystemMonitorEvent}.
165 *
166 * @param event the {@link SystemMonitorEvent}.
167 * @param cpuLoadPerCore the CPU load average per CPU core.
168 */
169 @VisibleForTesting
170 void setEventCpuUsageLevel(SystemMonitorEvent event, double cpuLoadPerCore) {
171 if (cpuLoadPerCore > HI_CPU_LOAD_PER_CORE_BASE_LEVEL) {
172 event.setCpuUsageLevel(SystemMonitorEvent.USAGE_LEVEL_HI);
173 } else if (cpuLoadPerCore > MED_CPU_LOAD_PER_CORE_BASE_LEVEL
174 && cpuLoadPerCore <= HI_CPU_LOAD_PER_CORE_BASE_LEVEL) {
175 event.setCpuUsageLevel(SystemMonitorEvent.USAGE_LEVEL_MED);
176 } else {
177 event.setCpuUsageLevel(SystemMonitorEvent.USAGE_LEVEL_LOW);
178 }
179 }
180
181 /**
182 * Sets the memory usage level for a {@link SystemMonitorEvent}.
183 *
184 * @param event the {@link SystemMonitorEvent}.
185 * @param memLoadRatio ratio of used memory to total memory.
186 */
187 @VisibleForTesting
188 void setEventMemUsageLevel(SystemMonitorEvent event, double memLoadRatio) {
189 if (memLoadRatio > HI_MEM_LOAD_BASE_LEVEL) {
190 event.setMemoryUsageLevel(SystemMonitorEvent.USAGE_LEVEL_HI);
191 } else if (memLoadRatio > MED_MEM_LOAD_BASE_LEVEL
192 && memLoadRatio <= HI_MEM_LOAD_BASE_LEVEL) {
193 event.setMemoryUsageLevel(SystemMonitorEvent.USAGE_LEVEL_MED);
194 } else {
195 event.setMemoryUsageLevel(SystemMonitorEvent.USAGE_LEVEL_LOW);
196 }
197 }
198
199 /**
200 * The Runnable to repeatedly getting system load data with some interval.
201 */
202 private void getSystemLoadRepeated() {
203 synchronized (mLock) {
204 try {
205 CpuLoadavg cpuLoadAvg = getCpuLoad();
206 if (cpuLoadAvg == null) {
207 return;
208 }
209 int numProcessors = Runtime.getRuntime().availableProcessors();
210
211 MemoryInfo memInfo = getMemoryLoad();
212
213 SystemMonitorEvent event = new SystemMonitorEvent();
214 setEventCpuUsageLevel(event, cpuLoadAvg.mOneMinuteVal / numProcessors);
215 setEventMemUsageLevel(event, 1 - memInfo.availMem / memInfo.totalMem);
216
217 mCallback.onSystemMonitorEvent(event);
218 } finally {
219 if (mSystemMonitorRunning) {
220 mWorkerHandler.postDelayed(this::getSystemLoadRepeated, POLL_INTERVAL_MILLIS);
221 }
222 }
223 }
224 }
225
226 /**
227 * Starts system load monitoring.
228 */
229 private void startSystemLoadMonitoring() {
230 synchronized (mLock) {
231 mWorkerHandler.post(this::getSystemLoadRepeated);
232 mSystemMonitorRunning = true;
233 }
234 }
235
236 /**
237 * Stops system load monitoring.
238 */
239 @GuardedBy("mLock")
240 private void stopSystemLoadMonitoringLocked() {
241 synchronized (mLock) {
242 mWorkerHandler.removeCallbacks(this::getSystemLoadRepeated);
243 mSystemMonitorRunning = false;
244 }
245 }
246
247 static final class CpuLoadavg {
248 float mOneMinuteVal;
249 float mFiveMinutesVal;
250 float mFifteenMinutesVal;
Howard Hao1f819812021-06-21 15:28:29 -0700251 }
252}