您的位置:首页 > 移动开发 > Android开发

[Android源码分析]蓝牙搜索过程中你所不知道的小细节

2013-12-16 09:43 531 查看
在上文中,我们说到蓝牙搜索并没有在收到inquiry complete的命令后就立即结束,而是会继续发送remote name request的command去得到对方的名字,本文就来和大家一起详细分析一下这些操作的流程。

10、remote name
request command的发送

Remote name request从名字上我们就可以很清晰地看到,它的主要作用就是得到对端设备的名字信息。我们来从spec上看一下,这个command的格式:



命令的参数还是比较清晰的,我们在之前都已经讲过了,就不重复了。我们来看一下,他会产生哪些event。在spec中这样描述的:

When the BR/EDRController receives the Remote_Name_Request command,the BR/EDR Controllershall send the Command Status event to the Host.When
the Link Managerhas completed the LMP messages to obtain the remote host supportedfeatures, if present, the BR/EDR Controller on the local
BR/EDR Controllershall send a Remote Host Supported Features Notification
event. When the LinkManager has completed the LMP messages to obtain the
remote name, theBR/EDR Controller on the local BR/EDR Controller shall send
a Remote Name RequestComplete event to the Host. If the remote host supported
features page ispresent, the Remote Host Supported Features Notification

event shall be sentbefore the Remote Name Request Complete event. If
not, only the RemoteName Request Complete event shall be sent.

大概的意思就是,首先会产生一个command status event,这个event我们在之前也看过,他除了inquiry的command会做处理外,其他的command并不会做特殊的处理。所以我们这里就不再看了哦。若是,我们没有和对方建立连接,也就是说没有ACL
Connection在,那么会先去得到对端的supported features,然后产生一个Remote
Host Supported Features Notification的event。若是已经有了ACLlink,并且在之前已经得到过了remote
name,则直接产生一个remote name request complete event即可。假如,我们正在建立的连接的过程中,那仍然需要先产生Remote
Host Supported Features Notification的event,然后产生RemoteName
Request Complete的event。其他情况就直接产生Remote Name Request
Complete event就好了。用几张图来更清晰地表达这个流程如下:



这里就是发送command,然后产生command status的event上来。



这是在没有连接的情况下,我们会收到两个event,一个是remote host supported features notification的event,一个是remotename
request complete的event。在有连接的情况下就如下图了,这个过程是可选的其实你可以理解,在建立连接的过程中,我们已经去得到name过,若是这种情况,我们直接把名字告诉host就可以了,而不需要再去和远端交互得到名字信息了。还是比较简单的吧,嘻嘻~~



下面我们就来看这个两个event吧,一个是remote host supported features notification的event,一个是remotename
request complete的event。

11、 remote hostsupported features notification event的分析

这个event总得来说还是比较简单的,他的格式如下所示:



总得来说就是两个参数,一个是bdaddr,一个是supported features,具体各个位表示的features的意思我就不多说了,大家自己查spec就可以知道了。我们来看一下代码:

static inline void remote_features_notify(int index, void *ptr)
{
        struct dev_info *dev = &devs[index];
        evt_remote_host_features_notify *evt = ptr;

        //根据features来设置legacy的值为false或者true
        if (evt->features[0] & 0x01)
                btd_event_set_legacy_pairing(&dev->bdaddr, &evt->bdaddr,
                                                                        FALSE);
        else
                btd_event_set_legacy_pairing(&dev->bdaddr, &evt->bdaddr,
                                                                        TRUE);

        //把相应的内容保存到features文件中
        write_features_info(&dev->bdaddr, &evt->bdaddr, NULL, evt->features);
}


就做了两件事情,一个是根据features赋值legacy,一个是保存相关的内容到features文件中。目前来看没有什么大的动作,我们暂时就分析到这里。

12、Remote Name request Complete的event分析


这个event用来表示remotename request的结束。他在spec中的表示如下:



我们可以看到,除了常规的status和bdaddr外,他还包括了remotename这个参数,他就是用来表示我们想要的user
friendly的name。在spec中明确规定它的最大长度是248byte,不能超过。


Android中对这个event的处理代码如下:

static inline void remote_name_information(int index, void *ptr)
{
        struct dev_info *dev = &devs[index];
        evt_remote_name_req_complete *evt = ptr;
        char name[MAX_NAME_LENGTH + 1];

        DBG("hci%d status %u", index, evt->status);

        memset(name, 0, sizeof(name));

        //没有问题,把evt name拷贝到name中
        if (!evt->status)
                memcpy(name, evt->name, MAX_NAME_LENGTH);

        btd_event_remote_name(&dev->bdaddr, &evt->bdaddr, evt->status, name);
}

