(一百九十三) Android Q 学习WiFi AP的临时禁用

1.临时禁用

WifiConfigManager

    /**
     * Helper method to mark a network temporarily disabled for network selection.
     */
    private void setNetworkSelectionTemporarilyDisabled(
            WifiConfiguration config, int disableReason) {
        NetworkSelectionStatus status = config.getNetworkSelectionStatus();
        status.setNetworkSelectionStatus(
                NetworkSelectionStatus.NETWORK_SELECTION_TEMPORARY_DISABLED);
        // Only need a valid time filled in for temporarily disabled networks.
        status.setDisableTime(mClock.getElapsedSinceBootMillis());
        status.setNetworkSelectionDisableReason(disableReason);
        if (mListener != null) {
            mListener.onSavedNetworkTemporarilyDisabled(config.networkId, disableReason);
        }
    }

可以看到临时禁用会回调一个listener,看下这个listener是哪里被初始化的

WifiConnectivityManager

        // Listen to WifiConfigManager network update events
        mConfigManager.setOnSavedNetworkUpdateListener(new OnSavedNetworkUpdateListener());

    private class OnSavedNetworkUpdateListener implements
            WifiConfigManager.OnSavedNetworkUpdateListener {
...
        @Override
        public void onSavedNetworkTemporarilyDisabled(int networkId, int disableReason) {
            if (disableReason == DISABLED_NO_INTERNET_TEMPORARY) return;
            mConnectivityHelper.removeNetworkIfCurrent(networkId);
        }
        @Override
        public void onSavedNetworkPermanentlyDisabled(int networkId, int disableReason) {
            // For DISABLED_NO_INTERNET_PERMANENT we do not need to remove the network
            // because supplicant won't be trying to reconnect. If this is due to a
            // preventAutomaticReconnect request from ConnectivityService, that service
            // will disconnect as appropriate.
            if (disableReason == DISABLED_NO_INTERNET_PERMANENT) return;
            mConnectivityHelper.removeNetworkIfCurrent(networkId);
            updatePnoScan();
        }
        private void updatePnoScan() {
            // Update the PNO scan network list when screen is off. Here we
            // rely on startConnectivityScan() to perform all the checks and clean up.
            if (!mScreenOn) {
                localLog("Saved networks updated");
                startConnectivityScan(false);
            }
        }
    }

看下ConnectivityHelper的逻辑

    /**
     * Remove the request |networkId| from supplicant if it's the current network,
     * if the current configured network matches |networkId|.
     *
     * @param networkId network id of the network to be removed from supplicant.
     */
    public void removeNetworkIfCurrent(int networkId) {
        mWifiNative.removeNetworkIfCurrent(mWifiNative.getClientInterfaceName(), networkId);
    }

    /** Remove the request |networkId| from supplicant if it's the current network,
     * if the current configured network matches |networkId|.
     *
     * @param ifaceName Name of the interface.
     * @param networkId network id of the network to be removed from supplicant.
     */
    public void removeNetworkIfCurrent(@NonNull String ifaceName, int networkId) {
        mSupplicantStaIfaceHal.removeNetworkIfCurrent(ifaceName, networkId);
    }


    /**
     * Remove the request |networkId| from supplicant if it's the current network,
     * if the current configured network matches |networkId|.
     *
     * @param ifaceName Name of the interface.
     * @param networkId network id of the network to be removed from supplicant.
     */
    public void removeNetworkIfCurrent(@NonNull String ifaceName, int networkId) {
        synchronized (mLock) {
            if (getCurrentNetworkId(ifaceName) == networkId) {
                // Currently we only save 1 network in supplicant.
                removeAllNetworks(ifaceName);
            }
        }
    }

    /**
     * Remove all networks from supplicant
     *
     * @param ifaceName Name of the interface.
     */
    public boolean removeAllNetworks(@NonNull String ifaceName) {
        synchronized (mLock) {
            ArrayList<Integer> networks = listNetworks(ifaceName);
            if (networks == null) {
                Log.e(TAG, "removeAllNetworks failed, got null networks");
                return false;
            }
            for (int id : networks) {
                if (!removeNetwork(ifaceName, id)) {
                    Log.e(TAG, "removeAllNetworks failed to remove network: " + id);
                    return false;
                }
            }
            // Reset current network info.  Probably not needed once we add support to remove/reset
            // current network on receiving disconnection event from supplicant (b/32898136).
            mCurrentNetworkRemoteHandles.remove(ifaceName);
            mCurrentNetworkLocalConfigs.remove(ifaceName);
            return true;
        }
    }

