Wlan学习备忘(下)

HSM(对应的类是StateMachine)和AsyncChannel是Android Java Framework中两个重要
的类。不过,它们目前还仅由Framework内部使用,SDK中并没有包含它们。这两个类的作
用如下。
·HSM在传统状态机对所有状态都一视同仁的基础上做了一些改变,使得状态和状态之
间有了层级关系。HSM中的状态层级关系与Java中父子类的派生和继承关系类似,即在父状
态中实现generic的功能,而在子状态中实现一些特定的处理。不过,和Java中类派生不同的
是,HSM中父子状态对应的是毫无派生关系的两个类,所以使用时需要创建两个对象。而
Java中子类则从其父类派生,实际使用时创建一个子类对象即可,该子类对象就能完成父类
的工作。
·AsyncChannel用于两个Handler之间的通信。具体的通信方式为源Handler通过
sendMessage向目标Handler发送消息,而目标Handler通过replyToMessage回复源Handler处理

结果。注意,这两个Handler可位于同一个进程,也可分属两个不同的进程。

StateMachine
HSM在framework中的实现都是继承StateMachine,有WiFiStateMachine,WifiWatchdogMachine等。
下面先说StateMachine的实现规则和原理:
addState:添加一个状态。同时还可指定父状态。
·transitionTo:将状态机切换到某个状态。
·obtainMessage:由于HSM内部是围绕一个Handler来工作的,所以外界只能调用HSM的
obtainMessage以获取一个Message ① 。
·sendMessage:发送消息给HSM。HSM中的Handler会处理它。
·deferMessage:保留某个消息。该消息将留待下一个新状态中去处理。其内部实现就是
把这些被deferred的message保存到一个队列中。当HSM切换到新状态后,这些deferred消息将
被移到HSM内部Handler所对应消息队列的头部,从而新状态能首先处理这些deferred消息。
·start:启动状态机。
·停止状态机可使用quit或quitNow函数。这两个函数均会发送SM_QUIT_CMD消息给HSM
内部的Handler,不过效果略有区别。当使用quit时,SM_QUIT_CMD添加在消息队列尾;而
使用quitNow时,SM_QUIT_CMD被添加到消息队列头。
假设某个statemachine有如下关系的state:

·SM启动后,初始状态的EA将按派生顺序执行。即其祖先状态的enter()先执行,子状态的
enter()后执行。以示例代码中的初始状态mS5为例。当HSM的start调用完毕后,enter就()调用顺序为
mP0、mP1、mS1、mS5。

·当State发生切换时,旧State的exit先执行,新State的enter后执行,并且新旧State派生树
上对应的State也需要执行exit或enter函数。以mS5切换到mS4为例,在此切换过程中,首先执
行的是exit,其顺序是mS5,mS1。注意,exit执行的终点是离mS4和mS5最近的一个公共
(即同时是mS4和mS5的祖先)祖先State(此处是mP1),但公共祖先状态的exit不会执
行。然后执行的是enter,其顺序是mS2、mS4。同理,公共祖先的EA也不会执行。细心的读者
可以发现,HSM中enter和exit执行顺序和C++类构造/析构函数执行顺序类似。enter执行顺序由
祖先类开始直至子孙类,而析构函数的执行先从子孙类开始,直到祖先类。

·State处理Message时,如果子状态不能处理(返回NOT_HANDLED),则交给父状态去
处理。这一点也和C++中类的派生函数类似。


AsyncChannel
AsyncChannel用于两个Handler之间的通信,其用法包含两种不同的应用模式(usage
model)。
·简单的request/response模式下,Server端无须维护Client的信息,它只要处理来自Client
的请求即可。连接时,Client调用connectSync(同步连接)或connect(异步连接,连接成功
后Client会收到CMD_CHANNEL_HALF_CONNECTED消息)即可连接到Server。
·与request/response模式相反,AsyncChannel中另外一种应用模式就是Server端维护Client
的信息。这样,Server可以向Client发送自己的状态或者其他一些有意义的信息。与这种模式
类似的应用场景就是第4章介绍的wpa_cli和wpa_supplicant。wpa_cli可以发送命令给WPAS去
执行。同时,WPAS也会将自己的状态及其他一些消息通知给wpa_cli。
在WifiService相关模块中,第二种应用模式使用得较多。另外,AsyncChannel中Client和
Server端在最开始建立连接关系时,可以采用同步或异步的方式

