Android wifi framework预备知识点-StateMachine

本文详细介绍了Android状态机StateMachine的概念和实现,以ConcreteClientModeManager.java中的ClientModeStateMachine为例,阐述了状态机的初始化、状态转换过程,以及状态之间的进入和退出操作。通过对StateMachine的深入分析,有助于理解Android WiFi框架的工作原理。

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

系列文章目录

第一章 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");
    }
...
}

  其中,几个关键的全局变量:

  1. maxDepth:状态机中,所有状态里面继承关系最长一条,作为最大深度保存在该变量内;
  2. 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会出另外一篇文章进行单独分析。

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

坚持不秃0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值