【安卓Framework学习】Wifi框架学习之连接与断开流程

系列文章目录

【安卓Framework学习】Wifi框架学习之核心类.
【安卓Framework学习】Wifi框架学习之开启与关闭流程.
【安卓Framework学习】Wifi框架学习之wifi状态机.
【安卓Framework学习】Wifi框架学习之扫描模式及扫描流程.
【安卓Framework学习】Wifi框架学习之热点评分机制.
【安卓Framework学习】安卓连接管理(ConnectivityService)之wifi连接及注册.


文章目录

  • 系列文章目录
  • 前言
  • 一、wifi连接流程
    • 1、wifi连接源码流程分析
    • 2、wifi连接流程图
  • 二、wifi断开流程
    • 1、wifi断开源码流程分析
    • 2、wifi断开流程图
  • 总结


前言

前几篇介绍了wifi框架中的状态机和wifi的开关流程,本篇将基于wifi的连接和断开功能分析其代码流程。由于连接和断开是wifi功能中用得最多也是最重要的功能之一,可以说前几篇都也为这一篇做了铺垫。本篇代码主要基于安卓11源码进行分析。


一、wifi连接流程

1、wifi连接源码流程分析

接上一篇【安卓Framework学习】Wifi框架学习之开启与关闭流程,从这一篇可以知道,在wifi被打开时,在ClientModeManager.ConnectModeState中会将ClientModeImpl的状态切换到DisconnectedState,如下代码。

	ClientModeManager.ConnectModeState
	private class ConnectModeState extends State {
	    @Override
	    public void enter() {
	        Log.d(TAG, "entering ConnectModeState");
	        mClientModeImpl.setOperationalMode(ClientModeImpl.CONNECT_MODE,
	                mClientInterfaceName);
	        mModeListener.onStarted();
	        updateConnectModeState(WifiManager.WIFI_STATE_ENABLED,
	                WifiManager.WIFI_STATE_ENABLING);
	        // Inform sar manager that wifi is Enabled
	        mSarManager.setClientWifiState(WifiManager.WIFI_STATE_ENABLED);
	    }
		/*省略其他代码*/
	}

可以看到,在ConnectModeState.enter中切换了ClientModeImpl的状态,转到ClientModeImpl.setOperationalMode方法中。

	public void setOperationalMode(int mode, String ifaceName) {
	   /*省略其他代码*/
	    mModeChange = true;
	    if (mode != CONNECT_MODE) {
	        /*省略其他代码*/
	    } else {
	        if (ifaceName != null) {
	            mInterfaceName = ifaceName;
	            updateInterfaceCapabilities(ifaceName);
	            transitionTo(mDisconnectedState);
	            mWifiScoreReport.setInterfaceName(ifaceName);
	        } else {
	            /*省略其他代码*/
	        }
	    }
	    sendMessageAtFrontOfQueue(CMD_SET_OPERATIONAL_MODE);
	}

由于已经是开启状态,wifi的接口名是有的,所以ifaceName不为空,可以看到在setOperationalMode方法中将状态切换到DisconnectedState了。接下来将从应用调用连接某个指定AP开始分析。

在开发应用的过程中,都会用到WifiManager,下面从WifiManager调用连接wifi开始。代码如下。

	public boolean enableNetwork(int netId, boolean attemptConnect) {
	    try {
	        return mService.enableNetwork(netId, attemptConnect, mContext.getOpPackageName());
	    } catch (RemoteException e) {
	        throw e.rethrowFromSystemServer();
	    }
	}

这个方法,在安卓10的时候被弃用了,由于本人项目中使用的是安卓9,并且后续内部连接的过程都相差无几,所以这里还是从这个方法入手。转入到WifiServiceImpl中的同名方法。

	public boolean enableNetwork(int netId, boolean disableOthers, String packageName) {
	    /*省略其他代码*/
	    if (!isTargetSdkLessThanQOrPrivileged(packageName, Binder.getCallingPid(), Binder.getCallingUid())) {
	        mLog.info("enableNetwork not allowed for uid=%")
	                .c(Binder.getCallingUid()).flush();
	        return false;
	    }
	    int callingUid = Binder.getCallingUid();
	   /*省略其他代码*/
	    if (disableOthers) {
	        return triggerConnectAndReturnStatus(netId, callingUid);
	    } else {
	        return mWifiThreadRunner.call(
	                () -> mWifiConfigManager.enableNetwork(netId, false, callingUid, packageName),
	                false);
	    }
	}

