美文网首页wifiAndroid开发Android开发经验谈
Android wifi源码分析(一) Wifi启动流程

Android wifi源码分析(一) Wifi启动流程

作者: 朋永 | 来源:发表于2017-09-16 15:26 被阅读352次

    最近在解决wifi的一些问题,故研究下wifi源码。
    该源码是基于Android4.3,其他版本略有改动,大致流程一样。
    这篇主要说一下wifi的启动流程。


    WifiManager

    先从wifi的开启来看,WifiManager中提供了接口用来控制wifi开关,setWifiEnabled,参数true表示开启、false表示关闭。

    public boolean setWifiEnabled(boolean enabled) {
        try {
            return mService.setWifiEnabled(enabled);
        } catch (RemoteException e) {
            return false;
        }
    }
    

    该方法调用到WifiService中的setWifiEnabled。


    WifiService

    public synchronized boolean setWifiEnabled(boolean enable) {
        enforceChangePermission();
        Slog.d(TAG, "setWifiEnabled: " + enable + " pid=" + Binder.getCallingPid()
                    + ", uid=" + Binder.getCallingUid());
        if (DBG) {
            Slog.e(TAG, "Invoking mWifiStateMachine.setWifiEnabled\n");
        }
    
        /*
        * Caller might not have WRITE_SECURE_SETTINGS,
        * only CHANGE_WIFI_STATE is enforced
        */
    
        long ident = Binder.clearCallingIdentity();
        try {
            if (! mSettingsStore.handleWifiToggled(enable)) {
                // Nothing to do if wifi cannot be toggled
                return true;
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    
        mWifiController.sendMessage(CMD_WIFI_TOGGLED);
        return true;
    }
    

    enforceChangePermission 判断调用的进程是否有权限。想要开关wifi需要CHANGE_WIFI_STATE 权限。
    handleWifiToggled 判断飞行模式、保存wifi 操作的状态。
    向WifiController发送消息。CMD_WIFI_TOGGLED


    WifiController

    WifiController在WIfiService的构造函数中初始化、并开始运行。
    WifiController继承了StateMachine,是一个状态机。其构造函数如下

    WifiController(Context context, WifiService service, Looper looper) {
      super(TAG, looper);
    //....
      addState(mDefaultState);
          addState(mApStaDisabledState, mDefaultState);
          addState(mStaEnabledState, mDefaultState);
              addState(mDeviceActiveState, mStaEnabledState);
              addState(mDeviceInactiveState, mStaEnabledState);
                  addState(mScanOnlyLockHeldState, mDeviceInactiveState);
                  addState(mFullLockHeldState, mDeviceInactiveState);
                  addState(mFullHighPerfLockHeldState, mDeviceInactiveState);
                  addState(mNoLockHeldState, mDeviceInactiveState);
          addState(mStaDisabledWithScanState, mDefaultState);
          addState(mApEnabledState, mDefaultState);
          addState(mEcmState, mDefaultState);
      if (mSettingsStore.isScanAlwaysAvailable()) {
          setInitialState(mApStaDisabledState);
      } else {
          setInitialState(mApStaDisabledState);
      }
    }
    

    上述函数中addState的格式,可以看出各状态之间的关系。
    然后通过wifi是否可以一直扫描(isScanAlwaysAvailable)设置状态机初始状态。
    ApStaDisabledState和StaDisabledWithScanState两种状态处理CMD_WIFI_TOGGLED消息时一样的。
    看看ApStaDisabledState是如何处理的。

    class ApStaDisabledState extends State {
        private int mDeferredEnableSerialNumber = 0;
        private boolean mHaveDeferredEnable = false;
        private long mDisabledTimestamp;
    
        @Override
        public void enter() {
            mWifiStateMachine.setSupplicantRunning(false);
            // Supplicant can't restart right away, so not the time we switched off
            mDisabledTimestamp = SystemClock.elapsedRealtime();
            mDeferredEnableSerialNumber++;
            mHaveDeferredEnable = false;
        }
        @Override
        public boolean processMessage(Message msg) {
            switch (msg.what) {
                case CMD_WIFI_TOGGLED:
                case CMD_AIRPLANE_TOGGLED:
                    if (mSettingsStore.isWifiToggleEnabled()) {
                        if (doDeferEnable(msg)) {
                            if (mHaveDeferredEnable) {
                                //  have 2 toggles now, inc serial number an ignore both
                                mDeferredEnableSerialNumber++;
                            }
                            mHaveDeferredEnable = !mHaveDeferredEnable;
                            break;
                        }
                        if (mDeviceIdle == false) {
                            transitionTo(mDeviceActiveState);
                        } else {
                            checkLocksAndTransitionWhenDeviceIdle();
                        }
                    }
                    break;
                //。。。。。
            }
            return HANDLED;
        }
    
        private boolean doDeferEnable(Message msg) {
            long delaySoFar = SystemClock.elapsedRealtime() - mDisabledTimestamp;
            if (delaySoFar >= mReEnableDelayMillis) {
                return false;
            }
    
            log("WifiController msg " + msg + " deferred for " +
                    (mReEnableDelayMillis - delaySoFar) + "ms");
    
            // need to defer this action.
            Message deferredMsg = obtainMessage(CMD_DEFERRED_TOGGLE);
            deferredMsg.obj = Message.obtain(msg);
            deferredMsg.arg1 = ++mDeferredEnableSerialNumber;
            sendMessageDelayed(deferredMsg, mReEnableDelayMillis - delaySoFar + DEFER_MARGIN_MS);
            return true;
        }
    }
    

    mSettingsStore.isWifiToggleEnabled()用来获取保存的wifi操作的状态,如果是开则继续。
    wpa supplicant 关闭后不能立即启动,mReEnableDelayMillis为重新开启wifi的延时时间(从系统数据库获取,获取不到则默认值为500ms),这里deferEnable用来判断时间,延缓启动。
    之后切换到DeviceActiveState。
    DeviceActiveState为StaEnabledState的子状态,所以会先调用父状态的enter()函数,然后调用子状态的enter()函数。分别看一下两个enter函数。

    1 WifiController --StaEnabledState

    先看看StaEnabledState的enter()函数:

    class StaEnabledState extends State {  
        @Override  
        public void enter() {  
            mWifiStateMachine.setSupplicantRunning(true);  
        }  
        ...  
        }  
    }  
    

    mWifiStateMachine.setSupplicantRunning(true),WifiStateMachine发送Message--CMD_STAET_SUPPLICANT。

    WIfiStateMachine

    看看处理该消息

    class InitialState extends State {
        @Override
        public boolean processMessage(Message message) {
            switch (message.what) {
                case CMD_START_SUPPLICANT:
                    //加载Wifi驱动
                    if (mWifiNative.loadDriver()) {
                        try {//加载固件
                            mNwService.wifiFirmwareReload(mInterfaceName, "STA");
                        } catch (Exception e) {
                        }
    
                        try {
                            // A runtime crash can leave the interface up and
                            // this affects connectivity when supplicant starts up.
                            // Ensure interface is down before a supplicant start.
                            mNwService.setInterfaceDown(mInterfaceName);
                            // Set privacy extensions
                            mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
    
                           // IPv6 is enabled only as long as access point is connected since:
                           // - IPv6 addresses and routes stick around after disconnection
                           // - kernel is unaware when connected and fails to start IPv6 negotiation
                           // - kernel can start autoconfiguration when 802.1x is not complete
                            mNwService.disableIpv6(mInterfaceName);
                        } catch (RemoteException re) {
                            loge("Unable to change interface settings: " + re);
                        } catch (IllegalStateException ie) {
                            loge("Unable to change interface settings: " + ie);
                        }
    
                       /* Stop a running supplicant after a runtime restart
                        * Avoids issues with drivers that do not handle interface down
                        * on a running supplicant properly.
                        */
                        mWifiNative.killSupplicant(mP2pSupported);
                        //开启wpa_supplicant
                        if(mWifiNative.startSupplicant(mP2pSupported)) {
                            setWifiState(WIFI_STATE_ENABLING);
                            if (DBG) log("Supplicant start successful");
                            mWifiMonitor.startMonitoring();
                            transitionTo(mSupplicantStartingState);
                        } else {
                            loge("Failed to start supplicant!");
                        }
                    } else {
                        loge("Failed to load driver");
                    }
                    break;
    //.......
    }
    
    1. WifiNative.loadDriver():加载Wifi驱动。
      WifiNative.loadDriver()-->android_net_wifi_wifi.cpp(android_net_wifi_loadDriver)
      ->wifi.c(wifi_load_driver)。

    2. mNwService.wifiFirmwareReload(mInterfaceName, "STA"); 加载固件

    3. mWifiNative.startSupplicant(mP2pSupported) 先保证没有运行的wpa supplicant,然后开启wpa supplicant,其中参数mP2pSupported表示是否支持wifi 直连。
      WifiNative.startSupplicant()-->android_net_wifi_wifi.cpp(android_net_wifi_startSupplicant)
      ->wifi.c(wifi_start_supplicant)。

    4. setWifiState(WIFI_STATE_ENABLING) 发送广播(wifi状态改变 ,正在开启)。

    5. mWifiMonitor.startMonitoring()
      WifiMonitor创建一个线程MonitorThrad

    • connectToSupplicant最终调到wifi.c 中的wifi_connect_on_socket_path。在该函数中,将通过wpa_ctrl_open函数分别创建两个socket,一个是ctrl_conn, 用于向wpa_supplicant发送命令并接收response, 另一个是monitor_conn, 它一直阻塞等待从wpa_supplicant过来的event。连接成功后WifiMonitor会向WifiStateMachine发送一个代表socket通信建立成功的消息:SUP_CONNECTION_EVENT。
    • mWifiNative.waitForEvent-》wifi_wait_on_socket ,循环调用该函数,接收底层事件并分发处理。
    1. WifiStateMachine切换到SupplicantStartingState状态。步骤5是在另一个线程中运行,并且是耗时操作,所以WifiStateMachine先切换到SupplicantStartingState状态,然后接收到SUP_CONNECTION_EVENT消息。

    下面看看如何处理SUP_CONNECTION_EVENT消息。

    class SupplicantStartingState extends State {
        @Override
        public boolean processMessage(Message message) {
            switch(message.what) {
                case WifiMonitor.SUP_CONNECTION_EVENT:
                    if (DBG) log("Supplicant connection established");
                    setWifiState(WIFI_STATE_ENABLED);
                    mSupplicantRestartCount = 0;
                    /* Reset the supplicant state to indicate the supplicant
                     * state is not known at this time */
                    mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
                    /* Initialize data structures */
                    mLastBssid = null;
                    mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
                    mLastSignalLevel = -1;
    
                    mWifiInfo.setMacAddress(mWifiNative.getMacAddress());
                    mWifiConfigStore.loadAndEnableAllNetworks();
                    initializeWpsDetails();
    
                    sendSupplicantConnectionChangedBroadcast(true);
                    transitionTo(mDriverStartedState);
                    break;
    //......
    }
    

    setWifiState(WIFI_STATE_ENABLED),发送WIFI_STATE_ENABLED的广播。
    WifiConfigStore.loadAndEnableAllNetworks() 加载并enable所有保存在wpa_supplicant中的AP。这样会使这些保存的wifi实现自动重连。
    切换到DriverStartedState状态。
    DriverStartedState是SupplicantStartedState的子状态,所以先后运行SupplicantStartedState、DriverStartedState的enter方法。

    class SupplicantStartedState extends State {
            @Override
            public void enter() {
                /* Wifi is available as long as we have a connection to supplicant */
                mNetworkInfo.setIsAvailable(true);
    
                int defaultInterval = mContext.getResources().getInteger(
                        R.integer.config_wifi_supplicant_scan_interval);
                mSupplicantScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
                        Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS,
                        defaultInterval);
                //设置扫描时间间隔。
                mWifiNative.setScanInterval((int)mSupplicantScanIntervalMs / 1000);
            }
    //.....
    

    frameworks/base/core/res/res/values/config.xml中可以修改config_wifi_supplicant_scan_interval 的值。
    mNetworkInfo.setIsAvailable(true); 连接到wpa_supplicant,就可以使用wifi。
    setScanInterval 设置wifi扫描间隔时间。

    class DriverStartedState extends State {
            @Override
            public void enter() {
                mIsRunning = true;
                mInDelayedStop = false;
                mDelayedStopCounter++;
                updateBatteryWorkSource(null);
                /**
                 * Enable bluetooth coexistence scan mode when bluetooth connection is active.
                 * When this mode is on, some of the low-level scan parameters used by the
                 * driver are changed to reduce interference with bluetooth
                 */
                mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
                /* set country code */
                setCountryCode();
                /* set frequency band of operation */
                setFrequencyBand();
                /* initialize network state */
                setNetworkDetailedState(DetailedState.DISCONNECTED);
    
                /* Remove any filtering on Multicast v6 at start */
                mWifiNative.stopFilteringMulticastV6Packets();
    
                /* Reset Multicast v4 filtering state */
                if (mFilteringMulticastV4Packets.get()) {
                    mWifiNative.startFilteringMulticastV4Packets();
                } else {
                    mWifiNative.stopFilteringMulticastV4Packets();
                }
    
                if (mOperationalMode != CONNECT_MODE) {
                    mWifiNative.disconnect();
                    transitionTo(mScanModeState);
                } else {
                    /* Driver stop may have disabled networks, enable right after start */
                    mWifiConfigStore.enableAllNetworks();
                    mWifiNative.reconnect();
                    // Status pulls in the current supplicant state and network connection state
                    // events over the monitor connection. This helps framework sync up with
                    // current supplicant state
                    mWifiNative.status();
                    transitionTo(mDisconnectedState);
                }
    
                // We may have missed screen update at boot
                if (mScreenBroadcastReceived.get() == false) {
                    PowerManager powerManager = (PowerManager)mContext.getSystemService(
                            Context.POWER_SERVICE);
                    handleScreenStateChanged(powerManager.isScreenOn());
                } else {
                    // Set the right suspend mode settings
                    mWifiNative.setSuspendOptimizations(mSuspendOptNeedsDisabled == 0
                            && mUserWantsSuspendOpt.get());
                }
                mWifiNative.setPowerSave(true);
    
                if (mP2pSupported) mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_ENABLE_P2P);
    
                final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
                intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_ENABLED);
                mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
            }
    //.....
    

    setBluetoothCoexistenceScanMode 启用蓝牙连接时启用蓝牙共存扫描模式。当此模式打开时,驱动程序使用的一些低级扫描参数会被更改,以减少对蓝牙的干扰。
    mOperationalMode 默认为CONNECT_MODE,enableAllNetworks 将保存的wifi置为可连接的,并进行重连。
    mWifiNative.status() 将wpa_supplicant中状态进行同步。
    切换到DisconnectedState状态。

    2 WifiController --DeviceActiveState

    /* Parent: StaEnabledState */  
    class DeviceActiveState extends State {  
        @Override  
        public void enter() {  
            mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE);  
            mWifiStateMachine.setDriverStart(true);  
            mWifiStateMachine.setHighPerfModeEnabled(false);
        }
        ...  
    } 
    

    STA的3个操作状态:CONNECT_MODE,SCAN_ONLY_MODE,SCAN_ONLY_WIFI_OFF_MODE
    *在CONNECT_MODE中,STA可以扫描并连接到接入点
    *在SCAN_ONLY_MODE中,STA只能扫描接入点
    *在SCAN_ONLY_WIFI_OFF_MODE中,STA只能扫描(wifi关闭状态下)
    setOperationalMode 这里设置模式 可扫描、可连接。

    setDriverStart(true) 发送CMD_START_DRIVER 消息。该消息在DriverStartedState中处理。

    case CMD_START_DRIVER:
        if (mInDelayedStop) {
            mInDelayedStop = false;
            mDelayedStopCounter++;
            mAlarmManager.cancel(mDriverStopIntent);
            if (DBG) log("Delayed stop ignored due to start");
            if (mOperationalMode == CONNECT_MODE) {
                mWifiConfigStore.enableAllNetworks();
            }
        }
        break;
    

    如果正在延时关闭驱动,则取消关闭。并将保存的wifi都Enable。

    setHighPerfModeEnabled暂时不清楚什么情况。

    这就差不多整理完wifi启动的流程了。

    有什么问题和意见,欢迎提问、交流
    欢迎大家关注、评论、点赞
    你们的支持是我坚持的动力。

    欢迎关注我的微信公众号

    相关文章

      网友评论

      • simon_economic:请问,DriverStartedState enter()中的transitionTo() 是只有在处理了message之后才会跳转吗?
        小小混世魔王:楼主 我用了你的这个wifi扫描就是获取不到wifi的列表 你提供的那个apk又可以扫描到
        public void startScan(Context context) {
        if (Build.VERSION.SDK_INT >= 23) {
        if (!isOPen(context)) {
        openGPS(context);
        }

        }
        mWifiManager.startScan();
        //得到扫描结果
        List<ScanResult> results = mWifiManager.getScanResults();
        // 得到配置好的网络连接
        mWifiConfiguration = mWifiManager.getConfiguredNetworks();
        if (results == null) {
        if (mWifiManager.getWifiState() == 3) {
        Toast.makeText(context, "当前区域没有无线网络", Toast.LENGTH_SHORT).show();
        } else if (mWifiManager.getWifiState() == 2) {
        Toast.makeText(context, "wifi正在开启,请稍后扫描", Toast.LENGTH_SHORT).show();
        } else {
        Toast.makeText(context, "WiFi没有开启", Toast.LENGTH_SHORT).show();
        }
        } else {
        mWifiList = new ArrayList();
        for (ScanResult result : results) {
        if (result.SSID == null || result.SSID.length() == 0
        || result.capabilities.contains("[IBSS]")) {
        continue;
        }
        boolean found = false;
        Log.i("MainActivity", "result= " + result.SSID + " capabilities= " + result.capabilities);
        for (ScanResult item : mWifiList) {
        Log.i("MainActivity", "item= " + item.SSID + " capabilities=" + item.capabilities);
        if (item.SSID.equals(result.SSID) && item.capabilities.equals(result.capabilities)) {
        Log.i("MainActivity", "found true");
        found = true;
        break;
        }
        }
        if (!found) {
        mWifiList.add(result);
        }
        }
        }
        }
        朋永:直接跳转。

      本文标题:Android wifi源码分析(一) Wifi启动流程

      本文链接:https://www.haomeiwen.com/subject/knaysxtx.html