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

来源:互联网 发布:知乎 电动牙刷 编辑:程序博客网 时间:2024/06/02 12:33

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

[java] view plaincopy
  1.     BluetoothAdapterStateMachine(Context context, BluetoothService bluetoothService,  
  2.                                  BluetoothAdapter bluetoothAdapter) {  
  3. ……  
  4.     //bluetooth adapter的六个状态  
  5.         mBluetoothOn = new BluetoothOn();  
  6.         mSwitching = new Switching();  
  7.         mHotOff = new HotOff();  
  8.         mWarmUp = new WarmUp();  
  9.         mPowerOff = new PowerOff();  
  10.         mPerProcessState = new PerProcessState();  
  11.     ……  
  12. }  


 

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的状态,如下:

[java] view plaincopy
  1.  BluetoothAdapterStateMachine(Context context, BluetoothService bluetoothService,  
  2.                                  BluetoothAdapter bluetoothAdapter) {  
  3. ……  
  4.         setInitialState(mPowerOff); //初始化为PowerOff的状态  
  5.         mPublicState = BluetoothAdapter.STATE_OFF;  
  6.     }  

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

[java] view plaincopy
  1.  private class PowerOff extends State {  
  2.         @Override  
  3.         public void enter() {  
  4.             if (DBG) log("Enter PowerOff: " + getCurrentMessage().what);  
  5.         }  
  6.         @Override  
  7.         public boolean processMessage(Message message) {  
  8.             log("PowerOff process message: " + message.what);  
  9.   
  10.             boolean retValue = HANDLED;  
  11.             switch(message.what) {  
  12. //收到USER_TURN_ON的消息,一般而言,这个是用户在ui上点打开蓝牙会出现在这里。在bluetooth quick switch关闭的情况下,这个消息是蓝牙真正打开的第一步。当然,若是bluetooth quick switch打开了,是不会在这个状态收到这个消息的(除非出现了问题)  
  13.                 case USER_TURN_ON:    
  14.                     // starts turning on BT module, broadcast this out  
  15.                     //广播STATE_TURNING_ON的消息,可以做ui上显示等的处理  
  16.                     broadcastState(BluetoothAdapter.STATE_TURNING_ON);  
  17.                     //change到WarmUp的状态  
  18.                     transitionTo(mWarmUp);  
  19.                     //对蓝牙的初始化,我们暂时不管,后面我们有专门的文章来解释  
  20.                     if (prepareBluetooth()) {  
  21.                         // this is user request, save the setting  
  22.                         if ((Boolean) message.obj) {  
  23.                             persistSwitchSetting(true);  
  24.                         }  
  25.                         // We will continue turn the BT on all the way to the BluetoothOn state  
  26.                         //若是成功,我们会发TURN_ON_CONTINUE的msg,  
  27.                         //需要注意的是这个msg,是在warmup状态中进行处理的哦  
  28.                         deferMessage(obtainMessage(TURN_ON_CONTINUE));  
  29.                     } else {  
  30.                         //当然,若是失败,我们需要回到poweroff的状态  
  31.                         Log.e(TAG, "failed to prepare bluetooth, abort turning on");  
  32.                         transitionTo(mPowerOff);  
  33.                         broadcastState(BluetoothAdapter.STATE_OFF);  
  34.                     }  
  35.                     break;  
  36. //这个case,TURN_HOT就是在bluetooth quick switch打开的情况下,我们接收到这个msg,使得在开机之后,即使蓝牙没有打开,我们也会去做蓝牙controller初始化相关的操作,在上一篇文章中的《Android启动之bluetooth 》中我们在initAfterRegistration中就有这个msg的发出,同样是根据quick switch来判断的。  
  37.                 case TURN_HOT:  
  38.         //这里我们会发现,我们还是去初始化蓝牙相关的操作,不同的是,我们没有任何的广播消息发出,所以别的recever包括ui都是不知道我们偷偷做这个操作的。  
  39.                     if (prepareBluetooth()) {  
  40.                         transitionTo(mWarmUp); //在初始化成功后,我们仍然会change到WarmUp  
  41.                     }  
  42.                     break;  
  43. //对于飞行模式,我们并不陌生,它主要控制电话,wifi,和蓝牙,所以,毫无疑问,我们需要对飞行模式做一些处理。其实用脚趾头想我们都知道做了些什么,若是打开飞行模式之前蓝牙是打开的,那么关闭飞行模式,我们仍然需要打开它。若是打开飞行模式之前蓝牙是关闭的,那么关闭飞行模式的时候,我们根据quick switch的值来判断是否重新打开  
  44.                 case AIRPLANE_MODE_OFF:  
  45.                     if (getBluetoothPersistedSetting()) {  
  46. //之前是打开的,和USER_TURN_ON的处理是相同的  
  47.                         // starts turning on BT module, broadcast this out  
  48.                         broadcastState(BluetoothAdapter.STATE_TURNING_ON);  
  49.                         transitionTo(mWarmUp);  
  50.                         if (prepareBluetooth()) {  
  51.                             // We will continue turn the BT on all the way to the BluetoothOn state  
  52.                             deferMessage(obtainMessage(TURN_ON_CONTINUE));  
  53.                             transitionTo(mWarmUp);  
  54.                         } else {  
  55.                             Log.e(TAG, "failed to prepare bluetooth, abort turning on");  
  56.                             transitionTo(mPowerOff);  
  57.                             broadcastState(BluetoothAdapter.STATE_OFF);  
  58.                         }  
  59. //之前是关闭的,需要根据quick switch的值来进行判断,看是否发送TURN_HOT的msg  
  60.                     } else if (mContext.getResources().getBoolean  
  61.                             (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) {  
  62.                         sendMessage(TURN_HOT);  
  63.                     }  
  64.                     break;  
  65. //这个是上文所说的一些应用直接调用changeApplicationBluetoothState api之后会发出的msg,只会做蓝牙的打开操作,同样不会有任何的ui上的显示。  
  66.                 case PER_PROCESS_TURN_ON:  
  67.                     if (prepareBluetooth()) {  
  68.                         transitionTo(mWarmUp);  
  69.                     }  
  70.                     deferMessage(obtainMessage(PER_PROCESS_TURN_ON));  
  71.                     break;  
  72. //其它的msg都是无关紧要的,不加以分析了  
  73. ……  
  74.             return retValue;  
  75.         }  

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

[java] view plaincopy
  1.   private class WarmUp extends State {  
  2.   
  3.         @Override  
  4.         public void enter() {  
  5.             if (DBG) log("Enter WarmUp: " + getCurrentMessage().what);  
  6.         }  
  7.   
  8.         @Override  
  9.         public boolean processMessage(Message message) {  
  10.             log("WarmUp process message: " + message.what);  
  11.   
  12.             boolean retValue = HANDLED;  
  13.             switch(message.what) {  
  14. //sdp ok,则把刚刚prepare bluetooth中启动的一个定时器(10s)remove掉,同时change到hotoff的状态  
  15.                 case SERVICE_RECORD_LOADED:  
  16.                     removeMessages(PREPARE_BLUETOOTH_TIMEOUT);  
  17.                     transitionTo(mHotOff);  
  18.                     break;  
  19.                 case PREPARE_BLUETOOTH_TIMEOUT:  
  20.                     Log.e(TAG, "Bluetooth adapter SDP failed to load");  
  21. //若是tiemout的话,我们就只能change到poweroff状态了。  
  22.                     shutoffBluetooth();  
  23.                     transitionTo(mPowerOff);  
  24.                     broadcastState(BluetoothAdapter.STATE_OFF);  
  25.                     break;  
  26.                 case USER_TURN_ON: // handle this at HotOff state  
  27.                 case TURN_ON_CONTINUE: // Once in HotOff state, continue turn bluetooth  
  28.                                        // on to the BluetoothOn state  
  29.                 case AIRPLANE_MODE_ON:  
  30.                 case AIRPLANE_MODE_OFF:  
  31.                 case PER_PROCESS_TURN_ON:  
  32.                 case PER_PROCESS_TURN_OFF:  
  33. //收到以上这些消息都是直接defer就可以了,到下一个状态中去处理,再刚刚poweroff状态收到user_turn_on的时候,是发了TURN_ON_CONTINUE这个msg的,这个msg会在hotoff状态中继续起作用哦  
  34.                     deferMessage(message);  
  35.                     break;  
  36.                 case USER_TURN_OFF:  
  37.                     Log.w(TAG, "WarmUp received: " + message.what);  
  38.                     break;  
  39.                 default:  
  40.                     return NOT_HANDLED;  
  41.             }  
  42.             return retValue;  
  43.         }  
  44.   
  45.     }  


 

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

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

[java] view plaincopy
  1.  private class HotOff extends State {  
  2.         @Override  
  3.         public void enter() {  
  4.             if (DBG) log("Enter HotOff: " + getCurrentMessage().what);  
  5.         }  
  6.   
  7.         @Override  
  8.         public boolean processMessage(Message message) {  
  9.             log("HotOff process message: " + message.what);  
  10.   
  11.             boolean retValue = HANDLED;  
  12.             switch(message.what) {  
  13. //这里其实就是ui上打开蓝牙了,需要注意的是,这里是不是没有change到别的状态哦?  
  14. //这是为什么呢?这个case之后没有break啊,没有break,懂了吧。。。  
  15.                 case USER_TURN_ON:  
  16.                     broadcastState(BluetoothAdapter.STATE_TURNING_ON);  
  17.                     if ((Boolean) message.obj) {  
  18.                         persistSwitchSetting(true);  
  19.                     }  
  20. //这是什么,这是上面传下来的哦,所以ui在hotoff的状态下发这个msg,这里会继续处理的哦  
  21.                case TURN_ON_CONTINUE:  
  22. //这里就是蓝牙从hotoff到turnon之间要做的一个事情就是设置为connectable  
  23.                     mBluetoothService.switchConnectable(true);  
  24. //到switching的状态。  
  25.                     transitionTo(mSwitching);  
  26.                     break;  
  27. //这里有两个msg,一个是飞行模式开,一个turn cold,都会真正地去关闭蓝牙。  
  28. //所以无论quick switch是开还是关,之后飞行模式开了,都是会真正去关闭蓝牙的。  
  29.                 case AIRPLANE_MODE_ON:  
  30.                 case TURN_COLD:  
  31.                     shutoffBluetooth();  
  32.                     transitionTo(mPowerOff);  
  33.                     broadcastState(BluetoothAdapter.STATE_OFF);  
  34.                     break;  
  35. //同样的飞行模式的打开的处理  
  36.                 case AIRPLANE_MODE_OFF:  
  37.                     if (getBluetoothPersistedSetting()) {  
  38.                         broadcastState(BluetoothAdapter.STATE_TURNING_ON);  
  39.                         transitionTo(mSwitching);  
  40.                         mBluetoothService.switchConnectable(true);  
  41.                     }  
  42.                     break;  
  43.                     break;  
  44. //这里,不会走到swtiching的状态,会到mPerProcessState的状态,上面有详细的介绍  
  45.                 case PER_PROCESS_TURN_ON:  
  46.                     transitionTo(mPerProcessState);  
  47.   
  48.                     // Resend the PER_PROCESS_TURN_ON message so that the callback  
  49.                     // can be sent through.  
  50.                     deferMessage(message);  
  51.   
  52.                     mBluetoothService.switchConnectable(true);  
  53.                     break;  
  54.                 case PER_PROCESS_TURN_OFF:  
  55.                     perProcessCallback(false, (IBluetoothStateChangeCallback)message.obj);  
  56.                     break;  
  57.                 case USER_TURN_OFF: // ignore  
  58.                     break;  
  59.                 case POWER_STATE_CHANGED:  
  60.                     if ((Boolean) message.obj) {  
  61.                         recoverStateMachine(TURN_HOT, null);  
  62.                     }  
  63.                     break;  
  64.                 default:  
  65.                     return NOT_HANDLED;  
  66.             }  
  67.             return retValue;  
  68.         }  
  69.   
  70.     }  


 

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

[java] view plaincopy
  1.     private class Switching extends State {  
  2.   
  3.         @Override  
  4.         public void enter() {  
  5.             if (DBG) log("Enter Switching: " + getCurrentMessage().what);  
  6.         }  
  7.         @Override  
  8.         public boolean processMessage(Message message) {  
  9.             log("Switching process message: " + message.what);  
  10.   
  11.             boolean retValue = HANDLED;  
  12.             switch(message.what) {  
  13. //从注释可以清晰地看到,这个msg是我们上面调用BluetoothService.switchConnectable(true);这个函数的正确回应  
  14.                 case SCAN_MODE_CHANGED:  
  15.                     // This event matches mBluetoothService.switchConnectable action  
  16.                     if (mPublicState == BluetoothAdapter.STATE_TURNING_ON) {  
  17.                         // set pairable if it's not  
  18. //设为可配对  
  19.                         mBluetoothService.setPairable();  
  20.                     //初始化bluetooth  
  21.                         mBluetoothService.initBluetoothAfterTurningOn();  
  22. //正式change到bluetoothon的状态  
  23.                         transitionTo(mBluetoothOn);  
  24.                         broadcastState(BluetoothAdapter.STATE_ON);  
  25.                         // run bluetooth now that it's turned on  
  26.                         // Note runBluetooth should be called only in adapter STATE_ON  
  27. //这个函数做的事情其实就是打开后的自动连接了。  
  28.                         mBluetoothService.runBluetooth();  
  29.                     }  
  30.                     break;  
  31.                 case POWER_STATE_CHANGED:  
  32.                     removeMessages(POWER_DOWN_TIMEOUT);  
  33. //power可以理解为电压的关闭  
  34.                     if (!((Boolean) message.obj)) {  
  35.                         if (mPublicState == BluetoothAdapter.STATE_TURNING_OFF) {  
  36. //就是关闭了,change到hotoff状态  
  37.                             transitionTo(mHotOff);  
  38.                             finishSwitchingOff();  
  39. //若是quick switch没有设置,则会change到power off的状态  
  40.                             if (!mContext.getResources().getBoolean  
  41.                            (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) {  
  42.                                 deferMessage(obtainMessage(TURN_COLD));  
  43.                             }  
  44.                         }  
  45.                     } else {  
  46. //在turning on就没有什么好做的,否则,就是看到poweroff还是hotoff了  
  47.                         if (mPublicState != BluetoothAdapter.STATE_TURNING_ON) {  
  48.                             if (mContext.getResources().getBoolean  
  49.                             (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) {  
  50.                                 recoverStateMachine(TURN_HOT, null);  
  51.                             } else {  
  52.                                 recoverStateMachine(TURN_COLD, null);  
  53.                             }  
  54.                         }  
  55.                     }  
  56.                     break;  
  57. //所有的remote device都断开连接了,则我们会在5s后发送一个POWER_DOWN_TIMEOUT的msg,这个msg和下面几个msg都是从on->switching的过程中要处理的,可以见下面bluetooth_on的msg处理分析  
  58.                case ALL_DEVICES_DISCONNECTED:  
  59.                     removeMessages(DEVICES_DISCONNECT_TIMEOUT);  
  60.                     mBluetoothService.switchConnectable(false);  
  61.                     sendMessageDelayed(POWER_DOWN_TIMEOUT, POWER_DOWN_TIMEOUT_TIME);  
  62.                     break;  
  63. //设备断开超时了,直接复位一下  
  64.                 case DEVICES_DISCONNECT_TIMEOUT:  
  65.                     sendMessage(ALL_DEVICES_DISCONNECTED);  
  66.                     // reset the hardware for error recovery  
  67.                     Log.e(TAG, "Devices failed to disconnect, reseting...");  
  68.                     deferMessage(obtainMessage(TURN_COLD));  
  69.                     if (mContext.getResources().getBoolean  
  70.                         (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) {  
  71.                         deferMessage(obtainMessage(TURN_HOT));  
  72.                     }  
  73.                     break;  
  74. //power down timeout,也是直接复位  
  75.            case POWER_DOWN_TIMEOUT:  
  76.                     transitionTo(mHotOff);  
  77.                     finishSwitchingOff();  
  78.                     // reset the hardware for error recovery  
  79.                     Log.e(TAG, "Devices failed to power down, reseting...");  
  80.                     deferMessage(obtainMessage(TURN_COLD));  
  81.                     if (mContext.getResources().getBoolean  
  82.                         (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) {  
  83.                         deferMessage(obtainMessage(TURN_HOT));  
  84.                     }  
  85.                     break;  
  86. ……  


 

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

[java] view plaincopy
  1. private class BluetoothOn extends State {  
  2.   
  3.         @Override  
  4.         public void enter() {  
  5.             if (DBG) log("Enter BluetoothOn: " + getCurrentMessage().what);  
  6.         }  
  7.         @Override  
  8.         public boolean processMessage(Message message) {  
  9.             log("BluetoothOn process message: " + message.what);  
  10.   
  11.             boolean retValue = HANDLED;  
  12.             switch(message.what) {  
  13. //这个是off的操作,就是ui上的关闭了  
  14.                 case USER_TURN_OFF:  
  15.                     if ((Boolean) message.obj) {  
  16.                         persistSwitchSetting(false);  
  17.                     }  
  18.   
  19.                     if (mBluetoothService.isDiscovering()) {  
  20.                         mBluetoothService.cancelDiscovery();  
  21.                     }  
  22.                     if (!mBluetoothService.isApplicationStateChangeTrackerEmpty()) {  
  23.                         transitionTo(mPerProcessState);  
  24.                         deferMessage(obtainMessage(TURN_HOT));  
  25.                         break;  
  26.                     }  
  27. //同样要注意,这里没有break哦  
  28.                     //$FALL-THROUGH$ to AIRPLANE_MODE_ON  
  29. //和飞行模式打开的操作  
  30.                 case AIRPLANE_MODE_ON:  
  31.                     broadcastState(BluetoothAdapter.STATE_TURNING_OFF);  
  32.                     transitionTo(mSwitching);  
  33.                     if (mBluetoothService.getAdapterConnectionState() !=  
  34.                         BluetoothAdapter.STATE_DISCONNECTED) {  
  35. //需要把所有已经连接的设备都disconnect掉  
  36.                         mBluetoothService.disconnectDevices();  
  37.                         sendMessageDelayed(DEVICES_DISCONNECT_TIMEOUT,  
  38.                                            DEVICES_DISCONNECT_TIMEOUT_TIME);  
  39.                     } else {  
  40. //没有连接设备,直接power down  
  41.                         mBluetoothService.switchConnectable(false);  
  42.                         sendMessageDelayed(POWER_DOWN_TIMEOUT, POWER_DOWN_TIMEOUT_TIME);  
  43.                     }  
  44.   
  45.                     // we turn all the way to PowerOff with AIRPLANE_MODE_ON  
  46.                     if (message.what == AIRPLANE_MODE_ON) {  
  47.                         // We inform all the per process callbacks  
  48.                         allProcessesCallback(false);  
  49.                         deferMessage(obtainMessage(AIRPLANE_MODE_ON));  
  50.                     }  
  51.                     break;  
  52. ……  
  53.             return retValue;  
  54.         }  
  55.   
  56.     }  

 

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

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

 

修改记录1:原本的状态图中,hotoff到poweroff有一个箭头画反了。


0 0