这个方法的disableOthers表示是否需要让其他的热点配置信息不可用,一般情况下在使用的时候都会设置为true,表示只能用指定的热点配置信息。转入到triggerConnectAndReturnStatus中。

	private boolean triggerConnectAndReturnStatus(int netId, int callingUid) {
	    /*省略其他代码*/
	    mClientModeImpl.connect(null, netId, new Binder(), connectListener,
	            connectListener.hashCode(), callingUid);
	    // now wait for response.
	    /*省略其他代码*/
	}

直接调用到了ClientModeImpl.connect方法中,转到connect方法。

	public void connect(WifiConfiguration config, int netId, @Nullable IBinder binder,
	        @Nullable IActionListener callback, int callbackIdentifier, int callingUid) {
	    mWifiInjector.getWifiThreadRunner().post(() -> {
	       /*省略其他代码*/
	        NetworkUpdateResult result = null;
	        if (config != null) {
	            /*省略其他代码*/
	        } else {
	            if (mWifiConfigManager.getConfiguredNetwork(netId) == null) {
	                loge("connectNetwork Invalid network Id=" + netId);
	                sendActionListenerFailure(callbackIdentifier, WifiManager.ERROR);
	                return;
	            }
	            result = new NetworkUpdateResult(netId);
	        }
	        /*省略其他代码*/
	        Message message =
	                obtainMessage(CMD_CONNECT_NETWORK, -1, callbackIdentifier, result);
	        message.sendingUid = callingUid;
	        sendMessage(message);
	    });
	}

connect方法中,判断了需要连接的热点配置信息是否已经保存,然后就对状态机发送了CMD_CONNECT_NETWORK消息。在本篇开始的时候提到了,在开启wifi后,ClientModeImpl处于DisconnectedState状态。转入DisconnectedState状态,这里不再贴DisconnectedState.processMessage方法的源码,因为DisconnectedState状态没有对CMD_CONNECT_NETWORK消息进行处理,而是继续交给了其父状态ConnectModeState处理了,ConnectModeState.processMessage源码如下。

	public boolean processMessage(Message message) {
		/*省略其他代码*/
		switch (message.what) {
		/*省略其他代码*/
			case CMD_CONNECT_NETWORK:
			    callbackIdentifier = message.arg2;
			    result = (NetworkUpdateResult) message.obj;
			    netId = result.getNetworkId();
			    connectToUserSelectNetwork(
			            netId, message.sendingUid, result.hasCredentialChanged());
			    mWifiMetrics.logStaEvent(
			            StaEvent.TYPE_CONNECT_NETWORK,
			            mWifiConfigManager.getConfiguredNetwork(netId));
			    sendActionListenerSuccess(callbackIdentifier);
			    break;
			/*省略其他代码*/
			default:
			    handleStatus = NOT_HANDLED;
			    break;
		}
		if (handleStatus == HANDLED) {
                logStateAndMessage(message, this);
            }
        return handleStatus;
	}

可以看到,ConnectModeState在处理CMD_CONNECT_NETWORK消息时,调用了connectToUserSelectNetwork,在看connectToUserSelectNetwork方法。

	private void connectToUserSelectNetwork(int netId, int uid, boolean forceReconnect) {
	    /*省略其他代码*/
	    if (!forceReconnect && (mLastNetworkId == netId || mTargetNetworkId == netId)) {
	        /*省略其他代码*/
	    } else {
	        mWifiConnectivityManager.prepareForForcedConnection(netId);
	        if (uid == Process.SYSTEM_UID) {
	            mWifiMetrics.setNominatorForNetwork(netId,
	                    WifiMetricsProto.ConnectionEvent.NOMINATOR_MANUAL);
	        }
	        startConnectToNetwork(netId, uid, SUPPLICANT_BSSID_ANY);
	    }
	}

