zoukankan      html  css  js  c++  java
  • Android4.0中蓝牙适配器state machine(状态机)的分析

    今天晓东和大家来一起看一下Android4.0中蓝牙适配器(Bluetooth Adapter)的状态机变化的过程。首先,我们需要了解一下,蓝牙适配器究竟有哪些状态,从代码可以清晰地看到(frameworks/base/core/java/android/server/bluetoothadapterstatemachine.java):

        BluetoothAdapterStateMachine(Context context, BluetoothService bluetoothService,
                                     BluetoothAdapter bluetoothAdapter) {
    ……
    	//bluetooth adapter的六个状态
            mBluetoothOn = new BluetoothOn();
            mSwitching = new Switching();
            mHotOff = new HotOff();
            mWarmUp = new WarmUp();
            mPowerOff = new PowerOff();
            mPerProcessState = new PerProcessState();
    	……
    }
    


     

    bluetooth adapter有六个状态,分别为:

    1BluetoothOn:就是打开的状态。

    2Switching:可以认为是正在打开的状态。

    3HotOff:这个状态可以理解为一个预热状态,他是在上电之后进行了一系列硬件初始化成功之后的状态,但是这种状态并不表现到ui上。但是从耗电的状态来看,他和2.3bluetooth on是一样的。

    4WarmUp:可以理解为正在预热的状态,就是处于从断电到HotOff的状态。

    5PowerOff:就是掉电的状态,也就是正在的关闭状态,这个时候bluetooth是没有耗电(准确说是耗电很少)。

    6PerProcessState:他也是位于HotOffBluetoothOn之间的一个状态,和Switching的差别在于Swtiching是我们通过ui上去打开的“正在打开”的状态,而perprocess则是应用于一些临时使用蓝牙的application,这些application并不需要完整的蓝牙功能(比如说在蓝牙打开后的自动连接等),也不需要ui上去显示蓝牙的打开。所以,有这样一个过渡的状态,在changeBluetoothOn的时候并不会发出类似state_onbroadcaset。当然,这个状态的使用场合并不是很多,大家了解一下就可以了。

             各个状态之间的变化如下图所示。

    从图中可以看出,这六个状态中有3个状态是bluetooth有可能长期处于的状态,也就是非中间状态,他们是BluetoothOnHotOff以及PowerOff。还有3个状态是中间状态,分别是SwitchingWarmUp以及PerProcessState

    从代码来看,在最开始会处于PowerOff的状态,如下:

     BluetoothAdapterStateMachine(Context context, BluetoothService bluetoothService,
                                     BluetoothAdapter bluetoothAdapter) {
    ……
            setInitialState(mPowerOff); //初始化为PowerOff的状态
            mPublicState = BluetoothAdapter.STATE_OFF;
        }
    

    因此,我们首先从PowerOff状态出发来分析:

     private class PowerOff extends State {
            @Override
            public void enter() {
                if (DBG) log("Enter PowerOff: " + getCurrentMessage().what);
            }
            @Override
            public boolean processMessage(Message message) {
                log("PowerOff process message: " + message.what);
    
                boolean retValue = HANDLED;
                switch(message.what) {
    //收到USER_TURN_ON的消息,一般而言,这个是用户在ui上点打开蓝牙会出现在这里。在bluetooth quick switch关闭的情况下,这个消息是蓝牙真正打开的第一步。当然,若是bluetooth quick switch打开了,是不会在这个状态收到这个消息的(除非出现了问题)
                    case USER_TURN_ON:  
                        // starts turning on BT module, broadcast this out
    					//广播STATE_TURNING_ON的消息,可以做ui上显示等的处理
                        broadcastState(BluetoothAdapter.STATE_TURNING_ON);
    					//change到WarmUp的状态
                        transitionTo(mWarmUp);
    					//对蓝牙的初始化,我们暂时不管,后面我们有专门的文章来解释
                        if (prepareBluetooth()) {
                            // this is user request, save the setting
                            if ((Boolean) message.obj) {
                                persistSwitchSetting(true);
                            }
                            // We will continue turn the BT on all the way to the BluetoothOn state
    						//若是成功,我们会发TURN_ON_CONTINUE的msg,
    						//需要注意的是这个msg,是在warmup状态中进行处理的哦
                            deferMessage(obtainMessage(TURN_ON_CONTINUE));
                        } else {
    						//当然,若是失败,我们需要回到poweroff的状态
                            Log.e(TAG, "failed to prepare bluetooth, abort turning on");
                            transitionTo(mPowerOff);
                            broadcastState(BluetoothAdapter.STATE_OFF);
                        }
                        break;
    //这个case,TURN_HOT就是在bluetooth quick switch打开的情况下,我们接收到这个msg,使得在开机之后,即使蓝牙没有打开,我们也会去做蓝牙controller初始化相关的操作,在上一篇文章中的《Android启动之bluetooth 》中我们在initAfterRegistration中就有这个msg的发出,同样是根据quick switch来判断的。
                    case TURN_HOT:
    		//这里我们会发现,我们还是去初始化蓝牙相关的操作,不同的是,我们没有任何的广播消息发出,所以别的recever包括ui都是不知道我们偷偷做这个操作的。
                        if (prepareBluetooth()) {
                            transitionTo(mWarmUp); //在初始化成功后,我们仍然会change到WarmUp
                        }
                        break;
    //对于飞行模式,我们并不陌生,它主要控制电话,wifi,和蓝牙,所以,毫无疑问,我们需要对飞行模式做一些处理。其实用脚趾头想我们都知道做了些什么,若是打开飞行模式之前蓝牙是打开的,那么关闭飞行模式,我们仍然需要打开它。若是打开飞行模式之前蓝牙是关闭的,那么关闭飞行模式的时候,我们根据quick switch的值来判断是否重新打开
                    case AIRPLANE_MODE_OFF:
                        if (getBluetoothPersistedSetting()) {
    //之前是打开的,和USER_TURN_ON的处理是相同的
                            // starts turning on BT module, broadcast this out
                            broadcastState(BluetoothAdapter.STATE_TURNING_ON);
                            transitionTo(mWarmUp);
                            if (prepareBluetooth()) {
                                // We will continue turn the BT on all the way to the BluetoothOn state
                                deferMessage(obtainMessage(TURN_ON_CONTINUE));
                                transitionTo(mWarmUp);
                            } else {
                                Log.e(TAG, "failed to prepare bluetooth, abort turning on");
                                transitionTo(mPowerOff);
                                broadcastState(BluetoothAdapter.STATE_OFF);
                            }
    //之前是关闭的,需要根据quick switch的值来进行判断,看是否发送TURN_HOT的msg
                        } else if (mContext.getResources().getBoolean
                                (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) {
                            sendMessage(TURN_HOT);
                        }
                        break;
    //这个是上文所说的一些应用直接调用changeApplicationBluetoothState api之后会发出的msg,只会做蓝牙的打开操作,同样不会有任何的ui上的显示。
                    case PER_PROCESS_TURN_ON:
                        if (prepareBluetooth()) {
                            transitionTo(mWarmUp);
                        }
                        deferMessage(obtainMessage(PER_PROCESS_TURN_ON));
                        break;
    //其它的msg都是无关紧要的,不加以分析了
    ……
                return retValue;
            }
    

    到这里,我们可以看到Poweroff状态下的msg分析就已经都完成了,比较关键的几个msgUSER_TURN_ON—UI上打开蓝牙的msgTURN_HOT—quick switch打开的时候,默认打开蓝牙的msgAIRPLANE_MODE_OFF—飞行模式关闭的msgok,下面我们去看一下warmup这个状态的处理。

      private class WarmUp extends State {
    
            @Override
            public void enter() {
                if (DBG) log("Enter WarmUp: " + getCurrentMessage().what);
            }
    
            @Override
            public boolean processMessage(Message message) {
                log("WarmUp process message: " + message.what);
    
                boolean retValue = HANDLED;
                switch(message.what) {
    //sdp ok,则把刚刚prepare bluetooth中启动的一个定时器(10s)remove掉,同时change到hotoff的状态
                    case SERVICE_RECORD_LOADED:
                        removeMessages(PREPARE_BLUETOOTH_TIMEOUT);
                        transitionTo(mHotOff);
                        break;
                    case PREPARE_BLUETOOTH_TIMEOUT:
                        Log.e(TAG, "Bluetooth adapter SDP failed to load");
    //若是tiemout的话,我们就只能change到poweroff状态了。
                        shutoffBluetooth();
                        transitionTo(mPowerOff);
                        broadcastState(BluetoothAdapter.STATE_OFF);
                        break;
                    case USER_TURN_ON: // handle this at HotOff state
                    case TURN_ON_CONTINUE: // Once in HotOff state, continue turn bluetooth
                                           // on to the BluetoothOn state
                    case AIRPLANE_MODE_ON:
                    case AIRPLANE_MODE_OFF:
                    case PER_PROCESS_TURN_ON:
                    case PER_PROCESS_TURN_OFF:
    //收到以上这些消息都是直接defer就可以了,到下一个状态中去处理,再刚刚poweroff状态收到user_turn_on的时候,是发了TURN_ON_CONTINUE这个msg的,这个msg会在hotoff状态中继续起作用哦
                        deferMessage(message);
                        break;
                    case USER_TURN_OFF:
                        Log.w(TAG, "WarmUp received: " + message.what);
                        break;
                    default:
                        return NOT_HANDLED;
                }
                return retValue;
            }
    
        }
    


     

    总的来看,warmup状态并没有做什么特别的事情,就是检测了sdp是否ok,去除了preparetimeout,仅此而已。所以,继续看hotoff的状态。

    hotoff状态比较神奇,他就是为quick swtich而生的。对蓝牙controller而言,他是一个打开的状态,对上层应用来说,他就是一个关闭的状态。

     private class HotOff extends State {
            @Override
            public void enter() {
                if (DBG) log("Enter HotOff: " + getCurrentMessage().what);
            }
    
            @Override
            public boolean processMessage(Message message) {
                log("HotOff process message: " + message.what);
    
                boolean retValue = HANDLED;
                switch(message.what) {
    //这里其实就是ui上打开蓝牙了,需要注意的是,这里是不是没有change到别的状态哦?
    //这是为什么呢?这个case之后没有break啊,没有break,懂了吧。。。
                    case USER_TURN_ON:
                        broadcastState(BluetoothAdapter.STATE_TURNING_ON);
                        if ((Boolean) message.obj) {
                            persistSwitchSetting(true);
                        }
    //这是什么,这是上面传下来的哦,所以ui在hotoff的状态下发这个msg,这里会继续处理的哦
                   case TURN_ON_CONTINUE:
    //这里就是蓝牙从hotoff到turnon之间要做的一个事情就是设置为connectable
                        mBluetoothService.switchConnectable(true);
    //到switching的状态。
                        transitionTo(mSwitching);
                        break;
    //这里有两个msg,一个是飞行模式开,一个turn cold,都会真正地去关闭蓝牙。
    //所以无论quick switch是开还是关,之后飞行模式开了,都是会真正去关闭蓝牙的。
                    case AIRPLANE_MODE_ON:
                    case TURN_COLD:
                        shutoffBluetooth();
                        transitionTo(mPowerOff);
                        broadcastState(BluetoothAdapter.STATE_OFF);
                        break;
    //同样的飞行模式的打开的处理
                    case AIRPLANE_MODE_OFF:
                        if (getBluetoothPersistedSetting()) {
                            broadcastState(BluetoothAdapter.STATE_TURNING_ON);
                            transitionTo(mSwitching);
                            mBluetoothService.switchConnectable(true);
                        }
                        break;
                        break;
    //这里,不会走到swtiching的状态,会到mPerProcessState的状态,上面有详细的介绍
                    case PER_PROCESS_TURN_ON:
                        transitionTo(mPerProcessState);
    
                        // Resend the PER_PROCESS_TURN_ON message so that the callback
                        // can be sent through.
                        deferMessage(message);
    
                        mBluetoothService.switchConnectable(true);
                        break;
                    case PER_PROCESS_TURN_OFF:
                        perProcessCallback(false, (IBluetoothStateChangeCallback)message.obj);
                        break;
                    case USER_TURN_OFF: // ignore
                        break;
                    case POWER_STATE_CHANGED:
                        if ((Boolean) message.obj) {
                            recoverStateMachine(TURN_HOT, null);
                        }
                        break;
                    default:
                        return NOT_HANDLED;
                }
                return retValue;
            }
    
        }
    


     

    从代码来看,hotoff的状态就是对上层ui上的一些操作进行了响应,以及兼容了no quick swtich下面的ui打开操作。它会changeswtiching的状态或者mPerProcessState的状态。我们来看看吧

        private class Switching extends State {
    
            @Override
            public void enter() {
                if (DBG) log("Enter Switching: " + getCurrentMessage().what);
            }
            @Override
            public boolean processMessage(Message message) {
                log("Switching process message: " + message.what);
    
                boolean retValue = HANDLED;
                switch(message.what) {
    //从注释可以清晰地看到,这个msg是我们上面调用BluetoothService.switchConnectable(true);这个函数的正确回应
                    case SCAN_MODE_CHANGED:
                        // This event matches mBluetoothService.switchConnectable action
                        if (mPublicState == BluetoothAdapter.STATE_TURNING_ON) {
                            // set pairable if it's not
    //设为可配对
                            mBluetoothService.setPairable();
    					//初始化bluetooth
                            mBluetoothService.initBluetoothAfterTurningOn();
    //正式change到bluetoothon的状态
                            transitionTo(mBluetoothOn);
                            broadcastState(BluetoothAdapter.STATE_ON);
                            // run bluetooth now that it's turned on
                            // Note runBluetooth should be called only in adapter STATE_ON
    //这个函数做的事情其实就是打开后的自动连接了。
                            mBluetoothService.runBluetooth();
                        }
                        break;
                    case POWER_STATE_CHANGED:
                        removeMessages(POWER_DOWN_TIMEOUT);
    //power可以理解为电压的关闭
                        if (!((Boolean) message.obj)) {
                            if (mPublicState == BluetoothAdapter.STATE_TURNING_OFF) {
    //就是关闭了,change到hotoff状态
                                transitionTo(mHotOff);
                                finishSwitchingOff();
    //若是quick switch没有设置,则会change到power off的状态
                                if (!mContext.getResources().getBoolean
                               (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) {
                                    deferMessage(obtainMessage(TURN_COLD));
                                }
                            }
                        } else {
    //在turning on就没有什么好做的,否则,就是看到poweroff还是hotoff了
                            if (mPublicState != BluetoothAdapter.STATE_TURNING_ON) {
                                if (mContext.getResources().getBoolean
                                (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) {
                                    recoverStateMachine(TURN_HOT, null);
                                } else {
                                    recoverStateMachine(TURN_COLD, null);
                                }
                            }
                        }
                        break;
    //所有的remote device都断开连接了,则我们会在5s后发送一个POWER_DOWN_TIMEOUT的msg,这个msg和下面几个msg都是从on->switching的过程中要处理的,可以见下面bluetooth_on的msg处理分析
                   case ALL_DEVICES_DISCONNECTED:
                        removeMessages(DEVICES_DISCONNECT_TIMEOUT);
                        mBluetoothService.switchConnectable(false);
                        sendMessageDelayed(POWER_DOWN_TIMEOUT, POWER_DOWN_TIMEOUT_TIME);
                        break;
    //设备断开超时了,直接复位一下
                    case DEVICES_DISCONNECT_TIMEOUT:
                        sendMessage(ALL_DEVICES_DISCONNECTED);
                        // reset the hardware for error recovery
                        Log.e(TAG, "Devices failed to disconnect, reseting...");
                        deferMessage(obtainMessage(TURN_COLD));
                        if (mContext.getResources().getBoolean
                            (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) {
                            deferMessage(obtainMessage(TURN_HOT));
                        }
                        break;
    //power down timeout,也是直接复位
               case POWER_DOWN_TIMEOUT:
                        transitionTo(mHotOff);
                        finishSwitchingOff();
                        // reset the hardware for error recovery
                        Log.e(TAG, "Devices failed to power down, reseting...");
                        deferMessage(obtainMessage(TURN_COLD));
                        if (mContext.getResources().getBoolean
                            (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) {
                            deferMessage(obtainMessage(TURN_HOT));
                        }
                        break;
    ……
    


     

    从上面可以看到switching主要是等待BluetoothService.switchConnectable(true)的结果,若是ok,就会到turn on的状态了,进入到打开的模式。至于PerProcessState的状体和swtiching的状态比较类似,只是少了一些通知而已,大家自己去分析哦。下面我们继续看bluetooth on的状态。

    private class BluetoothOn extends State {
    
            @Override
            public void enter() {
                if (DBG) log("Enter BluetoothOn: " + getCurrentMessage().what);
            }
            @Override
            public boolean processMessage(Message message) {
                log("BluetoothOn process message: " + message.what);
    
                boolean retValue = HANDLED;
                switch(message.what) {
    //这个是off的操作,就是ui上的关闭了
                    case USER_TURN_OFF:
                        if ((Boolean) message.obj) {
                            persistSwitchSetting(false);
                        }
    
                        if (mBluetoothService.isDiscovering()) {
                            mBluetoothService.cancelDiscovery();
                        }
                        if (!mBluetoothService.isApplicationStateChangeTrackerEmpty()) {
                            transitionTo(mPerProcessState);
                            deferMessage(obtainMessage(TURN_HOT));
                            break;
                        }
    //同样要注意,这里没有break哦
                        //$FALL-THROUGH$ to AIRPLANE_MODE_ON
    //和飞行模式打开的操作
                    case AIRPLANE_MODE_ON:
                        broadcastState(BluetoothAdapter.STATE_TURNING_OFF);
                        transitionTo(mSwitching);
                        if (mBluetoothService.getAdapterConnectionState() !=
                            BluetoothAdapter.STATE_DISCONNECTED) {
    //需要把所有已经连接的设备都disconnect掉
                            mBluetoothService.disconnectDevices();
                            sendMessageDelayed(DEVICES_DISCONNECT_TIMEOUT,
                                               DEVICES_DISCONNECT_TIMEOUT_TIME);
                        } else {
    //没有连接设备,直接power down
                            mBluetoothService.switchConnectable(false);
                            sendMessageDelayed(POWER_DOWN_TIMEOUT, POWER_DOWN_TIMEOUT_TIME);
                        }
    
                        // we turn all the way to PowerOff with AIRPLANE_MODE_ON
                        if (message.what == AIRPLANE_MODE_ON) {
                            // We inform all the per process callbacks
                            allProcessesCallback(false);
                            deferMessage(obtainMessage(AIRPLANE_MODE_ON));
                        }
                        break;
    ……
                return retValue;
            }
    
        }
    

     

    所以,看起来bluetooth_on的处理也比较单纯,就是一些关闭的尾巴处理。

    至此,bluetoothadapterstate machine就已经全部分析完成了,回顾一下,有六个状态,BluetoothOnSwitchingHotOffWarmUpPowerOffPerProcessState,他们之间可以相互转换,从而达到打开关闭蓝牙的目的。



     

  • 相关阅读:
    为什么 PCB 生产时推荐出 Gerber 给工厂?
    Fedora Redhat Centos 有什么区别和关系?
    【KiCad】 如何给元件给元件的管脚加上划线?
    MCU ADC 进入 PD 模式后出现错误的值?
    FastAdmin 生产环境升级注意
    EMC EMI 自行评估记录
    如何让你的 KiCad 在缩放时不眩晕?
    KiCad 5.1.0 正式版终于发布
    一次单片机 SFR 页引发的“事故”
    java基础之集合
  • 原文地址:https://www.cnblogs.com/riskyer/p/3333766.html
Copyright © 2011-2022 走看看