大概意思是如果该网络被禁用掉了,那么同步通知到supplicant将该网络移除

2.临时禁用的意义

2.1 pno

   /**
     * Retrieves a list of all the saved networks before enabling disconnected/connected PNO.
     *
     * PNO network list sent to the firmware has limited size. If there are a lot of saved
     * networks, this list will be truncated and we might end up not sending the networks
     * with the highest chance of connecting to the firmware.
     * So, re-sort the network list based on the frequency of connection to those networks
     * and whether it was last seen in the scan results.
     *
     * @return list of networks in the order of priority.
     */
    public List<WifiScanner.PnoSettings.PnoNetwork> retrievePnoNetworkList() {
        List<WifiScanner.PnoSettings.PnoNetwork> pnoList = new ArrayList<>();
        List<WifiConfiguration> networks = new ArrayList<>(getInternalConfiguredNetworks());
        // Remove any permanently or temporarily disabled networks.
        Iterator<WifiConfiguration> iter = networks.iterator();
        while (iter.hasNext()) {
            WifiConfiguration config = iter.next();
            if (config.ephemeral || config.isPasspoint()
                    || config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()
                    || config.getNetworkSelectionStatus().isNetworkTemporaryDisabled()) {
                iter.remove();
            }
        }
...
            if (mVerboseLoggingEnabled) {
                Log.v(TAG, "retrievePnoNetworkList " + pnoNetwork.ssid + ":"
                        + Arrays.toString(pnoNetwork.frequencies));
            }
        }
        return pnoList;
    }

pno扫描会提前移除被禁用的