由于我们分析的是刚打开wifi然后去链接,所以会进入到下面的分支,直接看startConnectToNetwork方法。

	public void startConnectToNetwork(int networkId, int uid, String bssid) {
	    sendMessage(CMD_START_CONNECT, networkId, uid, bssid);
	}

由于到这里还没有切换状态,所以还是ConnectModeState处理CMD_START_CONNECT消息。

	public boolean processMessage(Message message) {
		/*省略其他代码*/
		switch (message.what) {
		/*省略其他代码*/
			case CMD_START_CONNECT:
			    /* connect command coming from auto-join */
			    netId = message.arg1;
			    int uid = message.arg2;
			    bssid = (String) message.obj;
			    mSentHLPs = false;
			    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 "
			            + " 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;
			    // Update scorecard while there is still state from existing connection
			    int scanRssi = mWifiConfigManager.findScanRssi(netId,
			            mWifiHealthMonitor.getScanRssiValidTimeMs());
			    mWifiScoreCard.noteConnectionAttempt(mWifiInfo, scanRssi, config.SSID);
			    mBssidBlocklistMonitor.updateFirmwareRoamingConfiguration(config.SSID);
			    updateWifiConfigOnStartConnection(config, bssid);
			    reportConnectionAttemptStart(config, mTargetBssid,
			            WifiMetricsProto.ConnectionEvent.ROAM_UNRELATED);
			    String currentMacAddress = mWifiNative.getMacAddress(mInterfaceName);
			    mWifiInfo.setMacAddress(currentMacAddress);
			    Log.i(TAG, "Connecting with " + currentMacAddress + " as the mac address");
			    mTargetWifiConfiguration = config;
			    /* Check for FILS configuration again after updating the config */
			    if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.FILS_SHA256)
			            || config.allowedKeyManagement.get(
			            WifiConfiguration.KeyMgmt.FILS_SHA384)) {
			        boolean isIpClientStarted = startIpClient(config, true);
			        if (isIpClientStarted) {
			            mIpClientWithPreConnection = true;
			            break;
			        }
			    }
			    connectToNetwork(config);
			    break;
			/*省略其他代码*/
			default:
			    handleStatus = NOT_HANDLED;
			    break;
		}
		if (handleStatus == HANDLED) {
                logStateAndMessage(message, this);
            }
        return handleStatus;
	}

前面都是记录和配置校验连接信息,其实真正连接的就是最后调用的connectToNetwork方法。

	void connectToNetwork(WifiConfiguration config) {
	    if ((config != null) && mWifiNative.connectToNetwork(mInterfaceName, config)) {
	        mWifiInjector.getWifiLastResortWatchdog().noteStartConnectTime();
	        mWifiMetrics.logStaEvent(StaEvent.TYPE_CMD_START_CONNECT, config);
	        mLastConnectAttemptTimestamp = mClock.getWallClockMillis();
	        mIsAutoRoaming = false;
	        if (getCurrentState() != mDisconnectedState) {
	            transitionTo(mDisconnectingState);
	        }
	    } else {
	        loge("CMD_START_CONNECT Failed to start connection to network " + config);
			mTargetWifiConfiguration = null;
			stopIpClient();
			reportConnectionAttemptEnd(
			        WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED,
			        WifiMetricsProto.ConnectionEvent.HLF_NONE,
			        WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN);
	    }
	}

由于在连接时,传入的参数肯定不会是空,所以连接是否成功就完全取决于WifiNative.connectToNetwork方法是否成功,转入到WifiNative中。

	public boolean connectToNetwork(@NonNull String ifaceName, WifiConfiguration configuration) {
	    // Abort ongoing scan before connect() to unblock connection request.
	    mWifiCondManager.abortScan(ifaceName);
	    return mSupplicantStaIfaceHal.connectToNetwork(ifaceName, configuration);
	}

这时候到了WifiNative中基本上是属于与HAl层进行交互了,但这里还是继续分析下去,进入到SupplicantStaIfaceHal中。

