[Android13 development WIFI adds frequency band setting item (2.4G/5G/automatic) change notes]

BUG requirement description:

提示:这里简述项目相关背景:

Customer requirements in Android13 add WIFI band options:
2.4G only, only APs connected to 2.4G are displayed.
5G only, only 5G APs can be displayed and linked.
Automatic: Automatically displayed, that is, everything is displayed normally.

Solve the original intention

提示:这里描述项目中遇到的问题:

As a beginner, I was quite confused when I first received this bug. Thanks to the articles published by CSDN seniors, they helped me a lot. Standing on the shoulders of giants and seeing further, I will further add my own understanding to the articles of my predecessors and analyze the logic in them, hoping to help everyone!

You need to have a little knowledge about WIFI

1.android wifi scanresult.interface
The ScanResult class implements the Parcelable interface: the ScanResult class describes information about a discovered AP. In addition to the properties described in this class, supplicant also tracks the quality, noise, and maxbitrate properties, but does not notify the client of them immediately.
2.1. Directory location:
/frameworks/base/wifi/java/android/net/wifi/

3. 24G and 5G core methods in WIFI frequency band
1. public boolean is24GHz(): Directly call is24GHz(frequency), that is, use your own channel frequency value to judge, if If the frequency value is between 2400 and 2500, (excluding the two boundary values ​​of 2400 and 2500), it returns true, otherwise it returns false.

2.public boolean is5GHz(int freq): Use the incoming freq value to judge. If it is between 4900~5900 and does not contain boundaries, it will return true, otherwise it will return false.

Changed style display

Add third-level menu_WIFI frequency band
Click the WIFI frequency band setting item and a prompt option box will pop up.

Implementation ideas

Use ListPreference to statically add the "WLAN Band" menu item in the settings page, save the set wifi band value in the settings database through options, and finally process the logic in WifiManager.

Code implementation steps (nanny-style teaching):

1. First of all, the first step is to add the corresponding layout at the corresponding layout position.
system/vendor/mediatek/proprietary/packages/apps/MtkSettings/res/xml/wifi_configure_settings.xml

Resource files need to be configured separately in strings.xml

<ListPreference
        android:key="wifi_frequency_options"
        android:title="@string/wifi_frequency_options_title"//需要自己去string.xml中定义
        android:summary="@string/wifi_frequency_options_summary"//需要自己去string.xml中定义
        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"/>

layout page position
layout display


2.vendor/mediatek/proprietary/packages/apps/MtkSettings/res/values/arrays.xml
Add the corresponding array and add the corresponding options

<string-array name="wifi_frequency_options_entries">
        <item>Automatic</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>

Reference article add location
option location

3. Add a new control file
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 true; //可以设置相应宏控判定该模块是否呈现
    }

    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)]);
    }
}

4. Add the corresponding control module to the control class in the WIFI settings item
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 wjj for wifi frequency options  start
        controllers.add(new WifiFrequencyBandPreferenceController(context, getSettingsLifecycle(), wifiManager));
        //add by wjj for wifi frequency options  end
        return controllers;
    }

    @Override
    public void onAttach(Context context) {
    
    
        super.onAttach(context);

5. The core logic implementation class is in the getScanResults() method in the wifimanager.java class
First, set the corresponding macro definition at the top
system/packages/modules/Wifi/framework/java/android/net/wifi/WifiManager.java

	//wjj add
	final int wifi_band_5_ghz = 0;
	final int wifi_band_24_ghz = 1;
	final int wifi_band_5_24ghz = 2;
	//wjj end

Then add the corresponding judgment logic in the corresponding method

    /**
     * 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 wjj for wifi frequency options start
                if (true) {
    
     //此处可以进一步设置判断
                    List<ScanResult> results = mService.getScanResults(mContext.getOpPackageName(),mContext.getAttributionTag()); //设置scanResult类型的列表 result 获取每一条wifi字段包含许多属性
                    int wifiFrequency = Settings.Global.getInt(mContext.getContentResolver(), "wifi_frequency_band", 2); //wifiFrequency 只获取其中的频段
                    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)){
    
    //异常值即频段只有2.4G但band等级达到5G的异常值(不是很懂怎么表述,希望大佬指正)意思大概就是异常值移除
                                iterator.remove();//移除操作
                                if (ScanResult.is24GHz(wifiInfo.getFrequency())) {
    
    //符合2.4G
                                    disableEphemeralNetwork(wifiInfo.getSSID());//隐藏相应的显示信息
                                    disconnect();//断开链接
                                }
                            }
                            if((ScanResult.is5GHz(result.frequency) || result.frequency >= 5900 )
                                    && (wifiFrequency == wifi_band_24_ghz)){
    
    //符合5G频段或6G但wifi等级却是2.4G的异常值移除
                                iterator.remove();
                                if (ScanResult.is5GHz(wifiInfo.getFrequency()) || result.frequency >= 5900) {
    
    //符合5G或以上条件的
                                    disableEphemeralNetwork(wifiInfo.getSSID());//隐藏
                                    disconnect();//断开链接
                                }
                            }
                        }
                    }
                    return results;
                } else {
    
    //扫描结果全部进行显示
                    return mService.getScanResults(mContext.getOpPackageName(),
                            mContext.getAttributionTag());
                }
            //add by wjj for wifi frequency options end
        } catch (RemoteException e) {
    
    //异常处理
            throw e.rethrowFromSystemServer();
        }
    }

Bug optimization 1: Solve the problem of sometimes not automatically disconnecting or reconnecting when switching the wifi frequency
1.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 wjj for wifi frequency options 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 wjj for wifi frequency options end
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;
            }
        }

2. Improve the control class you added
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 wjj for wifi frequency options  start
                    mWifiManager.startScan();
                    //add by wjj for wifi frequency options  end
                }else{
    
    
                    mWifiManager.setWifiEnabled(true);
                    mWifiManager.setWifiEnabled(false);
                }
            }
        }
        return true;
    }

Bug Optimization 2: This is an optimization step worth learning from the predecessors
For dual-band wifi optimization: if dual-band wifi is not optimized in android13, when the 2.4g frequency band is selected , connect to dual-band-in-one wifi. This wifi is not a 2.4g frequency (or 5g frequency). The normal phenomenon should be connected to a dual-band-in-one wifi 2.4g frequency.

    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;
        }
    }

Summarize:

1. Calling WifiManager related APIs (setWifiEnable, startScan, getScanResults, etc.) for applications on the target platform in Android 10 or higher will not take effect and will return false
2. Call Wi-Fi information requires the following permissions
Add the location in the settings menu file (usually added)

    <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" />


Final summary:

Friends, I refer to a senior named ysliu and make further supplements on his basis. As someone who has just entered the workplace, I really need such a senior who writes on the blackboard very carefully. If you Now that you have seen this, you can go and read what the seniors wrote, it is really detailedSenior’s CSDN. I hope the things I recorded can help more people! – An android development practitioner who just entered the workplace and studied big data at university.

Links to more excellent articles by seniors (the hyperlink above is also acceptable):

Lysssx senior

Guess you like

Origin blog.csdn.net/weixin_48495360/article/details/134199805