关闭

[Android源码分析]蓝牙搜索过程分析

标签: android蓝牙搜索扫描
2538人阅读 评论(3) 收藏 举报
分类:


在完成打开蓝牙的分析之后,我们就正式进入到蓝牙使用的阶段了。毫无疑问,我们第一个对蓝牙的操作当然就是扫描设备了。那就是这样一个点击“扫描设备”究竟干了些什么,晓东和大家来仔细分析一下。

         1、扫描设备按键的处理

         代码的实现看起来很清晰,



 @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case MENU_ID_SCAN:
                //点击搜索,首先检查是否打开BT
                if (mLocalAdapter.getBluetoothState() == BluetoothAdapter.STATE_ON) {
			//开始扫描
                    startScanning();
                }
                return true;
	所以,就是调用	startScanning来进行真正的扫描操作。该函数的代码实现如下:
    private void startScanning() {
        //这里就是若是开始没有搜索过,第一次搜索需要显示那个搜索到的设备的那个组的ui
        if (!mAvailableDevicesCategoryIsPresent) {
            getPreferenceScreen().addPreference(mAvailableDevicesCategory);
        }

        //这里的true,就是强制扫描,强制扫描和不强制扫描的差别,我们后面来看
        mLocalAdapter.startScanning(true);
    }
    void startScanning(boolean force) {
        // Only start if we're not already scanning
        //先检查是否正在扫描。
        if (!mAdapter.isDiscovering()) {
            if (!force) {
                // Don't scan more than frequently than SCAN_EXPIRATION_MS,
                // unless forced
                //不是强制扫描,则5分钟之内再次搜索不响应                if (mLastScan + SCAN_EXPIRATION_MS > System.currentTimeMillis()) {
                    return;
                }

                // If we are playing music, don't scan unless forced.
                //若是在听音乐,也不会扫描
                A2dpProfile a2dp = mProfileManager.getA2dpProfile();
                if (a2dp != null && a2dp.isA2dpPlaying()) {
                    return;
                }
            }

                //开始扫描,并得到扫描的时间点
            if (mAdapter.startDiscovery()) {
                mLastScan = System.currentTimeMillis();
            }
        }

这里有人就会问题,不强制扫描一般在什么情况下发生啊,一个比较常见的情形就是比如我们选择一个文件进行发送,然后选择蓝牙,就会跳出一系列的蓝牙设备,假如你用得多的话,就会发现这里有时会去扫描,有时则不会。这个时候其实就是用的不强制扫描,所以会出现不进行真正扫描的情况。


