android中Bluetooth属性获取代码分析

来源:互联网 发布:海马模拟器有mac版 编辑:程序博客网 时间:2024/05/20 22:00
1    Bluetooth属性获取流程
在BluetoothAdapterProperties.java中,有一个Map类型的变量:mPropertiesMap,该变量中就保存着当前运行中蓝牙的所有的属性值。所有需要读取当前蓝牙属性的地方都需要从该变量中获得。而该变量的值有两种获得方法,一个是直接从Kernel中读取,另外一种是由驱动上报属性值的变动,然后由Framework层进行获取。
1.1属性直接从Kernel读取
在BluetoothAdapterProperties->getAllProperties()中有:
mPropertiesMap.clear();        String properties[] = (String[]) mService                .getAdapterPropertiesNative();   //JNI调用从Kernel获得属性值        // The String Array consists of key-value pairs.        if (properties == null) {            Log.e(TAG, "*Error*: GetAdapterProperties returned NULL");            return;        }        for (int i = 0; i < properties.length; i++) {            String name = properties[i];            String newValue = null;            if (name == null) {                Log.e(TAG, "Error:Adapter Property at index " + i + " is null");                continue;            }            if (name.equals("Devices") || name.equals("UUIDs")) {                StringBuilder str = new StringBuilder();                int len = Integer.valueOf(properties[++i]);                for (int j = 0; j < len; j++) {                    str.append(properties[++i]);                    str.append(",");                }                if (len > 0) {                    newValue = str.toString();                }            } else {                newValue = properties[++i];            }    mPropertiesMap.put(name, newValue);   //将读取的属性值存入mPropertiesMap中        }
下面接着来看mService.getAdapterPropertiesNative();调用,这里mService是一个BluetoothService对象,而getAdapterPropertiesNative()是一个Native方法,所以直接跟到其具体实现android_server_bluetoothservice.cpp的getAdapterPropertiesNative()中去,在其代码里有:
reply = dbus_func_args_timeout(env,                                   nat->conn, -1, get_adapter_path(env, object),                                   DBUS_ADAPTER_IFACE, "GetProperties",                                   DBUS_TYPE_INVALID);

这里既是其获取具体属性的地方,而”GetProperties”则是后续继续调用的地方。不过到了这里,可能会发现,找不到这个函数了,其实这个函数是在蓝牙的底层实现里面,具体的文件位置可能根据android版本的不同而有所不同,我的版本下文件位置在:external\bluetooth\bluez\src\adapter.c里面。
在该文件里有:
{ "GetProperties",    "",    "a{sv}",get_properties        },

既与GetProperties对应的函数实现是:get_properties(),进去看看:
/* Address */    property = srcaddr;    dict_append_entry(&dict, "Address", DBUS_TYPE_STRING, &property);    /* Name */    memset(str, 0, sizeof(str));    strncpy(str, (char *) adapter->name, MAX_NAME_LENGTH);    property = str;    dict_append_entry(&dict, "Name", DBUS_TYPE_STRING, &property);    /* Class */    dict_append_entry(&dict, "Class",                DBUS_TYPE_UINT32, &adapter->dev_class);    /* Powered */    value = (adapter->up && !adapter->off_requested) ? TRUE : FALSE;    dict_append_entry(&dict, "Powered", DBUS_TYPE_BOOLEAN, &value);    /* Discoverable */    value = adapter->scan_mode & SCAN_INQUIRY ? TRUE : FALSE;    dict_append_entry(&dict, "Discoverable", DBUS_TYPE_BOOLEAN, &value);    /* Pairable */    dict_append_entry(&dict, "Pairable", DBUS_TYPE_BOOLEAN,                &adapter->pairable);    /* DiscoverableTimeout */    dict_append_entry(&dict, "DiscoverableTimeout",                DBUS_TYPE_UINT32, &adapter->discov_timeout);    /* PairableTimeout */    dict_append_entry(&dict, "PairableTimeout",                DBUS_TYPE_UINT32, &adapter->pairable_timeout);    if (adapter->state == STATE_DISCOV)        value = TRUE;    else        value = FALSE;    /* Discovering */    dict_append_entry(&dict, "Discovering", DBUS_TYPE_BOOLEAN, &value);    /* Devices */    devices = g_new0(char *, g_slist_length(adapter->devices) + 1);    for (i = 0, l = adapter->devices; l; l = l->next, i++) {        struct btd_device *dev = l->data;        devices[i] = (char *) device_get_path(dev);    }    dict_append_array(&dict, "Devices", DBUS_TYPE_OBJECT_PATH,                                &devices, i);    g_free(devices);    /* UUIDs */    uuids = g_new0(char *, sdp_list_len(adapter->services) + 1);    for (i = 0, list = adapter->services; list; list = list->next) {        sdp_record_t *rec = list->data;        char *uuid;        uuid = bt_uuid2string(&rec->svclass);        if (uuid)            uuids[i++] = uuid;    }
可以看到,这里就是蓝牙获取具体属性的地方了。其中Name代表了蓝牙的名称,Discoverable代表了是否可以被发现,Powered代表了是否是打开状态,Devices代表了上次成功配对的设备,UUIDs代表了上次配对的蓝牙支持的一些协议等。
那么,这些属性又是从哪里获取的呢?利用adb shell连上手机,进入/data/misc/bluetoothd,可以看到好多个MAC地址形式的文件夹,随便进入一个文件夹,可以看到里面有很多的配置文件,而那些属性值就是在这里面读取出来的。比如在profiles里面就保存了上次连接过的设备信息,在config里面保存了蓝牙的名称,可否配对等一些信息。
1.2驱动上报属性改变
    在BluetoothEventLoop.java中有方法:onPropertyChanged(),对其解释是:
/**
     * Called by native code on a PropertyChanged signal from
     * org.bluez.Adapter. This method is also called from
     * {@link BluetoothAdapterStateMachine} to set the "Pairable"
     * property when Bluetooth is enabled.
     * @param propValues a string array containing the key and one or more
     *  values.
     */
既当从org.bluez.Adapter发出了一个PropertyChanged的信号后,native代码就会调用onPropertyChanged()方法。
if (name.equals("Name")) {            .        } else if (name.equals("Pairable") || name.equals("Discoverable")) {                   .                   .        } else if (name.equals("Discovering")) {                   .        } else if (name.equals("Devices") || name.equals("UUIDs")) {            String value = null;            int len = Integer.valueOf(propValues[1]);            if (len > 0) {                StringBuilder str = new StringBuilder();                for (int i = 2; i < propValues.length; i++) {                    str.append(propValues[i]);                    str.append(",");                }                value = str.toString();            }            String adapterObjectPath = adapterProperties.getObjectPath();            if ((value != null)  && name.equals("UUIDs")) {                adapterProperties.setProperty(name, value);                updateBTState(value);            } else if ((value != null) && (value.startsWith(adapterObjectPath))) {                // Devices Prop expect value starts with obj path                adapterProperties.setProperty(name, value);            }        } else if (name.equals("Powered")) {mBluetoothState.sendMessage(BluetoothAdapterStateMachine.POWER_STATE_CHANGED,                propValues[1].equals("true") ? new Boolean(true) : new Boolean(false));        } else if (name.equals("DiscoverableTimeout")) {            adapterProperties.setProperty(name, propValues[1]);        } else if (name.equals("Class")) {            adapterProperties.setProperty(name, propValues[1]);        }
可以看到,当蓝牙的属性值有了改变以后,就会立即反映到BluetoothAdapterProperties.java的变量mPropertiesMap中去,同时会发送相应的事件广播,来通知关注该属性改变的程序。
当获取蓝牙属性不正确时会导致一些问题,而获取蓝牙属性不正确很大的一个原因就是因为蓝牙的MAC地址没有固定,导致每次打开蓝牙时,MAC地址都不同。而在上面我们看到过,每个MAC地址代表的文件里面都储存了蓝牙的相关属性信息,当重新打开蓝牙,MAC地址和上次不同时,就会导致读取不到上次的一些属性信息,从而导致错误。
那么怎么检查MAC地址是不是固定了呢,一个比较简单的方法是连上手机后用ADB SHELL命令进入/data/misc/bluetoothd文件夹观看是否有多个MAC地址形式的文件夹,如果有,则说明MAC地址有可能是不固定的。
其实,还有一个比较简便的方法,通过命令可以看出:
进入/data/misc/bluetoothd下输入命令:hcitool dev 如下:
root@android:/data/misc/bluetoothd # hcitool devhcitool devDevices:        hci0    00:A0:C6:45:86:75
hci0后面跟的就是当前蓝牙的MAC地址,然后关闭蓝牙,再打开,再执行一次上面的命令,看蓝牙地址是否一样,如不一样,则说明MAC地址是没有固定的,那用工具将其地址写好就可以了。

     

原创粉丝点击