public boolean connectToNetwork(@NonNull String ifaceName, @NonNull WifiConfiguration config) {
    synchronized (mLock) {
        WifiConfiguration currentConfig = getCurrentNetworkLocalConfig(ifaceName);
        if (WifiConfigurationUtil.isSameNetwork(config, currentConfig)) {
          /*省略其他代码*/  
        } else {
            mCurrentNetworkRemoteHandles.remove(ifaceName);
            mCurrentNetworkLocalConfigs.remove(ifaceName);
            if (!removeAllNetworks(ifaceName)) {
                loge("Failed to remove existing networks");
                return false;
            }
            Pair<SupplicantStaNetworkHal, WifiConfiguration> pair =
                    addNetworkAndSaveConfig(ifaceName, config);
            if (pair == null) {
                loge("Failed to add/save network configuration: " + config.getKey());
                return false;
            }
            mCurrentNetworkRemoteHandles.put(ifaceName, pair.first);
            mCurrentNetworkLocalConfigs.put(ifaceName, pair.second);
        }
        SupplicantStaNetworkHal networkHandle =
                checkSupplicantStaNetworkAndLogFailure(ifaceName, "connectToNetwork");
        if (networkHandle == null) {
            loge("No valid remote network handle for network configuration: "
                    + config.getKey());
            return false;
        }
        PmkCacheStoreData pmkData = mPmkCacheEntries.get(config.networkId);
        if (pmkData != null
                && !WifiConfigurationUtil.isConfigForPskNetwork(config)
                && pmkData.expirationTimeInSec > mClock.getElapsedSinceBootMillis() / 1000) {
            logi("Set PMK cache for config id " + config.networkId);
            if (networkHandle.setPmkCache(pmkData.data)) {
                mWifiMetrics.setConnectionPmkCache(true);
            }
        }
        if (!networkHandle.select()) {
            loge("Failed to select network configuration: " + config.getKey());
            return false;
        }
        return true;
    }
}

上述代码中,不针对每个方法进行分析,有些会带过,需要的可自行查看。首先传进来的连接配置信息肯定是没连接过的,所以直接走到下面的分支。开始会移除掉本地缓存Iface对应的SupplicantStaNetworkHal对象和Iface对应的的WifiConfiguration对象,然后通过addNetworkAndSaveConfig方法获得新的SupplicantStaNetworkHal对象并和WifiConfiguration生成一个Pair,再看addNetworkAndSaveConfig.

	private Pair<SupplicantStaNetworkHal, WifiConfiguration>
	        addNetworkAndSaveConfig(@NonNull String ifaceName, WifiConfiguration config) {
	    synchronized (mLock) {
	        logi("addSupplicantStaNetwork via HIDL");
	        if (config == null) {
	            loge("Cannot add NULL network!");
	            return null;
	        }
	        SupplicantStaNetworkHal network = addNetwork(ifaceName);
	        if (network == null) {
	            loge("Failed to add a network!");
	            return null;
	        }
	        boolean saveSuccess = false;
	        try {
	            saveSuccess = network.saveWifiConfiguration(config);
	        } catch (IllegalArgumentException e) {
	            Log.e(TAG, "Exception while saving config params: " + config, e);
	        }
	        /*省略其他代码*/ 
	        return new Pair(network, new WifiConfiguration(config));
	    }
	}

上述代码中addNetwork其实就是从java进程缓存中获取一个SupplicantStaNetworkHal对象,然后通过saveWifiConfiguration方法将要连接的配置信息通过HIDL设置保存到HAL层中。再次看connectToNetwork方法。在获得SupplicantStaNetworkHal 对象后,并且此时也保存了要连接的配置信息,然后调用了SupplicantStaNetworkHal .select方法通知HAL层通过保存的配置信息去连接对应的热点。后续实际连接的操作就都在HAL层及驱动层了,Framework中java部分就到这里结束了,后续等HAL层通过调用java层的回调函数通知安卓系统具体的连接过程。现在看是如何将连接过程的广播发送上来的。部分代码不做往上分析,直接先用结果,在开启wifi的时候,WifiNative中会调用SupplicantStaIfaceHal.setupIface方法。

	public boolean setupIface(@NonNull String ifaceName) {
	    final String methodStr = "setupIface";
	    if (checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr) != null) return false;
	    ISupplicantIface ifaceHwBinder;
	    if (isV1_1()) {
	        ifaceHwBinder = addIfaceV1_1(ifaceName);
	    } else {
	        ifaceHwBinder = getIfaceV1_0(ifaceName);
	    }
	    if (ifaceHwBinder == null) {
	        Log.e(TAG, "setupIface got null iface");
	        return false;
	    }
	    try {
	        ISupplicantStaIface iface = setupStaIface(ifaceName, ifaceHwBinder);
	        mISupplicantStaIfaces.put(ifaceName, iface);
	    } catch (RemoteException e) {
	        loge("setup StaIface failed: " + e.toString());
	        return false;
	    }
	    return true;
	}