void btd_event_remote_name(bdaddr_t *local, bdaddr_t *peer, uint8_t status,
                                char *name)
{
        struct btd_adapter *adapter;
        char srcaddr[18], dstaddr[18];
        struct btd_device *device;
        struct remote_dev_info match, *dev_info;

        if (status == 0) {
                //看是否是uft8格式的
                if (!g_utf8_validate(name, -1, NULL)) {
                        int i;
                        //把non ascii字符用空格替代

                        /* Assume ASCII, and replace all non-ASCII with
                         * spaces */
                        for (i = 0; name[i] != '\0'; i++) {
                                if (!isascii(name[i]))
                                        name[i] = ' ';
                        }
                        /* Remove leading and trailing whitespace characters */
                        g_strstrip(name);
                }

                //写到names文件中
                write_device_name(local, peer, name);
        }
        //得到对应的device
        if (!get_adapter_and_device(local, peer, &adapter, &device, FALSE))
                return;

        ba2str(local, srcaddr);
        ba2str(peer, dstaddr);

        if (status != 0)
                goto proceed;

        bacpy(&match.bdaddr, peer);
        match.name_status = NAME_ANY;

        //赋值到device info的name变量中
        dev_info = adapter_search_found_devices(adapter, &match);
        if (dev_info) {
                g_free(dev_info->name);
                dev_info->name = g_strdup(name);
                //同样会通知上层,device found,device info改变,通知上层
                adapter_emit_device_found(adapter, dev_info);
        }

        //向上层通知name和alias的property change,详细见12.1
        if (device)
                device_set_name(device, name);

proceed:
        /* remove from remote name request list */
        //把这个从found device列表中remove
        adapter_remove_found_device(adapter, peer);

        /* check if there is more devices to request names */
        //看是否还有设备需要resolve name,若是有,继续这个循环,这里return,若是没有,就设为idle了
        if (adapter_resolve_names(adapter) == 0)
                return;

        //没有就设为idle了
        adapter_set_state(adapter, STATE_IDLE);
}


12.1 device_set_name的分析

这个函数其实蛮简单的,就是像上层回报name和alias两个property
change。

void device_set_name(struct btd_device *device, const char *name)
{
        DBusConnection *conn = get_dbus_connection();

        if (strncmp(name, device->name, MAX_NAME_LENGTH) == 0)
                return;

        strncpy(device->name, name, MAX_NAME_LENGTH);
		//向上层回报Name的property change的signal
        emit_property_changed(conn, device->path,
                                DEVICE_INTERFACE, "Name",
                                DBUS_TYPE_STRING, &name);

        if (device->alias != NULL)
                return;
		//向上层回报Alias的property change的signal
        emit_property_changed(conn, device->path,
                                DEVICE_INTERFACE, "Alias",
                                DBUS_TYPE_STRING, &name);
}


12.2 framework层对Name的deviceproperty
change的处理
if (name.equals("Name")) {
            mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
                //发送ACTION_NAME_CHANGED的broadcast
            Intent intent = new Intent(BluetoothDevice.ACTION_NAME_CHANGED);
            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
            intent.putExtra(BluetoothDevice.EXTRA_NAME, propValues[1]);
            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
            mContext.sendBroadcast(intent, BLUETOOTH_PERM);


对于这个broadcast的处理如下:
addHandler(BluetoothDevice.ACTION_NAME_CHANGED, new NameChangedHandler());

    private class NameChangedHandler implements Handler {
        public void onReceive(Context context, Intent intent,
                BluetoothDevice device) {
        //更新名字
            mDeviceManager.onDeviceNameUpdated(device);
        }
}
    public void onDeviceNameUpdated(BluetoothDevice device) {
        //找到对应的设备
        CachedBluetoothDevice cachedDevice = findDevice(device);
        if (cachedDevice != null) {
                //更新名字
            cachedDevice.refreshName();
        }
    }
    void refreshName() {
        //得到名字
        fetchName();
        //改变名字的显示 
        dispatchAttributesChanged();
    }

    private void fetchName() {
        //注意这里先得到的alias名字,其实写的并不合理,会有一些隐藏的问题在里面
        mName = mDevice.getAliasName();

        //若是empty,就得到address,也就是显示地址了
        if (TextUtils.isEmpty(mName)) {
            mName = mDevice.getAddress();
            if (DEBUG) Log.d(TAG, "Device has no name (yet), use address: " + mName);
        }
    }


所以,还是很清晰吧,就是把ui上的名字改掉。只是在名字的获取上还是有一些潜在的问题。
至于framework中对alias的deviceproperty
change的处理就很简单了,就是修改了alias的property。就不再详细写出了。

13、adapterstate到idle的分析

这个就是扫描在bluez层做的最后一件事了,他的代码实现如下:

case STATE_IDLE:
                update_oor_devices(adapter);

                //就是向上层发出discovering flase的property change
                discov_active = FALSE;
                emit_property_changed(connection, path,
                                        ADAPTER_INTERFACE, "Discovering",
                                        DBUS_TYPE_BOOLEAN, &discov_active);

                //若是还有discover的session的话,就在timeout之后再次开始,这个一般是不会有啦。
                if (adapter_has_discov_sessions(adapter)) {
                        adapter->scheduler_id = g_timeout_add_seconds(
                                                main_opts.discov_interval,
                                                discovery_cb, adapter);
                }
                break;


很清晰,就是向上层发送了一个discovering的false的property
change。和开始的discovering true的property change是对应的,我们就不详细分析,大概的操作就是把上层在扫描的圈圈停止掉,把开始扫描的ui返灰去除掉之类的操作。大家对着discovering
true的分析看就明白了。
至此,蓝牙扫描从上到下涉及到的内容就已经全部分析完成了哦,你还有什么疑问吗?

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


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: