上文我们详细介绍了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-0X9E8B3F,The 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 Host,when 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 status的event,这个event产生后就表示蓝牙已经开始扫描设备了。然后会有inquiry resultevent会回报上来。需要注意的有可能多个设备在event中回报上来,所以需要继续解析这个event。在最后,inquiry complete event会上来表示inquiry完成了。所以,下面我们就会主要讨论这几个event。
5、command statusevent的处理。
在对event的解析函数中是这样写的:
switch (eh->evt) { case EVT_CMD_STATUS: cmd_status(index, ptr); break;
所以会调用cmd_status这个函数:
static inline void cmd_status(int index, void *ptr) { evt_cmd_status *evt = ptr; uint16_t opcode = btohs(evt->opcode); //很多cmd都会产生这个event,只是唯独对inquiry需要做一些特殊的处理 //若是inquiry的cmd有一个特殊的操作 if (opcode == cmd_opcode_pack(OGF_LINK_CTL, OCF_INQUIRY)) cs_inquiry_evt(index, evt->status); } static inline void cs_inquiry_evt(int index, uint8_t status) { //这里有个inquiry的错误操作 //原本就是打印一个错误的信息,感觉对错误的处理还是有所欠缺啊 if (status) { error("Inquiry Failed with status 0x%02x", status); return; } //这里在ok的情况下,需要设置状态为inq,主要就是向上层回复discovering的property change set_state(index, DISCOV_INQ); } Set_State的函数片段: case DISCOV_SCAN: //设置adapter的state adapter_set_state(adapter, STATE_DISCOV); break; adapter_set_state的函数片段: case STATE_DISCOV: //设置discov_active标志 discov_active = TRUE; //向上层回复discovering的property change emit_property_changed(connection, path, ADAPTER_INTERFACE, "Discovering", DBUS_TYPE_BOOLEAN, &discov_active); break; 从上面的spec分析中我们已经知道cmd status就是表示inquiry开始了,所以我们有必要通知上层了,比如说上层的progress的小圆圈可以转起来了。我们回到上层去看看: //这个函数位于eventloop中 } else if (name.equals("Discovering")) { Intent intent; //收到discovering的property change的处理 adapterProperties.setProperty(name, propValues[1]); //根据这个值来发送对应的broadcast if (propValues[1].equals("true")) { intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_STARTED); } else { // Stop the discovery. mBluetoothService.cancelDiscovery(); intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); } mContext.sendBroadcast(intent, BLUETOOTH_PERM);
所以,framework层对这个的处理就是发送一个ACTION_DISCOVERY_STARTED的broadcast,这样所有的receiver就可以动起来了。
6、ACTION_DISCOVERY_STARTED的receiver分析
从代码中我们可以看到这个action一共有两个receiver,一个是静态注册的一个是动态注册是:
6.1 BluetoothDiscoveryReceiver
这个receiver是在settings中的androidmanifest中注册的:
<receiver android:name=".bluetooth.BluetoothDiscoveryReceiver"> <intent-filter> <action android:name="android.bluetooth.adapter.action.DISCOVERY_STARTED" /> <action android:name="android.bluetooth.adapter.action.DISCOVERY_FINISHED" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </receiver>这个 receiver 的具体操作如下:
public final class BluetoothDiscoveryReceiver extends BroadcastReceiver { private static final String TAG = "BluetoothDiscoveryReceiver"; @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); Log.v(TAG, "Received: " + action); if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_STARTED) || action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) { //就是更新共享空间中的扫描开始和扫描结束的时间 LocalBluetoothPreferences.persistDiscoveringTimestamp(context); } } }
其实就是更新时间戳,事实上,我们在开始扫描的时候有检查过这个时间戳,这里也就是那个地方用的。
6.2 ScanningStateChangedHandler
这个receiver是一个handler,他的注册如下:
addHandler(BluetoothAdapter.ACTION_DISCOVERY_STARTED, new ScanningStateChangedHandler(true)); //处理函数如下 private class ScanningStateChangedHandler implements Handler { private final boolean mStarted; ScanningStateChangedHandler(boolean started) { mStarted = started; } public void onReceive(Context context, Intent intent, BluetoothDevice device) { //调用注册的callback中的onScanningStateChanged函数,具体见6.2.1 synchronized (mCallbacks) { for (BluetoothCallback callback : mCallbacks) { callback.onScanningStateChanged(mStarted); } } //这个函数就是把上次扫描到的设备拿清除掉,所以,我们会发现在扫描的开始原来的设备就都不见了,具体见6.2.2 mDeviceManager.onScanningStateChanged(mStarted); LocalBluetoothPreferences.persistDiscoveringTimestamp(context); } }
6.2.1 注册的callback
从代码中我们可以看到有以下callback:
1)DeviceListPreferenceFragment.java public void onScanningStateChanged(boolean started) { if (started == false) { //这个是扫描结束的处理 removeOutOfRangeDevices(); } updateProgressUi(started); } private void updateProgressUi(boolean start) { //就是那个小圆圈就开始动起来了 if (mDeviceListGroup instanceof ProgressCategory) { ((ProgressCategory) mDeviceListGroup).setProgress(start); } }
6.2.2 mDeviceManager.onScanningStateChanged的分析
public synchronized void onScanningStateChanged(boolean started) { // If starting a new scan, clear old visibility // Iterate in reverse order since devices may be removed. //扫描所有的cached的device for (int i = mCachedDevices.size() - 1; i >= 0; i--) { CachedBluetoothDevice cachedDevice = mCachedDevices.get(i); //假如是开始扫描,就是把他不显示了 if (started) { cachedDevice.setVisible(false); } else { //扫描结束,若是他不是已经配对的,并且是不可见的,我们就把它从cacheddevices中remove掉。 //这些设备其实就是那些之前扫描到过,但是这次扫描没有扫描到的设备。 //没有开始就把设备remove掉的一个好处就是,若是这次再次扫描到,我们不需要再加入进去 if (cachedDevice.getBondState() == BluetoothDevice.BOND_NONE && cachedDevice.isVisible() == false) { mCachedDevices.remove(cachedDevice); } } } }
至此,command statusevent的处理就全部ok了,主要就是向上层汇报说我们开始扫描设备了,你可以把ui上上次扫描的设备去除掉,并且把开始扫描的ui上的小圈圈动起来了。
若您觉得该文章对您有帮助,请在下面用鼠标轻轻按一下“顶”,哈哈~~·