精华内容
下载资源
问答
  • 有时候,我们的wifi网络设置了最大连接数,该数字小于我们的需求数,我们可以考虑利用路由器来进一步分发wifi网络。下面以tp-link的产品为例介绍如何分发网络。 1、开启路由器,让其发出广播信号 2、使用电脑无线...

           有时候,我们的wifi网络设置了最大连接数,该数字小于我们的需求数,我们可以考虑利用路由器来进一步分发wifi网络。下面以tp-link的产品为例介绍如何分发网络。

    1、开启路由器,让其发出广播信号

    2、使用电脑无线功能连接该路由器,登录路由器首页(路由器域名及登录密码见其标签)

    3、修改路由设置信息,如下列图

                                                                                    图1. 路由设置-上网设置

                                                                                         图2.路由设置-无线设置

    另外,LAN口设置以及DHCP服务器设置均选择自动设置即可。

    4、在应用管理模块中设置无线连接,根据提示,配置好本路由器的主路由器信息,并保存即可。最终配置成功的页面如下图所示:

                                                                                    图3:应用管理-无线桥接

                                                                                   图4:应用管理-信号强度调节

    5、上述配置好了之后,在网络状态模块中可以看到wifi信号分发情况,如下图所示:

                                                                                            图5:网络状态

    注:设备管理中可看到已连接的设备数量及ip地址。

    这样,就可以连接该路由器上网了

    展开全文
  • 用于Arduino WiFi Shield 101和MKR1000开发板的WiFi101库 该库为基于ATMEL WINC1500 WiFi模块的设备实现了网络驱动程序。 有关此库的更多信息,请访问 执照 版权所有(c)Arduino LLC。 保留所有权利。 该库是...
  • 最近在解决wifi的一些问题,故研究下wifi源码。 该源码是基于Android4.3,其他版本略有改动,大致流程一样。 这篇主要说一下wifi的启动流程。

    最近在解决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 ,循环调用该函数,接收底层事件并分发处理。
    6. 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启动的流程了。

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

    展开全文
  • Android wifi探究二:Wifi framework层源码分析

    千次阅读 多人点赞 2016-10-11 17:27:21
    上一篇博客初步认识了wpa_supplicant的作用和使用方法,并且尝试着梳理了wifi的大框架,不过,java层的框架我们忽略了,没有分析,也就是说上一篇博客非常肤浅的支出了wifi代码的结构,仅此而已,那么这篇博客,将...

    上一篇博客初步认识了wpa_supplicant的作用和使用方法,并且尝试着梳理了wifi的大框架,不过,java层的框架我们忽略了,没有分析,也就是说上一篇博客简单的指出了wifi代码的大框架,那么这篇博客,将尝试梳理wifi的java层框架。
    声明:本人使用的Android源码是6.0版本。

    一.WifiService的启动

    WifiService的启动可用如下简单时序图表示:
    这里写图片描述
    启动过程的图示画的比较简单,下面就顺着这个思路理一下代码的实现。
    在SystemServer.java的 startOtherServices() 方法中,启动了WifiService,代码如下:

                    mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
                    mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
                    mSystemServiceManager.startService(
                                "com.android.server.wifi.WifiScanningService");
    
                    mSystemServiceManager.startService("com.android.server.wifi.RttService");

    其中用到的WIFI_P2P_SERVICE_CLASS,WIFI_SERVICE_CLASS两个变量的值如下:

        private static final String WIFI_SERVICE_CLASS =
                "com.android.server.wifi.WifiService";
        private static final String WIFI_P2P_SERVICE_CLASS =
                "com.android.server.wifi.p2p.WifiP2pService";

    以上代码可以看到,SystemServier中启动的Wifi相关的服务有四个,从上往下依次是P2P wifi服务,普通wifi,wifi扫描附近热点的服务以及以太网服务。

    • p2p wifi服务主要为Wi-Fi Direct提供相应的服务,Wi-Fi Direct是一种全新的技术,即使在没有传统的Wi-Fi网络或Wi-Fi接入点的环境中,仍然能够在诸如智能手机和数码相机等设备间实现点对点Wi-Fi连接。
    • wifi 服务这是我们关注的重点。
      下面我们看下wifi服务的启动过程。
        @SuppressWarnings("unchecked")
        public SystemService startService(String className) {
            final Class<SystemService> serviceClass;
            try {
                serviceClass = (Class<SystemService>)Class.forName(className);
            } catch (ClassNotFoundException ex) {
                Slog.i(TAG, "Starting " + className);
                throw new RuntimeException("Failed to create service " + className
                        + ": service class not found, usually indicates that the caller should "
                        + "have called PackageManager.hasSystemFeature() to check whether the "
                        + "feature is available on this device before trying to start the "
                        + "services that implement it", ex);
            }
            return startService(serviceClass);
        }

    startService方法中通过Class.forName获得一个Class实例,但是这还不是WifiService的实例,然后调用startService进一步处理。这两个startService方法是重载方法,他们的参数类型不同。

       public <T extends SystemService> T startService(Class<T> serviceClass) {
            final String name = serviceClass.getName();
            Slog.i(TAG, "Starting " + name);
    
            // Create the service.
            if (!SystemService.class.isAssignableFrom(serviceClass)) {
                throw new RuntimeException("Failed to create " + name
                        + ": service must extend " + SystemService.class.getName());
            }
            final T service;
            try {
                Constructor<T> constructor = serviceClass.getConstructor(Context.class);
                service = constructor.newInstance(mContext);
            } catch (InstantiationException ex) {
                throw new RuntimeException("Failed to create service " + name
                        + ": service could not be instantiated", ex);
            } catch (IllegalAccessException ex) {
                throw new RuntimeException("Failed to create service " + name
                        + ": service must have a public constructor with a Context argument", ex);
            } catch (NoSuchMethodException ex) {
                throw new RuntimeException("Failed to create service " + name
                        + ": service must have a public constructor with a Context argument", ex);
            } catch (InvocationTargetException ex) {
                throw new RuntimeException("Failed to create service " + name
                        + ": service constructor threw an exception", ex);
            }
    
            // Register it.
            mServices.add(service);
    
            // Start it.
            try {
                service.onStart();
            } catch (RuntimeException ex) {
                throw new RuntimeException("Failed to start service " + name
                        + ": onStart threw an exception", ex);
            }
            return service;
        }

    在这个startService方法中,使用 Constructor constructor = serviceClass.getConstructor(Context.class);
    service = constructor.newInstance(mContext);构建了一个WifiService的实例,然后使用mServices.add(service);向系统注册WifiService,并调用WifiService的onStart方法。构造WifiService会调用WifiService的构造方法,它的构造方法如下:

        public WifiService(Context context) {
            super(context);
            mImpl = new WifiServiceImpl(context);
        }

    wifiService构造函数有新建了一个WifiServiceImpl实例,它才是Wifi管理服务真正的实现者,构造函数调用后不是调用了WifiService的onStart方法吗?

        @Override
        public void onStart() {
            Log.i(TAG, "Registering " + Context.WIFI_SERVICE);
            publishBinderService(Context.WIFI_SERVICE, mImpl);
        }

    在onStart方法中发布了Wifi服务,发布的WifiServiceImpl的实例。发布的过程如下:
    1.

        protected final void publishBinderService(String name, IBinder service) {
            publishBinderService(name, service, false);
        }

    2.

        protected final void publishBinderService(String name, IBinder service,
                boolean allowIsolated) {
            ServiceManager.addService(name, service, allowIsolated);
        }

    也就是说还是调用了ServiceManager的addService方法,这里就不再深入了,再深入就有点跑偏了。
    通过以上分析,我们知道了真的wifi服务是WifiServiceImpl,它的构造方法如下:

        public WifiServiceImpl(Context context) {
            mContext = context;
    
            mInterfaceName =  SystemProperties.get("wifi.interface", "wlan0");
    
            mTrafficPoller = new WifiTrafficPoller(mContext, mInterfaceName);
            mWifiStateMachine = new WifiStateMachine(mContext, mInterfaceName, mTrafficPoller);
            mWifiStateMachine.enableRssiPolling(true);
            mBatteryStats = BatteryStatsService.getService();
            mPowerManager = context.getSystemService(PowerManager.class);
            mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
            mUserManager = UserManager.get(mContext);
    
            mNotificationController = new WifiNotificationController(mContext, mWifiStateMachine);
            mSettingsStore = new WifiSettingsStore(mContext);
    
            HandlerThread wifiThread = new HandlerThread("WifiService");
            wifiThread.start();
            mClientHandler = new ClientHandler(wifiThread.getLooper());
            mWifiStateMachineHandler = new WifiStateMachineHandler(wifiThread.getLooper());
            mWifiController = new WifiController(mContext, this, wifiThread.getLooper());
        }

    这里面做的事情还是很多的,主要有以下几点:
    1.mInterfaceName 是从系统属性中获取的,它的值一般就是wlan0;
    2.mTrafficPoller 这个实例的作用从其类的简介(Polls for traffic stats and notifies the clients )上可以看出他是用来查询流量统计信息比通知给客户端的。
    3.mWifiStateMachine 这个实例代表着一个Wifi状态机,它定义了wifi的很多状态,通过消息驱动状态的转变。
    4.mBatteryStats ,mPowerManager 用于wifi的电源管理,
    5.mNotificationController 处理打开“打开wifi并且可以使用“的通知。
    6.wifiThread 它是一个HandlerThread 的实例,HandlerThread 是一个内部有Looper的线程,wifiThread会一直监听消息,消息到来以后,通过mClientHandler 的handleMessage来处理消息。
    7.WifiStateMachineHandler 用于发送和处理wifi状态机相关的消息。
    8.mWifiController 是另一个状态机,它和mWifiStateMachine 不同,mWifiStateMachine 表述wifi具体的状态,比如supplicant启动/关闭状态,driver启动/关闭状态等,mWifiController 则更高一级的控制wifi设备的开关状态,wifi热点的开关状态等。
    理解mWifiController 和mWifiStateMachine 对于理解Android wifi框架至关重要。所以接下来,我们就着重分析wifi状态机的工作原理。

    二.wifi状态机工作原理

    我们说mWifiController 是高级别的wifi状态机,因为它管理的状态是wifi开关,wifi热点开关等状态,只有在wifi开关等具体状态下,判断wifi处于启动扫描附近热点状态等才是有意义的。
    状态机无非就是一个定义了很多状态的机器,它收到消息后,会根据消息来切换这个机器的状态。mWifiController 的状态构造在它的构造方法中:

            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);

    结构图如下:
    图A
    每一个状态机都有一个初始状态:

            if (isScanningAlwaysAvailable) {
                setInitialState(mStaDisabledWithScanState);
            } else {
                setInitialState(mApStaDisabledState);
            }

    mWifiStateMachine 则表述wifi更加细致的状态,它的状态构建也是在构造函数中:

            addState(mDefaultState);
                addState(mInitialState, mDefaultState);
                addState(mSupplicantStartingState, mDefaultState);
                addState(mSupplicantStartedState, mDefaultState);
                    addState(mDriverStartingState, mSupplicantStartedState);
                    addState(mDriverStartedState, mSupplicantStartedState);
                        addState(mScanModeState, mDriverStartedState);
                        addState(mConnectModeState, mDriverStartedState);
                            addState(mL2ConnectedState, mConnectModeState);
                                addState(mObtainingIpState, mL2ConnectedState);
                                addState(mVerifyingLinkState, mL2ConnectedState);
                                addState(mConnectedState, mL2ConnectedState);
                                addState(mRoamingState, mL2ConnectedState);
                            addState(mDisconnectingState, mConnectModeState);
                            addState(mDisconnectedState, mConnectModeState);
                            addState(mWpsRunningState, mConnectModeState);
                    addState(mWaitForP2pDisableState, mSupplicantStartedState);
                    addState(mDriverStoppingState, mSupplicantStartedState);
                    addState(mDriverStoppedState, mSupplicantStartedState);
                addState(mSupplicantStoppingState, mDefaultState);
                addState(mSoftApStartingState, mDefaultState);
                addState(mSoftApStartedState, mDefaultState);
                    addState(mTetheringState, mSoftApStartedState);
                    addState(mTetheredState, mSoftApStartedState);
                    addState(mUntetheringState, mSoftApStartedState);
    
            setInitialState(mInitialState);

    结构图如下:
    图B
    并且初始化状态为mInitialState。
    以上Android wifi框架中两个重要的状态机,那么状态机的工作机制是怎么样的呢?
    以下是状态机工作原理简介:
    状态机中的一个状态由State类的实例表示,State实例必须实现processMessage方法用来处理消息。并且可选的实现enter/exit/getName三个方法,enter/exit 等价于类的构造方法和销毁方法,本别用于初始化和清理一个状态。getName方法返回State的名字,默认的实现是返回类名。
    当我们创建一个状态机时,需要使用addState方法给状态机添加状态,正如前面所展示的那样。setInitialState用于初始化一个状态机的初始状态。构建好一个状态机以后,我们需要调用start方法启动这个状态机,它就像一个机器,造好以后,加油或者充电,然后发动它,它就进入工作状态了。这个过程会调用初始化状态的enter方法初始化初始状态,如果初始状态由父状态,就会递归调用父状态,知道所有父状态的enter方法被调用。这样才算是完全初始化好了一个状态机,start方法还会时状态机进入已经构造结束阶段,这个时候,当有消息到来时,状态机就可以处理消息了。
    处理消息的过程和初始化类似。当消息到来以后,当前状态就会调用processMessage来处理消息,如果当前消息能够处理消息,那么消息处理过程就结束了,此时会根据具体情况选择切换或者不切换状态机的状态。如果当前State不能处理消息,那么就会递交父State的processMessage来处理,父状态如果还不能处理就继续往上递交。如果一个消息从未被处理,unhandledMessage方法会被调用,这是最后处理这个消息的机会了。
    如果我们期望停止状态机,可以调用quitNow或者quit方法。
    当我们切换状态时,旧State的exit方法会被调用而新State的enter方法会被调用,同时他们父State也会做相同的事情。但是如果两个状态由相同的父状态,那么这个时候他们父状态就没有必要做任何操作了,因为它的状态其实并没有变。
    以上就是一个状态机的工作原理的简要概述。我们可以想象,当应用程序需要扫描附近的热点时,如果wifi状态机正处于开启状态,那么上层的操作会导致wifi状态机接收到一个消息,开启的状态对它处理后,发现需要把wifi状态机切换到scan状态,于是开启状态的exit方法被调用,scan状态的enter方法被调用。切换不应该只是wifi状态机状态的切换,这个过程应该会调用底层的代码真正的把wifi的状态切换到对应的状态。切换过去以后wpa_supplicant会返回响应的事件,这又会导致响应的消息被wifi状态机接受,从而又促使wifi状态机状态的切换。

    三.wifi框架梳理

    再分析wifi框架之前,我先把我理解的wifi框架以图的形式展示出来:
    这里写图片描述
    android6.0的wifi框架中有两个非常重要的状态机:WifiController和WifiStateMachine,它们一起管理着wifi的各个状态以及状态之间的切换。WifiMonitor负责从wpa_supplicant接收事件,并且和WifiStateMachine交互。它们最终都会调用wifiNative,最终和wpa_supplicant交互。
    拍砖要趁早,如果理解的不对,希望指出来。
    下面我将会从给wap_supplicant发送命令与接收wpa_supplicant发出来的事件两方面梳理wifi框架的具体实现。

    向wpa_supplicant发送命令

    想象一下在应用程序我们怎么连接wifi:
    1打开和关闭wifi
    WifiManager wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
    wifi.setWifiEnabled(true);
    2扫描附近热点
    startScan();之后,接受WifiManager.SCAN_RESULTS_AVAILABLE_ACTION的广播会触发,在这个广播中调用getScanResults()方法可以获得一个List,它里面的每一个条目就是一个可连接的热点。
    wifi.startScan();
    results = wifi.getScanResults();
    一般我们可能都需要做这两步吧,那么这两步会导致wifi状态机做怎么样的改变呢?
    首先,用一张图来概括这个过程,然后大家可以顺着这个图的思路分析代码:
    这里写图片描述
    代码的分析过程如下:
    首先从setWifiEnabled开始,wifi是一个WifiServiceImpl的客户端,它会通过binder和WifiServiceImpl的实例交互,也就是我么在WifiService中通过addService方法向系统注册的mImpl对象,忘记的可以回头看看前面。

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

    最终调用的的是WifiServiceImpl中的setWifiEnabled方法:

        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;
        }

    这个方法首先要使用mSettingsStore.handleWifiToggled(enable)来判断wifi状态是否可以切换,不能切换就直接返回。如果wifi状态可以切换,那么接下来会使用mWifiController.sendMessage(CMD_WIFI_TOGGLED);来给mWifiController状态佳发送一条消息,我们说过mWifiController状态机控制wifi设备的开关灯状态,由此开来这句话是对的哈。WifiController的sendMessage方法定义如下:

        /**
         * Enqueue a message to this state machine.
         *
         * Message is ignored if state machine has quit.
         */
        public final void sendMessage(int what) {
            // mSmHandler can be null if the state machine has quit.
            SmHandler smh = mSmHandler;
            if (smh == null) return;
    
            smh.sendMessage(obtainMessage(what));
        }

    这里有使用了smh.sendMessage真正的向WifiController状态机的消息队列中发送消息。这时候,因为是使用mSmHandler来发送的消息,所以mSmHandler的handlerMessage方法会被调用:

        @Override
            public final void handleMessage(Message msg) {
                if (!mHasQuit) {
                    if (mDbg) mSm.log("handleMessage: E msg.what=" + msg.what);
    
                    /** Save the current message */
                    mMsg = msg;
    
                    /** State that processed the message */
                    State msgProcessedState = null;
                    if (mIsConstructionCompleted) {
                        /** Normal path */
                        msgProcessedState = processMsg(msg);
                    } else if (!mIsConstructionCompleted && (mMsg.what == SM_INIT_CMD)
                            && (mMsg.obj == mSmHandlerObj)) {
                        /** Initial one time path. */
                        mIsConstructionCompleted = true;
                        invokeEnterMethods(0);
                    } else {
                        throw new RuntimeException("StateMachine.handleMessage: "
                                + "The start method not called, received msg: " + msg);
                    }
                    performTransitions(msgProcessedState, msg);
    
                    // We need to check if mSm == null here as we could be quitting.
                    if (mDbg && mSm != null) mSm.log("handleMessage: X");
                }
            }
    

    这个方法首先判断状态机是否退出,如果没有退出,就进一步判断状态机是否构建完成,如果构建完成的话,就调用processMsg来处理消息。
    下面看看处理消息的过程:

            private final State processMsg(Message msg) {
                StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
                if (mDbg) {
                    mSm.log("processMsg: " + curStateInfo.state.getName());
                }
    
                if (isQuit(msg)) {
                    transitionTo(mQuittingState);
                } else {
                    while (!curStateInfo.state.processMessage(msg)) {
                        /**
                         * Not processed
                         */
                        curStateInfo = curStateInfo.parentStateInfo;
                        if (curStateInfo == null) {
                            /**
                             * No parents left so it's not handled
                             */
                            mSm.unhandledMessage(msg);
                            break;
                        }
                        if (mDbg) {
                            mSm.log("processMsg: " + curStateInfo.state.getName());
                        }
                    }
                }
                return (curStateInfo != null) ? curStateInfo.state : null;
            }
    

    1.首先获得当前的状态。‘
    /* Stack used to manage the current hierarchy of states /
    private StateInfo mStateStack[];
    就是说mStateStack就是保存着当前的状态链。mStateStackTopIndex指向的状当前状态链最顶层的状态,比如说有如下状态链:
    m
    /\
    a b
    /\
    c d
    加入当前状态链是mdb,mStateStackTopIndex指向的就是d状态了。
    2.判断是不是退出消息,如果是,就把状态机的状态置为退出状态。
    3.调用当前状态的processMessage方法处理消息,如果当前状态没有处理消息,就调用其父状态的processMessage处理消息,依次往上。如果所有状态都没有处理消息,最终unhandledMessage方法就会被调用,这正如我们之前在wifi状态机原理中所说的那样。
    4.如果消息被处理了,就返回curStateInfo,否则返回null。
    curStateInfo是StateInfo的实例,StateInfo封装了State的状态信息类,这个类用于维护状态机中状态的层次关系。
    这个时候,假如我们的状态机的状态为StaEnabledState状态,那么它的processMessage方法就会被调用:

        public boolean processMessage(Message msg) {
                switch (msg.what) {
                    case CMD_WIFI_TOGGLED:
                        if (mWifiStateMachine.syncGetWifiState() == 1) {
                            log("wifi start fail before, now start it again");
                            mWifiStateMachine.setSupplicantRunning(true);
                        }
                        if (! mSettingsStore.isWifiToggleEnabled() || msg.arg1 == WifiManager.WIFI_STATE_DISABLED) {
                            if (mSettingsStore.isScanAlwaysAvailable()) {
                                transitionTo(mStaDisabledWithScanState);
                            } else {
                                transitionTo(mApStaDisabledState);
                            }
                        }
                        break;
                        ...

    1.当消息为CMD_AIRPLANE_TOGGLED的时候,在这种状态下,会使用mWifiStateMachine.setSupplicantRunning(true);来启动wpa_supplicant服务,这就是为什么上一篇博客 《Android wifi框架分析一:初步认识wpa_supplicant与wifi框架梳理》我们说打开wifi后wpa_supplicant就会启动的原因。然后把状态机切换到响应的状态。
    2.WifiStateMachine.setSupplicantRunning方法如下:

        public void setSupplicantRunning(boolean enable) {
            if (enable) {
                sendMessage(CMD_START_SUPPLICANT);
            } else {
                sendMessage(CMD_STOP_SUPPLICANT);
            }
        }

    我们看到这个时候逻辑就转到WifiStateMachine中了,这是一个新的状态机,工作原理都是一样的,这里就是发送对应的消息,这会促使WifiStateMachine做相应的操作。
    3.transitionTo只是把简单的给mDestState变量赋值:

            private final void transitionTo(IState destState) {
                mDestState = (State) destState;
                if (mDbg) mSm.log("transitionTo: destState=" + mDestState.getName());
            }

    我们说过状态机的切换要调用旧状态的exit方法,新状态的enter方法,那么回到handleMessage方法,在processMsg方法调用结束后,performTransitions方法就会被调用:

           private void performTransitions(State msgProcessedState, Message msg) {
                /**
                 * If transitionTo has been called, exit and then enter
                 * the appropriate states. We loop on this to allow
                 * enter and exit methods to use transitionTo.
                 */
                State orgState = mStateStack[mStateStackTopIndex].state;
    
                /**
                 * Record whether message needs to be logged before we transition and
                 * and we won't log special messages SM_INIT_CMD or SM_QUIT_CMD which
                 * always set msg.obj to the handler.
                 */
                boolean recordLogMsg = mSm.recordLogRec(mMsg) && (msg.obj != mSmHandlerObj);
    
                if (mLogRecords.logOnlyTransitions()) {
                    /** Record only if there is a transition */
                    if (mDestState != null) {
                        mLogRecords.add(mSm, mMsg, mSm.getLogRecString(mMsg), msgProcessedState,
                                orgState, mDestState);
                    }
                } else if (recordLogMsg) {
                    /** Record message */
                    mLogRecords.add(mSm, mMsg, mSm.getLogRecString(mMsg), msgProcessedState, orgState,
                            mDestState);
                }
    
                State destState = mDestState;
                if (destState != null) {
                    /**
                     * Process the transitions including transitions in the enter/exit methods
                     */
                    while (true) {
                        if (mDbg) mSm.log("handleMessage: new destination call exit/enter");
    
                        /**
                         * Determine the states to exit and enter and return the
                         * common ancestor state of the enter/exit states. Then
                         * invoke the exit methods then the enter methods.
                         */
                        StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
                        invokeExitMethods(commonStateInfo);
                        int stateStackEnteringIndex = moveTempStateStackToStateStack();
                        invokeEnterMethods(stateStackEnteringIndex);
    
                        /**
                         * Since we have transitioned to a new state we need to have
                         * any deferred messages moved to the front of the message queue
                         * so they will be processed before any other messages in the
                         * message queue.
                         */
                        moveDeferredMessageAtFrontOfQueue();
    
                        if (destState != mDestState) {
                            // A new mDestState so continue looping
                            destState = mDestState;
                        } else {
                            // No change in mDestState so we're done
                            break;
                        }
                    }
                    mDestState = null;
                }

    1.首先从mStateStack[mStateStackTopIndex].state获取当前的状态
    2.State destState = mDestState;从mDestState中获取目标状态。mDestState不就是我们在transitionTo中设置的目标状态吗?这里就用到了。
    3.这里面invokeExitMethods和invokeEnterMethods方法就是调用对应状态的额exit和enter方法的。

    至此状态机的操作结束,setWifiEnabled造成的影响终于结束了。

    wifi.startScan()
    那么 wifi.startScan(); 又做了什么事情呢?
    因为我们已经详细分析过setWifiEnabled,这里简单分析:
    WifiServiceImpl中的startScan方法被调用:

      public void startScan(ScanSettings settings, WorkSource workSource) {
     ...
            mWifiStateMachine.startScan(Binder.getCallingUid(), scanRequestCounter++,
                    settings, workSource);
        }

    WifiStateMachine.startScan方法被调用:

        public void startScan(int callingUid, int scanCounter,
                              ScanSettings settings, WorkSource workSource) {
            Bundle bundle = new Bundle();
            bundle.putParcelable(CUSTOMIZED_SCAN_SETTING, settings);
            bundle.putParcelable(CUSTOMIZED_SCAN_WORKSOURCE, workSource);
            bundle.putLong(SCAN_REQUEST_TIME, System.currentTimeMillis());
            sendMessage(CMD_START_SCAN, callingUid, scanCounter, bundle);
        }

    都是一样的套路,给状态机发消息。
    handleMessage方法被调用,和之前一模一样。
    然后当前状态的processMessage被调用来处理消息,不能处理就调用其父状态的processMessage来处理,以此类推。
    假设我们处于DriverStartedState状态,其processMessage方法如下:

        @Override
            public boolean processMessage(Message message) {
                logStateAndMessage(message, getClass().getSimpleName());
    
                switch(message.what) {
                    case CMD_START_SCAN:
                        handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
                        break;

    调用handleScanRequest进一步处理:

        private void handleScanRequest(int type, Message message) {
            ...
    
            // call wifi native to start the scan
            if (startScanNative(type, freqs)) {
                // only count battery consumption if scan request is accepted
                noteScanStart(message.arg1, workSource);
                // a full scan covers everything, clearing scan request buffer
                if (freqs == null)
                    mBufferedScanMsg.clear();
                messageHandlingStatus = MESSAGE_HANDLING_STATUS_OK;
                if (workSource != null) {
                    // External worksource was passed along the scan request,
                    // hence always send a broadcast
                    mSendScanResultsBroadcast = true;
                }
                return;
            }
            ...
        }
    

    可以看到调用startScanNative进一步处理:

        private boolean startScanNative(int type, String freqs) {
            if (mWifiNative.scan(type, freqs)) {
                mIsScanOngoing = true;
                mIsFullScanOngoing = (freqs == null);
                lastScanFreqs = freqs;
                return true;
            }
            return false;
        }

    调用到mWifiNative.scan,看过上一篇博客的就会知道WifiNative会直接调用jni方法,进一步调用wifi.c中的方法和wpa_supplicant交互。整个wifi的框架就走了一遍。
    执行完以后wpa_supplicant还要有事件返回呀,这个事件谁来接收?

    从wpa_supplicant接收事件-WifiMonitor

    WifiStateMachine的构造函数中创建了一个WifiMonitor的实例:

        public WifiStateMachine(Context context, String wlanInterface,
                                WifiTrafficPoller trafficPoller) {
            super("WifiStateMachine");
            mContext = context;
            mSetCountryCode = Settings.Global.getString(
                    mContext.getContentResolver(), Settings.Global.WIFI_COUNTRY_CODE);
            mInterfaceName = wlanInterface;
            mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");
            mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
                    BatteryStats.SERVICE_NAME));
    
            IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
            mNwService = INetworkManagementService.Stub.asInterface(b);
    
            mP2pSupported = mContext.getPackageManager().hasSystemFeature(
                    PackageManager.FEATURE_WIFI_DIRECT);
    
            mWifiNative = new WifiNative(mInterfaceName);
            mWifiConfigStore = new WifiConfigStore(context,this,  mWifiNative);
            mWifiAutoJoinController = new WifiAutoJoinController(context, this,
                    mWifiConfigStore, mWifiConnectionStatistics, mWifiNative);
            mWifiMonitor = new WifiMonitor(this, mWifiNative);

    然后在InitialState的processMessage中调用mWifiMonitor.startMonitoring();启动监听。

           @Override
            public boolean processMessage(Message message) {
                logStateAndMessage(message, getClass().getSimpleName());
                switch (message.what) {
                    case CMD_START_SUPPLICANT:
                         ...
                        if (mWifiNative.loadDriver()) {
                            try {
                                mNwService.wifiFirmwareReload(mInterfaceName, "STA");
                            } catch (Exception e) {
                                loge("Failed to reload STA firmware " + e);
                                // Continue
                            }
    
                           ...
    
                            if (mWifiNative.startSupplicant(mP2pSupported)) {
                                setWifiState(WIFI_STATE_ENABLING);
                                // MStar Android Patch Begin
                                mUnwantedNetwork = false;
                                // MStar Android Patch End
                                if (DBG) log("Supplicant start successful");
                                mWifiMonitor.startMonitoring();
                                transitionTo(mSupplicantStartingState);
                            } else {
                                loge("Failed to start supplicant!");
                            }
    

    startMonitoring方法定义如下:

        public void startMonitoring() {
            WifiMonitorSingleton.sInstance.startMonitoring(mInterfaceName);
        }

    调用它内部类WifiMonitorSingleton中的startMonitoring做进一步处理:

           public synchronized void startMonitoring(String iface) {
                WifiMonitor m = mIfaceMap.get(iface);
                if (m == null) {
                    Log.e(TAG, "startMonitor called with unknown iface=" + iface);
                    return;
                }
    
                Log.d(TAG, "startMonitoring(" + iface + ") with mConnected = " + mConnected);
    
                if (mConnected) {
                    m.mMonitoring = true;
                    m.mStateMachine.sendMessage(SUP_CONNECTION_EVENT);
                } else {
                    if (DBG) Log.d(TAG, "connecting to supplicant");
                    int connectTries = 0;
                    while (true) {
                        if (false == m.mPlugFlag) {
                            m.mStateMachine.sendMessage(SUP_DISCONNECTION_EVENT);
                            Log.e(TAG, "startMonitoring(" + iface + ") failed! Wifi device unplug!");
                            break;
                        }
                        if (mWifiNative.connectToSupplicant()) {
                            m.mMonitoring = true;
                            m.mStateMachine.sendMessage(SUP_CONNECTION_EVENT);
                            mConnected = true;
                            new MonitorThread(mWifiNative, this).start();
                            break;
                        }
                        if (connectTries++ < 5) {
                            try {
                                Thread.sleep(1000);
                            } catch (InterruptedException ignore) {
                            }
                        } else {
                            m.mStateMachine.sendMessage(SUP_DISCONNECTION_EVENT);
                            Log.e(TAG, "startMonitoring(" + iface + ") failed!");
                            break;
                        }
                    }
                }
            }
    

    1.首先要判断是够和wpa_supplicant建立连接,如果建立连接了,就一个wifi状态机发送一个SUP_CONNECTION_EVENT消息。如果没有建立连接就尝试建立连接。
    2.使用mWifiNative.connectToSupplicant()和wpa_supplicant建立连接。
    3.创建MonitorThread监听线程。
    这个线程工作非常简单,定义如下:

        private static class MonitorThread extends Thread {
            private final WifiNative mWifiNative;
            private final WifiMonitorSingleton mWifiMonitorSingleton;
            private final LocalLog mLocalLog = WifiNative.getLocalLog();
    
            public MonitorThread(WifiNative wifiNative, WifiMonitorSingleton wifiMonitorSingleton) {
                super("WifiMonitor");
                mWifiNative = wifiNative;
                mWifiMonitorSingleton = wifiMonitorSingleton;
            }
    
            public void run() {
                if (DBG) {
                    Log.d(TAG, "MonitorThread start with mConnected=" +
                         mWifiMonitorSingleton.mConnected);
                }
                //noinspection InfiniteLoopStatement
                for (;;) {
                    if (!mWifiMonitorSingleton.mConnected) {
                        if (DBG) Log.d(TAG, "MonitorThread exit because mConnected is false");
                        break;
                    }
                    String eventStr = mWifiNative.waitForEvent();
    
                    // Skip logging the common but mostly uninteresting events
                    if (eventStr.indexOf(BSS_ADDED_STR) == -1
                            && eventStr.indexOf(BSS_REMOVED_STR) == -1) {
                        if (DBG) Log.d(TAG, "Event [" + eventStr + "]");
                        mLocalLog.log("Event [" + eventStr + "]");
                    }
    
                    if (mWifiMonitorSingleton.dispatchEvent(eventStr)) {
                        if (DBG) Log.d(TAG, "Disconnecting from the supplicant, no more events");
                        break;
                    }
                }
            }
        }

    1.使用mWifiNative.waitForEvent();监听wpa_supplicant发送上来的事件。
    2.使用mWifiMonitorSingleton.dispatchEvent(eventStr)分发事件。
    时间分发过程如下:

            private synchronized boolean dispatchEvent(String eventStr) {
                String iface;
                // IFNAME=wlan0 ANQP-QUERY-DONE addr=18:cf:5e:26:a4:88 result=SUCCESS
                if (eventStr.startsWith("IFNAME=")) {
                    int space = eventStr.indexOf(' ');
                    if (space != -1) {
                        iface = eventStr.substring(7, space);
                        if (!mIfaceMap.containsKey(iface) && iface.startsWith("p2p-")) {
                            // p2p interfaces are created dynamically, but we have
                            // only one P2p state machine monitoring all of them; look
                            // for it explicitly, and send messages there ..
                            iface = "p2p0";
                        }
                        eventStr = eventStr.substring(space + 1);
                    } else {
                        // No point dispatching this event to any interface, the dispatched
                        // event string will begin with "IFNAME=" which dispatchEvent can't really
                        // do anything about.
                        Log.e(TAG, "Dropping malformed event (unparsable iface): " + eventStr);
                        return false;
                    }
                } else {
                    // events without prefix belong to p2p0 monitor
                    iface = "p2p0";
                }
    
                if (VDBG) Log.d(TAG, "Dispatching event to interface: " + iface);
    
                WifiMonitor m = mIfaceMap.get(iface);
                if (m != null) {
                    if (m.mMonitoring) {
                        if (m.dispatchEvent(eventStr, iface)) {
                            mConnected = false;
                            return true;
                        }
    
                        return false;
                    } else {
                        if (DBG) Log.d(TAG, "Dropping event because (" + iface + ") is stopped");
                        return false;
                    }
                } else {
                    if (DBG) Log.d(TAG, "Sending to all monitors because there's no matching iface");
                    boolean done = false;
                    boolean isMonitoring = false;
                    boolean isTerminating = false;
                    if (eventStr.startsWith(EVENT_PREFIX_STR)
                            && eventStr.contains(TERMINATING_STR)) {
                        isTerminating = true;
                    }
                    for (WifiMonitor monitor : mIfaceMap.values()) {
                        if (monitor.mMonitoring) {
                            isMonitoring = true;
                            if (monitor.dispatchEvent(eventStr, iface)) {
                                done = true;
                            }
                        }
                    }
    
                    if (!isMonitoring && isTerminating) {
                        done = true;
                    }
    
                    if (done) {
                        mConnected = false;
                    }
    
                    return done;
                }
            }
        }

    1.wpa_supplicant发送上来的时间是一个字符串,这里需要对这个字符串做一个解析,找到对应接口的WifiMontor.
    2.调用对应WifiMontor的事件分发方法:monitor.dispatchEvent(eventStr, iface)。
    wifi的接口一般为wlan0,此时下面的方法会被调用:

        /* @return true if the event was supplicant disconnection */
        private boolean dispatchEvent(String eventStr, String iface) {
    
            if (DBG) {
                // Dont log CTRL-EVENT-BSS-ADDED which are too verbose and not handled
                if (eventStr != null && !eventStr.contains("CTRL-EVENT-BSS-ADDED")) {
                    logDbg("WifiMonitor:" + iface + " cnt=" + Integer.toString(eventLogCounter)
                            + " dispatchEvent: " + eventStr);
                }
            }
    
            if (!eventStr.startsWith(EVENT_PREFIX_STR)) {
                if (eventStr.startsWith(WPA_EVENT_PREFIX_STR) &&
                        0 < eventStr.indexOf(PASSWORD_MAY_BE_INCORRECT_STR)) {
                   mStateMachine.sendMessage(AUTHENTICATION_FAILURE_EVENT, eventLogCounter);
                } else if (eventStr.startsWith(WPS_SUCCESS_STR)) {
                    mStateMachine.sendMessage(WPS_SUCCESS_EVENT);
                } else if (eventStr.startsWith(WPS_FAIL_STR)) {
                    handleWpsFailEvent(eventStr);
                } else if (eventStr.startsWith(WPS_OVERLAP_STR)) {
                    mStateMachine.sendMessage(WPS_OVERLAP_EVENT);
                } else if (eventStr.startsWith(WPS_TIMEOUT_STR)) {
                    mStateMachine.sendMessage(WPS_TIMEOUT_EVENT);
                } else if (eventStr.startsWith(P2P_EVENT_PREFIX_STR)) {
                    handleP2pEvents(eventStr);
                } else if (eventStr.startsWith(HOST_AP_EVENT_PREFIX_STR)) {
                    handleHostApEvents(eventStr);
                } else if (eventStr.startsWith(ANQP_DONE_STR)) {
                    try {
                        handleAnqpResult(eventStr);
                    }
                    catch (IllegalArgumentException iae) {
                        Log.e(TAG, "Bad ANQP event string: '" + eventStr + "': " + iae);
                    }
                } else if (eventStr.startsWith(GAS_QUERY_PREFIX_STR)) {        // !!! clean >>End
                    handleGasQueryEvents(eventStr);
                } else if (eventStr.startsWith(RX_HS20_ANQP_ICON_STR)) {
                    if (mStateMachine2 != null)
                        mStateMachine2.sendMessage(RX_HS20_ANQP_ICON_EVENT,
                                eventStr.substring(RX_HS20_ANQP_ICON_STR_LEN + 1));
                } else if (eventStr.startsWith(HS20_PREFIX_STR)) {                  // !!! <<End
                    handleHs20Events(eventStr);
                } else if (eventStr.startsWith(REQUEST_PREFIX_STR)) {
                    handleRequests(eventStr);
                } else if (eventStr.startsWith(TARGET_BSSID_STR)) {
                    handleTargetBSSIDEvent(eventStr);
                } else if (eventStr.startsWith(ASSOCIATED_WITH_STR)) {
                    handleAssociatedBSSIDEvent(eventStr);
                } else if (eventStr.startsWith(AUTH_EVENT_PREFIX_STR) &&
                        eventStr.endsWith(AUTH_TIMEOUT_STR)) {
                    mStateMachine.sendMessage(AUTHENTICATION_FAILURE_EVENT);
                } else {
                    if (DBG) Log.w(TAG, "couldn't identify event type - " + eventStr);
                }
                eventLogCounter++;
                return false;
            }
    
            String eventName = eventStr.substring(EVENT_PREFIX_LEN_STR);
            int nameEnd = eventName.indexOf(' ');
            if (nameEnd != -1)
                eventName = eventName.substring(0, nameEnd);
            if (eventName.length() == 0) {
                if (DBG) Log.i(TAG, "Received wpa_supplicant event with empty event name");
                eventLogCounter++;
                return false;
            }
            /*
            * Map event name into event enum
            */
            int event;
            if (eventName.equals(CONNECTED_STR))
                event = CONNECTED;
            else if (eventName.equals(DISCONNECTED_STR))
                event = DISCONNECTED;
            else if (eventName.equals(STATE_CHANGE_STR))
                event = STATE_CHANGE;
            else if (eventName.equals(SCAN_RESULTS_STR))
                event = SCAN_RESULTS;
            else if (eventName.equals(SCAN_FAILED_STR))
                event = SCAN_FAILED;
            else if (eventName.equals(LINK_SPEED_STR))
                event = LINK_SPEED;
            else if (eventName.equals(TERMINATING_STR))
                event = TERMINATING;
            else if (eventName.equals(DRIVER_STATE_STR))
                event = DRIVER_STATE;
            else if (eventName.equals(EAP_FAILURE_STR))
                event = EAP_FAILURE;
            else if (eventName.equals(ASSOC_REJECT_STR))
                event = ASSOC_REJECT;
            else if (eventName.equals(TEMP_DISABLED_STR)) {
                event = SSID_TEMP_DISABLE;
            } else if (eventName.equals(REENABLED_STR)) {
                event = SSID_REENABLE;
            } else if (eventName.equals(BSS_ADDED_STR)) {
                event = BSS_ADDED;
            } else if (eventName.equals(BSS_REMOVED_STR)) {
                event = BSS_REMOVED;
            } else
                event = UNKNOWN;
    
            String eventData = eventStr;
            if (event == DRIVER_STATE || event == LINK_SPEED)
                eventData = eventData.split(" ")[1];
            else if (event == STATE_CHANGE || event == EAP_FAILURE) {
                int ind = eventStr.indexOf(" ");
                if (ind != -1) {
                    eventData = eventStr.substring(ind + 1);
                }
            } else {
                int ind = eventStr.indexOf(" - ");
                if (ind != -1) {
                    eventData = eventStr.substring(ind + 3);
                }
            }
    
            if ((event == SSID_TEMP_DISABLE)||(event == SSID_REENABLE)) {
                String substr = null;
                int netId = -1;
                int ind = eventStr.indexOf(" ");
                if (ind != -1) {
                    substr = eventStr.substring(ind + 1);
                }
                if (substr != null) {
                    String status[] = substr.split(" ");
                    for (String key : status) {
                        if (key.regionMatches(0, "id=", 0, 3)) {
                            int idx = 3;
                            netId = 0;
                            while (idx < key.length()) {
                                char c = key.charAt(idx);
                                if ((c >= 0x30) && (c <= 0x39)) {
                                    netId *= 10;
                                    netId += c - 0x30;
                                    idx++;
                                } else {
                                    break;
                                }
                            }
                        }
                    }
                }
                mStateMachine.sendMessage((event == SSID_TEMP_DISABLE)?
                        SSID_TEMP_DISABLED:SSID_REENABLED, netId, 0, substr);
            } else if (event == STATE_CHANGE) {
                handleSupplicantStateChange(eventData);
            } else if (event == DRIVER_STATE) {
                handleDriverEvent(eventData);
            } else if (event == TERMINATING) {
                /**
                 * Close the supplicant connection if we see
                 * too many recv errors
                 */
                if (eventData.startsWith(WPA_RECV_ERROR_STR)) {
                    if (++sRecvErrors > MAX_RECV_ERRORS) {
                        if (DBG) {
                            Log.d(TAG, "too many recv errors, closing connection");
                        }
                    } else {
                        eventLogCounter++;
                        return false;
                    }
                }
    
                // Notify and exit
                mStateMachine.sendMessage(SUP_DISCONNECTION_EVENT, eventLogCounter);
                return true;
            } else if (event == EAP_FAILURE) {
                if (eventData.startsWith(EAP_AUTH_FAILURE_STR)) {
                    logDbg("WifiMonitor send auth failure (EAP_AUTH_FAILURE) ");
                    mStateMachine.sendMessage(AUTHENTICATION_FAILURE_EVENT, eventLogCounter);
                }
            } else if (event == ASSOC_REJECT) {
                Matcher match = mAssocRejectEventPattern.matcher(eventData);
                String BSSID = "";
                int status = -1;
                if (!match.find()) {
                    if (DBG) Log.d(TAG, "Assoc Reject: Could not parse assoc reject string");
                } else {
                    BSSID = match.group(1);
                    try {
                        status = Integer.parseInt(match.group(2));
                    } catch (NumberFormatException e) {
                        status = -1;
                    }
                }
                mStateMachine.sendMessage(ASSOCIATION_REJECTION_EVENT, eventLogCounter, status, BSSID);
            } else if (event == BSS_ADDED && !VDBG) {
                // Ignore that event - it is not handled, and dont log it as it is too verbose
            } else if (event == BSS_REMOVED && !VDBG) {
                // Ignore that event - it is not handled, and dont log it as it is too verbose
            }  else {
                    handleEvent(event, eventData);
            }
            sRecvErrors = 0;
            eventLogCounter++;
            return false;
        }

    部分时间会调用handleEvent(event, eventData);进一步处理:

       void handleEvent(int event, String remainder) {
            if (DBG) {
                logDbg("handleEvent " + Integer.toString(event) + "  " + remainder);
            }
            switch (event) {
                case DISCONNECTED:
                    handleNetworkStateChange(NetworkInfo.DetailedState.DISCONNECTED, remainder);
                    break;
    
                case CONNECTED:
                    handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED, remainder);
                    break;
    
                case SCAN_RESULTS:
                    mStateMachine.sendMessage(SCAN_RESULTS_EVENT);
                    break;
    
                case SCAN_FAILED:
                    mStateMachine.sendMessage(SCAN_FAILED_EVENT);
                    break;
    
                case UNKNOWN:
                    if (DBG) {
                        logDbg("handleEvent unknown: " + Integer.toString(event) + "  " + remainder);
                    }
                    break;
                default:
                    break;
            }
        }
    

    这样我们之前的操作:
    2扫描附近热点
    startScan();之后,接受WifiManager.SCAN_RESULTS_AVAILABLE_ACTION的广播会触发,在这个广播中调用getScanResults()方法可以获得一个List,它里面的每一个条目就是一个可连接的热点。
    wifi.startScan();
    results = wifi.getScanResults();
    为什么会有广播通知扫描结果已经可用了就有答案了。
    我们startScan以后,wpa_supplicant扫描结束后会发送SCAN_RESULTS事件上来,在handleEvent方法中,检测到事件为SCAN_RESULTS后就向wifi状态机发送一个消息:mStateMachine.sendMessage(SCAN_RESULTS_EVENT);在wifi状态机中处理过程如下所示:

                   case WifiMonitor.SCAN_FAILED_EVENT:
                        maybeRegisterNetworkFactory(); // Make sure our NetworkFactory is registered
                        closeRadioScanStats();
                        noteScanEnd();
                        setScanResults();
                        // when wpa_supplicant start, it will execute scan, lead to WifiStateMachine's scan command
                        // operate failed, because wpa_supplicant's scan is ongoing, mIsFullScanOngoing still false,
                        // it can not send ScanResultsBroadcast, app can not receive ScanResultsBroadcast, so that
                        // sometime app refresh the ap list is slow in first open wifi, so now, when it receive
                        // SCAN_RESULTS_EVENT, send ScanResultsBroadcast directly, to refresh ap list
                        //if (mIsFullScanOngoing || mSendScanResultsBroadcast) {
                        /* Just updated results from full scan, let apps know about this */
                        boolean scanSucceeded = message.what == WifiMonitor.SCAN_RESULTS_EVENT;
                        sendScanResultsAvailableBroadcast(scanSucceeded);
                        //}
    
                        mSendScanResultsBroadcast = false;
                        mIsScanOngoing = false;
                        mIsFullScanOngoing = false;
                        if (mBufferedScanMsg.size() > 0)
                            sendMessage(mBufferedScanMsg.remove());
                        break;

    1.使用setScanResults设置扫描的结果。
    2.使用sendScanResultsAvailableBroadcast(scanSucceeded);发送扫描结果可用的广播。

    综上,整个Android系统的wifi框架我们通过对源码的追踪,梳理了一遍。wifi框架中有两个非常重要的状态机:mWifiController 和mWifiStateMachine,他们相互配合,维护这wifi的各个状态。mWifiStateMachine内部使用WifiNative向wpa_supplicant发送各种命令。WifiStateMachine中还有一个WifiMonitor,它负责接收wpa_supplicant发送上来的事件。这样,结合事件的发送和接收,我们就理清了应用程序和wpa_supplicant交互的过程。

    展开全文
  • wifi万能钥匙

    2017-11-06 18:51:59
    测试是的阿大声道法守法发送父亲法守法法守法是分发发法守法法守法1分发是发分发
  • WifiController 是高级别的wifi状态机,因为它管理的状态是wifi开关,wifi热点开关等状态,只有在wifi开关等具体状态下,判断wifi处于启动扫描附近热点状态等才是有意义的。 WifiController(Context context, ...

    一 前言

          android框架层的函数调用是出了名的绕,开发者可能因为各种原因比如避免冲突、条件判断、函数封装等等各种各样需要考虑的因素而使得框架层的方法调用显得比较长,所以看源码的时候先看大体流程,有需要再深入某些重要的细节。不然一入源码深似海,会淹没在茫茫源码中。繁杂的东西,自己只要不凌乱,找好线索,进行总结,便会简单。

     

    二 图示调用流程

     

                                              

    三 代码具体流程

    1 应用层

    1.1 packages/apps/settings/WifiSettings.java

     在onStart()创建一个WifiEnabler对象,实现wifi开关功能。

    @Override
    public void onStart() {
        super.onStart();
    
        // On/off switch is hidden for Setup Wizard (returns null)
        mWifiEnabler = createWifiEnabler();
    
        if (mIsRestricted) {
            restrictUi();
            return;
        }
    
        onWifiStateChanged(mWifiManager.getWifiState());
    }
    private WifiEnabler createWifiEnabler() {
        final SettingsActivity activity = (SettingsActivity) getActivity();
         return new WifiEnabler(activity, new SwitchBarController(activity.getSwitchBar()),
                mMetricsFeatureProvider);
    }

     

    1.2 packages/apps/settings/WifiEnabler.java

    开启Wifi开关变会有下面的操作。

    @Override
        public boolean onSwitchToggled(boolean isChecked) {
        //Do nothing if called as a result of a state machine event
        if (mStateMachineEvent) {
            return true;
        }
        // Show toast message if Wi-Fi is not allowed in airplane mode
        if (isChecked && !WirelessUtils.isRadioAllowed(mContext, Settings.Global.RADIO_WIFI)) {
            Toast.makeText(mContext, R.string.wifi_in_airplane_mode, Toast.LENGTH_SHORT).show();
            // Reset switch to off. No infinite check/listener loop.
            mSwitchWidget.setChecked(false);
            return false;
        }
    
        if (isChecked) {
            mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_WIFI_ON);
        } else {
            // Log if user was connected at the time of switching off.
            mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_WIFI_OFF,
            mConnected.get());
        }
        if (!mWifiManager.setWifiEnabled(isChecked)) {
            // Error
            mSwitchWidget.setEnabled(true);
            Toast.makeText(mContext, R.string.wifi_error, Toast.LENGTH_SHORT).show();
        }
        return true;
    }

     点击开关会调用mWifiManager.setWifiEnabled。

     

    2 java 框架层

    2.1 frameworks/base/wifi/java/android/net/wifi/WifiManager.java

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

     

    2.2 frameworks/base/wifi/java/android/net/wifi/IWifiManager.aidl

    boolean setWifiEnabled(String packageName, boolean enable);

     

    2.3 frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiSeviceImpl.java

    @Override
    public synchronized boolean setWifiEnabled(String packageName, boolean enable)throws RemoteException {
        if (enforceChangePermission(packageName) != MODE_ALLOWED) {            return false;
        }
    
        Slog.d(TAG, "setWifiEnabled: " + enable + " pid=" + Binder.getCallingPid()
                + ", uid=" + Binder.getCallingUid() + ", package=" + packageName);
        mLog.info("setWifiEnabled package=% uid=% enable=%").c(packageName).c(Binder.getCallingUid()).c(enable).flush();
    
        boolean isFromSettings = checkNetworkSettingsPermission(
                    Binder.getCallingPid(), Binder.getCallingUid());
    
        // If Airplane mode is enabled, only Settings is allowed to toggle Wifi
        if (mSettingsStore.isAirplaneModeOn() && !isFromSettings) {
                mLog.info("setWifiEnabled in Airplane mode: only Settings can enable wifi").flush();
                return false;
        }
    
        // If SoftAp is enabled, only Settings is allowed to toggle wifi
        boolean apEnabled = mWifiApState == WifiManager.WIFI_AP_STATE_ENABLED;
    
        if (apEnabled && !isFromSettings) {
            mLog.info("setWifiEnabled SoftAp not disabled: only Settings can enable wifi").flush();
            return false;
        }
    
        /*
        * 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);
            }
    
    
        if (mPermissionReviewRequired) {
            final int wiFiEnabledState = getWifiEnabledState();
            if (enable) {
                if (wiFiEnabledState == WifiManager.WIFI_STATE_DISABLING
                        || wiFiEnabledState == WifiManager.WIFI_STATE_DISABL){
                    if (startConsentUi(packageName, Binder.getCallingUid(),
                        WifiManager.ACTION_REQUEST_ENABLE)) {
                            return true;
                    }
            }
            } else if (wiFiEnabledState == WifiManager.WIFI_STATE_ENABLING
                || wiFiEnabledState == WifiManager.WIFI_STATE_ENABLED) {
                    if (startConsentUi(packageName, Binder.getCallingUid(),
                        WifiManager.ACTION_REQUEST_DISABLE)) {
                            return true;
                    }
            }
        }
    
        mWifiController.sendMessage(CMD_WIFI_TOGGLED);
        return true;
    }

           WifiManager.setWifiEnabled通过aidl跨进程调用到了WifiServiceImpl.setWifiEnabled,其中WifiServiceImpl是WifiService的实现类。在WifiServiceImpl的setWifiEnabled方法里做的一些事情:

          enforceChangePermission 判断调用的进程是否有权限。想要开关wifi需要CHANGE_WIFI_STATE 权限。

          isAirplaneModeOn 判断飞行模式。

          handleWifiToggled 保存wifi 操作的状态。

          向WifiController发送CMD_WIFI_TOGGLED消息。

     

    2.4 frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiController.java

            首先讲一下WifiController,它是一个状态机,可能Android每个版本的状态机不完全一样,比如Android P 和Android O比起来状态机减少了一些分支。 WifiController在WIfiServiceImpl的构造函数中初始化、并开始运行。

             WifiController 和WifiStateMachine 不同,WifiStateMachine是一个复杂的状态机,它维护了Wifi的启动、扫描、连接、断开等多个状态。WifiController 是高级别的wifi状态机,因为它管理的状态是wifi开关,wifi热点开关等状态,只有在wifi开关等具体状态下,判断wifi处于启动扫描附近热点状态等才是有意义的。

    WifiController(Context context, WifiStateMachine wsm, Looper                        
            wifiStateMachineLooper, WifiSettingsStore wss, Looper                                   
            wifiServiceLooper, FrameworkFacade f,WifiStateMachinePrime wsmp) {
        super(TAG, wifiServiceLooper);
        ...
    
        // CHECKSTYLE:OFF IndentationCheck
        addState(mDefaultState);
            addState(mStaDisabledState, mDefaultState);
            addState(mStaEnabledState, mDefaultState);
                addState(mDeviceActiveState, mStaEnabledState);
            addState(mStaDisabledWithScanState, mDefaultState);
            addState(mEcmState, mDefaultState);
        // CHECKSTYLE:ON IndentationCheck
        ...
    
        if (checkScanOnlyModeAvailable()) {
            setInitialState(mStaDisabledWithScanState);
        }else {
            setInitialState(mStaDisabledState);
        }
        ...
        
    }

     WifiController状态机各状态关系:

            

                                     

      状态机初始状态为StaDisabledState,在该状态下对CMD_WIFI_TOGGLED消息的处理:

    class StaDisabledState extends State {
        private int mDeferredEnableSerialNumber = 0;
        private boolean mHaveDeferredEnable = false;
        private long mDisabledTimestamp;
    
        @Override
        public void enter() {
            mWifiStateMachinePrime.disableWifi();
            // Supplicant can't restart right away, so note the time we switched off
            mDisabledTimestamp = SystemClock.elapsedRealtime();
            mDeferredEnableSerialNumber++;
            mHaveDeferredEnable = false;
            mWifiStateMachine.clearANQPCache();
        }
        @Override
        public boolean processMessage(Message msg) {
            switch (msg.what) {
                case CMD_WIFI_TOGGLED:
                    if (mSettingsStore.isWifiToggleEnabled()) {
                        if (doDeferEnable(msg)) {
                            if (mHaveDeferredEnable) {
                                //  have 2 toggles now, inc serial number and ignore both
                                mDeferredEnableSerialNumber++;
                            }
                            mHaveDeferredEnable = !mHaveDeferredEnable;
                            break;
                        }
                        transitionTo(mDeviceActiveState);
                    } else if (checkScanOnlyModeAvailable()) {
                        // only go to scan mode if we aren't in airplane mode
                        if (mSettingsStore.isAirplaneModeOn()) {
                            transitionTo(mStaDisabledWithScanState);
                        }
                    }
                    break;
                ...
                    
                default:
                    return NOT_HANDLED;
            }
            return HANDLED;
        }
    }

       在StaDisabledState状态下没做什么处理,接着转换到DeviceActiveState状态,StaEnabledState是它的父状态,由StateMachine的知识可知,转换到该状态时,会依次调用父、子状态的enter()函数。先看DeviceActiveState的父状态StaEnabledState:

    class StaEnabledState extends State {
        @Override
            public void enter() {
                log("StaEnabledState.enter()");
            }
    
            @Override
            public boolean processMessage(Message msg) {
                switch (msg.what) {
                    case CMD_WIFI_TOGGLED:
                        if (! mSettingsStore.isWifiToggleEnabled()) {
                            if (checkScanOnlyModeAvailable()) {
                                transitionTo(mStaDisabledWithScanState);
                            } else {
                                transitionTo(mStaDisabledState);
                            }
                        }
                        break;
                    ...
                    
    }

    StaEnabledState状态下也没做什么处理,再接着看DeviceActiveState状态:

    /**
    * Parent: StaEnabledState
    *
    * TODO (b/79209870): merge DeviceActiveState and StaEnabledState into a single state
    */
    class DeviceActiveState extends State {
        @Override
        public void enter() {
            mWifiStateMachinePrime.enterClientMode();
            mWifiStateMachine.setHighPerfModeEnabled(false);
        }
        ...
    }
                

    可以看到在DeviceActiveState状态下主要做了两个操作mWifiStateMachinePrime.enterClientMode()和
            mWifiStateMachine.setHighPerfModeEnabled(false),主要看mWifiStateMachinePrime.enterClientMode()。

     

    2.5 frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiStateMachinePrime.java

    /**
    * Method to switch wifi into client mode where connections to configured networks will be
    * attempted.
    */
    public void enterClientMode() {
        changeMode(ModeStateMachine.CMD_START_CLIENT_MODE);
    }
    private void changeMode(int newMode) {
        mModeStateMachine.sendMessage(newMode);
    }

    ModeStateMachine又是一个状态机,不过这个状态机比较简单只有三个状态,初始状态为WifiDisabledState。

    private class ModeStateMachine extends StateMachine {
        // Commands for the state machine  - these will be removed,
        // along with the StateMachine itself
        public static final int CMD_START_CLIENT_MODE    = 0;
        public static final int CMD_START_SCAN_ONLY_MODE = 1;
        public static final int CMD_DISABLE_WIFI         = 3;
    
        private final State mWifiDisabledState = new WifiDisabledState();
        private final State mClientModeActiveState = new ClientModeActiveState();
        private final State mScanOnlyModeActiveState = new ScanOnlyModeActiveState();
    
        ModeStateMachine() {
            super(TAG, mLooper);
    
            addState(mClientModeActiveState);
            addState(mScanOnlyModeActiveState);
            addState(mWifiDisabledState);
    
            Log.d(TAG, "Starting Wifi in WifiDisabledState");
            setInitialState(mWifiDisabledState);
            start();
            ...
        }
     }

    那就看看ModeStateMachine的相关处理,先是在初始状态WifiDisabledState中。

     

    class WifiDisabledState extends ModeActiveState {
        @Override
        public void enter() {
            Log.d(TAG, "Entering WifiDisabledState");
            mDefaultModeManager.sendScanAvailableBroadcast(mContext, false);
            mScanRequestProxy.enableScanningForHiddenNetworks(false);
            mScanRequestProxy.clearScanResults();
        }
    
        @Override
        public boolean processMessage(Message message) {
        Log.d(TAG, "received a message in WifiDisabledState: " + message);
            if (checkForAndHandleModeChange(message)) {
                return HANDLED;
            }
        return NOT_HANDLED;
        }
    
        @Override
        public void exit() {
            // do not have an active mode manager...  nothing to clean up
        }
    }
    private boolean checkForAndHandleModeChange(Message message) {
        switch(message.what) {
            case ModeStateMachine.CMD_START_CLIENT_MODE:
                Log.d(TAG, "Switching from " + getCurrentMode() + " to ClientMode");
                mModeStateMachine.transitionTo(mClientModeActiveState);
                break;
            case ModeStateMachine.CMD_START_SCAN_ONLY_MODE:
                Log.d(TAG, "Switching from " + getCurrentMode() + " to ScanOnlyMode");
                mModeStateMachine.transitionTo(mScanOnlyModeActiveState);
                break;
            case ModeStateMachine.CMD_DISABLE_WIFI:
                Log.d(TAG, "Switching from " + getCurrentMode() + " to WifiDisabled");
                mModeStateMachine.transitionTo(mWifiDisabledState);
                break;
            default:
                return NOT_HANDLED;
        }
        return HANDLED;
    }

    状态机从WifiDisabledState状态转向ClientModeActiveState状态,所以再继续看ClientModeActiveState。

    class ClientModeActiveState extends ModeActiveState {
        ClientListener mListener;
        private class ClientListener implements ClientModeManager.Listener {
            @Override
            public void onStateChanged(int state) {
                // make sure this listener is still active
                if (this != mListener) {
                    Log.d(TAG, "Client mode state change from previous manager");
                    return;
                }
    
                Log.d(TAG, "State changed from client mode. state = " + state);
    
                if (state == WifiManager.WIFI_STATE_UNKNOWN) {
                    // error while setting up client mode or an unexpected failure.
                    mModeStateMachine.sendMessage(CMD_CLIENT_MODE_FAILED, this);
                } else if (state == WifiManager.WIFI_STATE_DISABLED) {
                    // client mode stopped
                    mModeStateMachine.sendMessage(CMD_CLIENT_MODE_STOPPED, this);
                } else if (state == WifiManager.WIFI_STATE_ENABLED) {
                    // client mode is ready to go
                    Log.d(TAG, "client mode active");
                } else {
                    // only care if client mode stopped or started, dropping
                }
            }
        }
    
        @Override
        public void enter() {
            Log.d(TAG, "Entering ClientModeActiveState");
    
            mListener = new ClientListener();
            mManager = mWifiInjector.makeClientModeManager(mListener);
            mManager.start();
            mActiveModeManagers.add(mManager);
    
            updateBatteryStatsWifiState(true);
        }
        ...
    }

    这里的mManager是ActiveModeManager,是个接口,这里的ClientModeManager实现了这个接口。我们继续走下去,去看ClientModeManager,主要看mManager.start()这个调用。

     

    2.6 frameworks/opt/net/wifi/service/java/com/android/server/wifi/ClientModeManager.java

    /**
    * Start client mode.
    */
    public void start() {
        mStateMachine.sendMessage(ClientModeStateMachine.CMD_START);
    }

    ClientModeStateMachine也是个状态机,该状态机只有两个状态,初始状态为IdleState。

    private class ClientModeStateMachine extends StateMachine {
        // Commands for the state machine.
        public static final int CMD_START = 0;
        public static final int CMD_INTERFACE_STATUS_CHANGED = 3;
        public static final int CMD_INTERFACE_DESTROYED = 4;
        public static final int CMD_INTERFACE_DOWN = 5;
        private final State mIdleState = new IdleState();
        private final State mStartedState = new StartedState();
        ...
    
        ClientModeStateMachine(Looper looper) {
            super(TAG, looper);
    
            addState(mIdleState);
            addState(mStartedState);
    
            setInitialState(mIdleState);
            start();
        }
        ...
    }

    看一下初始状态Idlesate状态的处理

    private class IdleState extends State {
    
        @Override
        public void enter() {
            Log.d(TAG, "entering IdleState");
            mClientInterfaceName = null;
            mIfaceIsUp = false;
        }
    
        @Override
        public boolean processMessage(Message message) {
            switch (message.what) {
                case CMD_START:
                    updateWifiState(WifiManager.WIFI_STATE_ENABLING,
                            WifiManager.WIFI_STATE_DISABLED);
    
                    mClientInterfaceName = mWifiNative.setupInterfaceForClientMode(
                            false /* not low priority */, mWifiNativeInterfaceCallback);
                    if (TextUtils.isEmpty(mClientInterfaceName)) {
                        Log.e(TAG, "Failed to create ClientInterface. Sit in Idle");
                        updateWifiState(WifiManager.WIFI_STATE_UNKNOWN,
                                WifiManager.WIFI_STATE_ENABLING);
                        updateWifiState(WifiManager.WIFI_STATE_DISABLED,
                                WifiManager.WIFI_STATE_UNKNOWN);
                        break;
                    }
                    sendScanAvailableBroadcast(false);
                    mScanRequestProxy.enableScanningForHiddenNetworks(false);
                    mScanRequestProxy.clearScanResults();
                    transitionTo(mStartedState);
                    break;
                default:
                    Log.d(TAG, "received an invalid message: " + message);
                    return NOT_HANDLED;
            }
            return HANDLED;
        }
    }

    主要看 mWifiNative.setupInterfaceForClientMode的操作。

     

    2.7 frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiNative.java

    /**
    * Setup an interface for Client mode operations.
    *
    * * This method configures an interface in STA mode in all the native daemons
    * (wificond, wpa_supplicant & vendor HAL).
    *
    * @param lowPrioritySta The requested STA has a low request priority (lower probability of
    *                       getting created, higher probability of getting destroyed).
    * @param interfaceCallback Associated callback for notifying status changes for the iface.
    * @return Returns the name of the allocated interface, will be null on failure.
    */
    public String setupInterfaceForClientMode(boolean lowPrioritySta,
    @NonNull InterfaceCallback interfaceCallback) {
        synchronized (mLock) {
            if (!startHal()) {
                Log.e(TAG, "Failed to start Hal");
                mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToHal();
                return null;
            }
            if (!startSupplicant()) {
                Log.e(TAG, "Failed to start supplicant");
                mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToSupplicant();
                return null;
            }
            Iface iface = mIfaceMgr.allocateIface(Iface.IFACE_TYPE_STA);
            if (iface == null) {
                Log.e(TAG, "Failed to allocate new STA iface");
                return null;
            }
            iface.externalListener = interfaceCallback;
            iface.name = createStaIface(iface, lowPrioritySta);
            if (TextUtils.isEmpty(iface.name)) {
                Log.e(TAG, "Failed to create STA iface in vendor HAL");
                mIfaceMgr.removeIface(iface.id);
                mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToHal();
                return null;
            }
            if (mWificondControl.setupInterfaceForClientMode(iface.name) == null) {
                Log.e(TAG, "Failed to setup iface in wificond on " + iface);
                teardownInterface(iface.name);
                mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToWificond();
                return null;
            }
            if (!mSupplicantStaIfaceHal.setupIface(iface.name)) {
                Log.e(TAG, "Failed to setup iface in supplicant on " + iface);
                teardownInterface(iface.name);
                mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToSupplicant();
                return null;
            }
            iface.networkObserver = new NetworkObserverInternal(iface.id);
            if (!registerNetworkObserver(iface.networkObserver)) {
                Log.e(TAG, "Failed to register network observer on " + iface);
                teardownInterface(iface.name);
                return null;
            }
            mWifiMonitor.startMonitoring(iface.name);
            // Just to avoid any race conditions with interface state change callbacks,
            // update the interface state before we exit.
            onInterfaceStateChanged(iface, isInterfaceUp(iface.name));
            initializeNwParamsForClientInterface(iface.name);
            Log.i(TAG, "Successfully setup " + iface);
            return iface.name;
        }
    }
    

    到这里就可以看到一些关键性的操作:

                             启动Hal:startHal()

                             启动supplicant:startSupplicant()

                             加载驱动(loadDriver):setupInterfaceForClientMode()

                             启动WifiMonitor:WifiMonitor.startMonitoring()

           WifiMonitor.startMonitoring():这一步主要是在WifiMonitor中建立与wpa_supplicant通信的socket通道、创建一个线程接收底层事件并分发处理。这里会创建两个socket通道与wpa_s通信,一个用于下发指令,另一个用于接收事件。成功后WifiMonitor会向WifiStateMachine发送一个代表socket通信建立成功的消息:SUP_CONNECTION_EVENT;收到这个消息就表示Wifi已经启动成功了。

         这次的Wifi启动源码就讲到Android的java框架层,下次接着继续往下走流程:    

                    HIDL—WPA适配层—wpa_supplicant—linux kernel 

        下面几层的简单说明:

    3 HIDL

         Wifi到AndoidO之后不再使用jni,所以AndroidP也一样不再使用jni来实现Java代码与本地的C/C++代码交互,而是使用HIDL,Hardware Interface Define Language。

     

    4 WPA适配层

          wpa_supplicant适配层是通用的wpa_supplicant的封装,在Android中作为WiFi部分的硬件抽象层来使用。wpa_supplicant适配层主要用于与wpa_supplicant守护进程的通信,以提供给Android框架使用,它实现了加载、控制和消息监控等功能。

     

    5 wpa_supplicant

          wpa_supplicant是一个开源项目,已经被移植到Linux,Windows以及很多嵌入式系统上。它是WPA的应用层认证客户端,负责完成认证相关的登录、加密等工作。              

        wpa_supplicant是一个独立运行的守护进程,其核心是一个消息循环,在消息循环中处理WPA状态机、控制命令、驱动事件、配置信息等。wpa_supplicant有很多控制接口,也提供命令行和通行界面的控制模式:而Android与wpa_supplicant的通信通过Socket完成。

             这个工程中的内容编译后主要结果是生成动态库libwpa_client.so和可执行程序wpa_supplicant。

     

    6 Linux Kernel

    Wifi的内核驱动程序。

     

    四 总结

             Android P和Android O相比还是有不少变化的,主要是关键性的操作不再走WifiStateMachine这个线了,而是通过另外两个比较简单的状态机ModeStateMachine和ClientModeStateMachine。

    展开全文
  • Internet共享热点-用于将计算机(以太网,3G / 4G / 5G调制解调器,其他WiFi接入点)的可用Internet连接分发到多个Wi-Fi设备,例如计算机,智能手机,平板电脑,smartTV等。 兼容性:赢7、8、8.1、10。语言:英语,...
  • 路由器wan口直连外网,重启路由器后,pc连接此路由器的WiFi可以分配到IP。 但是,如果路由器中间加了一层交换机就不行了。即,路由器wan口连接交换机,交换机另一个口连接外网,在网线都连接的情况下,重启路由器。...
  • 许多医院建立了功能强大的医疗信息管理系统(如HIS、PACS等),医护人员可以通过计算机接入有线网络访问这类管理系统,并实现医生查房、病人监护、药剂师配药和分发、医疗设备管理和实时监控、药品库存管理、电子...
  • 在ESP32和ESP8266板上实现WIFI网络IP地址扫描器的项目 ESP32 v1:在“ esp lolin32”板中运行版本1时,速度非常慢,但结果正确。 开发工具 1-带有Visual Studio Code的PlatformIO IDE 2- C ++和Arduino编程语言 ...
  • Android 11 WiFi启动流程

    千次阅读 2021-02-24 10:06:32
    本节重点基于Android11分析讨论WiFi开启流程。用户点击一下“WiFi”开关,WiFi开启了。看似如此简单操作,但系统流程调用还是相当复杂。因Android11版本框架中WiFi打开流程有一部分变化。接下来我们一起分析下...
  • 在多个Android设备上分发下载。 正在进行的项目,可在多个android手机上分发下载内容,最好在3g等不同的互联网连接上进行。 直接使用wifi进行连接。 在当前的实现中,您需要通过2个不同的android手机上的android ...
  • WIFI学习笔记 - Framework (1) WIFI学习笔记 - Framework (2) 前言 基于Android P源码学习; 代码片为了方便阅读段经过删、裁减,请以实际源码为准; 入口 根据前两篇的分析结果,调用栈都汇聚到了WifiNative: ...
  • Android之wifi工作流程

    千次阅读 2018-03-05 18:15:23
    Android Wifi的工作流程一、WIFI工作相关部分Wifi 网卡状态1. WIFI_STATE_DISABLED:WIFI网卡不可用2. WIFI_STATE_DISABLING:WIFI正在关闭3. WIFI_STATE_ENABLED:WIFI网卡可用4. WIFI_STATE_ENABLING:WIFI网卡...
  • Android P WiFi的 Disconnect 流程 汇总

    千次阅读 2019-01-05 17:07:40
    文章目录WiFi 断开Flow框架代码流程 WiFi 断开Flow框架 代码流程
  • Android6.0 打开wifi

    千次阅读 2018-09-14 14:53:28
    想象一下在应用程序我们怎么连接wifi: 1打开和关闭wifi WifiManager wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE); wifi.setWifiEnabled(true); 2扫描附近热点 startScan();之后,接受...
  • 2.2.1. WIFI驱动功能定义 5 2.2.2. WIFI安全定义 6 2.3. 系统资源需求与依赖 7 2.3.1. 硬件资源框图 7 2.3.2. 软件资源 8 2.4. 软件设计方案 8 2.4.1. WIFI驱动框架结构 8 2.4.2. LIBWIFI模块驱动软件方框图 10 2.4....
  • 许多医院建立了功能强大的医疗信息管理系统(如HIS、PACS等),医护人员可以通过计算机接入有线网络访问这类管理系统,并实现医生查房、病人监护、药剂师配药和分发、医疗设备管理和实时监控、药品库存管理、电子...
  • esp_wifi_repeater, 全功能WiFi中继器

    千次阅读 2019-01-16 20:59:50
    esp_wifi_repeater, 全功能WiFi中继器( 正确 源代码名称:esp_wifi_repeater源代码网址:http://www.github.com/martin-ger/esp_wifi_repeateresp_wifi_repeate...
  • Android WiFi从app到driver详解

    千次阅读 2017-08-02 15:49:00
    (1) Wifi模块相关文件的解析 (2) Wpa_supplicant解析 (3) Wifi的启动流程(有代码供参考分析)   一,Wifi模块相关文件解析 1) wifisettings.Java packages/apps/Settings/src/...
  • 基于rk3288平台android5.1系统的wifi流程分析 ---- 打开wifi,扫描热点
  • 上一篇文章说过WiFi都是由哪些原因造成的信息泄露,接下来我们从技术原理角度来看一看! 大家是不是都有过这样的经历,有时候感觉自己家地网变慢了,是不是被别人蹭网了呢?或者有时候我们也会出门蹭一蹭别人家的网...
  • 在介绍SDIO wifi Marvell8801/Marvell88w8801之前先附上模块链接:点击购买Marvell8801模块 // 点击购买...SDIO wifi Marvell8801/Marvell88w8801 介绍(一) ---- 芯片介绍 SDIO wifi Marvell8801/Marvell88w8...
  • 如何策划一个流量分发类的产品

    千次阅读 2018-09-07 19:20:01
    流量分发是互联网上最常见的一种产品类型,我们常用的产品中,包括百度、去哪儿网、淘宝,以及各种安卓应用商店,其产品核心部分都可以看做是在做分发。 流量分发的逻辑很简单,即互联网产品收集到足够多的流量...
  • 随身wifi可以插4G的卡,其实就是一个手机热点,把移动数据的信号给分发出去。当然因为用的是4G网络,所以可以到处带着跑,走到哪里都可以给手机,平板提供网络。 但不足之处在于: 第一,它的网络延迟普遍比路...
  • WiFi从ap模式修改为sta模式

    万次阅读 2019-05-02 18:25:07
    WiFi从ap模式修改为sta模式 基于Linux操作系统,瑞芯微rv1108处理器,使用的是ITM-UT15的WiFi模块。 系统上电后初始化的模式是ap模式。 1.需要在代码中找到相关的配置代码注释掉 2.第一步操作后,编译,烧写...
  • 在活动中实时监听网络连接状态,包括移动网络和wifi网络。 执照 版权所有 2014 工磊 根据 Apache 许可,版本 2.0(“许可”)获得许可; 除非遵守许可,否则您不得使用此文件。 您可以在以下网址获取许可证副本 ...
  • Android -- Wifi启动流程分析

    万次阅读 2016-05-30 21:01:55
    Android -- Wifi启动流程分析 Android网络各个模式中,Wifi应该是目前最常用的一种网络方式了;下面就简单介绍下Android中Wifi的启动流程。 当我在Setting菜单里点击打开Wifi时,调用的入口函数是WifiManager::...
  • 版权所有(C)1989、1991 Free Software Foundation,Inc.,位于马萨诸塞州波士顿,第五楼,富兰克林街51号,马萨诸塞州02110-1301美国允许所有人复制和分发本许可证文档的逐字记录副本,但不允许对其进行更改。
  • CPPFLAGS := -Wall -Werror -Wextra LOCAL_SRC_FILES := \ hidl_struct_util.cpp \ hidl_sync_util.cpp \ ringbuffer.cpp \ wifi.cpp \ wifi_ap_iface.cpp \ wifi_chip.cpp \ wifi_feature_flags.cpp \ wifi_legacy_...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 7,086
精华内容 2,834
热门标签
关键字:

WIFI分发