Android WiFi连接断开流程

本文简单介绍下WiFi连接与断开流程,参考源码Android P。

一、WiFi连接流程

WifiManager提供了两个connect接口,一个是connect(WifiConfiguration config, ActionListener listener)一个是connect(int networkId, ActionListener listener),也就是添加一个新的网络或者是连接一个已经保存的网络。

    /**
     * Connect to a network with the given configuration. The network also
     * gets added to the list of configured networks for the foreground user.
     *
     * For a new network, this function is used instead of a
     * sequence of addNetwork(), enableNetwork(), and reconnect()
     *
     * @param config the set of variables that describe the configuration,
     *            contained in a {@link WifiConfiguration} object.
     * @param listener for callbacks on success or failure. Can be null.
     * @throws IllegalStateException if the WifiManager instance needs to be
     * initialized again
     *
     * @hide
     */
    @SystemApi
    public void connect(WifiConfiguration config, ActionListener listener) {
        if (config == null) throw new IllegalArgumentException("config cannot be null");
        // Use INVALID_NETWORK_ID for arg1 when passing a config object
        // arg1 is used to pass network id when the network already exists
        getChannel().sendMessage(CONNECT_NETWORK, WifiConfiguration.INVALID_NETWORK_ID,
                putListener(listener), config);
    }

    /**
     * Connect to a network with the given networkId.
     *
     * This function is used instead of a enableNetwork() and reconnect()
     *
     * @param networkId the ID of the network as returned by {@link #addNetwork} or {@link
     *        getConfiguredNetworks}.
     * @param listener for callbacks on success or failure. Can be null.
     * @throws IllegalStateException if the WifiManager instance needs to be
     * initialized again
     * @hide
     */
    public void connect(int networkId, ActionListener listener) {
        if (networkId < 0) throw new IllegalArgumentException("Network id cannot be negative");
        getChannel().sendMessage(CONNECT_NETWORK, networkId, putListener(listener));
    }

都是发送消息CONNECT_NETWORK,WifiServiceImpl中会继续向WifiStateMachine发送消息,

	case WifiManager.CONNECT_NETWORK: {
	    if (checkChangePermissionAndReplyIfNotAuthorized(
	            msg, WifiManager.CONNECT_NETWORK_FAILED)) {
	        WifiConfiguration config = (WifiConfiguration) msg.obj;
	        int networkId = msg.arg1;
	        Slog.d(TAG, "CONNECT "
	                + " nid=" + Integer.toString(networkId)
	                + " config=" + config
	                + " uid=" + msg.sendingUid
	                + " name="
	                + mContext.getPackageManager().getNameForUid(msg.sendingUid));
	        if (config != null) {
	            /* Command is forwarded to state machine */
	            mWifiStateMachine.sendMessage(Message.obtain(msg));
	        } else if (config == null
	                && networkId != WifiConfiguration.INVALID_NETWORK_ID) {
	                mWifiStateMachine.sendMessage(Message.obtain(msg));
	        } else {
	                Slog.e(TAG, "ClientHandler.handleMessage ignoring invalid msg=" + msg);
	                replyFailed(msg, WifiManager.CONNECT_NETWORK_FAILED,
	                            WifiManager.INVALID_ARGS);
	        }
	    }
	    break;
	}

在WifiStateMachine ConnectModeState状态中处理CONNECT_NETWORK,

	case WifiManager.CONNECT_NETWORK:
	    /**
	     * The connect message can contain a network id passed as arg1 on message or
	     * or a config passed as obj on message.
	     * For a new network, a config is passed to create and connect.
	     * For an existing network, a network id is passed
	     */
	     netId = message.arg1;
	     config = (WifiConfiguration) message.obj;
	     boolean hasCredentialChanged = false;
	     // New network addition.
	     if (config != null) {  //传入的config为null,不会进入
	         result = mWifiConfigManager.addOrUpdateNetwork(config, message.sendingUid);//添加网络配置
	         if (!result.isSuccess()) {
	             loge("CONNECT_NETWORK adding/updating config=" + config + " failed");
	             messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
	             replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
	                        WifiManager.ERROR);
	             break;
	         }
	             netId = result.getNetworkId();
	             hasCredentialChanged = result.hasCredentialChanged();
	     }
	     if (!connectToUserSelectNetwork(
	                netId, message.sendingUid, hasCredentialChanged)) {
	         messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
	         replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
	                WifiManager.NOT_AUTHORIZED);
	         break;
	     }
	     mWifiMetrics.logStaEvent(StaEvent.TYPE_CONNECT_NETWORK, config);
	            broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_SAVED, config);
	     replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
	     break;

