Android4.0中蓝牙适配器state machine(状态机)的分析

摘要:
今天晓东和大家来一起看一下Android4.0中蓝牙适配器的状态机变化的过程。2)Switching:可以认为是正在打开的状态。但是从耗电的状态来看,他和2.3中bluetoothon是一样的。所以,有这样一个过渡的状态,在change到BluetoothOn的时候并不会发出类似state_on的broadcaset。从图中可以看出,这六个状态中有3个状态是bluetooth有可能长期处于的状态,也就是非中间状态,他们是BluetoothOn,HotOff以及PowerOff。还有3个状态是中间状态,分别是Switching,WarmUp以及PerProcessState。在bluetoothquickswitch关闭的情况下,这个消息是蓝牙真正打开的第一步。

今天晓东和大家来一起看一下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。当然,这个状态的使用场合并不是很多,大家了解一下就可以了。

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

Android4.0中蓝牙适配器state machine(状态机)的分析第1张

从图中可以看出,这六个状态中有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,他们之间可以相互转换,从而达到打开关闭蓝牙的目的。

免责声明:文章转载自《Android4.0中蓝牙适配器state machine(状态机)的分析》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇突袭HTML5之Javascript API扩展2 地理信息服务基因芯片和NGS的关系下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

状态机学习及对一段 java 代码的改写

《敏捷软件开发:原则、模式与实践》这本书中的第 29 章讲解了 State 模式,例子是地铁里的十字转门。书中对于状态机的实现,有一个范例是使用迁移表来实现(程序 29.12, P382),这里不列出详细代码。我在这个例子里面了解到,原来 java 还支持 private interface,并且可以不创建接口的实例,而直接生成一个实现了接口的对象。使用的...

C#异步编程之(三):深入 Async 和 Await 的实现及其成本

From: http://msdn.microsoft.com/zh-cn/magazine/hh456402.aspx异步性能:了解 Async 和 Await 的成本Stephen Toub 异步编程长时间以来一直都是那些技能高超、喜欢挑战自我的开发人员涉足的领域 — 这些人愿意花费时间,充满热情并拥有心理承受能力,能够在非线性的控制流程中不断地...

ip黑白名单防火墙frdev的原理与实现

汤之盘铭曰 苟日新 日日新 又日新 康诰曰 作新民 诗曰 周虽旧邦 其命维新 是故 君子无所不用其极                           ——礼记·大学 在上一篇文章《DDoS攻防战 (二) :CC攻击工具实现与防御理论》中,笔者阐述了一个防御状态机,它可用来抵御来自应用层的DDoS攻击,但是该状态机依赖一个能应对大量条目快速增删的ip黑白...

TI AM335X 网卡驱动解析

1.CPSW驱动及设备的初始化; (1)首先驱动注册cpsw_driver ,会自动进入cpsw_probe执行; 1 static struct platform_driver cpsw_driver = { 2 .driver = { 3 .name = "cpsw", 4 .owner = T...

P4: Programming Protocol-Independent Packet Processors

P4: Programming Protocol-Independent Packet Processors 摘要 P4是一门高级语言,用于编程与协议无关的数据包处理器。P4与SDN控制协议相关联,类似与OpenFlow。当前,OpenFlow明确指定了运行的协议头。这个设置在几年内,从12个字段增长到41个字段,并且增加了协议头的复杂性,却没有提供应有的...

spring 状态机

前言:“状态机”见名知意,用状态去管理业务操作,打个比方:0~1岁(出生状态),1~3岁(认知状态),3~6岁(启蒙状态),6~22岁(学习状态),22~60(工作状态),60以后(退休状态),那么人一生成长经历则是(状态跳转):出生状态  -> 认知状态  -> 启蒙状态  -> 学习状态 ->  工作状态  -> 退休状态...