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