此后将继续调用connectToUserSelectNetwork,去连接用户点击去连接的网络。

    /**
     * Initiates connection to a network specified by the user/app. This method checks if the
     * requesting app holds the NETWORK_SETTINGS permission.
     *
     * @param netId Id network to initiate connection.
     * @param uid UID of the app requesting the connection.
     * @param forceReconnect Whether to force a connection even if we're connected to the same
     *                       network currently.
     */
    private boolean connectToUserSelectNetwork(int netId, int uid, boolean forceReconnect) {
        logd("connectToUserSelectNetwork netId " + netId + ", uid " + uid
                + ", forceReconnect = " + forceReconnect);
        if (mWifiConfigManager.getConfiguredNetwork(netId) == null) {
            loge("connectToUserSelectNetwork Invalid network Id=" + netId);
            return false;
        }
        if (!mWifiConfigManager.enableNetwork(netId, true, uid)
                || !mWifiConfigManager.updateLastConnectUid(netId, uid)) {
            logi("connectToUserSelectNetwork Allowing uid " + uid
                    + " with insufficient permissions to connect=" + netId);
        } else if (mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)) {
            // Note user connect choice here, so that it will be considered in the next network
            // selection.
            mWifiConnectivityManager.setUserConnectChoice(netId);
        }
        if (!forceReconnect && mWifiInfo.getNetworkId() == netId) {
            // We're already connected to the user specified network, don't trigger a
            // reconnection unless it was forced.
            logi("connectToUserSelectNetwork already connecting/connected=" + netId);
        } else {
            mWifiConnectivityManager.prepareForForcedConnection(netId);
            startConnectToNetwork(netId, uid, SUPPLICANT_BSSID_ANY);
        }
        return true;
    }

    /**
     * Automatically connect to the network specified
     *
     * @param networkId ID of the network to connect to
     * @param uid UID of the app triggering the connection.
     * @param bssid BSSID of the network
     */
    public void startConnectToNetwork(int networkId, int uid, String bssid) {
        sendMessage(CMD_START_CONNECT, networkId, uid, bssid);
    }

走到startConnectToNetwork()方法中,CMD_START_CONNECT消息再一次在ConnectModeState中处理。

	case CMD_START_CONNECT:
	/* connect command coming from auto-join */
	netId = message.arg1;
	int uid = message.arg2;
	bssid = (String) message.obj;
	
	synchronized (mWifiReqCountLock) {
	    if (!hasConnectionRequests()) {
	        if (mNetworkAgent == null) {
	            loge("CMD_START_CONNECT but no requests and not connected,"
	                    + " bailing");
	            break;
	        } else if (!mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)) {
	            loge("CMD_START_CONNECT but no requests and connected, but app "
	                    + "does not have sufficient permissions, bailing");
	            break;
	        }
	    }
	}
	
	config = mWifiConfigManager.getConfiguredNetworkWithoutMasking(netId);
	logd("CMD_START_CONNECT sup state "
	        + mSupplicantStateTracker.getSupplicantStateName()
	        + " my state " + getCurrentState().getName()
	        + " nid=" + Integer.toString(netId)
	        + " roam=" + Boolean.toString(mIsAutoRoaming));
	if (config == null) {
	    loge("CMD_START_CONNECT and no config, bail out...");
	    break;
	}
	mTargetNetworkId = netId;
	setTargetBssid(config, bssid);
	
	if (mEnableConnectedMacRandomization.get()) {
	    configureRandomizedMacAddress(config);
	}
	
	String currentMacAddress = mWifiNative.getMacAddress(mInterfaceName);
	mWifiInfo.setMacAddress(currentMacAddress);
	Log.i(TAG, "Connecting with " + currentMacAddress + " as the mac address");
	
	reportConnectionAttemptStart(config, mTargetRoamBSSID,
	        WifiMetricsProto.ConnectionEvent.ROAM_UNRELATED);
	if (mWifiNative.connectToNetwork(mInterfaceName, config)) {//启动连接
	    mWifiMetrics.logStaEvent(StaEvent.TYPE_CMD_START_CONNECT, config);
	    lastConnectAttemptTimestamp = mClock.getWallClockMillis();
	    targetWificonfiguration = config;
	    mIsAutoRoaming = false;
	    if (getCurrentState() != mDisconnectedState) {
	        transitionTo(mDisconnectingState);
	    }
	} else {//无法启动连接
	    loge("CMD_START_CONNECT Failed to start connection to network " + config);
	    reportConnectionAttemptEnd(
	            WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED,
	            WifiMetricsProto.ConnectionEvent.HLF_NONE);
	    replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
	            WifiManager.ERROR);
	    break;
	}
	break;

