Android AccessPoint 已连接的wifi信息未显示处理
一、前言
一个wifi列表未显示已连接的wifi信息问题记录。
虽然新版本的原生Settings应用基本不会遇到这个问题,但是万一遇到可以参考这个简单适配。
背景:很久以前的项目代码,移植的Settings的代码,代码获取的wifi列表信息是使用SettingsLib的接口获取 AccessPoint 列表数据。
目前只有wifi5的模组硬件会遇到这个问题,wifi6模组的硬件没有这个问题。
有可能是底层返回的某个信息不符合正常逻辑,所以才出现这样的问题。
本文只是简单记录一下,估计对大部分开发人员是没啥用的。
本文最后又原生Settings 的Wifi列表界面显示分析,有兴趣的可以看看。
二、简单分析和处理
1、系统应用中获取Wifi列表的代码
WifiTracker mWifiTracker = WifiTrackerFactory.create(
getActivity(), this, getLifecycle(), true, true);
List<AccessPoint> accessPoints = mWifiTracker.getAccessPoints();
无论是 WifiTracker 还是 AccessPoint 都是已经过时的API。
但是系统代码中还是可以正常使用。
/** Update the internal list of access points. */
private void updateAccessPoints(final List<ScanResult> newScanResults,
List<WifiConfiguration> configs) {
WifiConfiguration connectionConfig = null;
if (mLastInfo != null) {
connectionConfig = getWifiConfigurationForNetworkId(mLastInfo.getNetworkId(), configs);
}
+ boolean isHasConnectWifi = false;
List<ScanResult> cachedScanResults = new ArrayList<>(mScanResultCache.values());
// Add a unique Passpoint AccessPoint for each Passpoint profile's unique identifier.
accessPoints.addAll(updatePasspointAccessPoints(
mWifiManager.getAllMatchingWifiConfigs(cachedScanResults), cachedAccessPoints));
// Add OSU Provider AccessPoints
accessPoints.addAll(updateOsuAccessPoints(
mWifiManager.getMatchingOsuProviders(cachedScanResults), cachedAccessPoints));
if (mLastInfo != null && mLastNetworkInfo != null) {
for (AccessPoint ap : accessPoints) {
ap.update(connectionConfig, mLastInfo, mLastNetworkInfo);
}
}
+ //add judge connected wifi by lwz
+ String ssid = "";
+ int networkId = -1;
+ if (mLastInfo != null) { //上次连接成功的wifi信息对象
+ ssid = mLastInfo.getSSID();
+ networkId = mLastInfo.getNetworkId();
+ Log.i(TAG, "updateAccessPoints last ssid = " + ssid + " , networkId = " + networkId);
+ }
+ isHasConnectWifi = false;
+ for (AccessPoint as : accessPoints) {
+ if (removeDoubleQuotes(ssid).equals(removeDoubleQuotes(as.getSsidStr())) &&
+ as.getConfig() != null && networkId == +as.getConfig().networkId) {
+ isHasConnectWifi = true;
+ }
+
+ }
+
+ if (!isHasConnectWifi) {
+ Log.i(TAG, "updateAccessPoints isHasConnectWifi = +" + isHasConnectWifi + ", connectionConfig = " + connectionConfig);
+ }
// If there were no scan results, create an AP for the currently connected network (if
// it exists).
- if (accessPoints.isEmpty() && connectionConfig != null) {
+ if (connectionConfig != null && (accessPoints.isEmpty() || !isHasConnectWifi)) {
AccessPoint activeAp = new AccessPoint(mContext, connectionConfig);
activeAp.update(connectionConfig, mLastInfo, mLastNetworkInfo);
accessPoints.add(activeAp);
}
}
+ //remove shuanyinhao ""
+ private String removeDoubleQuotes(String var0) {
+ if (TextUtils.isEmpty(var0)) {
+ return "";
+ } else {
+ int var1 = var0.length();
+ return var1 > 1 && var0.charAt(0) == '"' && var0.charAt(var1 - 1) == '"' ? var0.substring(1, var1 - 1) : var0;
+ }
+ }
这里可以看到代码中有保存上次连接的wifi信息。
如果返回的 AccessPoint 列表中未返回这个信息就添加这个信息。
三、其他
1、Android AccessPoint 已连接的wifi信息未显示处理小结
本文没有深入研究为啥已连接的Wifi信息未显示的具体原因,
只介绍了一个解决方案:修改 updateAccessPoints 的列表数据;
如果要深入分析可以在 updateAccessPoints 的多个判断出来添加打印,查看是哪里过滤了这个Wifi信息。
这个问题并不容易出现,是某些定制板子会出现,并且是连接隐藏的Wifi才会有这个问题,
后续没有硬件研究了,就不管了。
2、Android14 原生Settings 的Wifi列表界面分析
(1)AndroidManifest.xml 中代码
<activity
android:name="Settings$WifiSettingsActivity"
android:exported="true"
android:configChanges="orientation|keyboardHidden|screenSize">
<intent-filter android:priority="1">
<action android:name="android.settings.WIFI_SETTINGS"/>
</intent-filter>
// wifi 列表具体显示的Fragment:NetworkProviderSettings
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.network.NetworkProviderSettings"/>
...
</activity>
Settings代码中是没有 WifiSettingsActivity.java 的;
WifiSettingsActivity 只是Settings.java 的内部类。
(2)Settings.java
Settings\src\com\android\settings\Settings.java
public class Settings extends SettingsActivity {
public static class WifiSettingsActivity extends SettingsActivity { /* empty */ }
public static class NetworkProviderSettingsActivity extends SettingsActivity { /* empty */ }
public static class NetworkSelectActivity extends SettingsActivity { /* empty */ }
。。。
}
SettingsActivity.java 确实是存在的,是一个通用的Activity类。
(2) adb shell或者串口拉起Wifi设置界面的命令1:
am start -a android.settings.WIFI_SETTINGS
//$号必须转义,否则无效!
am start -n com.android.settings/.Settings\$WifiSettingsActivity
(3)从全局搜索 WifiSettings 看,还有其他Java类也是跳转到Wifi列表界面的
比如:WifiPickerActivity.java
public class WifiPickerActivity extends SettingsActivity implements ButtonBarHandler {
//这里可以看到最对某些Intent进行过滤和设置
@Override
public Intent getIntent() {
Intent modIntent = new Intent(super.getIntent());
if (!modIntent.hasExtra(EXTRA_SHOW_FRAGMENT)) {
modIntent.putExtra(EXTRA_SHOW_FRAGMENT, getWifiSettingsClass().getName());
modIntent.putExtra(EXTRA_SHOW_FRAGMENT_TITLE_RESID, R.string.wifi_select_network);
}
return modIntent;
}
...
/* package */ Class<? extends PreferenceFragmentCompat> getWifiSettingsClass() {
return WifiSettings.class;
}
}
AndroidManifest.xml 中定义WifiPickerActivity的代码
<activity
android:name=".wifi.WifiPickerActivity"
android:permission="android.permission.CHANGE_WIFI_STATE"
android:exported="true">
<intent-filter android:priority="1">
<action android:name="android.net.wifi.PICK_WIFI_NETWORK" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
android:value="true" />
</activity>
这里只是简单找的其中一个,估计还有一些类也是会跳转到Wifi列表设置界面的。
(4)adb shell或者串口拉起Wifi设置界面的命令2:
am start -n com.android.settings/.wifi.WifiPickerActivity
am start -a android.net.wifi.PICK_WIFI_NETWORK
但是代码中没有看到实现的地方???
(5)NetworkProviderSettings.java
具体实现是在 NetworkProviderSettings.java ,一个Fragment 界面中。
下面是部分代码:
/**
* UI for Mobile network and Wi-Fi network settings.
*
* TODO(b/167474581): Define the intent android.settings.NETWORK_PROVIDER_SETTINGS in Settings.java.
*/
@SearchIndexable
public class NetworkProviderSettings extends RestrictedSettingsFragment ... {
private static final String TAG = "NetworkProviderSettings";
//已连接的Wifi条目
static final String PREF_KEY_CONNECTED_ACCESS_POINTS = "connected_access_point";
//保存的Wifi条目
static final String PREF_KEY_FIRST_ACCESS_POINTS = "first_access_points";
//剩余的Wifi列表
private static final String PREF_KEY_ACCESS_POINTS = "access_points";
//Wifi偏好设置条目
private static final String PREF_KEY_CONFIGURE_NETWORK_SETTINGS = "configure_network_settings";
//每个条目的 Preference 对象
PreferenceCategory mConnectedWifiEntryPreferenceCategory;
@VisibleForTesting
PreferenceCategory mFirstWifiEntryPreferenceCategory;
@VisibleForTesting
PreferenceCategory mWifiEntryPreferenceCategory;
//Preference 对象绑定,初始化
private void addPreferences() {
addPreferencesFromResource(R.xml.network_provider_settings);
mConnectedWifiEntryPreferenceCategory = findPreference(PREF_KEY_CONNECTED_ACCESS_POINTS);
mFirstWifiEntryPreferenceCategory = findPreference(PREF_KEY_FIRST_ACCESS_POINTS);
//wifi 列表
mWifiEntryPreferenceCategory = findPreference(PREF_KEY_ACCESS_POINTS);
mConfigureWifiSettingsPreference = findPreference(PREF_KEY_CONFIGURE_NETWORK_SETTINGS);
}
//Wifi列表更新主要代码:
protected void updateWifiEntryPreferences() {
...
List<WifiEntry> wifiEntries = mWifiPickerTracker.getWifiEntries();
for (WifiEntry wifiEntry : wifiEntries) {
hasAvailableWifiEntries = true;
String key = wifiEntry.getKey();
LongPressWifiEntryPreference pref =
(LongPressWifiEntryPreference) getCachedPreference(key);
...
//wifi 列表
mWifiEntryPreferenceCategory.addPreference(pref);
}
}
}
不同的Android版本上的Settings实现是有差异的;
特别是Android9前后Settings实现逻辑变化很大。
Android9 或者更低的版本使用的是:
List accessPoints = mWifiTracker.getAccessPoints();
Android11 或者更新的版本获取Wifi使用的是:
List wifiEntries = mWifiPickerTracker.getWifiEntries();
因为 AccessPoint 过时了,所以使用的是 WifiEntry;
AccessPoint 是系统SettingsLib包封装的对象,WifiEntry 是系统framework包的对象;
WifiTracker 对象是SettingsLib的一个封装类,扫描和获取Wifi列表都是里面实现的;
所以在Settings全局搜索是无法查看到普通扫描和接收Wifi信息的代码的;
普通应用扫描和获取Wifi列表的代码:
WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
//Wifi扫描
wifiManager.startScan();
// 获取扫描结果,一般是接收WifiManager.SCAN_RESULTS_AVAILABLE_ACTION 后获取
wifiScanResults = wifiManager.getScanResults();
原生Settings是搜索不到上面这些代码的;大部分Wifi具体操作都是使用的SettingsLib的封装方法。