Android P WiFi自动连接评分机制

2020-07-15 11:07:50 浏览数 (1)

1、WifiConnectivityManager的初始化

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

代码语言:javascript复制
private void setupClientMode() {
    Log.d(TAG, "setupClientMode() ifacename = "   mInterfaceName);
    mWifiStateTracker.updateState(WifiStateTracker.INVALID);

    if (mWifiConnectivityManager == null) {
        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

代码语言:javascript复制
public WifiConnectivityManager makeWifiConnectivityManager(WifiInfo wifiInfo,
                                                           boolean hasConnectionRequests) {
    return new WifiConnectivityManager(mContext, getScoringParams(),
            mWifiStateMachine, getWifiScanner(),
            mWifiConfigManager, wifiInfo, mWifiNetworkSelector, mWifiConnectivityHelper,
            mWifiLastResortWatchdog, mOpenNetworkNotifier, mCarrierNetworkNotifier,
            mCarrierNetworkConfig, mWifiMetrics, mWifiStateMachineHandlerThread.getLooper(),
            mClock, mConnectivityLocalLog, hasConnectionRequests, mFrameworkFacade,
            mSavedNetworkEvaluator, mScoredNetworkEvaluator, mPasspointNetworkEvaluator);
}

构造方法里注册了3个NetworkEvaluator frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiConnectivityManager.java

代码语言:javascript复制
WifiConnectivityManager(Context context, ScoringParams scoringParams,
        WifiStateMachine stateMachine,
        WifiScanner scanner, WifiConfigManager configManager, WifiInfo wifiInfo,
    // Register the network evaluators
    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

代码语言:javascript复制
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;
}

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

public static final int MAX_NUM_EVALUATORS = EVALUATOR_MIN_PRIORITY;

/**
 * WiFi Network Selector supports various types of networks. Each type can
 * have its evaluator to choose the best WiFi network for the device to connect
 * to. When registering a WiFi network evaluator with the WiFi Network Selector,
 * the priority of the network must be specified, and it must be a value between
 * 0 and (EVALUATOR_MIN_PIRORITY - 1) with 0 being the highest priority. Wifi
 * Network Selector iterates through the registered scorers from the highest priority
 * to the lowest till a network is selected.
 */
public static final int EVALUATOR_MIN_PRIORITY = 6;

2、WifiConnectivityManager的网络评估

处理扫描结果 frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiConnectivityManager.java

代码语言:javascript复制
private boolean handleScanResults(List<ScanDetail> scanDetails, String listenerName) {
    // Check if any blacklisted BSSIDs can be freed.
    refreshBssidBlacklist();

    if (mStateMachine.isSupplicantTransientState()) {
        localLog(listenerName
                  " onResults: No network selection because supplicantTransientState 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());
            if (mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()) {
                mCarrierNetworkNotifier.handleScanResults(
                        mNetworkSelector.getFilteredScanDetailsForCarrierUnsavedNetworks(
                                mCarrierNetworkConfig));
            }
        }
        return false;
    }
}

如注释所说:处理周期性,单次和Pno ScanListener的“ onResult”回调。 执行潜在网络候选者的选择,启动与该网络的连接尝试。

然后看candidate是如何生成的: frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiNetworkSelector.java

代码语言:javascript复制
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());

    // Always get the current BSSID from WifiInfo in case that firmware initiated
    // roaming happened.
    String currentBssid = wifiInfo.getBSSID();

    // Shall we start network selection at all?
    if (!isNetworkSelectionNeeded(scanDetails, wifiInfo, connected, disconnected)) {
        return null;
    }

    // Update the registered network evaluators.
    for (NetworkEvaluator registeredEvaluator : mEvaluators) {
        if (registeredEvaluator != null) {
            registeredEvaluator.update(scanDetails);
        }
    }

    // Filter out unwanted networks.
    mFilteredNetworks = filterScanResults(scanDetails, bssidBlacklist,
            connected, currentBssid);
    if (mFilteredNetworks.size() == 0) {
        return null;
    }

    // Go through the registered network evaluators from the highest priority
    // one to the lowest till a network is selected.
    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;
}
2.1、判断是否需要网络选择
代码语言:javascript复制
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) {
        // Is roaming allowed?
        if (!mEnableAutoJoinWhenAssociated) {
            localLog("Switching networks in connected state is not allowed."
                      " Skip network selection.");
            return false;
        }

        // Has it been at least the minimum interval since last network selection?
        if (mLastNetworkSelectionTimeStamp != INVALID_TIME_STAMP) {
            long gap = mClock.getElapsedSinceBootMillis()
                        - mLastNetworkSelectionTimeStamp;
            if (gap < MINIMUM_NETWORK_SELECTION_INTERVAL_MS) {
                localLog("Too short since last network selection: "   gap   " ms."
                          " Skip network selection.");
                return false;
            }
        }

        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 {
        // No network selection if WifiStateMachine is in a state other than
        // CONNECTED or DISCONNECTED.
        localLog("WifiStateMachine is in neither CONNECTED nor DISCONNECTED state."
                  " Skip network selection.");
        return false;
    }
}
2.2、SavedNetworkEvaluator的筛选