判断中的启动连接是调的WifiNative的connectToNetwork()方法,该方法提供的网络配置添加到wpa_supplicant并启动它的连接。

    /**
     * Add the provided network configuration to wpa_supplicant and initiate connection to it.
     * This method does the following:
     * 1. Abort any ongoing scan to unblock the connection request.
     * 2. Remove any existing network in wpa_supplicant(This implicitly triggers disconnect).
     * 3. Add a new network to wpa_supplicant.
     * 4. Save the provided configuration to wpa_supplicant.
     * 5. Select the new network in wpa_supplicant.
     * 6. Triggers reconnect command to wpa_supplicant.
     *
     * @param ifaceName Name of the interface.
     * @param configuration WifiConfiguration parameters for the provided network.
     * @return {@code true} if it succeeds, {@code false} otherwise
     */
    public boolean connectToNetwork(@NonNull String ifaceName, WifiConfiguration configuration) {
        // Abort ongoing scan before connect() to unblock connection request.
        mWificondControl.abortScan(ifaceName);//中止正在进行的单次扫描。
        return mSupplicantStaIfaceHal.connectToNetwork(ifaceName, configuration);//添加网络配置
    }

 SupplicantStaIfaceHal中继续调用connectToNetwork,在其中调用select()。

    /**
     * Add the provided network configuration to wpa_supplicant and initiate connection to it.
     * This method does the following:
     * 1. If |config| is different to the current supplicant network, removes all supplicant
     * networks and saves |config|.
     * 2. Select the new network in wpa_supplicant.
     *
     * @param ifaceName Name of the interface.
     * @param config WifiConfiguration parameters for the provided network.
     * @return {@code true} if it succeeds, {@code false} otherwise
     */
    public boolean connectToNetwork(@NonNull String ifaceName, @NonNull WifiConfiguration config) {
        synchronized (mLock) {
            logd("connectToNetwork " + config.configKey());
            WifiConfiguration currentConfig = getCurrentNetworkLocalConfig(ifaceName);
            if (WifiConfigurationUtil.isSameNetwork(config, currentConfig)) {
                String networkSelectionBSSID = config.getNetworkSelectionStatus()
                        .getNetworkSelectionBSSID();
                String networkSelectionBSSIDCurrent =
                        currentConfig.getNetworkSelectionStatus().getNetworkSelectionBSSID();
                if (Objects.equals(networkSelectionBSSID, networkSelectionBSSIDCurrent)) {
                    logd("Network is already saved, will not trigger remove and add operation.");
                } else {
                    logd("Network is already saved, but need to update BSSID.");
                    if (!setCurrentNetworkBssid(
                            ifaceName,
                            config.getNetworkSelectionStatus().getNetworkSelectionBSSID())) {
                        loge("Failed to set current network BSSID.");
                        return false;
                    }
                    mCurrentNetworkLocalConfigs.put(ifaceName, new WifiConfiguration(config));
                }
            } else {
                mCurrentNetworkRemoteHandles.remove(ifaceName);
                mCurrentNetworkLocalConfigs.remove(ifaceName);
                if (!removeAllNetworks(ifaceName)) {
                    loge("Failed to remove existing networks");
                    return false;
                }
                Pair pair =
                        addNetworkAndSaveConfig(ifaceName, config);
                if (pair == null) {
                    loge("Failed to add/save network configuration: " + config.configKey());
                    return false;
                }
                mCurrentNetworkRemoteHandles.put(ifaceName, pair.first);
                mCurrentNetworkLocalConfigs.put(ifaceName, pair.second);
            }
            SupplicantStaNetworkHal networkHandle =
                    checkSupplicantStaNetworkAndLogFailure(ifaceName, "connectToNetwork");
            if (networkHandle == null || !networkHandle.select()) {
                loge("Failed to select network configuration: " + config.configKey());
                return false;
            }
            return true;
        }
    }

 在SupplicantStaNetworkHal中调用select触发与此网络的连接。

    /**
     * Trigger a connection to this network.
     *
     * @return true if it succeeds, false otherwise.
     */
    public boolean select() {
        synchronized (mLock) {
            final String methodStr = "select";
            if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
            try {
                SupplicantStatus status =  mISupplicantStaNetwork.select();
                return checkStatusAndLogFailure(status, methodStr);
            } catch (RemoteException e) {
                handleRemoteException(e, methodStr);
                return false;
            }
        }
    }

