Android 13 WiFi设置下增加2.4G & 5G WiFi频率切换菜单
一 .大致思路逻辑
1.主要思路:
wifi频率切换可以理解为就是将扫描到的wifi进行过滤只显示2.4G或5G的wifi。
2.大致逻辑:
设置页面里使用ListPreference增加"WLAN频段"菜单项,通过选项将设定的wifi频段值保存在settings数据库中,最后在WifiManager里处理逻辑。
二 .增加wifi频率切换菜单
菜单路径:设置—网络和互联网—互联网—网络偏好设置—WLAN频段
1.system/vendor/mediatek/proprietary/packages/apps/MtkSettings/res/xml/wifi_configure_settings.xml
<SwitchPreference
android:key="wifi_cellular_data_fallback"
android:title="@string/wifi_cellular_data_fallback_title"
android:summary="@string/wifi_cellular_data_fallback_summary"
settings:controller="com.android.settings.wifi.CellularFallbackPreferenceController" />
<!-- add by ysliu for wifi frequency options 2023.8.16 start -->
<ListPreference
android:key="wifi_frequency_options"
android:title="@string/wifi_frequency_options_title"
android:summary="@string/wifi_frequency_options_summary"
android:negativeButtonText="@android:string/cancel"
android:persistent="false"
android:entries="@array/wifi_frequency_options_entries"
android:entryValues="@array/wifi_frequency_options_values"
settings:controller="com.android.settings.wifi.WifiFrequencyBandPreferenceController"/>
<!-- add by ysliu for wifi frequency options 2023.8.16 end -->
<Preference
android:key="install_credentials"
android:title="@string/wifi_install_credentials"/>
2.system/vendor/mediatek/proprietary/packages/apps/MtkSettings/res/values/arrays.xml
<!-- An allowlist which packages won't show summary in battery usage screen.
[CHAR LIMIT=NONE] -->
<string-array name="allowlist_hide_summary_in_battery_usage" translatable="false"></string-array>
<!-- add by ysliu for wifi frequency options 2023.8.16 start -->
<!-- 设置wifi频率,0表示只5g,1表示只2.4g,2表示5g和2.4g都支持 -->
<string-array name="wifi_frequency_options_entries">
<item>2.4 GHz&5 GHz</item>
<item>2.4 GHz only</item>
<item>5 GHz only</item>
</string-array>
<string-array name="wifi_frequency_options_values">
<item>2</item>
<item>1</item>
<item>0</item>
</string-array>
<!-- add by ysliu for wifi frequency options 2023.8.16 end -->
</resources>
3.system/vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/wifi/ConfigureWifiSettings.java
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
final WifiManager wifiManager = (WifiManager) getSystemService(WIFI_SERVICE);
final List<AbstractPreferenceController> controllers = new ArrayList<>();
controllers.add(new WifiP2pPreferenceController(context, getSettingsLifecycle(),
wifiManager));
/// M: For wapi cert manager feature @{
controllers.add(new WapiCertPreferenceController(context));
/// @}
//add by ysliu for wifi frequency options 2023.8.16 start
controllers.add(new WifiFrequencyBandPreferenceController(context, getSettingsLifecycle(), wifiManager));
//add by ysliu for wifi frequency options 2023.8.16 end
return controllers;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
4.新增文件:system/vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/wifi/WifiFrequencyBandPreferenceController.java
package com.android.settings.wifi;
import android.content.Context;
import android.net.wifi.WifiManager;
import android.provider.Settings;
import android.util.Log;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.xchengtech.ProjectConfig;
public class WifiFrequencyBandPreferenceController extends AbstractPreferenceController implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
private static Context mContext;
private WifiManager mWifiManager;
public String getPreferenceKey() {
return "wifi_frequency_options";
}
public boolean isAvailable() {
return ProjectConfig.FEATURE_T1625_COMMON_SWITCH_SUPPORT;
}
public WifiFrequencyBandPreferenceController(Context context, Lifecycle lifecycle, WifiManager wifiManager) {
super(context);
mContext = context;
this.mWifiManager = wifiManager;
}
public void displayPreference(PreferenceScreen preferenceScreen) {
super.displayPreference(preferenceScreen);
ListPreference listPreference = (ListPreference) preferenceScreen.findPreference("wifi_frequency_options");
int frequencyBand = getFrequencyBand();
listPreference.setValue("" + frequencyBand);
updateFrequencyBandSummary(listPreference, "" + frequencyBand);
}
public boolean onPreferenceChange(Preference preference, Object obj) {
String str = (String) obj;
int parseInt = Integer.parseInt(str);
updateFrequencyBandSummary((ListPreference) preference, str);
boolean b = Settings.Global.putInt(mContext.getContentResolver(), "wifi_frequency_band", parseInt);
if (b) {
if (null != mWifiManager) {
if (mWifiManager.isWifiEnabled()) {
mWifiManager.setWifiEnabled(false);
mWifiManager.setWifiEnabled(true);
}else{
mWifiManager.setWifiEnabled(true);
mWifiManager.setWifiEnabled(false);
}
}
}
return true;
}
private int getFrequencyBand() {
return Settings.Global.getInt(mContext.getContentResolver(), "wifi_frequency_band", 2);
}
private void updateFrequencyBandSummary(ListPreference listPreference, String str) {
listPreference.setSummary(listPreference.getEntries()[listPreference.findIndexOfValue(str)]);
}
}
三.主要处理逻辑
system/packages/modules/Wifi/framework/java/android/net/wifi/WifiManager.java
/**
* Return the results of the latest access point scan.
* @return the list of access points found in the most recent scan. An app must hold
* {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission
* and {@link android.Manifest.permission#ACCESS_WIFI_STATE} permission
* in order to get valid results.
*/
@RequiresPermission(allOf = {ACCESS_WIFI_STATE, ACCESS_FINE_LOCATION})
public List<ScanResult> getScanResults() {
try {
//add by ysliu for wifi frequency options 2023.8.16 start
if (isXxxProject) {
List<ScanResult> results = mService.getScanResults(mContext.getOpPackageName(),mContext.getAttributionTag());
int wifiFrequency = Settings.Global.getInt(mContext.getContentResolver(), "wifi_frequency_band", 2);
if(results != null && results.size() > 0){
WifiInfo wifiInfo = getConnectionInfo();
Iterator<ScanResult> iterator = results.iterator();
while (iterator.hasNext()) {
ScanResult result = iterator.next();
if(ScanResult.is24GHz(result.frequency) && (wifiFrequency == wifi_band_5_ghz)){
iterator.remove();
if (ScanResult.is24GHz(wifiInfo.getFrequency())) {
disableEphemeralNetwork(wifiInfo.getSSID());
disconnect();
}
}
if((ScanResult.is5GHz(result.frequency) || result.frequency >= 5900 )
&& (wifiFrequency == wifi_band_24_ghz)){
iterator.remove();
if (ScanResult.is5GHz(wifiInfo.getFrequency()) || result.frequency >= 5900) {
disableEphemeralNetwork(wifiInfo.getSSID());
disconnect();
}
}
}
}
return results;
} else {
return mService.getScanResults(mContext.getOpPackageName(),
mContext.getAttributionTag());
}
//add by ysliu for wifi frequency options 2023.8.16 end
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
四.bug优化
解决wifi频率切换时,有时不会自动断开或重新连接
system/packages/modules/Wifi/service/java/com/android/server/wifi/WifiConnectivityManager.java
/**
* Handles 'onResult' callbacks for the Periodic, Single & Pno ScanListener.
* Executes selection of potential network candidates, initiation of connection attempt to that
* network.
*/
private void handleScanResults(@NonNull List<ScanDetail> scanDetails,
@NonNull String listenerName,
boolean isFullScan,
@NonNull HandleScanResultsListener handleScanResultsListener) {
//add by ysliu for wifi frequency options 2023.8.16 start
Iterator<ScanDetail> iterator = scanDetails.iterator();
int wifi_frequency_band = Settings.Global.getInt(mContext.getContentResolver(), "wifi_frequency_band", 2);
while (iterator.hasNext()) {
ScanDetail result = iterator.next();
ScanResult sr = result.getScanResult();
if ((!ScanResult.is24GHz(sr.frequency) && (wifi_frequency_band == wifi_band_24_ghz))
|| (!ScanResult.is5GHz(sr.frequency) && wifi_frequency_band == wifi_band_5_ghz)) {
iterator.remove();
}
}
//add by ysliu for wifi frequency options 2023.8.16 start
List<WifiNetworkSelector.ClientModeManagerState> cmmStates = new ArrayList<>();
Set<String> connectedSsids = new HashSet<>();
boolean hasExistingSecondaryCmm = false;
for (ClientModeManager clientModeManager :
mActiveModeWarden.getInternetConnectivityClientModeManagers()) {
if (clientModeManager.getRole() == ROLE_CLIENT_SECONDARY_LONG_LIVED) {
hasExistingSecondaryCmm = true;
}
mWifiChannelUtilization.refreshChannelStatsAndChannelUtilization(
clientModeManager.getWifiLinkLayerStats(),
WifiChannelUtilization.UNKNOWN_FREQ);
WifiInfo wifiInfo = clientModeManager.syncRequestConnectionInfo();
if (clientModeManager.isConnected()) {
connectedSsids.add(wifiInfo.getSSID());
}
cmmStates.add(new WifiNetworkSelector.ClientModeManagerState(clientModeManager));
}
// We don't have any existing secondary CMM, but are we allowed to create a secondary CMM
// and do we have a request for OEM_PAID/OEM_PRIVATE request? If yes, we need to perform
// network selection to check if we have any potential candidate for the secondary CMM
// creation.
if (!hasExistingSecondaryCmm
&& (mOemPaidConnectionAllowed || mOemPrivateConnectionAllowed)) {
// prefer OEM PAID requestor if it exists.
WorkSource oemPaidOrOemPrivateRequestorWs =
mOemPaidConnectionRequestorWs != null
? mOemPaidConnectionRequestorWs
: mOemPrivateConnectionRequestorWs;
if (oemPaidOrOemPrivateRequestorWs == null) {
Log.e(TAG, "Both mOemPaidConnectionRequestorWs & mOemPrivateConnectionRequestorWs "
+ "are null!");
}
if (oemPaidOrOemPrivateRequestorWs != null
&& mActiveModeWarden.canRequestMoreClientModeManagersInRole(
oemPaidOrOemPrivateRequestorWs,
ROLE_CLIENT_SECONDARY_LONG_LIVED, false)) {
// Add a placeholder CMM state to ensure network selection is performed for a
// potential second STA creation.
cmmStates.add(new WifiNetworkSelector.ClientModeManagerState());
hasExistingSecondaryCmm = true;
}
}
...
system/vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/wifi/WifiFrequencyBandPreferenceController.java
public boolean onPreferenceChange(Preference preference, Object obj) {
String str = (String) obj;
int parseInt = Integer.parseInt(str);
updateFrequencyBandSummary((ListPreference) preference, str);
boolean b = Settings.Global.putInt(mContext.getContentResolver(), "wifi_frequency_band", parseInt);
if (b) {
if (null != mWifiManager) {
if (mWifiManager.isWifiEnabled()) {
mWifiManager.setWifiEnabled(false);
mWifiManager.setWifiEnabled(true);
//add by ysliu for wifi frequency options 2023.8.16 start
mWifiManager.startScan();
//add by ysliu for wifi frequency options 2023.8.16 end
}else{
mWifiManager.setWifiEnabled(true);
mWifiManager.setWifiEnabled(false);
}
}
}
return true;
}
五.对于双频合一的wifi优化
先了解下双频合一的wifi和频率
我们在淘宝上买路由器的时候,经常会搜索到标题包含 双频 的路由器,这里的双频指的是这个路由器可以开两个频段的网络,分别是 2.4GHz 频段和 5GHz 频段。具体区别如下:
2.4GHz 频段
2.4GHz 频段的网络接入速率为 72M,Wi-Fi 协议对应的是 802.11n。特点是速率低,信道少干扰大,穿墙能力强,覆盖距离远。频率在 2400 到 2500 之间,不包含头尾。
5GHz 频段
5GHz 频段的网络接入速率是 433M,Wi-Fi 协议对应的是 802.11ac。特点是速率高,信道多干扰小,穿墙能力差,覆盖距离也比较近。频率在 4900 到 5900 之间,不包含头尾。
双频合一
如果路由器开启了双频合一功能,那么这个路由器就会同时发出 2.4GHz 频段和 5GHz 频段的网络,但是它们在手机上显示的确是同一个 SSID(Wi-Fi 名称),我们连接的时候可能连接的是 2.4GHz 频段,也可能是 5GHz 频段,连接哪个取决于当时的环境、网络状况等原因。
双频路由器会 同时 发出两个频段的网络,但是却显示 同一个 SSID(Wi-Fi 名称)。如果这个时候我们路由器设置的 Wi-Fi 名称是 “wildma_wifi”,那么我们手机上就只显示一个该名字的 Wi-Fi,而不会显示两个同名的。
此需求对于双频合一的wifi问题点
android13中如果未对双频wifi优化,当选择2.4g频段时,连接双频合一wifi,此wifi不是2.4g频率(还是5g频率),
正常现象应该是连上双频合一wifi2.4g频率的。
代码修改:
packages/modules/Wifi/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java
public boolean startSingleScan(WifiNative.ScanSettings settings,
WifiNative.ScanEventHandler eventHandler) {
if (eventHandler == null || settings == null) {
Log.w(TAG, "Invalid arguments for startSingleScan: settings=" + settings
+ ",eventHandler=" + eventHandler);
return false;
}
synchronized (mSettingsLock) {
if (mLastScanSettings != null) {
Log.w(TAG, "A single scan is already running");
return false;
}
ChannelCollection allFreqs = mChannelHelper.createChannelCollection();
boolean reportFullResults = false;
for (int i = 0; i < settings.num_buckets; ++i) {
WifiNative.BucketSettings bucketSettings = settings.buckets[i];
if ((bucketSettings.report_events
& WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) {
reportFullResults = true;
}
allFreqs.addChannels(bucketSettings);
}
List<String> hiddenNetworkSSIDSet = new ArrayList<>();
if (settings.hiddenNetworks != null) {
boolean executeRoundRobin = true;
int maxNumScanSsids = mMaxNumScanSsids;
if (maxNumScanSsids <= 0) {
// Subtract 1 to account for the wildcard/broadcast probe request that
// wificond adds to the scan set.
mMaxNumScanSsids = mWifiNative.getMaxSsidsPerScan(getIfaceName()) - 1;
if (mMaxNumScanSsids > 0) {
maxNumScanSsids = mMaxNumScanSsids;
} else {
maxNumScanSsids = DEFAULT_NUM_HIDDEN_NETWORK_IDS_PER_SCAN;
executeRoundRobin = false;
}
}
int numHiddenNetworksPerScan =
Math.min(settings.hiddenNetworks.length, maxNumScanSsids);
if (numHiddenNetworksPerScan == settings.hiddenNetworks.length
|| mNextHiddenNetworkScanId >= settings.hiddenNetworks.length
|| !executeRoundRobin) {
mNextHiddenNetworkScanId = 0;
}
if (DBG) {
Log.d(TAG, "Scanning for " + numHiddenNetworksPerScan + " out of "
+ settings.hiddenNetworks.length + " total hidden networks");
Log.d(TAG, "Scan hidden networks starting at id=" + mNextHiddenNetworkScanId);
}
int id = mNextHiddenNetworkScanId;
for (int i = 0; i < numHiddenNetworksPerScan; i++, id++) {
hiddenNetworkSSIDSet.add(
settings.hiddenNetworks[id % settings.hiddenNetworks.length].ssid);
}
mNextHiddenNetworkScanId = id % settings.hiddenNetworks.length;
}
mLastScanSettings = new LastScanSettings(
mClock.getElapsedSinceBootNanos(),
reportFullResults, allFreqs, eventHandler);
boolean success = false;
Set<Integer> freqs = Collections.emptySet();
//add by ysliu 2023.9.28 start
//Set<Integer> freqs;
final int wifi_band_5_ghz = 0;
final int wifi_band_24_ghz = 1;
final int wifi_band_5_24ghz = 2;
int[] channels = null;
int[] channelDfs = null;
int frequencyBand = Settings.Global.getInt(mContext.getContentResolver(), "wifi_frequency_band", 2);
Log.d(TAG, "Ap_Frequency_Band = " + frequencyBand);
if (!allFreqs.isEmpty()) {
//freqs = allFreqs.getScanFreqs();
ArraySet<Integer> mChannels = new ArraySet<Integer>();
switch (frequencyBand) {
case wifi_band_24_ghz:
channels = mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_24_GHZ);
break;
case wifi_band_5_ghz:
channels = mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ);
channelDfs = mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY);
break;
default:
break;
}
if (null != channels) {
for (int chan : channels) {
mChannels.add(chan);
}
if (null != channelDfs) {
for (int chan : channelDfs) {
mChannels.add(chan);
}
}
freqs = new ArraySet<Integer>(mChannels);
} else {
freqs = allFreqs.getScanFreqs();
}
Log.d(TAG, "freqs=" + freqs);
//add by ysliu 2023.9.28 end
if (!allFreqs.isEmpty()) {
// freqs = allFreqs.getScanFreqs(); //modify by ysliu 2023.9.28
success = mWifiNative.scan(
getIfaceName(), settings.scanType, freqs, hiddenNetworkSSIDSet,
settings.enable6GhzRnr);
if (!success) {
Log.e(TAG, "Failed to start scan, freqs=" + freqs);
}
} else {
// There is a scan request but no available channels could be scanned for.
// We regard it as a scan failure in this case.
Log.e(TAG, "Failed to start scan because there is no available channel to scan");
}
if (success) {
if (DBG) {
Log.d(TAG, "Starting wifi scan for freqs=" + freqs
+ " on iface " + getIfaceName());
}
mScanTimeoutListener = new AlarmManager.OnAlarmListener() {
@Override
public void onAlarm() {
handleScanTimeout();
}
};
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
mClock.getElapsedSinceBootMillis() + SCAN_TIMEOUT_MS,
TIMEOUT_ALARM_TAG, mScanTimeoutListener, mEventHandler);
} else {
// indicate scan failure async
mEventHandler.post(() -> reportScanFailure());
}
}
return true;
}
}
六.总结
1.Android 10 或更高版本为目标平台的应用调用WifiManager相关api(setWifiEnable、startScan、getScanResults等方法)不会生效,会返回false
2.调用Wi-Fi信息需要以下权限
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
3.其实在framework层实现此功能的话,会有一些bug的,如果对wifi要求比较高的话,最好是在
kernel层实现2.4G&5G only功能,这样就不会有问题,以上仅供参考。