根据扫描结果评估所有网络,并返回选择用于连接的网络的WifiConfiguration frameworks/opt/net/wifi/service/java/com/android/server/wifi/SavedNetworkEvaluator.java

代码语言:javascript复制
/**
 * Evaluate all the networks from the scan results and return
 * the WifiConfiguration of the network chosen for connection.
 *
 * @return configuration of the chosen network;
 *         null if no network in this category is available.
 */
public WifiConfiguration evaluateNetworks(List<ScanDetail> scanDetails,
                WifiConfiguration currentNetwork, String currentBssid, boolean connected,
                boolean untrustedNetworkAllowed,
                List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks) {
    int highestScore = Integer.MIN_VALUE;
    ScanResult scanResultCandidate = null;
    WifiConfiguration candidate = null;
    StringBuffer scoreHistory = new StringBuffer();

    for (ScanDetail scanDetail : scanDetails) {
        ScanResult scanResult = scanDetail.getScanResult();

        // One ScanResult can be associated with more than one networks, hence we calculate all
        // the scores and use the highest one as the ScanResult's score.
        WifiConfiguration network =
                mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(scanDetail);

        if (network == null) {
            continue;
        }

        /**
         * Ignore Passpoint and Ephemeral networks. They are configured networks,
         * but without being persisted to the storage. They are evaluated by
         * {@link PasspointNetworkEvaluator} and {@link ScoredNetworkEvaluator}
         * respectively.
         */
        if (network.isPasspoint() || network.isEphemeral()) {
            continue;
        }

        WifiConfiguration.NetworkSelectionStatus status =
                network.getNetworkSelectionStatus();
        status.setSeenInLastQualifiedNetworkSelection(true);

        if (!status.isNetworkEnabled()) {
            continue;
        } else if (network.BSSID != null &&  !network.BSSID.equals("any")
                && !network.BSSID.equals(scanResult.BSSID)) {
            // App has specified the only BSSID to connect for this
            // configuration. So only the matching ScanResult can be a candidate.
            localLog("Network "   WifiNetworkSelector.toNetworkString(network)
                      " has specified BSSID "   network.BSSID   ". Skip "
                      scanResult.BSSID);
            continue;
        } else if (TelephonyUtil.isSimConfig(network)
                && !mWifiConfigManager.isSimPresent()) {
            // Don't select if security type is EAP SIM/AKA/AKA' when SIM is not present.
            continue;
        }

        int score = calculateBssidScore(scanResult, network, currentNetwork, currentBssid,
                scoreHistory);

        // Set candidate ScanResult for all saved networks to ensure that users can
        // override network selection. See WifiNetworkSelector#setUserConnectChoice.
        // TODO(b/36067705): consider alternative designs to push filtering/selecting of
        // user connect choice networks to RecommendedNetworkEvaluator.
        if (score > status.getCandidateScore() || (score == status.getCandidateScore()
                && status.getCandidate() != null
                && scanResult.level > status.getCandidate().level)) {
            mWifiConfigManager.setNetworkCandidateScanResult(
                    network.networkId, scanResult, score);
        }

        // If the network is marked to use external scores, or is an open network with
        // curate saved open networks enabled, do not consider it for network selection.
        if (network.useExternalScores) {
            localLog("Network "   WifiNetworkSelector.toNetworkString(network)
                      " has external score.");
            continue;
        }

        if (connectableNetworks != null) {
            connectableNetworks.add(Pair.create(scanDetail,
                    mWifiConfigManager.getConfiguredNetwork(network.networkId)));
        }

        if (score > highestScore
                || (score == highestScore
                && scanResultCandidate != null
                && scanResult.level > scanResultCandidate.level)) {
            highestScore = score;
            scanResultCandidate = scanResult;
            mWifiConfigManager.setNetworkCandidateScanResult(
                    network.networkId, scanResultCandidate, highestScore);
            // Reload the network config with the updated info.
            candidate = mWifiConfigManager.getConfiguredNetwork(network.networkId);
        }
    }

    if (scoreHistory.length() > 0) {
        localLog("n"   scoreHistory.toString());
    }

    if (scanResultCandidate == null) {
        localLog("did not see any good candidates.");
    }
    return candidate;
}

忽略Passpoint和临时网络。

代码语言:javascript复制
/**
 * Ignore Passpoint and Ephemeral networks. They are configured networks,
 * but without being persisted to the storage. They are evaluated by
 * {@link PasspointNetworkEvaluator} and {@link ScoredNetworkEvaluator}
 * respectively.
 */
if (network.isPasspoint() || network.isEphemeral()) {
    continue;
}

判断当前网络是否允许加入网络选择,如果不允许,则过滤掉

