• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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