以异步方式为例介绍第二
种应用模式中AsyncChannel的使用步骤。
1)Client调用AsyncChannel的connect函数。Client的Handler会收到一个名为
CMD_CHANNEL_HALF_CONNECTED消息。
2)Client在处理CMD_CHANNEL_HALF_CONNECTED消息时,需通过sendMessage函数
向Server端发送一个名为CMD_CHANNEL_FULL_CONNECTION的消息。
3)Server端的Handler将收到此CMD_CHANNEL_FULL_CONNECTION消息。成功处理
它后,Server端先调用AsyncChannel的connected函数,然后通过sendMessage函数向Client端发
送CMD_CHANNEL_FULLY_CONNECTED消息(特别注意,详情见下文)。
4)Client端收到CMD_CHANNEL_FULLY_CONNECTED消息。至此,Client和server端成
功建立连接。
5)Client和Server端的两个Handler可借助sendMessage和replyToMessge来完成请求消息及
回复消息的传递。注意,只有针对那些需要回复的情况,Server端才需调用
replyToMessage。
6)最后,Client和Server的任意一端都可以调用disconnect函数以结束连接。该函数将导
致Client和Server端都会收到CMD_CHANNEL_DISCONNECTED消息。
特别说明 上述步骤的描述来自AsyncChannel.java文件中的注释。但实际上Server端代
码在处理CMD_CHANNEL_FULL_CONNECTION消息时并不能按照上面的描述开展工作。因
为AsyncChannel对象一般由客户端创建,而CMD_CHANNEL_FULL_CONNECTION消息无法
携带AsyncChannel对象(AsyncChannel对象无法通过Binder进行跨进程传递)。所以,Server
端并不能获取客户端创建的这个AsyncChannel对象,它也就没办法调用AsyncChannel的
connected函数。

下面结合WifiManager中相关代码来介绍AsyncChannel中第二种模式涉及的一些重要函数:
在WifiManager和WifiService的通信中,AsyncChannel两端的Handler的分别是WifiManager#ServiceHandler
和WifiServiceImpl#ClientHandler。
虽然不知道他们是怎么透过binder通信,但是可以知道他们会收到这些跨进程的消息,这些消息都会在各自的handler中处理。

WifiService构造函数中主要工作是创建一些核心对象,其中:
·WifiStateMachine是WifiService中的核心

WifiStateMachine
WifiStateMachine类中重要的三个依赖,分别是WifiNative、WifiMonitor以及SupplicantStateTracker。
WifiNative:WifiNative用于和WPAS通信,其内部定义了较多的native方法(对应的JNI模块是android_net_wifi_Wifi)
WifiMonitor:WifiMonitor最重要的内容是其内部的WifiMonitor线程,该线程专门用于接收来自WPAS的消息
下面是该类中的重要方法:
·handleSupplicantStateChange用于处理WPAS的状态变化(见下文解释),它将先把这些
变化信息交给WifiStateMachine去处理。而WifiStateMachine将根据处理情况再决定是否需要
由下一节介绍的SupplicantStateTracker来处理。handleSupplicant StateChange代码比较简单。
·handleDriverEvent用于处理来Driver的信息 。
·handleEvent用于处理其他消息事件

SupplicantStateTracker
SupplicantStateTracker用于跟踪和处理WPAS的状态变化。SupplicantStateTracker也从
StateMachine派生,并且它还定义了8个状态对象
WPAS的状态指的是wpa_sm状态机中的状态,包括WPA_DISCONNECTED、
WPA_INTERFACE_DISABLED、WPA_INACTIVE、WPA_SCANNING、
WPA_AUTHENTICATING、WPA_ASSOCIATING、WPA_ASSOCIATED、
WPA_4WAY_HANDSHAKE、WPA_GROUP_HANDSHAKE、WPA_COMPLETED共10个状
态。WifiService定义了SupplicantState类来描述WPAS的状态,包括DISCONNECTED、
INTERFACE_DISABLED、INACTIVE、SCANNING、AUTHENTICATING、ASSOCIATING、
ASSOCIATED、FOUR_WAY_HANDSHAKE、GROUP_HANDSHAKE、COMPLETED、
DORMANT、UNINITIALIZED、INVALID共13个状态

WifiStateMachine共定义30个状态,其种类和层级关系如图:

