上文我们详细介绍了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);
-
-
- 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)
- {
-
-
- if (status) {
- error("Inquiry Failed with status 0x%02x", status);
- return;
- }
-
-
- set_state(index, DISCOV_INQ);
- }
- Set_State的函数片段:
- case DISCOV_SCAN:
-
- adapter_set_state(adapter, STATE_DISCOV);
- break;
- adapter_set_state的函数片段:
- case STATE_DISCOV:
-
- discov_active = TRUE;
-
- emit_property_changed(connection, path,
- ADAPTER_INTERFACE, "Discovering",
- DBUS_TYPE_BOOLEAN, &discov_active);
- break;
- 从上面的spec分析中我们已经知道cmd status就是表示inquiry开始了,所以我们有必要通知上层了,比如说上层的progress的小圆圈可以转起来了。我们回到上层去看看:
-
- } else if (name.equals("Discovering")) {
- Intent intent;
-
- adapterProperties.setProperty(name, propValues[1]);
-
- if (propValues[1].equals("true")) {
- intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
- } else {
-
- 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) {
-
- synchronized (mCallbacks) {
- for (BluetoothCallback callback : mCallbacks) {
- callback.onScanningStateChanged(mStarted);
- }
- }
-
- 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) {
-
-
-
- for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
- CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
-
- if (started) {
- cachedDevice.setVisible(false);
- } else {
-
-
-
- if (cachedDevice.getBondState() == BluetoothDevice.BOND_NONE &&
- cachedDevice.isVisible() == false) {
- mCachedDevices.remove(cachedDevice);
- }
- }
- }
- }
至此,command statusevent的处理就全部ok了,主要就是向上层汇报说我们开始扫描设备了,你可以把ui上上次扫描的设备去除掉,并且把开始扫描的ui上的小圈圈动起来了。