方法中先会用addIfaceV1_1addIfaceV1_0方法来获得一个ISupplicantIface的Binder对象,至于这里怎么获得的在本篇不做详细分析,感兴趣的可以自行研究,然后将继续调用setupStaIface方法。

	private ISupplicantStaIface setupStaIface(@NonNull String ifaceName,
	        @NonNull ISupplicantIface ifaceHwBinder) throws RemoteException {
	    /* Prepare base type for later cast. */
	    ISupplicantStaIface iface = getStaIfaceMockable(ifaceHwBinder);
	    /* try newer version first. */
	    if (trySetupStaIfaceV1_1(ifaceName, iface)) {
	        logd("Newer HAL is found, skip V1_0 remaining init flow.");
	        return iface;
	    }
	    SupplicantStaIfaceHalCallback callback = new SupplicantStaIfaceHalCallback(ifaceName);
	    if (!registerCallback(iface, callback)) {
	        throw new RemoteException("Init StaIface V1_0 failed.");
	    }
	    /* keep this in a store to avoid recycling by garbage collector. */
	    mISupplicantStaIfaceCallbacks.put(ifaceName, callback);
	    return iface;
	}

setupStaIface会先调用getStaIfaceMockableISupplicantIface的对象转换成ISupplicantStaIface对象,然后实例化了一个SupplicantStaIfaceHalCallback对象,通过registerCallback方法将SupplicantStaIfaceHalCallback对象注册到ISupplicantStaIface当中,这时候HAL层就拥有了java层的回调对象,继续看SupplicantStaIfaceHalCallback中的onStateChanged回调方法。

	public void onStateChanged(int newState, byte[/* 6 */] bssid, int id,
	                           ArrayList<Byte> ssid) {
	    synchronized (mLock) {
	        mStaIfaceHal.logCallback("onStateChanged");
	        SupplicantState newSupplicantState =
	                supplicantHidlStateToFrameworkState(newState);
	        WifiSsid wifiSsid =
	                WifiSsid.createFromByteArray(NativeUtil.byteArrayFromArrayList(ssid));
	        String bssidStr = NativeUtil.macAddressFromByteArray(bssid);
	        mStateIsFourway = (newState == ISupplicantStaIfaceCallback.State.FOURWAY_HANDSHAKE);
	        if (newSupplicantState == SupplicantState.COMPLETED) {
	            mWifiMonitor.broadcastNetworkConnectionEvent(
	                    mIfaceName, mStaIfaceHal.getCurrentNetworkId(mIfaceName), bssidStr);
	        }
	        mWifiMonitor.broadcastSupplicantStateChangeEvent(
	                mIfaceName, mStaIfaceHal.getCurrentNetworkId(mIfaceName), wifiSsid,
	                bssidStr, newSupplicantState);
	    }
	}

supplicantHidlStateToFrameworkState方法中,将HAL层传上来的状态转换成SupplicantState,然后通过WifiMonitor将相关信息通过广播发送出去。这里看WifiMonitor.broadcastSupplicantStateChangeEvent.

	public void broadcastSupplicantStateChangeEvent(String iface, int networkId, WifiSsid wifiSsid,
	                                                String bssid,
	                                                SupplicantState newSupplicantState) {
	    sendMessage(iface, SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
	            new StateChangeResult(networkId, wifiSsid, bssid, newSupplicantState));
	}

