android -- 蓝牙 bluetooth (三)搜索蓝牙
接上篇打开蓝牙继续,来一起看下蓝牙搜索的流程,触发蓝牙搜索的条件形式上有两种,一是在蓝牙设置界面开启蓝牙会直接开始搜索,另一个是先打开蓝牙开关在进入蓝牙设置界面也会触发搜索,也可能还有其它触发方式,但最后都要来到BluetoothSettngs.java的startScanning(),我们分析的起点也从这里开始,起步代码如下
- private void updateContent(int bluetoothState, boolean scanState) {
- if (numberOfPairedDevices == 0) {
- preferenceScreen.removePreference(mPairedDevicesCategory);
- if (scanState == true) {
- mActivityStarted = false;
- startScanning();
- } else<span style="font-family: Arial, Helvetica, sans-serif;"> ........</span>
- }
- private void startScanning() {
- if (!mAvailableDevicesCategoryIsPresent) {
- getPreferenceScreen().addPreference(mAvailableDevicesCategory);
- }
- mLocalAdapter.startScanning(true);
- }
其实在这里蓝牙搜索和打开流程是结构上是一致的,利用LocalBluetoothAdapter.java过渡到BluetoothAdapter.java再跳转至AdapterService.java要稍微留意下的是在这个过渡中startScaning()方法变成了startDiscovery()方法,看下代码:packages/apps/Settings/src/com/android/settings/bluetooth/LocalBluetoothAdapter.java
- void startScanning(boolean force) {
- if (!mAdapter.isDiscovering()) {
- if (!force) {
- // Don't scan more than frequently than SCAN_EXPIRATION_MS,
- // unless forced
- 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();
- }
- }
BluetoothAdapter.java的那一段,路径 /frameworks/base/core/java/android/bluetooth/BluetoothAdapter.java
- public boolean startDiscovery() {
- .............................
- AdapterService service = getService();
- if (service == null) return false;
- return service.startDiscovery();
- }
这个service代码写得很明白AdapterService,转了一圈从framework又回到packages了,
下面的代码路径自然是 :packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService.java,
- boolean startDiscovery() {
- enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
- "Need BLUETOOTH ADMIN permission");
- return startDiscoveryNative();
- }
和打开蓝牙根本就是一个套路,上面的流程略过一小步,很简单的不写了,下面要怎么走,估计大家也都猜到了,JNI应该出场了,
路径:/packages/apps/Bluetooth/jni/com_android_bluetooth_btservice_AdapterService.cpp
- static jboolean startDiscoveryNative(JNIEnv* env, jobject obj) {
- ALOGV("%s:",__FUNCTION__);
- jboolean result = JNI_FALSE;
- if (!sBluetoothInterface) return result;
- int ret = sBluetoothInterface->start_discovery();
- result = (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
- return result;
- }
在下面要去哪?稍微要动下脑筋,不过我们在上一篇android -- 蓝牙 bluetooth (二) 打开蓝牙已经说过怎么找了,注意android.mk文件,先找头文件,再找对应的实现C文件代码。就是现在回顾下,蓝牙打开和搜索的代码流程我们都看了,跳转都是一个套路,settings界面发起,LocalBluetoothAdapter.java过渡,去framework的转转(BluetoothAdapter.java)后回到packages的AdapterService.java,再走JNI来的external。流程就是这样的,相信类似的功能跳转(比如蓝牙配对,关闭蓝牙,停止扫描这些)大家都应该熟悉了,后面再有类似的功能就写函数名一笔带过了,还有这里要注意的就是这个start_discovery()实现代码的寻找,留意mk文件就是了,不复杂。小结结束,继续看代码 路径:/external/bluetooth/bluedroid/btif/src/bluetooth.c
- static int start_discovery(void)
- {
- /* sanity check */
- if (interface_ready() == FALSE)
- return BT_STATUS_NOT_READY;
- return btif_dm_start_discovery();
- }
下面代码直接跳转就可以找到,路径external/bluetooth/bluedroid/btif/src/btif_dm.c
这个代码有点多,不过里面的信息也很多,所以连注释也一起保留的贴出来了,蓝牙的搜索实现并没有像蓝牙打开那样交由vendor厂商实现,在这里已经写出来了,仔细看下那些#if和#else,都是一些查询条件的调置,#if (BLE_INCLUDED == TRUE) 这个应该就google为蓝牙4.0 LE作的准备了,也算是今年google I/O大会上宣布即将支持蓝牙4.0低能耗版一个佐证吧,对于代码里面那些字符串的含义看这里好了external/bluetooth/bluedroid/bta/include/bta_api.h,一个头文件,大部分字符串和结构体的定义都在这了,多少还有些注释。
- bt_status_t btif_dm_start_discovery(void)
- {
- tBTA_DM_INQ inq_params;
- tBTA_SERVICE_MASK services = 0;
- BTIF_TRACE_EVENT1("%s", __FUNCTION__);
- /* TODO: Do we need to handle multiple inquiries at the same time? */
- /* Set inquiry params and call API */
- #if (BLE_INCLUDED == TRUE)
- inq_params.mode = BTA_DM_GENERAL_INQUIRY|BTA_BLE_GENERAL_INQUIRY;
- #else
- inq_params.mode = BTA_DM_GENERAL_INQUIRY;
- #endif
- inq_params.duration = BTIF_DM_DEFAULT_INQ_MAX_DURATION;
- inq_params.max_resps = BTIF_DM_DEFAULT_INQ_MAX_RESULTS;
- inq_params.report_dup = TRUE;
- inq_params.filter_type = BTA_DM_INQ_CLR;
- /* TODO: Filter device by BDA needs to be implemented here */
- /* Will be enabled to TRUE once inquiry busy level has been received */
- btif_dm_inquiry_in_progress = FALSE;
- /* find nearby devices */
- BTA_DmSearch(&inq_params, services, bte_search_devices_evt);
- return BT_STATUS_SUCCESS;
- }
BTA_DmSearch()方法是看起来是要搜索了,不过里面这个家伙bte_search_devices_evt才是真正干活的主力,所以我们先看它,在这个函数里
- static void bte_search_devices_evt(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data) {
- UINT16 param_len = 0;
- if (p_data)
- param_len += sizeof(tBTA_DM_SEARCH);
- /* Allocate buffer to hold the pointers (deep copy). The pointers will point to the end of the tBTA_DM_SEARCH */
- switch (event)
- {
- case BTA_DM_INQ_RES_EVT:
- {
- if (p_data->inq_res.p_eir)
- param_len += HCI_EXT_INQ_RESPONSE_LEN;
- }
- break;
- ..............................
- }
- BTIF_TRACE_DEBUG3("%s event=%s param_len=%d", __FUNCTION__, dump_dm_search_event(event), param_len);
- /* if remote name is available in EIR, set teh flag so that stack doesnt trigger RNR */
- if (event == BTA_DM_INQ_RES_EVT)
- p_data->inq_res.remt_name_not_required = check_eir_remote_name(p_data, NULL, NULL);
- btif_transfer_context (btif_dm_search_devices_evt , (UINT16) event, (void *)p_data, param_len,
- (param_len > sizeof(tBTA_DM_SEARCH)) ? search_devices_copy_cb : NULL);
- }
在上面的这个函数里又有这个bte_search_devices_evt,在它里我们能看一个 HAL_CBACK,这是要往回发消息了,看下这个函数的全貌,说是全貌,不过还是只贴出一个case分支,太长了,大家还是自行还源码吧。到这里已经可以知道扫描到蓝牙设备的mac地址和设备名,那个bdcpy函数就是在解析mac地址,有了这些,蓝牙搜索是到应该在界面展示成果的时候了,开始回调,忘记代码路径了,这个函数都在这个文件里: /external/bluetooth/bluedroid/btif/src/btif_dm.c
- static void btif_dm_search_devices_evt (UINT16 event, char *p_param)
- tBTA_DM_SEARCH *p_search_data;
- BTIF_TRACE_EVENT2("%s event=%s", __FUNCTION__, dump_dm_search_event(event));
- switch (event)
- {
- case BTA_DM_DISC_RES_EVT:
- {
- p_search_data = (tBTA_DM_SEARCH *)p_param;
- /* Remote name update */
- if (strlen((const char *) p_search_data->disc_res.bd_name))
- {
- bt_property_t properties[1];
- bt_bdaddr_t bdaddr;
- bt_status_t status;
- properties[0].type = BT_PROPERTY_BDNAME;
- properties[0].val = p_search_data->disc_res.bd_name;
- properties[0].len = strlen((char *)p_search_data->disc_res.bd_name);
- bdcpy(bdaddr.address, p_search_data->disc_res.bd_addr);
- status = btif_storage_set_remote_device_property(&bdaddr, &properties[0]);
- ASSERTC(status == BT_STATUS_SUCCESS, "failed to save remote device property", status);
- HAL_CBACK(bt_hal_cbacks, remote_device_properties_cb,
- status, &bdaddr, 1, properties);
- }
- /* TODO: Services? */
- }
- break;
一小段log,下面的文字就在上面的函数里打出来的,即便上面的写的函数没有,肯定也在附近了。
05-30 13:52:14.890 1578 2612 D bt-btif : search_devices_copy_cb: event=BTA_DM_INQ_RES_EVT
05-30 13:52:14.890 1578 2584 I bt-btif : btif_dm_search_devices_evt event=BTA_DM_INQ_RES_EVT
05-30 13:52:14.890 1578 2584 D bt-btif : btif_dm_search_devices_evt() ec:89:f5:ba:fb:03 device_type = 0x1
- void BTA_DmSearch(tBTA_DM_INQ *p_dm_inq, tBTA_SERVICE_MASK services, tBTA_DM_SEARCH_CBACK *p_cback)
- { tBTA_DM_API_SEARCH *p_msg;
- if ((p_msg = (tBTA_DM_API_SEARCH *) GKI_getbuf(sizeof(tBTA_DM_API_SEARCH))) != NULL)
- {
- memset(p_msg, 0, sizeof(tBTA_DM_API_SEARCH));
- p_msg->hdr.event = BTA_DM_API_SEARCH_EVT;
- memcpy(&p_msg->inq_params, p_dm_inq, sizeof(tBTA_DM_INQ));
- p_msg->services = services;
- p_msg->p_cback = p_cback;
- p_msg->rs_res = BTA_DM_RS_NONE;
- bta_sys_sendmsg(p_msg);
- }
- }
看了上面方法后我们 要回去了看看,代码通过JNI下来的,回去也是看JNI的回调方法
- method_deviceFoundCallback = env->GetMethodID(jniCallbackClass, "deviceFoundCallback", "([B)V");
deviceFoundCallback方法最后会来java层的/packages/apps/Bluetooth/src/com/android/bluetooth/btservice/RemoteDevices.java
- void deviceFoundCallback(byte[] address) {
- // The device properties are already registered - we can send the intent
- // now
- BluetoothDevice device = getDevice(address);
- debugLog("deviceFoundCallback: Remote Address is:" + device);
- DeviceProperties deviceProp = getDeviceProperties(device);
- if (deviceProp == null) {
- errorLog("Device Properties is null for Device:" + device);
- return;
- }
- Intent intent = new Intent(BluetoothDevice.ACTION_FOUND);
- intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
- intent.putExtra(BluetoothDevice.EXTRA_CLASS,
- new BluetoothClass(Integer.valueOf(deviceProp.mBluetoothClass)));
- intent.putExtra(BluetoothDevice.EXTRA_RSSI, deviceProp.mRssi);
- intent.putExtra(BluetoothDevice.EXTRA_NAME, deviceProp.mName);
- mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM);
- }
到这里就是给界面发广播,应用层收到广播显示出来,通过这个handle,这个handle可以在BluetoothEventManager.java的构造函数里找到,
- addHandler(BluetoothDevice.ACTION_FOUND, new DeviceFoundHandler());
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- BluetoothDevice device = intent
- .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
- Handler handler = mHandlerMap.get(action);
- if (handler != null) {
- handler.onReceive(context, intent, device);
- }
- }
- };
这里handle对应要看DeviceFoundHandler,也就是下面贴出来的代码,
- private class DeviceFoundHandler implements Handler {
- public void onReceive(Context context, Intent intent,
- BluetoothDevice device) {
- ........................
- // TODO Pick up UUID. They should be available for 2.1 devices.
- // Skip for now, there's a bluez problem and we are not getting uuids even for 2.1.
- CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
- if (cachedDevice == null) {
- cachedDevice = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, device);
- Log.d(TAG, "DeviceFoundHandler created new CachedBluetoothDevice: "
- + cachedDevice);
- // callback to UI to create Preference for new device
- dispatchDeviceAdded(cachedDevice);
- }
- ......................
- }
- }
在if语句中dispatchDeviceAdded()向界面分发消息,最后处理消息的地方在这里,已经到settings应用里了
- public void onDeviceAdded(CachedBluetoothDevice cachedDevice) {
- if (mDevicePreferenceMap.get(cachedDevice) != null) {
- return;
- }
- // Prevent updates while the list shows one of the state messages
- if (mLocalAdapter.getBluetoothState() != BluetoothAdapter.STATE_ON) return;
- if (mFilter.matches(cachedDevice.getDevice())) {
- createDevicePreference(cachedDevice);
- }
- }
上面代码中最后一个分支就是界面显示要做的事了,从settings界面开始再到settings界面显示出搜索到蓝牙结束,后面的代码不再写了,本文关心的东东到此结束。
- void createDevicePreference(CachedBluetoothDevice cachedDevice) {
- BluetoothDevicePreference preference = new BluetoothDevicePreference(
- getActivity(), cachedDevice);
- initDevicePreference(preference);
- mDeviceListGroup.addPreference(preference);
- mDevicePreferenceMap.put(cachedDevice, preference);
- }
到目前为止,包括前面的打开流程分析,还仅是针对代码流程做的分析,对于蓝牙协议方面东西还没有涉及,比如蓝牙是如何发现其它蓝牙设备,这个流程究竟是怎么工作还不是很清楚,后续会尽量关注这些问题,估计看起来就没那么容易,欢迎有经验的朋友指点一二,当然对于本文不足,欢迎拍砖讨论。分享是快乐的,谢谢!
android -- 蓝牙 bluetooth (三)搜索蓝牙的更多相关文章
- Bluetooth LE(低功耗蓝牙) - 第三部分
回顾 在本系列的前两篇文章中,我们已经了解了一些关于Bluetooth LE的背景并建立一个简单的Activity / Service框架. 在这篇文章中,我们将探讨Bluetooth LE的细节 ...
- ZT android -- 蓝牙 bluetooth (三)搜索蓝牙
android -- 蓝牙 bluetooth (三)搜索蓝牙 分类: Android的原生应用分析 2013-05-31 22:03 2192人阅读 评论(8) 收藏 举报 bluetooth蓝牙s ...
- 深入了解Android蓝牙Bluetooth——《进阶篇》
在 [深入了解Android蓝牙Bluetooth--<基础篇>](http://blog.csdn.net/androidstarjack/article/details/6046846 ...
- Android 蓝牙开发之搜索、配对、连接、通信大全
蓝牙( Bluetooth®):是一种无线技术标准,可实现固定设备.移动设备和楼宇个人域网之间的短距离数据 交换(使用2.4-2.485GHz的ISM波段的UHF无线电波).蓝牙设备最 ...
- 深入了解Android蓝牙Bluetooth ——《总结篇》
在我的上两篇博文中解说了有关android蓝牙的认识以及API的相关的介绍,蓝牙BLE的搜索,连接以及读取. 没有了解的童鞋们请參考: 深入了解Android蓝牙Bluetooth--<基础篇& ...
- ZT android -- 蓝牙 bluetooth (四)OPP文件传输
android -- 蓝牙 bluetooth (四)OPP文件传输 分类: Android的原生应用分析 2013-06-22 21:51 2599人阅读 评论(19) 收藏 举报 4.2源码AND ...
- ZT android -- 蓝牙 bluetooth (二) 打开蓝牙
android -- 蓝牙 bluetooth (二) 打开蓝牙 分类: Android的原生应用分析 2013-05-23 23:57 4773人阅读 评论(20) 收藏 举报 androidblu ...
- ZT android -- 蓝牙 bluetooth (五)接电话与听音乐
android -- 蓝牙 bluetooth (五)接电话与听音乐 分类: Android的原生应用分析 2013-07-13 20:53 2165人阅读 评论(9) 收藏 举报 蓝牙android ...
- Android源码分析(六)-----蓝牙Bluetooth源码目录分析
一 :Bluetooth 的设置应用 packages\apps\Settings\src\com\android\settings\bluetooth* 蓝牙设置应用及设置参数,蓝牙状态,蓝牙设备等 ...
随机推荐
- Unity 通过NGUI 完成单摄像机 制作地图
本次思想主要是通过 Ngui的Scroll View 主要是UIPanel的Clipping属性的Alipha Clip 调节窗口大小,遮蔽地图试地图实现在屏幕的部分显示.此方法的好处是不用担心sha ...
- union 和 union all 有什么不同?
假设我们有一个表 Student, 包括以下字段与数据:drop table student;create table student( idint primary key,name nvarchar ...
- BZOJ 1208: [HNOI2004]宠物收养所(BST)
本来想先用set写一遍,再自己写个splay或treap,不过用set过了之后就懒得去写了....以后有空再来写吧..(不会有空的吧= = ------------------------------ ...
- .net 更改时间格式
/// <summary> /// 更改时间格式[HH:mm:ss]到[HHmmss] /// </summary> /// <param name="inpu ...
- 【转】Win7下VS2010中配置Opencv2.4.4的方法(32位和64位都有效)(亲测成功)
在vs2010下配置opencv是件痛苦的事情,一点点错误可能就会导致莫名其妙的报错,各种error让人郁闷不已,这里提供给大家一篇vs2010下配置opencv2.4.4的方法,我是64位的win7 ...
- Android开发中怎样调用系统Email发送邮件(多种调用方式)
在Android中调用其他程序进行相关处理,几乎都是使用的Intent,所以,Email也不例外,所谓的调用Email,只是说Email可以接收Intent并做这些事情 我们都知道,在Android中 ...
- Maven 版 JPA 最佳实践(转)
项目结构图 数据库环境 数据库:MySQL 版本:5.x 数据库名:jpa-demo 用户名密码:root/1234 代码清单 1:数据库脚本: ? 1 2 3 4 5 6 7 8 9 10 11 1 ...
- Mysql-udf-http 插件的安装与使用
1. 在Linux系统上安装Mysql-udf-http ulimit -SHn wget http://curl.haxx.se/download/curl-7.21.1.tar.gz .tar ...
- swjtu 2213 A Game About Cards(模拟题)
题目链接:http://acm.swjtu.edu.cn/JudgeOnline/showproblem?problem_id=2213 思路分析:该问题与约瑟夫问题相似:每次将前n张牌放到队列的最后 ...
- IPhone多视图切换
处理IPhone多个view切换是我们常遇到的问题,接下来有个比较实用的方法: 而且还附有创建空项目,内存告急处理和动画效果的实现! 具体步骤: 1.创建一个空的项目,然后添加一个ViewContro ...