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

  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有六个状态,分别为:

1)BluetoothOn:就是打开的状态。

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

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

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

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

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

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

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

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

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

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

  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分析就已经都完成了,比较关键的几个msg是USER_TURN_ON—UI上打开蓝牙的msg;TURN_HOT—quick switch打开的时候,默认打开蓝牙的msg;AIRPLANE_MODE_OFF—飞行模式关闭的msg。ok,下面我们去看一下warmup这个状态的处理。

  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,去除了prepare了timeout,仅此而已。所以,继续看hotoff的状态。

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

  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打开操作。它会change到swtiching的状态或者mPerProcessState的状态。我们来看看吧

  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的状态。

  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的处理也比较单纯,就是一些关闭的尾巴处理。

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

Android4.0中蓝牙适配器state machine(状态机)的分析的更多相关文章

  1. 在Android4.0中Contacts拨号盘界面剖析(源码)

      通过在 ViewPager 的适配器对象中,发现过一下三行代码 private DialpadFragment mDialpadFragment; private CallLogFragment ...

  2. android4.0 中关于内外置sd卡的获取及读写权限问题

    from://http://blog.chinaunix.net/uid-26727976-id-3146895.html 在2.x的版本中,在manifest中配置的权限android.permis ...

  3. django 2.0 中URL的include方法使用分析

    一.问题出现: 在使用Django2.0,配置全局URL时,希望指向某个APP的URL,配置如下: from django.contrib import admin from django.conf. ...

  4. 在android4.0中实现View的拖动效果

    实现方法: 首先需要定义一个支持拖动的源组件和一个作为Drop区域的目标组件. 在支持拖动的组件中注册OnTouchListener 或LongClickListener监听事件,构建一个ClipDa ...

  5. [置顶] Android4.0中修改挂断键(ENDCALL)的默认行为

    文件: frameworks/base/core/java/android/provider/Setings.java public static final String END_BUTTON_BE ...

  6. android4.0以上访问网络不能在主线程中进行以及在线程中操作UI的解决方法

    MONO 调用一个线程操作UI 然后报Only the original thread that created a view hierarchy can touch its views.错误 goo ...

  7. android4.0蓝牙使能的详细解析

    本文详细分析了android4.0 中蓝牙使能的过程,相比较android2.3,4.0中的蓝牙最大的差别在于UI上on/off的伪开关.在android4.0中加入了 adapter的状态机.所谓的 ...

  8. Qt: The State Machine Framework 学习

    State Machine,即为状态机,是Qt中一项非常好的框架.State Machine包括State以及State间的Transition,构成状态和状态转移.通过状态机,我们可以很方便地实现很 ...

  9. android4.0蓝牙使能的详细解析 (转载)

    此博客是转载过来的哦... 给自己博客定几个部分: (1)写在前面的话:一些写博客时的废话. (2)内容简介:把文章的主要内容或者核心部分作一个框架性的概括,以方便大家阅读. (3)正文:这个不需要解 ...

随机推荐

  1. java设计模式--行为型模式--中介者模式

    怎么理解中介者模式,我姑且用房产中介来理解吧.呵呵 中介者模式: 中介者模式 概述 用一个中介对象来封装一系列的对象交互.中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之 ...

  2. 关于memecache的使用及清楚示意

    Memcache是danga.com的一个项目,最早是为 LiveJournal 服务的,目前全世界不少人使用这个缓存项目来构建自己大负载的网站,来分担数据库的压力.它可以应对任意多个连接,使用非阻塞 ...

  3. Ajax跨域访问解决办法

    方法1. jsonp实现ajax跨域访问示例 jsp代码: <body> <input type="button" onclick="testJsonp ...

  4. 数据库的优化tips

    数据库   TIPS:: 1.用于记录或者是数据分析的表创建时::使用Id作为主键,1,2,3...表示消息条数.用户账号id用于做外键.一个用户相应唯一个accountId             ...

  5. C#主要字典集合性能对比[转]

    A post I made a couple days ago about the side-effect of concurrency (the concurrent collections in ...

  6. 基于纹理边缘抑制的轮廓和边界检测(Contour and Boundary Detection)

    基于纹理边缘抑制的轮廓和边界检测(Contour and Boundary Detection) kezunhai@gmail.com http://blog.csdn.net/kezunhai 一幅 ...

  7. Android系统匿名共享内存(Anonymous Shared Memory)C++调用接口分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6939890 在Android系统中,针对移动设 ...

  8. SQL*Plus break与compute的简单用法

    SQL*Plus break与compute的简单用法在SQL*Plus提示符下输出求和报表,我们可以借助break与compute两个命令来实现.这个两个命令简单易用,可满足日常需求,其实质也相当于 ...

  9. unix系统非roo账号安装JDK

    AIX系统用户rusky(非root用户,没有权限修改/etc/profile和/etc/environment文件 )直接解压JDK.zip文件,解压后把JAVA目录拷贝到/home/rusky目录 ...

  10. Unity IOC注入详细配置(MVC,WebApi)

    一直想写一篇关于unity 详细的配置信息的文章,也算是自我总结吧 先介绍了unity , Unity是微软官方推荐使用的轻型的IOC框架,支持各种方式的注入 ,使用来解耦的利器. 获取unity 的 ...