在方法中,直接对外发送了一个SUPPLICANT_STATE_CHANGE_EVENT消息,这个消息是交给通过registerHandler方法注册过的Handler去处理的。而在ClientModeImpl.registerForWifiMonitorEvents方法中有注册Handler对象到WifiMonitor中,而registerForWifiMonitorEvents方法在ConnectModeState.enter中会被调用到。文章开始时说到,在开启成功后ClientModeImpl会被切换到DisconnectedStateConnectModeStateDisconnectedState的父状态,所以ConnectModeState.enter方法在开启wifi成功时就被调用了。这时ClientModeImpl处于DisconnectedState状态,再看DisconnectedState状态对消息SUPPLICANT_STATE_CHANGE_EVENT是如何处理的。

	public boolean processMessage(Message message) {
		switch (message.what) {
			/*省略其他代码*/
			case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
			    StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
				/*省略其他代码*/
			    if (SupplicantState.isConnecting(stateChangeResult.state)) {
			        WifiConfiguration config = mWifiConfigManager.getConfiguredNetwork(
			                stateChangeResult.networkId);
			        // Update Passpoint information before setNetworkDetailedState as
			        // WifiTracker monitors NETWORK_STATE_CHANGED_ACTION to update UI.
			        mWifiInfo.setFQDN(null);
			        mWifiInfo.setPasspointUniqueId(null);
			        mWifiInfo.setOsuAp(false);
			        mWifiInfo.setProviderFriendlyName(null);
			        if (config != null && (config.isPasspoint() || config.osu)) {
			            if (config.isPasspoint()) {
			                mWifiInfo.setFQDN(config.FQDN);
			                mWifiInfo.setPasspointUniqueId(config.getPasspointUniqueId());
			            } else {
			                mWifiInfo.setOsuAp(true);
			            }
			            mWifiInfo.setProviderFriendlyName(config.providerFriendlyName);
			        }
			    }
			    sendNetworkChangeBroadcast(
			            WifiInfo.getDetailedStateOf(stateChangeResult.state));
			    /* ConnectModeState does the rest of the handling */
			    handleStatus = NOT_HANDLED;
			    break;
		 	/*省略其他代码*/
		}
	}

DisconnectedState状态处理消息SUPPLICANT_STATE_CHANGE_EVENT时,最后调用了sendNetworkChangeBroadcast将状态通过广播(WifiManager.NETWORK_STATE_CHANGED_ACTION)发送出去,sendNetworkChangeBroadcast方法不再赘述,里面就是正常发广播的操作。再次回到onStateChanged回调方法中,当最新的状态newSupplicantState == SupplicantState.COMPLETED时,也就是连接完成时,会调用WifiMonitor.broadcastNetworkConnectionEvent方法,会发送一个NETWORK_CONNECTION_EVENT消息,这时ClientModeImpl仍处于DisconnectedState状态。

	public void broadcastNetworkConnectionEvent(String iface, int networkId, String bssid) {
	    sendMessage(iface, NETWORK_CONNECTION_EVENT, networkId, 0, bssid);
	}

由于DisconnectedState状态没有对NETWORK_CONNECTION_EVENT消息进行处理,将交由其父状态ConnectModeState进行处理。

	public boolean processMessage(Message message) {
		switch (message.what) {
			/*省略其他代码*/
			case WifiMonitor.NETWORK_CONNECTION_EVENT:
			   /*省略其他代码*/
			    if (config != null) {
			        /*省略其他代码*/
			        transitionTo(mObtainingIpState);
			    } else {
			        /*省略其他代码*/
			    }
			    break;
		 	/*省略其他代码*/
		}
	}

其他代码都省略了,值看最后的切换状态到ObtainingIpState,那么根据之前说的状态机的工作机制,会对新状态树新的状态调用enter方法,先看ObtainingIpState.enter.

	public void enter() {
	    WifiConfiguration currentConfig = getCurrentWifiConfiguration();
	    if (mIpClientWithPreConnection && mIpClient != null) {
	        mIpClient.notifyPreconnectionComplete(mSentHLPs);
	        mIpClientWithPreConnection = false;
	        mSentHLPs = false;
	    } else {
	        startIpClient(currentConfig, false);
	    }
	    // Get Link layer stats so as we get fresh tx packet counters
	    getWifiLinkLayerStats();
	}

