系列文章目录
第一章 android wifi framework预备知识点-StateMachine
前言
在刚开始学习wifi时,android framework层是最开始接触的东西,这里会遇到各种各样的工具类,有两个类的使用在wifi的framework层会使用的特别频繁,即stateMachine和asyncChannel,所以这里先对stateMachine做一个详细的分析和说明。
本文主要以android 12为主做分析
一、stateMachine是什么?
stateMachine:顾名思义就是状态机,但是具体如何实现的呢?又是如何使用的呢?
下文进行揭晓。
二、stateMachine具体实现
1. stateMachine
这里以ConcreteClientModeManager.java下ClientModeStateMachine类这个状态机为例进行剖析;
ConcreteClientModeManager(...) {
...
// 创建ClientModeStateMachine类的状态机
mStateMachine = new ClientModeStateMachine(looper);
mDeferStopHandler = new DeferStopHandler(looper);
...
// 向状态机发送CMD_START的cmd,这里在后面说明先保留code
mStateMachine.sendMessage(ClientModeStateMachine.CMD_START, mTargetRoleChangeInfo);
}
进入到ClientModeStateMachine类
private class ClientModeStateMachine extends StateMachine {
...
private final State mIdleState = new IdleState();
private final State mStartedState = new StartedState();
private final State mScanOnlyModeState = new ScanOnlyModeState();
private final State mConnectModeState = new ConnectModeState();
...
// looper是WifiHandlerThread线程的工作栈
ClientModeStateMachine(Looper looper) {
super(TAG, looper);
// CHECKSTYLE:OFF IndentationCheck
addState(mIdleState);
addState(mStartedState, mIdleState);
addState(mScanOnlyModeState, mStartedState);
addState(mConnectModeState, mStartedState);
// CHECKSTYLE:ON IndentationCheck
// 初始化到mIdleState状态
setInitialState(mIdleState);
start();
}
...
}
1.1 状态机初始化
从上面的code中,可以看到ClientModeStateMachine是继承自StateMachine类,并且在构造函数中调用了StateMachine类的构造函数,addState(),setInitialState()和start()函数。下面逐个分析:
- StateMachine.StateMachine()
protected StateMachine(String name) {
// 只给状态机名字时,会需要创建一个对应的装机线程
mSmThread = new HandlerThread(name);
mSmThread.start();
// 获得该线程的looper
Looper looper = mSmThread.getLooper();
// 初始化状态机
initStateMachine(name, looper);
}
protected StateMachine(String name, Looper looper) {
initStateMachine(name, looper);
}
protected StateMachine(String name, Handler handler) {
initStateMachine(name, handler.getLooper());
}
如上以上3中创建状态机的方式,案例中用的第二种,继续进入
private void initStateMachine(String name, Looper looper) {
mName = name;
mSmHandler = new SmHandler(looper, this);
}
private static class SmHandler extends Handler {
...
private SmHandler(Looper looper, StateMachine sm) {
super(looper);
mSm = sm;
// 添加两个状态:暂停和退出
addState(mHaltingState, null);
addState(mQuittingState, null);
}
...
}
mHaltingState:暂停状态,它将对所有消息都返回true。
mQuittingState:退出状态,它将对所有消息都返回NOT_HANDLE,然后不管是那种状态机的创建方式会清空所有状态,对于第一种创建方式还会额外把mSmThread线程也给销毁掉。
- StateMachine.addState(State state, State parent)
private final StateInfo addState(State state, State parent) {
if (mDbg) {
mSm.log("addStateInternal: E state=" + state.getName() + ",parent="
+ ((parent == null) ? "" : parent.getName()));
}
StateInfo parentStateInfo = null;
// 判断是否有父状态,传进来的如果为parent,则进到下一步
if (parent != null) {
parentStateInfo = mStateInfo.get(parent);
// 判断父状态信息,如果没有说明父状态没有被添加到状态机,添加父状态并创建父状态信息
if (parentStateInfo == null) {
// Recursively add our parent as it's not been added yet.
parentStateInfo = addState(parent, null);
}
}
StateInfo stateInfo = mStateInfo.get(state);
// 判断当前添加的状态是否有被添加过
// 如果没有添加,则创建对应的状态信息并保存到mStateInfo
// mStateInfo用作所有状态的一个全局存储变量
if (stateInfo == null) {
stateInfo = new StateInfo();
mStateInfo.put(state, stateInfo);
}
// 不给同一个状态设置两个父状态,一个状态只能有一个父状态,一个父状态可以有多个子状态
if ((stateInfo.parentStateInfo != null)
&& (stateInfo.parentStateInfo != parentStateInfo)) {
throw new RuntimeException("state already added");
}
stateInfo.state = state;
stateInfo.parentStateInfo = parentStateInfo;
stateInfo.active = false;
if (mDbg) mSm.log("addStateInternal: X stateInfo: " + stateInfo);
return stateInfo;
}
主要添加各个状态进状态机并把每个状态保存到mStateInfo全局变量,并且为每个状态设置或不设置父状态。mStateInfo主要保存状态,父状态信息和活跃状态。
注意:每个状态只能有一个父状态,每个父状态可以有多个子状态。
- StateMachine.setInitialState(State initialState)
private final void setInitialState(State initialState) {
if (mDbg) mSm.log("setInitialState: initialState=" + initialState.getName());
mInitialState = initialState;
}
就只是设置初始化的状态mInitialState,就是状态机开启后,第一个进入的状态。
- StateMachine.start()
public void start() {
// mSmHandler can be null if the state machine has quit.
SmHandler smh = mSmHandler;
if (smh == null) return;
/** Send the complete construction message */
smh.completeConstruction();
}
private static class SmHandler extends Handler {
...
/**
* Complete the construction of the state machine.
*/
private final void completeConstruction() {
if (mDbg) mSm.log("completeConstruction: E");
/**
* Determine the maximum depth of the state hierarchy
* so we can allocate the state stacks.
*/
// 计算状态机所有状态继承关系,得到最长的继承关系,就是最大深度
int maxDepth = 0;
for (StateInfo si : mStateInfo.values()) {
int depth = 0;
for (StateInfo i = si; i != null; depth++) {
i = i.parentStateInfo;
}
if (maxDepth < depth) {
maxDepth = depth;
}
}
if (mDbg) mSm.log("completeConstruction: maxDepth=" + maxDepth);
// 状态机状态堆栈和临时状态堆栈
mStateStack = new StateInfo[maxDepth];
mTempStateStack = new StateInfo[maxDepth];
// 初始化状态堆栈
setupInitialStateStack();
/** Sending SM_INIT_CMD message to invoke enter methods asynchronously */
sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
if (mDbg) mSm.log("completeConstruction: X");
}
...
}
其中,几个关键的全局变量:
- maxDepth:状态机中,所有状态里面继承关系最长一条,作为最大深度保存在该变量内;
- mStateStack和mTempStateStack:两个堆栈,堆栈长度为maxDepth。
private final void setupInitialStateStack() {
if (mDbg) {
mSm.log("setupInitialStateStack: E mInitialState=" + mInitialState.getName());
}
// 获取到setInitialState设置的状态的状态信息
StateInfo curStateInfo = mStateInfo.get(mInitialState);
// for循环的主要作用是初始化临时堆栈,获得最上层的状态信息,如下图所示
for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) {
mTempStateStack[mTempStateStackCount] = curStateInfo;
curStateInfo = curStateInfo.parentStateInfo;
}
// Empty the StateStack
mStateStackTopIndex = -1;
// 从临时堆栈移入到工作堆栈
moveTempStateStackToStateStack();
}
主要设置初始化的堆栈,这个函数会把当前状态的父状态、父父状态、父父父状态等父状态依次添加到临时状态。我们以开始的ClientModeStateMachine状态机为例,假设setInitialState(mScanOnlyModeState)如下图:

