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

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

4、inquiry cmd的格式分析。

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

[Android源码分析]从spec角度来详细分析inquiry command带来的影响_第1张图片

Inquiry command的格式[1]

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

[Android源码分析]从spec角度来详细分析inquiry command带来的影响_第2张图片

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的解析函数中是这样写的:

 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_STARTEDbroadcast,这样所有的receiver就可以动起来了。

6ACTION_DISCOVERY_STARTEDreceiver分析

    从代码中我们可以看到这个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上的小圈圈动起来了。


若您觉得该文章对您有帮助,请在下面用鼠标轻轻按一下“顶”,哈哈~~·



你可能感兴趣的:(android,扫描,蓝牙)