假设之前没有进行预连接或预连接不成功,那么会进入第二个分支,调用startIpClient.

	private boolean startIpClient(WifiConfiguration config, boolean isFilsConnection) {
	    /*省略部分代码*/
	    if (isFilsConnection) {
	       /*省略部分代码*/
	        mIpClient.startProvisioning(prov.build());
	    } else {
	        sendNetworkChangeBroadcast(DetailedState.OBTAINING_IPADDR);
	        /*省略部分代码*/
	        mIpClient.startProvisioning(prov.build());
	    }
	}

由于传入的isFilsConnectionfalse那么会进入下面的分支,会先发送DetailedState.OBTAINING_IPADDR状态的广播,然后再调用startProvisioning方法根据提供的配置参数进行网络配置。然后再看L2ConnectedState.enter.

	public void enter() {
	    mRssiPollToken++;
	    if (mEnableRssiPolling) {
	        mLinkProbeManager.resetOnNewConnection();
	        sendMessage(CMD_RSSI_POLL, mRssiPollToken, 0);
	    }
	    sendNetworkChangeBroadcast(DetailedState.CONNECTING);
	    /*省略其他代码*/
	}

L2ConnectedState.enter中,又调用了sendNetworkChangeBroadcast方法,并且发送的是DetailedState.CONNECTING状态。由于在ObtainingIpState.enter调用了startProvisioning进行配置,这里在配置成功后会调用ClientModeImpl.IpClientCallbacksImpl中的onProvisioningSuccess方法。

	public void onProvisioningSuccess(LinkProperties newLp) {
	    mWifiMetrics.logStaEvent(StaEvent.TYPE_CMD_IP_CONFIGURATION_SUCCESSFUL);
	    sendMessage(CMD_UPDATE_LINKPROPERTIES, newLp);
	    sendMessage(CMD_IP_CONFIGURATION_SUCCESSFUL);
	}

CMD_UPDATE_LINKPROPERTIES消息为更新连接配置,由于是刚连接不会发送广播,那么直接看CMD_IP_CONFIGURATION_SUCCESSFUL消息的处理,这时还是在ObtainingIpState的状态树下,因为ObtainingIpState没有对此消息进行处理,所以直接看其父状态L2ConnectedState的处理过程。

	public boolean processMessage(Message message) {
	/*省略其他代码*/
		switch (message.what) {
		/*省略其他代码*/
			case CMD_IP_CONFIGURATION_SUCCESSFUL:
			    if (getCurrentWifiConfiguration() == null) {
			        reportConnectionAttemptEnd(
			                WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION,
			                WifiMetricsProto.ConnectionEvent.HLF_NONE,
			                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN);
			        mWifiNative.disconnect(mInterfaceName);
			        transitionTo(mDisconnectingState);
			    } else {
			        handleSuccessfulIpConfiguration();
			        sendConnectedState();
			        transitionTo(mConnectedState);
			    }
			    break;
			/*省略其他代码*/
		}
		/*省略其他代码*/
	}

由于是连接过程,配置信息不为空,会进入下面的分支,这时候会调用sendConnectedState方法发送广播,对外发送了DetailedState.CONNECTED广播,表示成功连接上了某个热点,并且将状态切换到了ConnectedState

	private void sendConnectedState() {
	    mNetworkAgent.markConnected();
	    sendNetworkChangeBroadcast(DetailedState.CONNECTED);
	}

2、wifi连接流程图

【安卓Framework学习】Wifi框架学习之连接与断开流程_第1张图片

二、wifi断开流程

1、wifi断开源码流程分析

前面一部分分析了wifi开启后连接一个热点到连接成功的过程,本小结分析断开流程就相对要简单一些。仍然从用户点击断开调用WifiManager.disconnect方法开始。

	public boolean disconnect() {
	        try {
	            return mService.disconnect(mContext.getOpPackageName());
	        } catch (RemoteException e) {
	            throw e.rethrowFromSystemServer();
	        }
    }

转入到WifiServiceImpl.disconnect中。

	public boolean disconnect(String packageName) {
	    /*省略其他代码*/
	    mClientModeImpl.disconnectCommand();
	    return true;
	}

直接调用了ClientModeImpl.disconnectCommand中。

	public void disconnectCommand() {
	    sendMessage(CMD_DISCONNECT);
	}