代码语言:javascript复制
if (!status.isNetworkEnabled()) {
    continue;
} else if (network.BSSID != null &&  !network.BSSID.equals("any")
        && !network.BSSID.equals(scanResult.BSSID)) {
    // App has specified the only BSSID to connect for this
    // configuration. So only the matching ScanResult can be a candidate.
    localLog("Network "   WifiNetworkSelector.toNetworkString(network)
              " has specified BSSID "   network.BSSID   ". Skip "
              scanResult.BSSID);
    continue;

如果该网络被标记为使用外部得分,或者该网络是启用了保存已保存的开放网络的开放网络,则不要将其视为网络选择

代码语言:javascript复制
if (network.useExternalScores) {
    localLog("Network "   WifiNetworkSelector.toNetworkString(network)
              " has external score.");
    continue;
}
2.3、评分机制

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

代码语言:javascript复制
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(" ] ");
    // Calculate the RSSI score.
    int rssiSaturationThreshold = mScoringParams.getGoodRssi(scanResult.frequency);
    int rssi = scanResult.level < rssiSaturationThreshold ? scanResult.level
            : rssiSaturationThreshold;
    score  = (rssi   mRssiScoreOffset) * mRssiScoreSlope;
    sbuf.append(" RSSI score: ").append(score).append(",");

    // 5GHz band bonus.
    if (is5GHz) {
        score  = mBand5GHzAward;
        sbuf.append(" 5GHz bonus: ").append(mBand5GHzAward).append(",");
    }

    // Last user selection award.
    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(",");
        }
    }

    // Same network award.
    if (currentNetwork != null
            && (network.networkId == currentNetwork.networkId
            //TODO(b/36788683): re-enable linked configuration check
            /* || network.isLinked(currentNetwork) */)) {
        score  = mSameNetworkAward;
        sbuf.append(" Same network bonus: ").append(mSameNetworkAward).append(",");

        // When firmware roaming is supported, equivalent BSSIDs (the ones under the
        // same network as the currently connected one) get the same BSSID award.
        if (mConnectivityHelper.isFirmwareRoamingSupported()
                && currentBssid != null && !currentBssid.equals(scanResult.BSSID)) {
            score  = mSameBssidAward;
            sbuf.append(" Equivalent BSSID bonus: ").append(mSameBssidAward).append(",");
        }
    }

    // Same BSSID award.
    if (currentBssid != null && currentBssid.equals(scanResult.BSSID)) {
        score  = mSameBssidAward;
        sbuf.append(" Same BSSID bonus: ").append(mSameBssidAward).append(",");
    }

    // Security award.
    if (!WifiConfigurationUtil.isConfigForOpenNetwork(network)) {
        score  = mSecurityAward;
        sbuf.append(" Secure network bonus: ").append(mSecurityAward).append(",");
    }

    sbuf.append(" ## Total score: ").append(score).append("n");

    return score;
}
2.3.1、RSSI
代码语言:javascript复制
int rssiSaturationThreshold = mScoringParams.getGoodRssi(scanResult.frequency);
int rssi = scanResult.level < rssiSaturationThreshold ? scanResult.level
        : rssiSaturationThreshold;
score  = (rssi   mRssiScoreOffset) * mRssiScoreSlope;
2.3.2、5G

如果频率是5G,会加分

代码语言:javascript复制
if (is5GHz) {
    score  = mBand5GHzAward;
    sbuf.append(" 5GHz bonus: ").append(mBand5GHzAward).append(",");
}
2.3.3、Last user selection award

最后一个使用的网络会加分

代码语言:javascript复制
// Last user selection award.
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(",");
    }
}
2.3.4、Same network award

相同的networkId会加分,相同的networkId且支持漫游也会加分

代码语言:javascript复制
// Same network award.
if (currentNetwork != null
        && (network.networkId == currentNetwork.networkId
        //TODO(b/36788683): re-enable linked configuration check
        /* || network.isLinked(currentNetwork) */)) {
    score  = mSameNetworkAward;
    sbuf.append(" Same network bonus: ").append(mSameNetworkAward).append(",");

    // When firmware roaming is supported, equivalent BSSIDs (the ones under the
    // same network as the currently connected one) get the same BSSID award.
    if (mConnectivityHelper.isFirmwareRoamingSupported()
            && currentBssid != null && !currentBssid.equals(scanResult.BSSID)) {
        score  = mSameBssidAward;
        sbuf.append(" Equivalent BSSID bonus: ").append(mSameBssidAward).append(",");
    }
}
2.3.5、Same BSSID award

相同的BSSID会加分

代码语言:javascript复制
// Same BSSID award.
if (currentBssid != null && currentBssid.equals(scanResult.BSSID)) {
    score  = mSameBssidAward;
    sbuf.append(" Same BSSID bonus: ").append(mSameBssidAward).append(",");
}
2.3.6、Security award

不是开放的网络会加分

代码语言:javascript复制
// Security award.
if (!WifiConfigurationUtil.isConfigForOpenNetwork(network)) {
    score  = mSecurityAward;
    sbuf.append(" Secure network bonus: ").append(mSecurityAward).append(",");
}

3、总结

SavedNetworkEvaluator评分几大要素: rssi 5G Last user selection award Same network award 支持漫游 Same BSSID award Security award

0 人点赞