ISupplicantStaNetwork.hal中调用select。

  /**
   * Initiate connection to this network.
   *
   * @return status Status of the operation.
   *         Possible status codes:
   *         |SupplicantStatusCode.SUCCESS|,
   *         |SupplicantStatusCode.FAILURE_UNKNOWN|,
   *         |SupplicantStatusCode.FAILURE_NETWORK_INVALID|
   */
  select() generates (SupplicantStatus status);

 sta_network.cpp中调用select。

Return StaNetwork::select(select_cb _hidl_cb)
{
	return validateAndCall(
	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
	    &StaNetwork::selectInternal, _hidl_cb);
}

SupplicantStatus StaNetwork::selectInternal()
{
	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
	if (wpa_ssid->disabled == 2) {
		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
	}
	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
	wpa_s->scan_min_time.sec = 0;
	wpa_s->scan_min_time.usec = 0;
	wpa_supplicant_select_network(wpa_s, wpa_ssid);
	return {SupplicantStatusCode::SUCCESS, ""};
}

 wpa_supplicant.c中调用wpa_supplicant_select_network。

/**
 * wpa_supplicant_select_network - Attempt association with a network
 * @wpa_s: wpa_supplicant structure for a network interface
 * @ssid: wpa_ssid structure for a configured network or %NULL for any network
 */
void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s,
				   struct wpa_ssid *ssid)
{

	struct wpa_ssid *other_ssid;
	int disconnected = 0;

	if (ssid && ssid != wpa_s->current_ssid && wpa_s->current_ssid) {
		if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
			wpa_s->own_disconnect_req = 1;
		wpa_supplicant_deauthenticate(
			wpa_s, WLAN_REASON_DEAUTH_LEAVING);
		disconnected = 1;
	}

	if (ssid)
		wpas_clear_temp_disabled(wpa_s, ssid, 1);

	/*
	 * Mark all other networks disabled or mark all networks enabled if no
	 * network specified.
	 */
	for (other_ssid = wpa_s->conf->ssid; other_ssid;
	     other_ssid = other_ssid->next) {
		int was_disabled = other_ssid->disabled;
		if (was_disabled == 2)
			continue; /* do not change persistent P2P group data */

		other_ssid->disabled = ssid ? (ssid->id != other_ssid->id) : 0;
		if (was_disabled && !other_ssid->disabled)
			wpas_clear_temp_disabled(wpa_s, other_ssid, 0);

		if (was_disabled != other_ssid->disabled)
			wpas_notify_network_enabled_changed(wpa_s, other_ssid);
	}

	if (ssid && ssid == wpa_s->current_ssid && wpa_s->current_ssid &&
	    wpa_s->wpa_state >= WPA_AUTHENTICATING) {
		/* We are already associated with the selected network */
		wpa_printf(MSG_DEBUG, "Already associated with the "
			   "selected network - do nothing");
		return;
	}

	if (ssid) {
		wpa_s->current_ssid = ssid;
		eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
		wpa_s->connect_without_scan =
			(ssid->mode == WPAS_MODE_MESH) ? ssid : NULL;

		/*
		 * Don't optimize next scan freqs since a new ESS has been
		 * selected.
		 */
		os_free(wpa_s->next_scan_freqs);
		wpa_s->next_scan_freqs = NULL;
	} else {
		wpa_s->connect_without_scan = NULL;
	}

	wpa_s->disconnected = 0;
	wpa_s->reassociate = 1;

