• 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 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