[Android源码分析]从spec角度来详细分析inquiry command带来的影响

上文我们详细介绍了Android是如何通过ui操作到最终发出inquiry command来实现蓝牙的扫描功能的。本文晓东将会和大家一起来看看inquiry command的格式,以及发出这个command后会产生哪些影响。

4、inquiry cmd的格式分析。

        在蓝牙core spec中明确定义了inquirycmd的格式已经返回的event。我们来具体看看:

Inquiry command的格式[1]

针对这个command的参数设置如下:

LAP:在spec上是这样描述这个参数的:“The reserved LAP addresses are 0x9E8B00-0X9E8B3FThe general LAP is 0x9E8B33.”也就是说,LAP的地址范围是0x9E8B00-0X9E8B3F,一般而言我们设为0x9E8B33。从上面的代码我们也可以发现,android中的确使用的是推荐值:uint8_t lap[3] = {0x33, 0x8b, 0x9e };

    Inquiry length:这个顾名思义就是扫描的时间长度。上面的LENGTH_BR_INQ的值是0x08,简单计算一下08*1.28s,大概就是10s了,所以,我们从上层才会看到一般的android手机搜索的时间就是10s,若是需要修改,则可以改这边的参数,当然最长不能超过61.44s了。

    Num_Responses:就是响应的设备数目,这里设为0就是不限制搜索到的设备数目。当然一般而言,我们都不会设置搜素到的设备的,呵呵~~

    这个command发送下去会产生哪些event呢,spec中也是有明确规定的:

A Command Status event shall be sentfrom the BR/EDR Controller to the Hostwhen the BR/EDRController has started the Inquiry process. Unless filtered, an Inquiry Resultevent shall be created for each BR/EDR Controller which responds to the Inquirymessage. In addition, multiple BR/EDR Controllers which respond to the Inquiremessage may be combined into the same event. An Inquiry Complete event shall begenerated when the Inquiry process has completed.

    总的意思就是说,我们首先要产生一个command statusevent,这个event产生后就表示蓝牙已经开始扫描设备了。然后会有inquiry resultevent会回报上来。需要注意的有可能多个设备在event中回报上来,所以需要继续解析这个event。在最后,inquiry complete event会上来表示inquiry完成了。所以,下面我们就会主要讨论这几个event


5command statusevent的处理。

    在对event的解析函数中是这样写的:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. switch (eh->evt) {  
  2.        case EVT_CMD_STATUS:  
  3.                cmd_status(index, ptr);  
  4.                break;  

所以会调用cmd_status这个函数:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. static inline void cmd_status(int index, void *ptr)  
  2. {  
  3.         evt_cmd_status *evt = ptr;  
  4.         uint16_t opcode = btohs(evt->opcode);  
  5. //很多cmd都会产生这个event,只是唯独对inquiry需要做一些特殊的处理  
  6.         //若是inquiry的cmd有一个特殊的操作  
  7.         if (opcode == cmd_opcode_pack(OGF_LINK_CTL, OCF_INQUIRY))  
  8.                 cs_inquiry_evt(index, evt->status);  
  9. }  
  10. static inline void cs_inquiry_evt(int index, uint8_t status)  
  11. {  
  12.         //这里有个inquiry的错误操作  
  13.         //原本就是打印一个错误的信息,感觉对错误的处理还是有所欠缺啊  
  14.         if (status) {  
  15.                 error("Inquiry Failed with status 0x%02x", status);  
  16.                 return;  
  17.         }  
  18.   
  19.         //这里在ok的情况下,需要设置状态为inq,主要就是向上层回复discovering的property change  
  20.         set_state(index, DISCOV_INQ);  
  21. }  
  22. Set_State的函数片段:  
  23.        case DISCOV_SCAN:                 
  24.                 //设置adapter的state  
  25.                 adapter_set_state(adapter, STATE_DISCOV);  
  26.                 break;  
  27.       adapter_set_state的函数片段:  
  28.   case STATE_DISCOV:  
  29.                 //设置discov_active标志  
  30.                 discov_active = TRUE;  
  31.                 //向上层回复discovering的property change  
  32.                 emit_property_changed(connection, path,  
  33.                                         ADAPTER_INTERFACE, "Discovering",  
  34.                                         DBUS_TYPE_BOOLEAN, &discov_active);  
  35.                 break;  
  36. 从上面的spec分析中我们已经知道cmd status就是表示inquiry开始了,所以我们有必要通知上层了,比如说上层的progress的小圆圈可以转起来了。我们回到上层去看看:  
  37. //这个函数位于eventloop中  
  38.         } else if (name.equals("Discovering")) {  
  39.             Intent intent;  
  40.         //收到discovering的property change的处理  
  41.             adapterProperties.setProperty(name, propValues[1]);  
  42.         //根据这个值来发送对应的broadcast  
  43.             if (propValues[1].equals("true")) {  
  44.                 intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_STARTED);  
  45.             } else {  
  46.                 // Stop the discovery.  
  47.                 mBluetoothService.cancelDiscovery();  
  48.                 intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);  
  49.             }  
  50.             mContext.sendBroadcast(intent, BLUETOOTH_PERM);  