	if (wpa_s->connect_without_scan ||
	    wpa_supplicant_fast_associate(wpa_s) != 1) {
		wpa_s->scan_req = NORMAL_SCAN_REQ;
		wpas_scan_reset_sched_scan(wpa_s);
		wpa_supplicant_req_scan(wpa_s, 0, disconnected ? 100000 : 0);
	}

	if (ssid)
		wpas_notify_network_selected(wpa_s, ssid);
}

二、WiFi断开流程

WifiManager中提供断开当前网络连接的接口disconnect()。

    /**
     * Disassociate from the currently active access point. This may result
     * in the asynchronous delivery of state change events.
     * @return {@code true} if the operation succeeded
     */
    public boolean disconnect() {
        try {
            mService.disconnect(mContext.getOpPackageName());
            return true;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

WifiServiceImpl中调用disconnect。

    /**
     * see {@link android.net.wifi.WifiManager#disconnect()}
     */
    @Override
    public void disconnect(String packageName) {
        if (enforceChangePermission(packageName) != MODE_ALLOWED) {
            return;
        }
        mLog.info("disconnect uid=%").c(Binder.getCallingUid()).flush();
        mWifiStateMachine.disconnectCommand();
    }

WifiStateMachine中发送消息CMD_DISCONNECT。

    /**
     * Disconnect from Access Point
     */
    public void disconnectCommand() {
        sendMessage(CMD_DISCONNECT);
    }

L2ConnectedState状态中则处理断开消息。

	case CMD_DISCONNECT:
	    mWifiMetrics.logStaEvent(StaEvent.TYPE_FRAMEWORK_DISCONNECT,
	            StaEvent.DISCONNECT_GENERIC);
	    mWifiNative.disconnect(mInterfaceName);
	    transitionTo(mDisconnectingState);
	    break;

 WifiNative

    /**
     * Trigger a disconnection from the currently connected network.
     *
     * @param ifaceName Name of the interface.
     * @return true if request is sent successfully, false otherwise.
     */
    public boolean disconnect(@NonNull String ifaceName) {
        return mSupplicantStaIfaceHal.disconnect(ifaceName);
    }

SupplicantStaIfaceHal

    /**
     * Trigger a disconnection from the currently connected network.
     *
     * @param ifaceName Name of the interface.
     * @return true if request is sent successfully, false otherwise.
     */
    public boolean disconnect(@NonNull String ifaceName) {
        synchronized (mLock) {
            final String methodStr = "disconnect";
            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
            if (iface == null) return false;
            try {
                SupplicantStatus status = iface.disconnect();
                return checkStatusAndLogFailure(status, methodStr);
            } catch (RemoteException e) {
                handleRemoteException(e, methodStr);
                return false;
            }
        }
    }

ISupplicantStaIface 

  /**
   * Disconnect from the current active network.
   *
   * @return status Status of the operation.
   *         Possible status codes:
   *         |SupplicantStatusCode.SUCCESS|,
   *         |SupplicantStatusCode.FAILURE_UNKNOWN|,
   *         |SupplicantStatusCode.FAILURE_IFACE_INVALID|,
   *         |SupplicantStatusCode.FAILURE_IFACE_DISABLED|
   */
  disconnect() generates (SupplicantStatus status);

 sta_iface

Return StaIface::disconnect(disconnect_cb _hidl_cb)
{
	return validateAndCall(
	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
	    &StaIface::disconnectInternal, _hidl_cb);
}

SupplicantStatus StaIface::disconnectInternal()
{
	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
		return {SupplicantStatusCode::FAILURE_IFACE_DISABLED, ""};
	}
	wpas_request_disconnection(wpa_s);
	return {SupplicantStatusCode::SUCCESS, ""};
}

 wpa_supplicant

/**
 * wpas_request_disconnection - Request disconnection
 * @wpa_s: Pointer to the network interface
 *
 * This function is used to request disconnection from the currently connected
 * network. This will stop any ongoing scans and initiate deauthentication.
 */
void wpas_request_disconnection(struct wpa_supplicant *wpa_s)
{
#ifdef CONFIG_SME
	wpa_s->sme.prev_bssid_set = 0;
#endif /* CONFIG_SME */
	wpa_s->reassociate = 0;
	wpa_s->disconnected = 1;
	wpa_supplicant_cancel_sched_scan(wpa_s);
	wpa_supplicant_cancel_scan(wpa_s);
	wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
	eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
}

你可能感兴趣的:(WiFi,android,wifi)