blob: 28888f5c858124c290b056741ba79472be1df135 [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;
Howard Hao2b11eb52021-08-06 16:43:24 -070024
25import com.android.car.CarLog;
Howard Hao2b11eb52021-08-06 16:43:24 -070026import com.android.internal.annotations.VisibleForTesting;
Zhomart Mukhamejanov4d99b1e2021-10-15 21:36:52 -070027import com.android.server.utils.Slogf;
Howard Hao2b11eb52021-08-06 16:43:24 -070028
29import java.io.BufferedReader;
30import java.io.FileReader;
31import java.io.IOException;
32
Howard Hao1f819812021-06-21 15:28:29 -070033/**
34 * SystemMonitor monitors system states and report to listeners when there are
35 * important changes.
Rui Qiuac8b5ba2021-08-25 16:04:17 -070036 * All methods in this class should be invoked from the telemetry thread.
Howard Hao1f819812021-06-21 15:28:29 -070037 */
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
Rui Qiuac8b5ba2021-08-25 16:04:17 -070049 private final Handler mTelemetryHandler;
Howard Hao2b11eb52021-08-06 16:43:24 -070050
51 private final Context mContext;
52 private final ActivityManager mActivityManager;
53 private final String mLoadavgPath;
Howard Haoada36ef2021-08-25 11:45:42 -070054 private final Runnable mSystemLoadRunnable = this::getSystemLoadRepeated;
Howard Hao2b11eb52021-08-06 16:43:24 -070055
Howard Hao2b11eb52021-08-06 16:43:24 -070056 @Nullable private SystemMonitorCallback mCallback;
Howard Hao2b11eb52021-08-06 16:43:24 -070057 private boolean mSystemMonitorRunning = false;
58
Howard Hao1f819812021-06-21 15:28:29 -070059 /**
60 * Interface for receiving notifications about system monitor changes.
61 */
62 public interface SystemMonitorCallback {
63 /**
64 * Listens to system monitor event.
65 *
66 * @param event the system monitor event.
67 */
68 void onSystemMonitorEvent(SystemMonitorEvent event);
69 }
70
71 /**
Howard Hao2b11eb52021-08-06 16:43:24 -070072 * Creates a SystemMonitor instance set with default loadavg path.
73 *
74 * @param context the context this is running in.
75 * @param workerHandler a handler for running monitoring jobs.
76 * @return SystemMonitor instance.
77 */
78 public static SystemMonitor create(Context context, Handler workerHandler) {
79 return new SystemMonitor(context, workerHandler, LOADAVG_PATH);
80 }
81
82 @VisibleForTesting
Rui Qiuac8b5ba2021-08-25 16:04:17 -070083 SystemMonitor(Context context, Handler telemetryHandler, String loadavgPath) {
Howard Hao2b11eb52021-08-06 16:43:24 -070084 mContext = context;
Rui Qiuac8b5ba2021-08-25 16:04:17 -070085 mTelemetryHandler = telemetryHandler;
Howard Hao2b11eb52021-08-06 16:43:24 -070086 mActivityManager = (ActivityManager)
87 mContext.getSystemService(Context.ACTIVITY_SERVICE);
88 mLoadavgPath = loadavgPath;
89 }
90
91 /**
92 * Sets the {@link SystemMonitorCallback} to notify of system state changes.
Howard Hao1f819812021-06-21 15:28:29 -070093 *
94 * @param callback the callback to nofify state changes on.
95 */
96 public void setSystemMonitorCallback(SystemMonitorCallback callback) {
Rui Qiuac8b5ba2021-08-25 16:04:17 -070097 mCallback = callback;
98 if (!mSystemMonitorRunning) {
99 startSystemLoadMonitoring();
Howard Hao2b11eb52021-08-06 16:43:24 -0700100 }
101 }
102
103 /**
104 * Unsets the {@link SystemMonitorCallback}.
105 */
106 public void unsetSystemMonitorCallback() {
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700107 mTelemetryHandler.removeCallbacks(mSystemLoadRunnable);
108 mSystemMonitorRunning = false;
109 mCallback = null;
Howard Hao2b11eb52021-08-06 16:43:24 -0700110 }
111
112 /**
113 * Gets the loadavg data from /proc/loadavg, getting the first 3 averages,
114 * which are 1-min, 5-min and 15-min moving averages respectively.
115 *
116 * Requires Selinux permissions 'open', 'read, 'getattr' to proc_loadavg,
117 * which is set in Car/car_product/sepolicy/private/carservice_app.te.
118 *
119 * @return the {@link CpuLoadavg}.
120 */
121 @VisibleForTesting
122 @Nullable
123 CpuLoadavg getCpuLoad() {
124 try (BufferedReader reader = new BufferedReader(new FileReader(mLoadavgPath))) {
125 String line = reader.readLine();
126 String[] vals = line.split("\\s+", NUM_LOADAVG_VALS + 1);
127 if (vals.length < NUM_LOADAVG_VALS) {
Zhomart Mukhamejanov4d99b1e2021-10-15 21:36:52 -0700128 Slogf.w(CarLog.TAG_TELEMETRY, "Loadavg wrong format");
Howard Hao2b11eb52021-08-06 16:43:24 -0700129 return null;
130 }
131 CpuLoadavg cpuLoadavg = new CpuLoadavg();
132 cpuLoadavg.mOneMinuteVal = Float.parseFloat(vals[0]);
133 cpuLoadavg.mFiveMinutesVal = Float.parseFloat(vals[1]);
134 cpuLoadavg.mFifteenMinutesVal = Float.parseFloat(vals[2]);
135 return cpuLoadavg;
136 } catch (IOException | NumberFormatException ex) {
Zhomart Mukhamejanov4d99b1e2021-10-15 21:36:52 -0700137 Slogf.w(CarLog.TAG_TELEMETRY, "Failed to read loadavg file.", ex);
Howard Hao2b11eb52021-08-06 16:43:24 -0700138 return null;
139 }
140 }
141
142 /**
143 * Gets the {@link ActivityManager.MemoryInfo} for system memory pressure.
144 *
145 * Of the MemoryInfo fields, we will only be using availMem and totalMem,
146 * since lowMemory and threshold are likely deprecated.
147 *
148 * @return {@link MemoryInfo} for the system.
149 */
150 private MemoryInfo getMemoryLoad() {
151 MemoryInfo mi = new ActivityManager.MemoryInfo();
152 mActivityManager.getMemoryInfo(mi);
153 return mi;
154 }
155
156 /**
157 * Sets the CPU usage level for a {@link SystemMonitorEvent}.
158 *
159 * @param event the {@link SystemMonitorEvent}.
160 * @param cpuLoadPerCore the CPU load average per CPU core.
161 */
162 @VisibleForTesting
163 void setEventCpuUsageLevel(SystemMonitorEvent event, double cpuLoadPerCore) {
164 if (cpuLoadPerCore > HI_CPU_LOAD_PER_CORE_BASE_LEVEL) {
165 event.setCpuUsageLevel(SystemMonitorEvent.USAGE_LEVEL_HI);
166 } else if (cpuLoadPerCore > MED_CPU_LOAD_PER_CORE_BASE_LEVEL
167 && cpuLoadPerCore <= HI_CPU_LOAD_PER_CORE_BASE_LEVEL) {
168 event.setCpuUsageLevel(SystemMonitorEvent.USAGE_LEVEL_MED);
169 } else {
170 event.setCpuUsageLevel(SystemMonitorEvent.USAGE_LEVEL_LOW);
171 }
172 }
173
174 /**
175 * Sets the memory usage level for a {@link SystemMonitorEvent}.
176 *
177 * @param event the {@link SystemMonitorEvent}.
178 * @param memLoadRatio ratio of used memory to total memory.
179 */
180 @VisibleForTesting
181 void setEventMemUsageLevel(SystemMonitorEvent event, double memLoadRatio) {
182 if (memLoadRatio > HI_MEM_LOAD_BASE_LEVEL) {
183 event.setMemoryUsageLevel(SystemMonitorEvent.USAGE_LEVEL_HI);
184 } else if (memLoadRatio > MED_MEM_LOAD_BASE_LEVEL
185 && memLoadRatio <= HI_MEM_LOAD_BASE_LEVEL) {
186 event.setMemoryUsageLevel(SystemMonitorEvent.USAGE_LEVEL_MED);
187 } else {
188 event.setMemoryUsageLevel(SystemMonitorEvent.USAGE_LEVEL_LOW);
189 }
190 }
191
192 /**
193 * The Runnable to repeatedly getting system load data with some interval.
194 */
195 private void getSystemLoadRepeated() {
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700196 try {
197 CpuLoadavg cpuLoadAvg = getCpuLoad();
198 if (cpuLoadAvg == null) {
199 return;
200 }
201 int numProcessors = Runtime.getRuntime().availableProcessors();
Howard Hao2b11eb52021-08-06 16:43:24 -0700202
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700203 MemoryInfo memInfo = getMemoryLoad();
Howard Hao2b11eb52021-08-06 16:43:24 -0700204
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700205 SystemMonitorEvent event = new SystemMonitorEvent();
206 setEventCpuUsageLevel(event, cpuLoadAvg.mOneMinuteVal / numProcessors);
Rui Qiufd8cc592021-09-30 11:44:31 -0700207 setEventMemUsageLevel(event, 1 - (double) memInfo.availMem / memInfo.totalMem);
Howard Hao2b11eb52021-08-06 16:43:24 -0700208
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700209 mCallback.onSystemMonitorEvent(event);
210 } finally {
211 if (mSystemMonitorRunning) {
212 mTelemetryHandler.postDelayed(mSystemLoadRunnable, POLL_INTERVAL_MILLIS);
Howard Hao2b11eb52021-08-06 16:43:24 -0700213 }
214 }
215 }
216
217 /**
218 * Starts system load monitoring.
219 */
220 private void startSystemLoadMonitoring() {
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700221 mTelemetryHandler.post(mSystemLoadRunnable);
222 mSystemMonitorRunning = true;
Howard Hao2b11eb52021-08-06 16:43:24 -0700223 }
224
225 static final class CpuLoadavg {
226 float mOneMinuteVal;
227 float mFiveMinutesVal;
228 float mFifteenMinutesVal;
Howard Hao1f819812021-06-21 15:28:29 -0700229 }
230}