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.server.wifi; 18 19 import android.annotation.NonNull; 20 import android.net.wifi.WifiInfo; 21 import android.net.wifi.WifiManager; 22 import android.net.wifi.WifiManager.DeviceMobilityState; 23 import android.util.Log; 24 25 import java.util.Arrays; 26 27 /** 28 * Class for App and client mode RSSI monitoring. It processes the RSSI thresholds for these 29 * monitors and enables/disables the monitoring accordingly. It also changes the RSSI polling 30 * interval dynamically based on the client mode RSSI monitoring and device mobility state. 31 */ 32 public class RssiMonitor { 33 private static final String TAG = "RssiMonitor"; 34 private boolean mVerboseLoggingEnabled = false; 35 36 private final WifiGlobals mWifiGlobals; 37 private final WifiThreadRunner mWifiThreadRunner; 38 private final WifiInfo mWifiInfo; 39 private final WifiNative mWifiNative; 40 private final String mInterfaceName; 41 private final Runnable mUpdateCapabilityRunnable; 42 private final DeviceConfigFacade mDeviceConfigFacade; 43 44 private boolean mEnableClientRssiMonitor = false; 45 private boolean mIsPollRssiIntervalOverridden = false; 46 private int[] mAppThresholds = {}; 47 private byte[] mRssiRanges = {}; 48 RssiMonitor(WifiGlobals wifiGlobals, WifiThreadRunner wifiThreadRunner, WifiInfo wifiInfo, WifiNative wifiNative, String interfaceName, Runnable updateCapabilityRunnable, DeviceConfigFacade deviceConfigFacade)49 public RssiMonitor(WifiGlobals wifiGlobals, WifiThreadRunner wifiThreadRunner, 50 WifiInfo wifiInfo, WifiNative wifiNative, String interfaceName, 51 Runnable updateCapabilityRunnable, DeviceConfigFacade deviceConfigFacade) { 52 mWifiGlobals = wifiGlobals; 53 mWifiThreadRunner = wifiThreadRunner; 54 mWifiInfo = wifiInfo; 55 mWifiNative = wifiNative; 56 mInterfaceName = interfaceName; 57 mUpdateCapabilityRunnable = updateCapabilityRunnable; 58 mDeviceConfigFacade = deviceConfigFacade; 59 } 60 logd(String string)61 private void logd(String string) { 62 if (mVerboseLoggingEnabled) { 63 Log.d(getTag(), string); 64 } 65 } 66 getTag()67 private String getTag() { 68 return TAG + "[" + (mInterfaceName == null ? "unknown" : mInterfaceName) + "]"; 69 } 70 71 class RssiEventHandler implements WifiNative.WifiRssiEventHandler { 72 @Override onRssiThresholdBreached(byte curRssi)73 public void onRssiThresholdBreached(byte curRssi) { 74 if (mVerboseLoggingEnabled) { 75 logd("onRssiThresholdBreach event. Cur Rssi = " + curRssi); 76 } 77 // Corner case: if framework threshold is same as one of the app thresholds, 78 // processClientRssiThresholdBreached(curRssi) is called. The framework monitor will 79 // be disabled, and the RSSI monitor will be restarted with the app thresholds 80 // by calling handleRssiBreachRestartRssiMonitor(). From the App monitor perspective 81 // the actions are same as calling handleRssiBreachRestartRssiMonitor(curRssi) directly. 82 if (mEnableClientRssiMonitor && curRssi 83 <= mWifiGlobals.getClientRssiMonitorThresholdDbm()) { 84 mWifiThreadRunner.post(() -> processClientRssiThresholdBreached(curRssi)); 85 } else { 86 mWifiThreadRunner.post(() -> handleRssiBreachRestartRssiMonitor(curRssi)); 87 } 88 } 89 } 90 private final RssiEventHandler mRssiEventHandler = new RssiEventHandler(); 91 processClientRssiThresholdBreached(int curRssi)92 private void processClientRssiThresholdBreached(int curRssi) { 93 int shortInterval = mWifiGlobals.getPollRssiShortIntervalMillis(); 94 logd("Client mode RSSI monitor threshold breach event. RSSI polling interval changed to " 95 + shortInterval + " ms" + ", disable client mode RSSI monitor"); 96 mWifiGlobals.setPollRssiIntervalMillis(shortInterval); 97 disableClientRssiMonitorAndUpdateThresholds(curRssi); 98 } 99 handleRssiBreachRestartRssiMonitor(byte curRssi)100 private void handleRssiBreachRestartRssiMonitor(byte curRssi) { 101 if (curRssi == Byte.MAX_VALUE || curRssi == Byte.MIN_VALUE) { 102 Log.wtf(getTag(), "Process RSSI thresholds: Invalid rssi " + curRssi); 103 return; 104 } 105 for (int i = 1; i < mRssiRanges.length; i++) { 106 if (curRssi < mRssiRanges[i]) { 107 // Assume sorted values(ascending order) for rssi, 108 // bounded by high(127) and low(-128) at extremeties 109 byte maxRssi = mRssiRanges[i]; 110 byte minRssi = mRssiRanges[i - 1]; 111 // This value of hw has to be believed as this value is averaged and has breached 112 // the rssi thresholds and raised event to host. This would be eggregious if this 113 // value is invalid 114 mWifiInfo.setRssi(curRssi); 115 mUpdateCapabilityRunnable.run(); 116 int ret = startRssiMonitoringOffload(maxRssi, minRssi); 117 logd("Re-program RSSI thresholds " + ": [" + minRssi + ", " 118 + maxRssi + "], curRssi=" + curRssi + " ret=" + ret); 119 break; 120 } 121 } 122 } 123 startRssiMonitoringOffload(byte maxRssi, byte minRssi)124 private int startRssiMonitoringOffload(byte maxRssi, byte minRssi) { 125 return mWifiNative.startRssiMonitoring(mInterfaceName, maxRssi, minRssi, mRssiEventHandler); 126 } 127 stopRssiMonitoringOffload()128 private int stopRssiMonitoringOffload() { 129 return mWifiNative.stopRssiMonitoring(mInterfaceName); 130 } 131 132 /** 133 * Reset the RSSI monitor 134 */ reset()135 public void reset() { 136 mEnableClientRssiMonitor = false; 137 mAppThresholds = new int[] {}; 138 mRssiRanges = new byte[] {}; 139 if (!mIsPollRssiIntervalOverridden) { 140 int shortInterval = mWifiGlobals.getPollRssiShortIntervalMillis(); 141 mWifiGlobals.setPollRssiIntervalMillis(shortInterval); 142 } 143 stopRssiMonitoringOffload(); 144 } 145 146 /** 147 * Enable/Disable verbose logging. 148 * @param verbose true to enable and false to disable. 149 */ enableVerboseLogging(boolean verbose)150 public void enableVerboseLogging(boolean verbose) { 151 mVerboseLoggingEnabled = verbose; 152 } 153 154 /** 155 * Update the RSSI polling interval based on the current device mobility state and RSSI. 156 * If the device is stationary and RSSI is high, change to the long interval. Otherwise, 157 * change to the short interval. 158 * @param state the current device mobility state 159 */ updatePollRssiInterval(@eviceMobilityState int state)160 public void updatePollRssiInterval(@DeviceMobilityState int state) { 161 if (!mWifiGlobals.isAdjustPollRssiIntervalEnabled() 162 || !mDeviceConfigFacade.isAdjustPollRssiIntervalEnabled() 163 || mIsPollRssiIntervalOverridden) { 164 return; 165 } 166 int curRssi = mWifiInfo.getRssi(); 167 int rssiMonitorThreshold = mWifiGlobals.getClientRssiMonitorThresholdDbm(); 168 int rssiMonitorHysteresisDb = mWifiGlobals.getClientRssiMonitorHysteresisDb(); 169 if (state == WifiManager.DEVICE_MOBILITY_STATE_STATIONARY && curRssi 170 >= rssiMonitorThreshold + rssiMonitorHysteresisDb) { 171 setLongPollRssiInterval(); 172 } else if (state != WifiManager.DEVICE_MOBILITY_STATE_STATIONARY || curRssi 173 < rssiMonitorThreshold) { 174 setShortPollRssiInterval(); 175 } 176 } 177 setLongPollRssiInterval()178 private void setLongPollRssiInterval() { 179 int longInterval = mWifiGlobals.getPollRssiLongIntervalMillis(); 180 if (mWifiGlobals.getPollRssiIntervalMillis() == longInterval) { 181 return; 182 } 183 logd("RSSI polling interval changed to " + longInterval + " ms" 184 + ", enable client mode RSSI monitor"); 185 mWifiGlobals.setPollRssiIntervalMillis(longInterval); 186 enableClientRssiMonitorAndUpdateThresholds(mWifiInfo.getRssi()); 187 } 188 189 /** 190 * Change the RSSI polling interval to the short interval and disable client mode RSSI monitor 191 */ setShortPollRssiInterval()192 public void setShortPollRssiInterval() { 193 if (mIsPollRssiIntervalOverridden) { 194 return; 195 } 196 int shortInterval = mWifiGlobals.getPollRssiShortIntervalMillis(); 197 if (mWifiGlobals.getPollRssiIntervalMillis() == shortInterval) { 198 return; 199 } 200 logd("RSSI polling interval changed to " + shortInterval + " ms" 201 + ", disable client mode RSSI monitor"); 202 mWifiGlobals.setPollRssiIntervalMillis(shortInterval); 203 disableClientRssiMonitorAndUpdateThresholds(mWifiInfo.getRssi()); 204 } 205 enableClientRssiMonitorAndUpdateThresholds(int curRssi)206 private void enableClientRssiMonitorAndUpdateThresholds(int curRssi) { 207 mEnableClientRssiMonitor = true; 208 updateRssiRangesAndStartMonitor(curRssi); 209 } 210 disableClientRssiMonitorAndUpdateThresholds(int curRssi)211 private void disableClientRssiMonitorAndUpdateThresholds(int curRssi) { 212 mEnableClientRssiMonitor = false; 213 updateRssiRangesAndStartMonitor(curRssi); 214 } 215 216 /** 217 * Pass the updated App RSSI thresholds to RssiMonitor and start/restart RSSI monitoring if 218 * the thresholds are valid after processing. 219 */ updateAppThresholdsAndStartMonitor(@onNull int[] appThresholds)220 public void updateAppThresholdsAndStartMonitor(@NonNull int[] appThresholds) { 221 logd("Received app signal strength thresholds: " + Arrays.toString(appThresholds)); 222 mAppThresholds = appThresholds; 223 updateRssiRangesAndStartMonitor(mWifiInfo.getRssi()); 224 } 225 updateRssiRangesAndStartMonitor(int curRssi)226 private void updateRssiRangesAndStartMonitor(int curRssi) { 227 // 0. If there are no thresholds, or if the thresholds are invalid, 228 // stop RSSI monitoring. 229 // 1. Tell the hardware to start RSSI monitoring here, possibly adding MIN_VALUE and 230 // MAX_VALUE at the start/end of the thresholds array if necessary. 231 // 2. Ensure that when the hardware event fires, we fetch the RSSI from the hardware 232 // event, call mWifiInfo.setRssi() with it, and call updateCapabilities(), and then 233 // re-arm the hardware event. This needs to be done on the state machine thread to 234 // avoid race conditions. The RSSI used to re-arm the event (and perhaps also the one 235 // sent in the NetworkCapabilities) must be the one received from the hardware event 236 // received, or we might skip callbacks. 237 // 3. Ensure that when we disconnect, RSSI monitoring is stopped. 238 int[] mergedThresholds = mergeAppFrameworkThresholds(mAppThresholds); 239 if (mergedThresholds.length == 0) { 240 mRssiRanges = new byte[] {}; 241 stopRssiMonitoringOffload(); 242 return; 243 } 244 int [] rssiVals = Arrays.copyOf(mergedThresholds, mergedThresholds.length + 2); 245 rssiVals[rssiVals.length - 2] = Byte.MIN_VALUE; 246 rssiVals[rssiVals.length - 1] = Byte.MAX_VALUE; 247 Arrays.sort(rssiVals); 248 byte[] rssiRange = new byte[rssiVals.length]; 249 for (int i = 0; i < rssiVals.length; i++) { 250 int val = rssiVals[i]; 251 if (val <= Byte.MAX_VALUE && val >= Byte.MIN_VALUE) { 252 rssiRange[i] = (byte) val; 253 } else { 254 Log.e(getTag(), "Illegal value " + val + " for RSSI thresholds: " 255 + Arrays.toString(rssiVals)); 256 stopRssiMonitoringOffload(); 257 return; 258 } 259 } 260 mRssiRanges = rssiRange; 261 logd("Updated RSSI thresholds=" + Arrays.toString(mRssiRanges)); 262 handleRssiBreachRestartRssiMonitor((byte) curRssi); 263 } 264 mergeAppFrameworkThresholds(@onNull int[] appThresholds)265 private int[] mergeAppFrameworkThresholds(@NonNull int[] appThresholds) { 266 if (mEnableClientRssiMonitor) { 267 int[] mergedThresholds = Arrays.copyOf(appThresholds, 268 appThresholds.length + 1); 269 mergedThresholds[mergedThresholds.length - 1] = mWifiGlobals 270 .getClientRssiMonitorThresholdDbm(); 271 return mergedThresholds; 272 } else { 273 return appThresholds; 274 } 275 } 276 277 /** 278 * When link layer stats polling interval is overridden, set the fixed interval and disable 279 * client mode RSSI monitoring if it is enabled. When the polling interval is set to automatic 280 * handling, set the interval to the regular (short) interval, then the RSSI monitor will 281 * adjust the interval automatically 282 * @param newIntervalMs a non-negative integer, for the link layer stats polling interval 283 * in milliseconds. 284 * To set a fixed interval, use a positive value. 285 * For automatic handling of the interval, use value 0 286 */ overridePollRssiInterval(int newIntervalMs)287 public void overridePollRssiInterval(int newIntervalMs) { 288 if (mIsPollRssiIntervalOverridden && newIntervalMs == 0) { 289 setAutoPollRssiInterval(); 290 return; 291 } 292 if (newIntervalMs > 0) { 293 setFixedPollRssiInterval(newIntervalMs); 294 } 295 } 296 setAutoPollRssiInterval()297 private void setAutoPollRssiInterval() { 298 mIsPollRssiIntervalOverridden = false; 299 int regularInterval = mWifiGlobals.getPollRssiShortIntervalMillis(); 300 mWifiGlobals.setPollRssiIntervalMillis(regularInterval); 301 } 302 setFixedPollRssiInterval(int newIntervalMs)303 private void setFixedPollRssiInterval(int newIntervalMs) { 304 mIsPollRssiIntervalOverridden = true; 305 mWifiGlobals.setPollRssiIntervalMillis(newIntervalMs); 306 if (mEnableClientRssiMonitor) { 307 disableClientRssiMonitorAndUpdateThresholds(mWifiInfo.getRssi()); 308 } 309 } 310 } 311