2framework层和jni层中的startDiscovery的处理


         framework层中,这个函数的处理还是比较简单的:

  public synchronized boolean startDiscovery() {
        //首先检查是否有权限
        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                                "Need BLUETOOTH_ADMIN permission");
        //检查是否已经enable bt
        if (!isEnabledInternal()) return false;

        return startDiscoveryNative();
}
整个这个过程除了权限的检查和bt是否打开的检查,并没有做别的事情。所以,我们可以直接去看jni层的native函数即可。
static jboolean startDiscoveryNative(JNIEnv *env, jobject object) {
    LOGV("%s", __FUNCTION__);
#ifdef HAVE_BLUETOOTH
    DBusMessage *msg = NULL;
    DBusMessage *reply = NULL;
    DBusError err;
    const char *name;
    jboolean ret = JNI_FALSE;

    native_data_t *nat = get_native_data(env, object);
    if (nat == NULL) {
        goto done;
    }

    dbus_error_init(&err);

        //就是向bluez调用这个函数
    /* Compose the command */
    msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC,
                                       get_adapter_path(env, object),
                                       DBUS_AD

所以,还是蛮清楚的,就是调用bluez中的StartDiscovery method,我们去bluez中看看就知道了。


3bluezStartDiscoverymethod分析


         bluezadapter.c中,我们可以很清楚地看到:

static GDBusMethodTable adapter_methods[] = {
……
        { "StartDiscovery",     "",     "",     adapter_start_discovery },

         因此StartDiscovery对应的method函数就是adapter_start_discovery了,我们直接去看该函数即可:

//bluez中开始扫描的函数
static DBusMessage *adapter_start_discovery(DBusConnection *conn,
                                                DBusMessage *msg, void *data)
{
        struct session_req *req;
        struct btd_adapter *adapter = data;
        const char *sender = dbus_message_get_sender(msg);
        int err;

        //同样的,首先要检查adapter是否已经up
        if (!adapter->up)
                return btd_error_not_ready(msg);

        //看是否有disc的req在
        req = find_session(adapter->disc_sessions, sender);
        if (req) {
                session_ref(req);
                return dbus_message_new_method_return(msg);
        }

        //看是否有扫描的session在,若是有就直接return了
        if (adapter->disc_sessions)
                goto done;

        //把已经found的device清空了
        g_slist_foreach(adapter->found_devices, (GFunc) dev_info_free, NULL);
        g_slist_free(adapter->found_devices);
        adapter->found_devices = NULL;
        //out-of-order的设备也清空了
        g_slist_free(adapter->oor_devices);
        adapter->oor_devices = NULL;

        //开始discovery,见3.1
        err = start_discovery(adapter);
        if (err < 0 && err != -EINPROGRESS)
                return btd_error_failed(msg, strerror(-err));

done:
        req = create_session(adapter, conn, msg, 0,
                                session_owner_exit);
	//加入到disc session
        adapter->disc_sessions = g_slist_append(adapter->disc_sessions, req);

        return dbus_message_new_method_return(msg);
}

3.1 bluez中的start_discovery


这个函数会处理一些残余情况,就是把一些上次剩余的情况先干掉,然后再真正去发送对应的cmd

static int start_discovery(struct btd_adapter *adapter)
{
        /* Do not start if suspended */
        //suspended的状态也不进行discovery
        if (adapter->state == STATE_SUSPENDED)
                return 0;

        /* Postpone discovery if still resolving names */
        //若是在resolve name也不去discover,所以其实上层ui也是在resolve name之后才可以再次扫描的,若是我们需要修改,这里也是要修改的。
        if (adapter->state == STATE_RESOLVNAME)
                return -EINPROGRESS;

        //还没有name request就直接cancel掉了
        pending_remote_name_cancel(adapter);

        //就是发送inquiry的cmd
        return adapter_ops->start_discovery(adapter->dev_id);
}

我们到hciops中就可以看到:

static struct btd_adapter_ops hci_ops = {
……
        .start_discovery = hciops_start_discovery,

所以,上面adapter_ops->start_discovery所调用的就是hciops_start_discovery了。

static int hciops_start_discovery(int index)
{
        int adapter_type = get_adapter_type(index);

        switch (adapter_type) {
        case BR_EDR_LE:
                return hciops_start_inquiry(index, LENGTH_BR_LE_INQ);
        case BR_EDR:
                //bredr,就是发送inquiry的cmd
                return hciops_start_inquiry(index, LENGTH_BR_INQ);
        case LE_ONLY:
                return hciops_start_scanning(index, TIMEOUT_LE_SCAN);
        default:
                return -EINVAL;
        }
}

这里会根据不同的类型来做发送不同的cmd,我们现在已BREDR的设备为例来介绍。所以就看hciops_start_inquiry(index, LENGTH_BR_INQ);

static int hciops_start_inquiry(int index, uint8_t length)
{
        struct dev_info *dev = &devs[index];
        uint8_t lap[3] = { 0x33, 0x8b, 0x9e };
        inquiry_cp inq_cp;

        DBG("hci%d length %u", index, length);

        memset(&inq_cp, 0, sizeof(inq_cp));
        memcpy(&inq_cp.lap, lap, 3);
        inq_cp.length = length;
        inq_cp.num_rsp = 0x00;

        //发送inquiry的cmd,这里就是根据spec来进行组包发送了,具体的inquiry cmd格式见下面4
        if (hci_send_cmd(dev->sk, OGF_LINK_CTL,
                        OCF_INQUIRY, INQUIRY_CP_SIZE, &inq_cp) < 0)
                return -errno;

        return 0;
}

至此,到这边我们inquiry的command就发送给底层的bt controller,那再这之后会ui上何时开始显示搜索的圈圈,何时跳出扫描到的设备,又是何时结束扫描的,我们将在后面的文章中详细的给大家一一分析。





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



4
1

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:197344次
    • 积分:3370
    • 等级:
    • 排名:第9979名
    • 原创:61篇
    • 转载:0篇
    • 译文:6篇
    • 评论:205条
    博客公告
    本博客所有文章均为原创,欢迎交流,欢迎转载;转载请勿篡改内容,并且注明出处,禁止用于商业目的,谢谢!
    博客专栏
    最新评论