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