当WifiStateMachine开始运行后,其最终将进入DriverUnloadedState

Settings中Wi-Fi设置相关类:

WifiSettings创建了一个广播接收对象,该对象关注的广播事件较多,其中四个重要的广播事件如下。
·WIFI_STATE_CHANGED_ACTION:该广播事件反映了Wi-Fi功能所对应的状态,这些状态是WIFI_STATE_DISABLED(Wi-Fi功能已被关闭)、WIFI_STATE_DISABLING(Wi-Fi功能正在关闭中)、WIFI_STATE_ENABLED(Wi-Fi功能已被打开)、
WIFI_STATE_ENABLING(Wi-fi功能正在打开中)和WIFI_STATE_UNKNOWN(Wi-Fi功能状态未知)。
·SCAN_RESULTS_AVAILABLE_ACTION:该广播事件表示无线网络扫描完毕,可以从WPAS中获取扫描结果。
·SUPPLICANT_STATE_CHANGED_ACTION:该广播事件用于表示WPAS的状态发生了变化。它和5.2.3节中SupplicantStateTracker介绍的SupplicantState相关。
·NETWORK_STATE_CHANGED_ACTION:该广播事件用于表示Wi-Fi连接状态发生变化。其携带的信息一般是一个NetworkInfo对象。NetworkInfo类在前文代码注释中曾介绍过,它用于表达一个网络接口的状态(Describes the status of anetwork interface)。

由于WifiEnabler先注册广播接收对象,所以对于WIFI_STATE_CHANGED_ACTIONSUPPLICANT_STATE_CHANGED_ACTION和NETWORK_STATE_CHANGED_ACTION广播来说,WifiEnabler的广播接收对象将先被触发以处理这些消息,然后才是WifiSettings的广播接收对象 。
提示 研究WifiEnabler广播接收对象的代码后,会发现实际上它真正处理的只有WIFI_STATE_CHANGED_ACTION广播WifiEnabler将根据该广播的信息以更新Switch的界面

启用Wi-Fi功能
打开wifi开关:
1.首先:WifiManager#setWifiEnabled(isChecked)
setWifiEnabled流程:
1)WifiService的setWifiEnabled函数将调用WifiStateMachine中的同名函数。在WifiStateMachine中,CMD_LOAD_DRIVER和CMD_START_SUPPLICANT两个消息将发送给状态机去执行。WifiStateMachine最初的状态是DriverUnloadedState。
2)DriverUnloadedState接收到CMD_LOAD_DRIVER消息后将转入DriverLoadingState。而DriverLoadingState的enter函数将创建一个工作线程来执行WifiNative的loadDriver以加载Wlan驱动。如果driver加载成功,该线程会发送CMD_LOAD_DRIVER_SUCCESS消息给状态机。
3)DriverLoadingState将在其processMessage中处理CMD_START_SUPPLICANT和CMD_LOAD_DRIVER_SUCCESS消息。其中,DriverLoadingState会延迟对CMD_START_SUPPLICANT的处理。而对于CMD_LOAD_DRIVER_SUCCESS,DriverLoadingState将直接转入DriverLoadedState。
4)DriverLoadedState将继续处理CMD_START_SUPPLICANT。在其processMessage中,wpa_supplicant进程将被启动,并且WifiMonitor将建立WifiService和WPAS的关系。同时,状态机将转入SupplicantStartingState。另外,当WifiMonitor成功连接上WPAS后,它将发送一个SUP_CONNECTION_EVENT消息。
5)SupplicantStartingState将处理SUP_CONNECTION_EVENT消息。这些处理包括设置初始化WPS相关的信息、设置WifiState、发送消息给SupplicantStateTracker状态机、初始化WifiConfigStore等。最后,SupplicantStartingState将转入DriverStartedState。DriverStartedState的父状态是SupplicantStartedState。所以这两个状态的enter函数均会被调用。
6)SupplicantStartedState在其enter函数中将设置扫描间隔。而DriverStartedState在其enter函数中将完成诸如Country Code、Frequency Band、Bluetooth共存模式等设置工作。有些工作需要发送形如"DRIVER XXX"的命令给WPAS。最后,SupplicantStartedState将转入DisconnectedState。
7)DisconnectedState的enter函数将被执行(其父状态ConnectModeState的enter函数没有完成什么实质性的工作)。该函数主要完成了后台扫描及定时扫描工作的一些设置。

