Android 8.0/9.0 wifi 自動連線評分機制

肉滾滾和程式碼發表於2021-01-28

前言

Android N wifi auto connect流程分析
Android N selectQualifiedNetwork分析

Wifi自動連線時的評分機制

今天瞭解了一下Wifi自動連線時的評分機制,總結如下:
WifiConnectivityManager的初始化:
/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiStateMachine.java

    class SupplicantStartedState extends State {
            if (mWifiScanner == null) {
                mWifiScanner = mWifiInjector.getWifiScanner();

                synchronized (mWifiReqCountLock) {
                    mWifiConnectivityManager =
                            mWifiInjector.makeWifiConnectivityManager(mWifiInfo,
                                                                      hasConnectionRequests());
                    mWifiConnectivityManager.setUntrustedConnectionAllowed(mUntrustedReqCount > 0);
                    mWifiConnectivityManager.handleScreenStateChanged(mScreenOn);
                }
            }


/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiInjector.java

    public WifiConnectivityManager makeWifiConnectivityManager(WifiInfo wifiInfo,
                                                               boolean hasConnectionRequests) {
        return new WifiConnectivityManager(mContext, mWifiStateMachine, getWifiScanner(),
                mWifiConfigManager, wifiInfo, mWifiNetworkSelector, mWifiConnectivityHelper,
               mWifiLastResortWatchdog, mOpenNetworkNotifier, mWifiMetrics,
                mWifiStateMachineHandlerThread.getLooper(), mClock, mConnectivityLocalLog,
                hasConnectionRequests, mFrameworkFacade, mSavedNetworkEvaluator,
                mScoredNetworkEvaluator, mPasspointNetworkEvaluator);
    }


WifiConnectivityManager中會註冊三個Evaluator。
/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiConnectivityManager.java

        mNetworkSelector.registerNetworkEvaluator(savedNetworkEvaluator,
                SAVED_NETWORK_EVALUATOR_PRIORITY);
        if (hs2Enabled) {
            mNetworkSelector.registerNetworkEvaluator(passpointNetworkEvaluator,
                    PASSPOINT_NETWORK_EVALUATOR_PRIORITY);
        }
        mNetworkSelector.registerNetworkEvaluator(scoredNetworkEvaluator,
                SCORED_NETWORK_EVALUATOR_PRIORITY);

繼續看下注冊方法,其實就是初始化一個NetworkEvaluator陣列,大小為6,即優先順序從高到低0-5。
/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiNetworkSelector.java

    public static final int EVALUATOR_MIN_PRIORITY = 6;

    private final NetworkEvaluator[] mEvaluators = new NetworkEvaluator[MAX_NUM_EVALUATORS];

    public boolean registerNetworkEvaluator(NetworkEvaluator evaluator, int priority) {
        if (priority < 0 || priority >= EVALUATOR_MIN_PRIORITY) {
            localLog("Invalid network evaluator priority: " + priority);
            return false;
        }

        if (mEvaluators[priority] != null) {
            localLog("Priority " + priority + " is already registered by "
                    + mEvaluators[priority].getName());
            return false;
        }

        mEvaluators[priority] = evaluator;
        return true;
    }


WifiConnectivityManager的網路評估

    private boolean handleScanResults(List<ScanDetail> scanDetails, String listenerName) {
        if (mSupportCMCC && mWifiManagerEx.isAutoConnect() == false) {
            Log.i(TAG, "WifiManagerEx isAutoConnect false,  " + mWifiManagerEx.isAutoConnect());
            return false;
        }
        
        if (!mP2pWifiCoexistSupported && WifiP2pServiceImpl.mP2pConnectingOrConnected) {
            Log.i(TAG, "Do not auto connect when wifi and p2p should not coexsit");
            return false;
        }
    
        refreshBssidBlacklist();

        if (mStateMachine.isLinkDebouncing() || mStateMachine.isSupplicantTransientState()) {
            localLog(listenerName + " onResults: No network selection because linkDebouncing is "
                    + mStateMachine.isLinkDebouncing() + " and supplicantTransient is "
                    + mStateMachine.isSupplicantTransientState());
            return false;
        }

        localLog(listenerName + " onResults: start network selection");

        WifiConfiguration candidate =
                mNetworkSelector.selectNetwork(scanDetails, buildBssidBlacklist(), mWifiInfo,
                mStateMachine.isConnected(), mStateMachine.isDisconnected(),
                mUntrustedConnectionAllowed);
        mWifiLastResortWatchdog.updateAvailableNetworks(
                mNetworkSelector.getConnectableScanDetails());
        mWifiMetrics.countScanResults(scanDetails);
        if (candidate != null) {
            localLog(listenerName + ":  WNS candidate-" + candidate.SSID);
            connectToNetwork(candidate);
            return true;
        } else {
            if (mWifiState == WIFI_STATE_DISCONNECTED) {
                mOpenNetworkNotifier.handleScanResults(
                        mNetworkSelector.getFilteredScanDetailsForOpenUnsavedNetworks());
            }
            return false;
        }
    }

如api註釋所述,對週期性、單次和pno掃描的結果進行潛在網路候選者的選擇,如果有合適的網路,則進行連線

看下candiate是如何產生的:

    public WifiConfiguration selectNetwork(List<ScanDetail> scanDetails,
            HashSet<String> bssidBlacklist, WifiInfo wifiInfo,
        boolean connected, boolean disconnected, boolean untrustedNetworkAllowed) 
        mFilteredNetworks.clear();
       mConnectableNetworks.clear();
        if (scanDetails.size() == 0) {
           localLog("Empty connectivity scan result");
            return null;
        WifiConfiguration currentNetwork =
                mWifiConfigManager.getConfiguredNetwork(wifiInfo.getNetworkId());
        String currentBssid = wifiInfo.getBSSID();
        if (!isNetworkSelectionNeeded(scanDetails, wifiInfo, connected, disconnected)) {
            return null;
        }
        for (NetworkEvaluator registeredEvaluator : mEvaluators) {
            if (registeredEvaluator != null) {
                registeredEvaluator.update(scanDetails);
            }
        }
        mFilteredNetworks = filterScanResults(scanDetails, bssidBlacklist,
                connected, currentBssid);
        if (mFilteredNetworks.size() == 0) {
            return null;
        }
        WifiConfiguration selectedNetwork = null;
        for (NetworkEvaluator registeredEvaluator : mEvaluators) {
            if (registeredEvaluator != null) {
                localLog("About to run " + registeredEvaluator.getName() + " :");
                selectedNetwork = registeredEvaluator.evaluateNetworks(
                        new ArrayList<>(mFilteredNetworks), currentNetwork, currentBssid, connected,
                        untrustedNetworkAllowed, mConnectableNetworks);
                if (selectedNetwork != null) {
                    localLog(registeredEvaluator.getName() + " selects "
                            + WifiNetworkSelector.toNetworkString(selectedNetwork) + " : "
                            + selectedNetwork.getNetworkSelectionStatus().getCandidate().BSSID);
                    break;
                }
        if (selectedNetwork != null) {
         selectedNetwork = overrideCandidateWithUserConnectChoice(selectedNetwork);
            mLastNetworkSelectionTimeStamp = mClock.getElapsedSinceBootMillis();
       }
        return selectedNetwork;
    }

看下是否可以進行網路選擇:
1、 當前網路無效時
2、 當前沒有連線網路時

    private boolean isNetworkSelectionNeeded(List<ScanDetail> scanDetails, WifiInfo wifiInfo,
                        boolean connected, boolean disconnected) {
        if (scanDetails.size() == 0) {
            localLog("Empty connectivity scan results. Skip network selection.");
            return false;
        }

        if (connected) {
            if (isCurrentNetworkSufficient(wifiInfo, scanDetails)) {
                localLog("Current connected network already sufficient. Skip network selection.");
                return false;
            } else {
                localLog("Current connected network is not sufficient.");
                return true;
            }
        } else if (disconnected) {
            return true;
        } else {


判斷網路是否有效,主要是看以下幾點要素:

  • rssi(區分2.4G還是5G)
  • packet rate
  • ephemeral(短暫的)
  • open network
  • 5G優先順序>2.4G
    private boolean isCurrentNetworkSufficient(WifiInfo wifiInfo, List<ScanDetail> scanDetails) {
        WifiConfiguration network =
                   mWifiConfigManager.getConfiguredNetwork(wifiInfo.getNetworkId());
        int currentRssi = wifiInfo.getRssi();
        boolean hasQualifiedRssi =
                (wifiInfo.is24GHz() && (currentRssi > mThresholdQualifiedRssi24))
                        || (wifiInfo.is5GHz() && (currentRssi > mThresholdQualifiedRssi5));
        boolean hasActiveStream = (wifiInfo.getTxSuccessRatePps() > mStayOnNetworkMinimumTxRate)
                || (wifiInfo.getRxSuccessRatePps() > mStayOnNetworkMinimumRxRate);
        if (hasQualifiedRssi && hasActiveStream) {
            localLog("Stay on current network because of good RSSI and ongoing traffic");
            return true;
        }
        if (network.ephemeral) {
            localLog("Current network is an ephemeral one.");
            return false;
        }
        if (WifiConfigurationUtil.isConfigForOpenNetwork(network)) {
            localLog("Current network is a open one.");
            return false;
        }
        if (wifiInfo.is24GHz()) {
            if (is5GHzNetworkAvailable(scanDetails)) {
                localLog("Current network is 2.4GHz. 5GHz networks available.");
                return false;
            }
        }
        if (!hasQualifiedRssi) {
            localLog("Current network RSSI[" + currentRssi + "]-acceptable but not qualified.");
            return false;
        }

        return true;
    }


SavedNetworkEvaluator的篩選
首先遍歷掃描結果:

  for (ScanDetail scanDetail : scanDetails) {
            ScanResult scanResult = scanDetail.getScanResult();
            int highestScoreOfScanResult = Integer.MIN_VALUE;
            int candidateIdOfScanResult = WifiConfiguration.INVALID_NETWORK_ID;

            //過濾passpoint和ephemeral,如api所述這類網路交由PasspointNetworkEvaluator
            //和ScoredNetworkEvaluator進行評估
                if (network.isPasspoint() || network.isEphemeral()) {
                    continue;
                }

接著過濾沒有enabled的,scan bssid和config對不上的和eap-sim之類的網路但是沒插卡的。

                if (!status.isNetworkEnabled()) {
                    continue;
                } else if (network.BSSID != null &&  !network.BSSID.equals("any")
                        && !network.BSSID.equals(scanResult.BSSID)) {
                    localLog("Network " + WifiNetworkSelector.toNetworkString(network)+ " has specified BSSID " + 
                    network.BSSID + ". Skip " + scanResult.BSSID);
                    continue;
                } else if (TelephonyUtil.isSimConfig(network)
                        && !mWifiConfigManager.isSimPresent()) {
                    localLog("isSimPresent");
                    continue;
                }


網路評分的關鍵:計算BSSID的分數,評分幾大要素如下:

  • RSSI
  • 5G
  • lastUserSelectedNetworkId
  • currentNetwork
  • isFirmwareRoamingSupported
  • isConfigForOpenNetwork
    private int calculateBssidScore(ScanResult scanResult, WifiConfiguration network,
                        WifiConfiguration currentNetwork, String currentBssid,
                        StringBuffer sbuf) {
        int score = 0;
        boolean is5GHz = scanResult.is5GHz();

        sbuf.append("[ ").append(scanResult.SSID).append(" ").append(scanResult.BSSID)
                .append(" RSSI:").append(scanResult.level).append(" ] ");

        int rssiSaturationThreshold = is5GHz ? mThresholdSaturatedRssi5 : mThresholdSaturatedRssi24;
        int rssi = scanResult.level < rssiSaturationThreshold ? scanResult.level
                : rssiSaturationThreshold;
        score += (rssi + mRssiScoreOffset) * mRssiScoreSlope;
        sbuf.append(" RSSI score: ").append(score).append(",");

        //如果是5G頻段,會有獎勵。
        if (is5GHz) {
            score += mBand5GHzAward;
            sbuf.append(" 5GHz bonus: ").append(mBand5GHzAward).append(",");
        }
       //之前連線過也有獎勵
       int lastUserSelectedNetworkId = mWifiConfigManager.getLastSelectedNetwork();
        if (lastUserSelectedNetworkId != WifiConfiguration.INVALID_NETWORK_ID
                && lastUserSelectedNetworkId == network.networkId) {
            long timeDifference = mClock.getElapsedSinceBootMillis()
                    - mWifiConfigManager.getLastSelectedTimeStamp();
            if (timeDifference > 0) {
                int bonus = mLastSelectionAward - (int) (timeDifference / 1000 / 60);
                score += bonus > 0 ? bonus : 0;
                sbuf.append(" User selection ").append(timeDifference / 1000 / 60)
                        .append(" minutes ago, bonus: ").append(bonus).append(",");
            }
        }

        //如果是當前正在連線的網路,也會加分
        if (currentNetwork != null
                && (network.networkId == currentNetwork.networkId
  
                /* || network.isLinked(currentNetwork) */)) {
            score += mSameNetworkAward;
            sbuf.append(" Same network bonus: ").append(mSameNetworkAward).append(",");
            //支援漫遊也會有獎勵
            if (mConnectivityHelper.isFirmwareRoamingSupported()
                    && currentBssid != null && !currentBssid.equals(scanResult.BSSID)) {
                score += mSameBssidAward;
                sbuf.append(" Equivalent BSSID bonus: ").append(mSameBssidAward).append(",");
            }
        }

        //BSSID相同也會有獎勵
        if (currentBssid != null && currentBssid.equals(scanResult.BSSID)) {
            score += mSameBssidAward;
            sbuf.append(" Same BSSID bonus: ").append(mSameBssidAward).append(",");
        }

        //不是開放的網路,也會有獎勵
        if (!WifiConfigurationUtil.isConfigForOpenNetwork(network)) {
            score += mSecurityAward;
            sbuf.append(" Secure network bonus: ").append(mSecurityAward).append(",");
        sbuf.append(" ## Total score: ").append(score).append("\n");

        return score;
    }


相關文章