然后,调用moveTempStateStackToStateStack()函数;
private final int moveTempStateStackToStateStack() {
int startingIndex = mStateStackTopIndex + 1;
int i = mTempStateStackCount - 1;
int j = startingIndex;
while (i >= 0) {
if (mDbg) mSm.log("moveTempStackToStateStack: i=" + i + ",j=" + j);
mStateStack[j] = mTempStateStack[i];
j += 1;
i -= 1;
}
mStateStackTopIndex = j - 1;
if (mDbg) {
mSm.log("moveTempStackToStateStack: X mStateStackTop=" + mStateStackTopIndex
+ ",startingIndex=" + startingIndex + ",Top="
+ mStateStack[mStateStackTopIndex].state.getName());
}
return startingIndex;
}
主要把临时堆栈的状态又存放到工作堆栈中去,如下图:

进入第一个状态,还是从completeConstruction内:
// 发送状态机初始化指令
sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
通过这条sendMessage进入到SmHandler的handleMessage函数来处理SM_INI_CMD消息,如下;
public final void handleMessage(Message msg) {
if (!mHasQuit) {
if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) {
mSm.onPreHandleMessage(msg);
}
if (mDbg) mSm.log("handleMessage: E msg.what=" + msg.what);
/** 保存当前消息 */
mMsg = msg;
/** 状态处理消息 */
State msgProcessedState = null;
// 已初始化完成时,进入的消息处理分支
if (mIsConstructionCompleted || (mMsg.what == SM_QUIT_CMD)) {
/** Normal path */
msgProcessedState = processMsg(msg);
// 没有完成初始化时,进入的消息处理分支
} else if (!mIsConstructionCompleted && (mMsg.what == SM_INIT_CMD)
&& (mMsg.obj == mSmHandlerObj)) {
/** 设置初始化标志为true */
mIsConstructionCompleted = true;
// 调用状态的enter函数,并将状态的active设置为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");
if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) {
mSm.onPostHandleMessage(msg);
}
}
}
private final void invokeEnterMethods(int stateStackEnteringIndex) {
for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) {
if (stateStackEnteringIndex == mStateStackTopIndex) {
// Last enter state for transition
mTransitionInProgress = false;
}
if (mDbg) mSm.log("invokeEnterMethods: " + mStateStack[i].state.getName());
mStateStack[i].state.enter();
mStateStack[i].active = true;
}
mTransitionInProgress = false; // ensure flag set to false if no methods called
}
从上面临时堆栈和工作堆栈的移动后,可以得到工作堆栈(mStateStack)和工作堆栈栈顶索引值(mStateStackTopIndex)的数值,依次调用对用状态的enter函数。
如上,状态机的初始化就完成了。
1.2 状态机的状态转换
先简单梳理一下,ClientModeStateMachine.java里面ClientModeStateMachine状态机的树状结构图,如下:

我们知道ClientModeStateMachine设置初始化状态为setInitialState(mIdleState),如下code是给状态机ClientModeStateMachine发送了CMD_START的消息
ConcreteClientModeManager(...) {
....
mStateMachine = new ClientModeStateMachine(looper);
....
mStateMachine.sendMessage(ClientModeStateMachine.CMD_START, mTargetRoleChangeInfo);
}
上面的code直接去了状态机初始化和向状态机发送的第一条cmd部分的code。
public final void handleMessage(Message msg) {
if (!mHasQuit) {
if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) {
mSm.onPreHandleMessage(msg);
}
if (mDbg) mSm.log("handleMessage: E msg.what=" + msg.what);
/** 保存当前消息 */
mMsg = msg;
/** 状态处理消息 */
State msgProcessedState = null;
// 已初始化完成时,进入的消息处理分支
if (mIsConstructionCompleted || (mMsg.what == SM_QUIT_CMD)) {
/** Normal path */
msgProcessedState = processMsg(msg);
// 没有完成初始化时,进入的消息处理分支
} else if (!mIsConstructionCompleted && (mMsg.what == SM_INIT_CMD)
&& (mMsg.obj == mSmHandlerObj)) {
/** 设置初始化标志为true */
mIsConstructionCompleted = true;
// 调用状态的enter函数,并将状态的active设置为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");
if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) {
mSm.onPostHandleMessage(msg);
}
}
}
private final void invokeEnterMethods(int stateStackEnteringIndex) {
for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) {
if (stateStackEnteringIndex == mStateStackTopIndex) {
// Last enter state for transition
mTransitionInProgress = false;
}
if (mDbg) mSm.log("invokeEnterMethods: " + mStateStack[i].state.getName());
mStateStack[i].state.enter();
mStateStack[i].active = true;
}
mTransitionInProgress = false; // ensure flag set to false if no methods called
}
状态机的sendMessagexxx(),都会调用到SmHandler的handleMessage函数,这次进入到msgProcessedState = processMsg(msg)分支的code;
private final State processMsg(Message msg) {
StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
if (mDbg) {
mSm.log("processMsg: " + curStateInfo.state.getName());
}
if (isQuit(msg)) {
transitionTo(mQuittingState);
} else {
// 调用当前状态的processMessage函数
// 当前状态无法处理时,调用父状态状态的processMessage函数
// 如果父状态或者父父状态...等都无法处理时,说明该消息无法被处理
while (!curStateInfo.state.processMessage(msg)) {
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;
}
当设置初始化为setInitialState(mIdleState),根据状态机的树状结构图可以得到IdleState没有父状态,所以这里curStateInfo只有IdleState。
注意:
若当前状态处理了消息,也就是processMessage返回HANDLED(true),则表示消息已被处理;
若返回NOT_HANDLED(false),则表示消息没有被处理,那么消息会传递到其父状态进行处理;
上文中,我们跳转到IdleState.processMessage,看其处理情况;
public boolean processMessage(Message message) {
switch (message.what) {
case CMD_START:
// Always start in scan mode first.
RoleChangeInfo roleChangeInfo = (RoleChangeInfo) message.obj;
mClientInterfaceName = mWifiNative.setupInterfaceForClientInScanMode(
mWifiNativeInterfaceCallback, roleChangeInfo.requestorWs);
if (TextUtils.isEmpty(mClientInterfaceName)) {
Log.e(getTag(), "Failed to create ClientInterface. Sit in Idle");
mModeListener.onStartFailure(ConcreteClientModeManager.this);
break;
}
if (roleChangeInfo.role instanceof ClientConnectivityRole) {
sendMessage(CMD_SWITCH_TO_CONNECT_MODE, roleChangeInfo);
transitionTo(mStartedState);
} else {
mScanRoleChangeInfoToSetOnTransition = roleChangeInfo;
transitionTo(mScanOnlyModeState);
}
break;
default:
Log.d(getTag(), "received an invalid message: " + message);
return NOT_HANDLED;
}
return HANDLED;
}
我们只关注转变状态的情况,其余code会从另外的文章去分析技术细节,我们只关注状态机的变化,从上面的code可以看到,如下两条语句;
sendMessage(CMD_SWITCH_TO_CONNECT_MODE, roleChangeInfo);
transitionTo(mStartedState);
注意:
这段语句从先后顺序来看是先sendMessage消息会被先保存在handle的队列里面然后被handleMessage响应,再然后transitionTo转变状态,但是由于handle的单线程处理,由于当前线程正处于handleMessage的函数内,所以这个sendMessage的消息,会等到本次消息处理完后,才会被执行。
transitionTo函数如下:
public final void transitionTo(IState destState) {
mSmHandler.transitionTo(destState);
}
private static class SmHandler extends Handler {
....
private final void transitionTo(IState destState) {
if (mTransitionInProgress) {
Log.wtf(mSm.mName, "transitionTo called while transition already in progress to " +
mDestState + ", new target state=" + destState);
}
mDestState = (State) destState;
if (mDbg) mSm.log("transitionTo: destState=" + mDestState.getName());
}
....
}
可以看到就只是单纯的给mDestState这个全局变量进行了赋值;
我们回到processMessage函数,可以看到下面会进行break,然后返回了HANDLED,那么这个时候会回到SmHandler.processMsg内,该函数会返回IdleState状态,然后回到SmHandler.handleMessage函数内,会发现调用了performTransitions(msgProcessedState, msg)函数,我们进入到performTransitions函数内:
private void performTransitions(State msgProcessedState, Message msg) {
// 获取到当前状态
State orgState = mStateStack[mStateStackTopIndex].state;
...
// 赋值目标状态给到本地临时变量
State destState = mDestState;
if (destState != null) {
while (true) {
if (mDbg) mSm.log("handleMessage: new destination call exit/enter");
// 把目的状态放入到临时栈跟初始化时一样
// 目的状态在mTempStateStack[0]的位置
// 后面mTempStateStack[1~x]保存未激活的父状态,也就是active标志位为false的
// 才会被保存在临时栈内,最后返回最后一个被激活的父状态
StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
mTransitionInProgress = true;
// commonStateInfo状态的所有子状态全部执行exit函数
invokeExitMethods(commonStateInfo);
// 把临时堆栈内的状态赋值给到工作堆栈,并返回未被激活的父状态的索引值
int stateStackEnteringIndex = moveTempStateStackToStateStack();
// 从当前未被激活的父状态到目标状态,整个树链路的状态的enter函数被调用
// 并设置激活状态标志为active为true
invokeEnterMethods(stateStackEnteringIndex);
// 该函数是当调用了deferMessage函数后,才会生效
// 意义在于转到目标状态后,马上处理deferMessage所传递的消息
moveDeferredMessageAtFrontOfQueue();
if (destState != mDestState) {
// A new mDestState so continue looping
destState = mDestState;
} else {
// No change in mDestState so we're done
break;
}
}
mDestState = null;
}
...
}
从上面的code从和当前的用例来说,当前状态是IdleState转变到StartedState,自然也就只会调用StartedState的enter函数。
但是若当前的状态是ConnectModeState转变到ScanOnlyModeState,那么ConnectModeState的exit函数会被调用,StartedState状态不会动,ScanOnlyModeState的enter函数会被调用。
总结
以上,就是状态机的全部内容,关于AsyncChannel会出另外一篇文章进行单独分析。
本文详细介绍了Android状态机StateMachine的概念和实现,以ConcreteClientModeManager.java中的ClientModeStateMachine为例,阐述了状态机的初始化、状态转换过程,以及状态之间的进入和退出操作。通过对StateMachine的深入分析,有助于理解Android WiFi框架的工作原理。

2423

被折叠的 条评论
为什么被折叠?