这里直接发送了CMD_DISCONNECT消息给状态机。由于上部分分析了,在连接成功后将状态机ClientModeImpl的状态切换到了ConnectedState,由于ConnectedState对消息CMD_DISCONNECT没有处理,将交给其父状态L2ConnectedState处理此消息。

	public boolean processMessage(Message message) {
	/*省略其他代码*/
		switch (message.what) {
		/*省略其他代码*/
			case CMD_DISCONNECT:
			    mWifiMetrics.logStaEvent(StaEvent.TYPE_FRAMEWORK_DISCONNECT,
			            StaEvent.DISCONNECT_GENERIC);
			    mWifiNative.disconnect(mInterfaceName);
			    transitionTo(mDisconnectingState);
			    break;
			/*省略其他代码*/
		}
		/*省略其他代码*/
	}

L2ConnectedState中,直接调用了WifiNative.disconnect方法断开当前连接的热点,然后将状态机切换到DisconnectingState状态。在看WifiNative.disconnect.

	WifiNative.java
	public boolean disconnect(@NonNull String ifaceName) {
	    return mSupplicantStaIfaceHal.disconnect(ifaceName);
	}
	
	SupplicantStaIfaceHal.java
	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;
	        }
	    }
	}

可以看到WifiNative中直接调用了SupplicantStaIfaceHal.disconnect,通过HIDL调用了HAL层中的disconnect方法。由于在开启wifi时,向ISupplicantStaIface注册了回调类SupplicantStaIfaceHalCallback其父类为SupplicantStaIfaceCallbackImpl,再看SupplicantStaIfaceCallbackImpl的回调方法onDisconnect.

	public void onDisconnected(byte[/* 6 */] bssid, boolean locallyGenerated, int reasonCode) {
	    synchronized (mLock) {
	        /*省略其他代码*/
	        mWifiMonitor.broadcastNetworkDisconnectionEvent(
	                mIfaceName, locallyGenerated ? 1 : 0, reasonCode,
	                NativeUtil.macAddressFromByteArray(bssid));
	    }
	}

再看broadcastNetworkDisconnectionEvent方法。

	public void broadcastNetworkDisconnectionEvent(String iface, int local, int reason,
	                                               String bssid) {
	    sendMessage(iface, NETWORK_DISCONNECTION_EVENT, local, reason, bssid);
	}

这里之前讲到过WifiMonitor,会将各个类的Handler对象注册到其中。所以这里其实是给ClientModeImpl发送消息,由于回调是跨进程的,所以会排在当前消息后面。再回到L2ConnectedState处理CMD_DISCONNECT消息,会将状态切换到DisconnectingState,那么broadcastNetworkDisconnectionEvent方法发送的NETWORK_DISCONNECTION_EVENT是交给DisconnectingState状态处理了。由于DisconnectingState状态没有处理,会交由其父状态ConnectModeState处理。

	public boolean processMessage(Message message) {
	/*省略其他代码*/
		switch (message.what) {
		/*省略其他代码*/
			case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
			    if (mVerboseLoggingEnabled) log("ConnectModeState: Network connection lost ");
			    clearNetworkCachedDataIfNeeded(getTargetWifiConfiguration(), message.arg2);
			    handleNetworkDisconnect();
			    transitionTo(mDisconnectedState);
			    break;
			/*省略其他代码*/
		}
		/*省略其他代码*/
	}

这里只看handleNetworkDisconnect方法即可,因为其他方法对关闭流程没很大影响了。

	private void handleNetworkDisconnect() {
	    /*省略其他代码*/
	    sendNetworkChangeBroadcast(DetailedState.DISCONNECTED);
	    /*省略其他代码*/
	}

这里对外发送了wifi状态变化的广播,并且状态为DetailedState.DISCONNECTED,这个广播发送出去后表示wifi完全断开热点成功。

2、wifi断开流程图

【安卓Framework学习】Wifi框架学习之连接与断开流程_第2张图片


总结

相比起来,wifi连接的过程要复杂得很多,中间发送的连接状态广播也很多。其中主要还是通过开启时通过HIDL获得的ISupplicantStaIface对象对下面的连接断开添加网络请求等操作,所以后续需要继续跟进需要学习了解HIDL,其实是和AIDL类似的跨进程通信,有需要可以深入研究。

你可能感兴趣的:(android,学习,java)