当Wi-Fi功能被启用时,将收到WIFI_STATE_CHANGED_ACTION广播,而该广播的处理函数是updateWifiState:
wifimanger#startScan()
从WPAS中获取扫描结果,并保存到mScanResults变量中,发送SCAN_RESULTS_AVAILABLE_ACTION广播
在4.2api中,是有参数选择scan_type的,即选择passive或active,在5.0后就没有了
2.假设WPAS扫描完毕,则WifiSettings将收到SCAN_RESULTS_AVAILABLE_ACTION广
播,该广播的处理函数为updateAccessPoints,更新AP列表

点击列表选择ap
1.点击连接按钮触发
void submit(WifiConfigController configController) {
final WifiConfiguration config = configController.getConfig();
if (config == null) {
......
} ......// 其他处理
} else {
if (configController.isEdit() || requireKeyStore(config))
mWifiManager.save(config, mSaveListener);
else mWifiManager.connect(config, mConnectListener);// 连接目标无线网络
}
......
}
如果一切顺利,它将接收一个NETWORK_STATE_CHANGED_ACTION广播事件以告知手机成功已
经加入目标无线网络

connect流程:
connect的流程包括如下几个关键点。
·*整个流程起源于WifiManager向WifiStateMachine发送的CONNECT_NETWORK消息。

*·ConnectModeState将处理此消息。在其处理过程中,它将发送一系列命令给WPAS,而
WPAS将完成802.11规范中所定义的身份认证、关联、四次握手等工作。ConnectModeState随
之转入DisconnectingState。

*·当WPAS加入目标无线AP后,它将发送NETWORK_CONNECTION_EVENT给
WifiStateMachine。DisconnectingState的父状态ConnectModeState将处理此消息,具体处理过程
比较简单。最终,WifiStateMachine将转入ObtaingIpState。

*·在ObtaingIpState的enter函数中,DhcpStateMachine对象将被创建。DhcpStateMachine和
dpcpcd有关。相关代码比较简单,请读者自行阅读。在WifiStateMachine和DhcpStateMachine
的交互过程中,DhcpStateMachine将向WifiStateMachine发送CMD_PRE_DHCP_ACTION和
CMD_POST_DHCP_ACTION消息。在CMD_POST_DHCP_ACTION消息的处理过程
中,WifiStateMachine将转入VerifyingLinkState。

*·VerifyingLinkState将和WifiWatchdogStateMachine交互。WifiWatchdogStateMachine用于监
控无线网络信号的好坏。如果一切正确,它将转入CaptivePortalCheckState。

*·CaptivePortalCheckState用于检查目标无线网络提供商是否需要Captive Portal Check。如果一切正常,WifiStateMachine最终将转入ConnectedState。该状态就是本章第二条分析路线的终点。


WifiWatchdogStateMachine
WifiWatchdogStateMachine用于监控无线网络的信号质量,在WifiService的
checkAndStartWifi函数中被创建,其创建函数是makeWifiWatchdogStateMachine

WifiWatchdogStateMachine中的各个状态及层级关系:

WifiWatchdogStateMachine完全靠广播事件来驱动,相关代码在setupNetworkReceiver函数中

private void setupNetworkReceiver() {
        mBroadcastReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                String action = intent.getAction();
                if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
                    obtainMessage(EVENT_RSSI_CHANGE,
                            intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200), 0).sendToTarget();
                } else if (action.equals(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION)) {
                    sendMessage(EVENT_SUPPLICANT_STATE_CHANGE, intent);
                } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
                    sendMessage(EVENT_NETWORK_STATE_CHANGE, intent);
                } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
                    sendMessage(EVENT_SCREEN_ON);
                } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
                    sendMessage(EVENT_SCREEN_OFF);
                } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
                    sendMessage(EVENT_WIFI_RADIO_STATE_CHANGE,intent.getIntExtra(
                            WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN));
                }
            }
        };

        mIntentFilter = new IntentFilter();
        mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
        mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
        mIntentFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
        mIntentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
        mIntentFilter.addAction(Intent.ACTION_SCREEN_ON);
        mIntentFilter.addAction(Intent.ACTION_SCREEN_OFF);
        mContext.registerReceiver(mBroadcastReceiver, mIntentFilter);
    }







 

猜你喜欢

转载自blog.csdn.net/b1480521874/article/details/79051676