所以,framework层对这个的处理就是发送一个ACTION_DISCOVERY_STARTEDbroadcast,这样所有的receiver就可以动起来了。

6ACTION_DISCOVERY_STARTEDreceiver分析

    从代码中我们可以看到这个action一共有两个receiver,一个是静态注册的一个是动态注册是:

6.1 BluetoothDiscoveryReceiver

这个receiver是在settings中的androidmanifest中注册的:

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <receiver  
  2.     android:name=".bluetooth.BluetoothDiscoveryReceiver">  
  3.     <intent-filter>  
  4.         <action android:name="android.bluetooth.adapter.action.DISCOVERY_STARTED" />  
  5.         <action android:name="android.bluetooth.adapter.action.DISCOVERY_FINISHED" />  
  6.         <category android:name="android.intent.category.DEFAULT" />  
  7.     </intent-filter>  
  8. </receiver>  
这个 receiver 的具体操作如下:
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public final class BluetoothDiscoveryReceiver extends BroadcastReceiver {  
  2.     private static final String TAG = "BluetoothDiscoveryReceiver";  
  3.   
  4.     @Override  
  5.     public void onReceive(Context context, Intent intent) {  
  6.         String action = intent.getAction();  
  7.         Log.v(TAG, "Received: " + action);  
  8.   
  9.         if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_STARTED) ||  
  10.                 action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {  
  11.                 //就是更新共享空间中的扫描开始和扫描结束的时间  
  12.             LocalBluetoothPreferences.persistDiscoveringTimestamp(context);  
  13.         }  
  14.     }  
  15. }  

其实就是更新时间戳,事实上,我们在开始扫描的时候有检查过这个时间戳,这里也就是那个地方用的。

6.2 ScanningStateChangedHandler

这个receiver是一个handler,他的注册如下:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. addHandler(BluetoothAdapter.ACTION_DISCOVERY_STARTED, new ScanningStateChangedHandler(true));  
  2. //处理函数如下  
  3.     private class ScanningStateChangedHandler implements Handler {  
  4.         private final boolean mStarted;  
  5.   
  6.         ScanningStateChangedHandler(boolean started) {  
  7.             mStarted = started;  
  8.         }  
  9.         public void onReceive(Context context, Intent intent,  
  10.                 BluetoothDevice device) {  
  11. //调用注册的callback中的onScanningStateChanged函数,具体见6.2.1  
  12.             synchronized (mCallbacks) {  
  13.                 for (BluetoothCallback callback : mCallbacks) {  
  14.                     callback.onScanningStateChanged(mStarted);  
  15.                 }  
  16.             }  
  17. //这个函数就是把上次扫描到的设备拿清除掉,所以,我们会发现在扫描的开始原来的设备就都不见了,具体见6.2.2  
  18.             mDeviceManager.onScanningStateChanged(mStarted);  
  19.             LocalBluetoothPreferences.persistDiscoveringTimestamp(context);  
  20.         }  
  21. }  

6.2.1 注册的callback

从代码中我们可以看到有以下callback

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 1)DeviceListPreferenceFragment.java  
  2.     public void onScanningStateChanged(boolean started) {  
  3.         if (started == false) {  
  4.     //这个是扫描结束的处理  
  5.             removeOutOfRangeDevices();  
  6.         }  
  7.         updateProgressUi(started);  
  8.     }  
  9.   
  10.     private void updateProgressUi(boolean start) {  
  11.         //就是那个小圆圈就开始动起来了  
  12.         if (mDeviceListGroup instanceof ProgressCategory) {  
  13.             ((ProgressCategory) mDeviceListGroup).setProgress(start);  
  14.         }  
  15.     }  

6.2.2 mDeviceManager.onScanningStateChanged的分析

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public synchronized void onScanningStateChanged(boolean started) {  
  2.         // If starting a new scan, clear old visibility  
  3.         // Iterate in reverse order since devices may be removed.  
  4.         //扫描所有的cached的device  
  5.         for (int i = mCachedDevices.size() - 1; i >= 0; i--) {  
  6.             CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);  
  7.                 //假如是开始扫描,就是把他不显示了  
  8.             if (started) {  
  9.                 cachedDevice.setVisible(false);  
  10.             } else {  
  11.                 //扫描结束,若是他不是已经配对的,并且是不可见的,我们就把它从cacheddevices中remove掉。  
  12.                 //这些设备其实就是那些之前扫描到过,但是这次扫描没有扫描到的设备。  
  13.                 //没有开始就把设备remove掉的一个好处就是,若是这次再次扫描到,我们不需要再加入进去  
  14.                 if (cachedDevice.getBondState() == BluetoothDevice.BOND_NONE &&  
  15.                         cachedDevice.isVisible() == false) {  
  16.                     mCachedDevices.remove(cachedDevice);  
  17.                 }  
  18.             }  
  19.         }  
  20.     }  

至此,command statusevent的处理就全部ok了,主要就是向上层汇报说我们开始扫描设备了,你可以把ui上上次扫描的设备去除掉,并且把开始扫描的ui上的小圈圈动起来了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值