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 TAG + "#processClientRssiThresholdBreached"); 86 } else { 87 mWifiThreadRunner.post(() -> handleRssiBreachRestartRssiMonitor(curRssi), 88 TAG + "#handleRssiBreachRestartRssiMonitor"); 89 } 90 } 91 } 92 private final RssiEventHandler mRssiEventHandler = new RssiEventHandler(); 93 processClientRssiThresholdBreached(int curRssi)94 private void processClientRssiThresholdBreached(int curRssi) { 95 int shortInterval = mWifiGlobals.getPollRssiShortIntervalMillis(); 96 logd("Client mode RSSI monitor threshold breach event. RSSI polling interval changed to " 97 + shortInterval + " ms" + ", disable client mode RSSI monitor"); 98 mWifiGlobals.setPollRssiIntervalMillis(shortInterval); 99 disableClientRssiMonitorAndUpdateThresholds(curRssi); 100 } 101 handleRssiBreachRestartRssiMonitor(byte curRssi)102 private void handleRssiBreachRestartRssiMonitor(byte curRssi) { 103 if (curRssi == Byte.MAX_VALUE || curRssi == Byte.MIN_VALUE) { 104 Log.wtf(getTag(), "Process RSSI thresholds: Invalid rssi " + curRssi); 105 return; 106 } 107 for (int i = 1; i < mRssiRanges.length; i++) { 108 if (curRssi < mRssiRanges[i]) { 109 // Assume sorted values(ascending order) for rssi, 110 // bounded by high(127) and low(-128) at extremeties 111 byte maxRssi = mRssiRanges[i]; 112 byte minRssi = mRssiRanges[i - 1]; 113 // This value of hw has to be believed as this value is averaged and has breached 114 // the rssi thresholds and raised event to host. This would be eggregious if this 115 // value is invalid 116 mWifiInfo.setRssi(curRssi); 117 mUpdateCapabilityRunnable.run(); 118 int ret = startRssiMonitoringOffload(maxRssi, minRssi); 119 logd("Re-program RSSI thresholds " + ": [" + minRssi + ", " 120 + maxRssi + "], curRssi=" + curRssi + " ret=" + ret); 121 break; 122 } 123 } 124 } 125 startRssiMonitoringOffload(byte maxRssi, byte minRssi)126 private int startRssiMonitoringOffload(byte maxRssi, byte minRssi) { 127 return mWifiNative.startRssiMonitoring(mInterfaceName, maxRssi, minRssi, mRssiEventHandler); 128 } 129 stopRssiMonitoringOffload()130 private int stopRssiMonitoringOffload() { 131 return mWifiNative.stopRssiMonitoring(mInterfaceName); 132 } 133 134 /** 135 * Reset the RSSI monitor 136 */ reset()137 public void reset() { 138 mEnableClientRssiMonitor = false; 139 mAppThresholds = new int[] {}; 140 mRssiRanges = new byte[] {}; 141 if (!mIsPollRssiIntervalOverridden) { 142 int shortInterval = mWifiGlobals.getPollRssiShortIntervalMillis(); 143 mWifiGlobals.setPollRssiIntervalMillis(shortInterval); 144 } 145 stopRssiMonitoringOffload(); 146 } 147 148 /** 149 * Enable/Disable verbose logging. 150 * @param verbose true to enable and false to disable. 151 */ enableVerboseLogging(boolean verbose)152 public void enableVerboseLogging(boolean verbose) { 153 mVerboseLoggingEnabled = verbose; 154 } 155 156 /** 157 * Update the RSSI polling interval based on the current device mobility state and RSSI. 158 * If the device is stationary and RSSI is high, change to the long interval. Otherwise, 159 * change to the short interval. 160 * @param state the current device mobility state 161 */ updatePollRssiInterval(@eviceMobilityState int state)162 public void updatePollRssiInterval(@DeviceMobilityState int state) { 163 if (!mWifiGlobals.isAdjustPollRssiIntervalEnabled() 164 || !mDeviceConfigFacade.isAdjustPollRssiIntervalEnabled() 165 || mIsPollRssiIntervalOverridden) { 166 return; 167 } 168 int curRssi = mWifiInfo.getRssi(); 169 int rssiMonitorThreshold = mWifiGlobals.getClientRssiMonitorThresholdDbm(); 170 int rssiMonitorHysteresisDb = mWifiGlobals.getClientRssiMonitorHysteresisDb(); 171 if (state == WifiManager.DEVICE_MOBILITY_STATE_STATIONARY && curRssi 172 >= rssiMonitorThreshold + rssiMonitorHysteresisDb) { 173 setLongPollRssiInterval(); 174 } else if (state != WifiManager.DEVICE_MOBILITY_STATE_STATIONARY || curRssi 175 < rssiMonitorThreshold) { 176 setShortPollRssiInterval(); 177 } 178 } 179 setLongPollRssiInterval()180 private void setLongPollRssiInterval() { 181 int longInterval = mWifiGlobals.getPollRssiLongIntervalMillis(); 182 if (mWifiGlobals.getPollRssiIntervalMillis() == longInterval) { 183 return; 184 } 185 logd("RSSI polling interval changed to " + longInterval + " ms" 186 + ", enable client mode RSSI monitor"); 187 mWifiGlobals.setPollRssiIntervalMillis(longInterval); 188 enableClientRssiMonitorAndUpdateThresholds(mWifiInfo.getRssi()); 189 } 190 191 /** 192 * Change the RSSI polling interval to the short interval and disable client mode RSSI monitor 193 */ setShortPollRssiInterval()194 public void setShortPollRssiInterval() { 195 if (mIsPollRssiIntervalOverridden) { 196 return; 197 } 198 int shortInterval = mWifiGlobals.getPollRssiShortIntervalMillis(); 199 if (mWifiGlobals.getPollRssiIntervalMillis() == shortInterval) { 200 return; 201 } 202 logd("RSSI polling interval changed to " + shortInterval + " ms" 203 + ", disable client mode RSSI monitor"); 204 mWifiGlobals.setPollRssiIntervalMillis(shortInterval); 205 disableClientRssiMonitorAndUpdateThresholds(mWifiInfo.getRssi()); 206 } 207 enableClientRssiMonitorAndUpdateThresholds(int curRssi)208 private void enableClientRssiMonitorAndUpdateThresholds(int curRssi) { 209 mEnableClientRssiMonitor = true; 210 updateRssiRangesAndStartMonitor(curRssi); 211 } 212 disableClientRssiMonitorAndUpdateThresholds(int curRssi)213 private void disableClientRssiMonitorAndUpdateThresholds(int curRssi) { 214 mEnableClientRssiMonitor = false; 215 updateRssiRangesAndStartMonitor(curRssi); 216 } 217 218 /** 219 * Pass the updated App RSSI thresholds to RssiMonitor and start/restart RSSI monitoring if 220 * the thresholds are valid after processing. 221 */ updateAppThresholdsAndStartMonitor(@onNull int[] appThresholds)222 public void updateAppThresholdsAndStartMonitor(@NonNull int[] appThresholds) { 223 logd("Received app signal strength thresholds: " + Arrays.toString(appThresholds)); 224 mAppThresholds = appThresholds; 225 updateRssiRangesAndStartMonitor(mWifiInfo.getRssi()); 226 } 227 updateRssiRangesAndStartMonitor(int curRssi)228 private void updateRssiRangesAndStartMonitor(int curRssi) { 229 // 0. If there are no thresholds, or if the thresholds are invalid, 230 // stop RSSI monitoring. 231 // 1. Tell the hardware to start RSSI monitoring here, possibly adding MIN_VALUE and 232 // MAX_VALUE at the start/end of the thresholds array if necessary. 233 // 2. Ensure that when the hardware event fires, we fetch the RSSI from the hardware 234 // event, call mWifiInfo.setRssi() with it, and call updateCapabilities(), and then 235 // re-arm the hardware event. This needs to be done on the state machine thread to 236 // avoid race conditions. The RSSI used to re-arm the event (and perhaps also the one 237 // sent in the NetworkCapabilities) must be the one received from the hardware event 238 // received, or we might skip callbacks. 239 // 3. Ensure that when we disconnect, RSSI monitoring is stopped. 240 int[] mergedThresholds = mergeAppFrameworkThresholds(mAppThresholds); 241 if (mergedThresholds.length == 0) { 242 mRssiRanges = new byte[] {}; 243 stopRssiMonitoringOffload(); 244 return; 245 } 246 int [] rssiVals = Arrays.copyOf(mergedThresholds, mergedThresholds.length + 2); 247 rssiVals[rssiVals.length - 2] = Byte.MIN_VALUE; 248 rssiVals[rssiVals.length - 1] = Byte.MAX_VALUE; 249 Arrays.sort(rssiVals); 250 byte[] rssiRange = new byte[rssiVals.length]; 251 for (int i = 0; i < rssiVals.length; i++) { 252 int val = rssiVals[i]; 253 if (val <= Byte.MAX_VALUE && val >= Byte.MIN_VALUE) { 254 rssiRange[i] = (byte) val; 255 } else { 256 Log.e(getTag(), "Illegal value " + val + " for RSSI thresholds: " 257 + Arrays.toString(rssiVals)); 258 stopRssiMonitoringOffload(); 259 return; 260 } 261 } 262 mRssiRanges = rssiRange; 263 logd("Updated RSSI thresholds=" + Arrays.toString(mRssiRanges)); 264 handleRssiBreachRestartRssiMonitor((byte) curRssi); 265 } 266 mergeAppFrameworkThresholds(@onNull int[] appThresholds)267 private int[] mergeAppFrameworkThresholds(@NonNull int[] appThresholds) { 268 if (mEnableClientRssiMonitor) { 269 int[] mergedThresholds = Arrays.copyOf(appThresholds, 270 appThresholds.length + 1); 271 mergedThresholds[mergedThresholds.length - 1] = mWifiGlobals 272 .getClientRssiMonitorThresholdDbm(); 273 return mergedThresholds; 274 } else { 275 return appThresholds; 276 } 277 } 278 279 /** 280 * When link layer stats polling interval is overridden, set the fixed interval and disable 281 * client mode RSSI monitoring if it is enabled. When the polling interval is set to automatic 282 * handling, set the interval to the regular (short) interval, then the RSSI monitor will 283 * adjust the interval automatically 284 * @param newIntervalMs a non-negative integer, for the link layer stats polling interval 285 * in milliseconds. 286 * To set a fixed interval, use a positive value. 287 * For automatic handling of the interval, use value 0 288 */ overridePollRssiInterval(int newIntervalMs)289 public void overridePollRssiInterval(int newIntervalMs) { 290 if (mIsPollRssiIntervalOverridden && newIntervalMs == 0) { 291 setAutoPollRssiInterval(); 292 return; 293 } 294 if (newIntervalMs > 0) { 295 setFixedPollRssiInterval(newIntervalMs); 296 } 297 } 298 setAutoPollRssiInterval()299 private void setAutoPollRssiInterval() { 300 mIsPollRssiIntervalOverridden = false; 301 int regularInterval = mWifiGlobals.getPollRssiShortIntervalMillis(); 302 mWifiGlobals.setPollRssiIntervalMillis(regularInterval); 303 } 304 setFixedPollRssiInterval(int newIntervalMs)305 private void setFixedPollRssiInterval(int newIntervalMs) { 306 mIsPollRssiIntervalOverridden = true; 307 mWifiGlobals.setPollRssiIntervalMillis(newIntervalMs); 308 if (mEnableClientRssiMonitor) { 309 disableClientRssiMonitorAndUpdateThresholds(mWifiInfo.getRssi()); 310 } 311 } 312 } 313