Android wifi信号强度显示流程
- 1. RSSI
- 2. 从wpa_supplicant获取RSSI
- 3. framework层对wifi信号强度的处理
- 4. StatusBar 更新wifi信号强度
- 5. Settings中的wifi列表更新wifi信号强度
1. RSSI
wifi的信号强度使用RSSI表示, 在Android的Framework中会中会转换为对应的格数在状态栏来显示
RSSI(Received Signal Strength Indication) 代表接收的信号强度指示, 它与模块的发送功率以及天线的增益有关。单位为dBm, 发射端的发出的微波发射后通过空气传播衰减强烈, 在接收端接收到的功率必然会下降, 这时后的接收功率就是RSSI
信号的绝对功率的表示方式为:
p(dBW) = 10 * lg ( xW / 1W)
p(dBm) = 10 * lg ( x * 1000mW / 1mW)
可得:
1W = 30dBm = 0dBW
1mW = 0dBm
因为无线信号多为mW级别(空中传输的过程中衰减较大),所以对它进行了极化,转化为dBm而已,不表示信号是负的。1mW就是0dBm,小于1mW转换为dBm后就为负数
RSSI的获取 : 在104us内进行基带IQ功率积分得到RSSI的瞬时值,即RSSI(瞬时)=sum(I^2+Q^2)
;然后在约1秒内对8192个RSSI的瞬时值进行平均得到RSSI的平均值,即RSSI(平均)=sum(RSSI(瞬时))/8192
,同时给出1秒内RSSI瞬时值的最大值和RSSI瞬时值大于某一门限时的比率(RSSI瞬时值大于某一门限的个数/8192)。由于 RSSI是通过在数字域进行功率积分而后反推到天线口得到的,反向通道信号传输特性的不一致会影响RSSI的精度
2. 从wpa_supplicant获取RSSI
-
对于当前连接的AP, 可以使用wpa_cli的‘signal_poll’命令来获取
$ wpa_cli -p /data/misc/wifi/sockets/ -i wlan0 signal_poll < RSSI=-60 LINKSPEED=72 NOISE=9999 FREQUENCY=2412
从输出结果中可以看到当前AP的 rssi, linkspeed, frequency
android framework 在fetchRssiLinkSpeedAndFrequencyNative()中通过WifiNative.signalPoll()来执行wpa_cli的“signal_poll”来获取当前的信号强度
wifiservice在初始化时, 会调用enableRssiPolling()向WifiStateMachine发送“CMD_ENABLE_RSSI_POLL”来打开 rssi poll, WifiStateMachine在处理该消息时调用fetchRssiLinkSpeedAndFrequencyNative()获取一次当前AP的信号强度,然后发送“CMD_RSSI_POLL”消息, 而WifiStateMachine在处理该消息时调用fetchRssiLinkSpeedAndFrequencyNative()获取一次当前AP的信号强度, 然后再延时3s发送“CMD_RSSI_POLL”消息, 此后, 开始循环获取当前AP的信号强度
-
对于未连接的AP, 可以使用wpa_cli的“bss”命令从扫描结果中获取信息
$ wpa_cli -p /data/misc/wifi/sockets/ -i wlan0 BSS RANGE=0- MASK=0x21987 id=365 bssid=10:c3:7b:d2:df:68 freq=2412 level=-59 tsf=0000270980059560 flags=[WPA2-PSK-CCMP][WPS][ESS] ssid=TDC-647-US
里面的level即代表rssi, 若将“bss”命令的mask置为0x0, 可以获得所有的信息, 但是android framework中只关注其中一部分, 因此设置为0x21987
android framework 中在每次wpa_supplicant有新的扫描结果时, 通过 WifiNative.scanResults()来执行wpa_cli的“bss”命令, 获取扫描的结果
3. framework层对wifi信号强度的处理
在frameworks\base\wifi\java\android\net\wifi\WifiInfo.java中定义的无效rssi阈值,以及最大rssi和最小rssi如下
public static final int INVALID_RSSI = -127;
public static final int MIN_RSSI = -126;
public static final int MAX_RSSI = 200;
在fetchRssiLinkSpeedAndFrequencyNative()中, 通过WifiNative.signalPoll()获取当前AP的信号强度之后, 处理如下
- 对于小于INVALID_RSSI和大于MAX_RSSI的值, 都直接修改为INVALID_RSSI,
- 对于大于0的RSSI, 减去256, 因为有些厂商的实现为了避免负值, 会将其加上256, 再上报给wpa_supplicant
- 调用WifiManager.calculateSignalLevel()计算wifi信号的等级(即wifi信号的格数)
- 若wifi信号的等级发生了变化, 则调用sendRssiChangeBroadcast()发送广播(”android.net.wifi.RSSI_CHANGED“)进行通知
再来看wifi信号等级的计算过程, frameworks\base\wifi\java\android\net\wifi\WifiManager.java中定义了5个wifi信号强度的等级(0格到4格)
public static final int RSSI_LEVELS = 5;
private static final int MIN_RSSI = -100;
private static final int MAX_RSSI = -55;
public static int calculateSignalLevel(int rssi, int numLevels) {
if (rssi <= MIN_RSSI) {
return 0;
} else if (rssi >= MAX_RSSI) {
return numLevels - 1;
} else {
float inputRange = (MAX_RSSI - MIN_RSSI);
float outputRange = (numLevels - 1);
return (int)((float)(rssi - MIN_RSSI) * outputRange / inputRange);
}
}
- RSSI小于等于-100的为0格
- RSSI大于-55的为4格
- 按照RSSI分为(-100, -88), [-88, -78), [-78, -67), [-67, -55), [-55, 0) 5 个等级
再结合wifiinfo中定义的RSSI限值, wifi RSSI的等级划分如下:
- [-126, -88) 或者 [156, 168) 为 0 格
- [-88, -78) 或者 [168, 178) 为 1 格
- [-78, -67) 或者 [178, 189) 为 2 格
- [-67, -55) 或者 [189, 200) 为 3 格
- [-55, 0] 或者 为 4 格
一般 0到-50表示信号最好,-50到-70表示信号偏差,小于-70表示最差,有可能连接不上或者掉线,一般Wifi已断则值为-200
framework中在为statusbar计算wifi信号格树时, 调用WifiManager.calculateSignalLevel()传递的第二个参数为5(即信号划分为5个等级0~4), 而Settings在调用WifiManager.calculateSignalLevel()传递的第二个参数为4((即信号划分为5个等级0~3))
4. StatusBar 更新wifi信号强度
WifiStateMachine中在wifi信号等级变化后, 会发送“android.net.wifi.RSSI_CHANGED”并附带当前的rssi值, 而StatusBar中则会接收并处理这一广播
在StatusBar 的源码 frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
中
public NetworkControllerImpl(Context context) {
......
IntentFilter filter = new IntentFilter();
filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
......
context.registerReceiver(this, filter);
......
}
public void onReceive(Context context, Intent intent) {
......
if (action.equals(WifiManager.RSSI_CHANGED_ACTION)
|| action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)
|| action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
updateWifiState(intent);
refreshViews();
......
}
protected void updateWifiState(Intent intent) {
......
} else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
mWifiRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
mWifiLevel = WifiManager.calculateSignalLevel(mWifiRssi, WifiIcons.WIFI_LEVEL_COUNT);
}
updateWifiIcons();
}
protected void updateWifiIcons() {
int inetCondition = inetConditionForNetwork(ConnectivityManager.TYPE_WIFI);
if (mWifiConnected) {
mWifiIconId = WifiIcons.WIFI_SIGNAL_STRENGTH[inetCondition][mWifiLevel];
mQSWifiIconId = WifiIcons.QS_WIFI_SIGNAL_STRENGTH[inetCondition][mWifiLevel];
mContentDescriptionWifi = mContext.getString(
AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH[mWifiLevel]);
} else {
......
}
5. Settings中的wifi列表更新wifi信号强度
首先, 在settings中打开wifi列表时, WifiSettings内部会实例化一个内部类 Scanner
private static class Scanner extends Handler {
private int mRetry = 0;
private WifiSettings mWifiSettings = null;
Scanner(WifiSettings wifiSettings) {
mWifiSettings = wifiSettings;
}
void resume() {
if (!hasMessages(0)) {
sendEmptyMessage(0);
}
}
void forceScan() {
removeMessages(0);
sendEmptyMessage(0);
}
void pause() {
mRetry = 0;
removeMessages(0);
}
@Override
public void handleMessage(Message message) {
if (mWifiSettings.mWifiManager.startScan()) {
mRetry = 0;
} else if (++mRetry >= 3) {
mRetry = 0;
Activity activity = mWifiSettings.getActivity();
if (activity != null) {
Toast.makeText(activity, R.string.wifi_fail_to_scan, Toast.LENGTH_LONG).show();
}
return;
}
sendEmptyMessageDelayed(0, WIFI_RESCAN_INTERVAL_MS);
}
}
可以看到, Scanner在被触发后, 会间隔WIFI_RESCAN_INTERVAL_MS(一般为10秒)发起一次扫描
在有扫描结果时, 系统会发出 WifiManager.SCAN_RESULTS_AVAILABLE_ACTION 广播, 而WifiSetting会监听此广播, 并且在WifiSettings.handleEvent()中会调用updateAccessPoints()来更新ui界面
在wifi打开的情况下, 中会调用updateAccessPoints()会调用constructAccessPoints(), 首先编译所有已经保存的网络,构造对应的AccessPoint, 以ssid作为键, 添加到一个HashMap apMap中去, 然后再遍历所有的扫描结果,添加到apMap中去或者更新apMap中的结果(包括ssid), 最后,带哦用refersh() 更新wifi信号强度的图片
Settings中wifi列表的信号强度刷新主要依靠 WifiManager.SCAN_RESULTS_AVAILABLE_ACTION 广播来触发, 但是如下广播也会触发信号强度的刷新:
- WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION
- WifiManager.LINK_CONFIGURATION_CHANGED_ACTION
- WifiManager.NETWORK_STATE_CHANGED_ACTION
- WifiManager.RSSI_CHANGED_ACTION