2.2 SavedNetworkEvaluator

    /**
     * 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.
     */
    @Override
    public WifiConfiguration evaluateNetworks(List<ScanDetail> scanDetails,
                    WifiConfiguration currentNetwork, String currentBssid, boolean connected,
                    boolean untrustedNetworkAllowed,
                    @NonNull OnConnectableListener onConnectableListener) {
        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 network, hence we calculate all
            // the scores and use the highest one as the ScanResult's score.
            // TODO(b/112196799): this has side effects, rather not do that in an evaluator
            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();
            // TODO (b/112196799): another side effect
            status.setSeenInLastQualifiedNetworkSelection(true);

            if (!status.isNetworkEnabled()) {
                continue;

已保存网络评估器会直接不考虑被禁用的网络

2.3  评分

看起来是WifiConfigManager里对扫描列表做排序用的

        /**
         * Returns an integer representing a score for each configuration. The scores are assigned
         * based on the status of the configuration. The scores are assigned according to the order:
         * Fully enabled network > Temporarily disabled network > Permanently disabled network.
         */
        private int getNetworkStatusScore(WifiConfiguration config) {
            if (config.getNetworkSelectionStatus().isNetworkEnabled()) {
                return ENABLED_NETWORK_SCORE;
            } else if (config.getNetworkSelectionStatus().isNetworkTemporaryDisabled()) {
                return TEMPORARY_DISABLED_NETWORK_SCORE;
            } else {
                return PERMANENTLY_DISABLED_NETWORK_SCORE;
            }
        }

3.临时禁用何时恢复

        /**
         * network was temporary disabled. Can be re-enabled after a time period expire
         */
        public static final int NETWORK_SELECTION_TEMPORARY_DISABLED  = 1;

从定义来看是过一段时间会重启启用,那这段逻辑是哪里的呢?

WifiConnectivityManager

    /**
     * Select the best network from the ones in range.
     *
     * @param scanDetails    List of ScanDetail for all the APs in range
     * @param bssidBlacklist Blacklisted BSSIDs
     * @param wifiInfo       Currently connected network
     * @param connected      True if the device is connected
     * @param disconnected   True if the device is disconnected
     * @param untrustedNetworkAllowed True if untrusted networks are allowed for connection
     * @return Configuration of the selected network, or Null if nothing
     */
    @Nullable
    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 all configured networks before initiating network selection.
        updateConfiguredNetworks();


    /**
     * Iterate thru the list of configured networks (includes all saved network configurations +
     * any ephemeral network configurations created for passpoint networks, suggestions, carrier
     * networks, etc) and do the following:
     * a) Try to re-enable any temporarily enabled networks (if the blacklist duration has expired).
     * b) Clear the {@link WifiConfiguration.NetworkSelectionStatus#getCandidate()} field for all
     * of them to identify networks that are present in the current scan result.
     * c) Log any disabled networks.
     */
    private void updateConfiguredNetworks() {
        List<WifiConfiguration> configuredNetworks = mWifiConfigManager.getConfiguredNetworks();
        if (configuredNetworks.size() == 0) {
            localLog("No configured networks.");
            return;
        }

        StringBuffer sbuf = new StringBuffer();
        for (WifiConfiguration network : configuredNetworks) {
            // If a configuration is temporarily disabled, re-enable it before trying
            // to connect to it.
            mWifiConfigManager.tryEnableNetwork(network.networkId);

下一次评分的时候会将临时禁用的恢复回来

    /**
     * Attempt to re-enable a network for network selection, if this network was either:
     * a) Previously temporarily disabled, but its disable timeout has expired, or
     * b) Previously disabled because of a user switch, but is now visible to the current
     * user.
     *
     * @param config configuration for the network to be re-enabled for network selection. The
     *               network corresponding to the config must be visible to the current user.
     * @return true if the network identified by {@param config} was re-enabled for qualified
     * network selection, false otherwise.
     */
    private boolean tryEnableNetwork(WifiConfiguration config) {
        NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
        if (networkStatus.isNetworkTemporaryDisabled()) {
            long timeDifferenceMs =
                    mClock.getElapsedSinceBootMillis() - networkStatus.getDisableTime();
            int disableReason = networkStatus.getNetworkSelectionDisableReason();
            long disableTimeoutMs = NETWORK_SELECTION_DISABLE_TIMEOUT_MS[disableReason];
            if (timeDifferenceMs >= disableTimeoutMs) {
                return updateNetworkSelectionStatus(
                        config, NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
            }
        } else if (networkStatus.isDisabledByReason(
                NetworkSelectionStatus.DISABLED_DUE_TO_USER_SWITCH)) {
            return updateNetworkSelectionStatus(
                    config, NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
        }
        return false;
    }

临时禁用有个超时机制

    /**
     * Network Selection disable timeout for each kind of error. After the timeout milliseconds,
     * enable the network again.
     * These are indexed using the disable reason constants defined in
     * {@link android.net.wifi.WifiConfiguration.NetworkSelectionStatus}.
     */
    @VisibleForTesting
    public static final int[] NETWORK_SELECTION_DISABLE_TIMEOUT_MS = {
            Integer.MAX_VALUE,  // threshold for NETWORK_SELECTION_ENABLE
            15 * 60 * 1000,     // threshold for DISABLED_BAD_LINK
            5 * 60 * 1000,      // threshold for DISABLED_ASSOCIATION_REJECTION
            5 * 60 * 1000,      // threshold for DISABLED_AUTHENTICATION_FAILURE
            5 * 60 * 1000,      // threshold for DISABLED_DHCP_FAILURE
            5 * 60 * 1000,      // threshold for DISABLED_DNS_FAILURE
            10 * 60 * 1000,     // threshold for DISABLED_NO_INTERNET_TEMPORARY
            0 * 60 * 1000,      // threshold for DISABLED_WPS_START
            Integer.MAX_VALUE,  // threshold for DISABLED_TLS_VERSION
            Integer.MAX_VALUE,  // threshold for DISABLED_AUTHENTICATION_NO_CREDENTIALS
            Integer.MAX_VALUE,  // threshold for DISABLED_NO_INTERNET_PERMANENT
            Integer.MAX_VALUE,  // threshold for DISABLED_BY_WIFI_MANAGER
            Integer.MAX_VALUE,  // threshold for DISABLED_BY_USER_SWITCH
            Integer.MAX_VALUE,  // threshold for DISABLED_BY_WRONG_PASSWORD
            Integer.MAX_VALUE   // threshold for DISABLED_AUTHENTICATION_NO_SUBSCRIBED
    };

3.总结

临时禁用设计的比较巧妙,主要是来避免接下来即刻的自动连接的,根据禁用原因设置了不同的超时时间,当超时时间到了,并且下次framework扫描到了该ap,该ap就会从临时禁用的状态恢复回来。

发布了198 篇原创文章 · 获赞 65 · 访问量 16万+

猜你喜欢

转载自blog.csdn.net/sinat_20059415/article/details/103534753