【转】Android bluetooth介绍(三): 蓝牙扫描(scan)设备分析

原文网址:http://blog.csdn.net/xubin341719/article/details/38584469

关键词:蓝牙blueZ  A2DP、SINK、sink_connect、sink_disconnect、sink_suspend、sink_resume、sink_is_connected、sink_get_properties、AUDIO、DBUS
版本:基于android4.2之前版本 bluez
内核:linux/linux3.08
系统:android/android4.1.3.4
作者:xubin341719(欢迎转载,请注明作者,请尊重版权谢谢)
欢迎指正错误,共同学习、共同进步!!
参考网站:
http://blog.csdn.net/u011960402/article/details/17216563
http://www.cnblogs.com/fityme/archive/2013/04/13/3019471.html socket相关
http://hi.baidu.com/wwwkljoel/item/a35e5745d14e02e6bcf45170 setsockopt

Android bluetooth介绍(一):基本概念及硬件接口
Android bluetooth介绍(二): android 蓝牙代码架构及其uart 到rfcomm流程
Android bluetooth介绍(三): 蓝牙扫描(scan)设备分析
Android bluetooth介绍(四): a2dp connect流程分析

一、蓝牙扫描常用的方法:
         蓝牙扫描的可以通过两种途径实现:命令行hciitool扫描;Android界面触发,通过JNI、DUBS下发命令。
1、  命令行hciitool扫描(这部分通过Linux命令操作,跟android没有关系)
通过bluez的tool发送扫描命令,如:hcitoool scan
adb shell 下#hcitool  scan扫描结果

Hcitool扫描逻辑如下所示:

2、Android界面触发,通过JNI、DUBS下发命令:通过android界面点击搜索设备

应用扫描触发逻辑流程:自上而下三种颜色,分别代表应用部分、JNI部分、linux blueZ部分。
二、Hcitool触发逻辑分析
1、hcitool这部分代码比较简单,实现函数
idh.code\external\bluetooth\bluez\tools\hcitool.c代码大致流程如下:

通过所带的参数,找到cmd_scan,进入hci_inquriy。这个函数中创建一个BTPROTO_HCI的socket,通过ioctlHCINQUIRY向内核读取数据,保存返回信息。

2、内核层逻辑:
当然IOCTL只是其中一项。
idh.code\kernel\net\bluetooth\ hci_sock.c

[html] view plaincopy

  1. static const struct proto_ops hci_sock_ops = {
  2. …………
  3. .ioctl      = hci_sock_ioctl,
  4. .poll       = datagram_poll,
  5. .listen     = sock_no_listen,
  6. …………
  7. };

它的流程就是构造查询命令,放入命令队列,调度队列来发送命令,其中hci_send_frame后面会讲解,这里关键是命令的发送和数据的收集是分开的,所以它在里面会放弃2s的调度,以此来等待数据的收集,收集的数据放在hdev->inq_cache里面,我们来看看这个数据是如何取得的,如下图所示:

入口点hci_rx_work前面已经详细分析过了,这里就不说了,它里面会根据不同的事件类型做不同的处理,通常情况下,扫描都是带信号强度的扫描,所以走的hci_inquiry_result_with_rssi_evt路线,还有其它几种扫描方式,比如:HCI_EV_INQUIRY_RESULT,HCI_EV_EXTENDED_INQUIRY_RESULT等,处理逻辑都差不多的,里面会hci_inquiry_cache_update来把结果放到hdev->discovery链表里面去,供后面的查询;比如前面调用的inquiry_cache_dump函数就可以从这个链表里面把数据取出来,然后copy到用户层;
三、Android界面触发,通过JNI、DUBS下发命令
整体流程如下所示:

(一)、应用部分:
1、 idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\DeviceListPreferenceFragment.java

[html] view plaincopy

  1. @Override
  2. public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
  3. Preference preference) {
  4. …………
  5. mLocalAdapter.startScanning(true);
  6. return true;
  7. }

2、 idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\LocalBluetoothAdapter.java

[html] view plaincopy

  1. private final BluetoothAdapter mAdapter;
  2. void startScanning(boolean force) {
  3. // Only start if we‘re not already scanning
  4. if (!mAdapter.isDiscovering()) {
  5. if (!force) {
  6. // Don‘t scan more than frequently than SCAN_EXPIRATION_MS,
  7. // unless forced
  8. if (mLastScan + SCAN_EXPIRATION_MS > System.currentTimeMillis()) {
  9. return;
  10. }
  11. // If we are playing music, don‘t scan unless forced.
  12. A2dpProfile a2dp = mProfileManager.getA2dpProfile();
  13. if (a2dp != null && a2dp.isA2dpPlaying()) {
  14. return;
  15. }
  16. }
  17. if (mAdapter.startDiscovery()) {
  18. mLastScan = System.currentTimeMillis();
  19. }
  20. }
  21. }

3、idh.code\frameworks\base\core\java\android\bluetooth\BluetoothAdapter.java

[html] view plaincopy

  1. public boolean startDiscovery() {
  2. if (getState() != STATE_ON) return false;
  3. try {
  4. return mService.startDiscovery();
  5. } catch (RemoteException e) {Log.e(TAG, "", e);}
  6. return false;
  7. }

4、JNI函数的调用idh.code\frameworks\base\core\java\android\server\BluetoothService.java

[html] view plaincopy

  1. private native boolean startDiscoveryNative();//Native函数声明
  2. public class BluetoothService extends IBluetooth.Stub {
  3. private static final String TAG = "BluetoothService";
  4. private static final boolean DBG = true;
  5. …………
  6. public synchronized boolean startDiscovery() {
  7. mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
  8. "Need BLUETOOTH_ADMIN permission");
  9. if (!isEnabledInternal()) return false;
  10. return startDiscoveryNative();
  11. }
  12. ………………
  13. }

(二)、JNI部分:
1、android_server_BluetoothService.cpp中JNI函数的对照表
idh.code\frameworks\base\core\jni\android_server_BluetoothService.cpp

[html] view plaincopy

  1. static JNINativeMethod sMethods[] = {
  2. /* name, signature, funcPtr */
  3. ………………
  4. {"startDiscoveryNative", "()Z", (void*)startDiscoveryNative},
  5. {"stopDiscoveryNative", "()Z", (void *)stopDiscoveryNative},
  6. …………
  7. }

2、对应Native函数的实现
这里面有个知识点DBUS,这个后面我们单独去讲解
idh.code\frameworks\base\core\jni\android_server_BluetoothService.cpp

[html] view plaincopy

  1. #define BLUEZ_DBUS_BASE_IFC       "org.bluez"
  2. #define DBUS_ADAPTER_IFACE BLUEZ_DBUS_BASE_IFC ".Adapter"//其实DBUS_ADAPTER_IFACE 也就是 org.bluez.Adapter
  3. static jboolean startDiscoveryNative(JNIEnv *env, jobject object) {
  4. ………………
  5. /* Compose the command */
  6. msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC,
  7. get_adapter_path(env, object),
  8. DBUS_ADAPTER_IFACE, "StartDiscovery");
  9. …………
  10. }
  11. Native函数startDiscoveryNative和字符串StartDiscovery对应。

(三)、DBUS部分

1、DBUS对应方法的实现,这里跟JNI部分比较类似,也是用了函数结构体对应关系。
idh.code\external\bluetooth\bluez\src\adapter.c

[html] view plaincopy

  1. #define ADAPTER_INTERFACE   "org.bluez.Adapter"
  2. static GDBusMethodTable adapter_methods[] = {
  3. ………………
  4. { "ReleaseSession", "", "", release_session     },
  5. { "StartDiscovery", "", "", adapter_start_discovery },
  6. { "StopDiscovery",  "", "", adapter_stop_discovery,
  7. G_DBUS_METHOD_FLAG_ASYNC},
  8. ………………
  9. }

字符StartDiscovery又对应C中的实现函数adapter_start_discovery。
2、adapter_start_discovery的实现
idh.code\external\bluetooth\bluez\src\adapter.c

[html] view plaincopy

  1. static DBusMessage *adapter_start_discovery(DBusConnection *conn,
  2. DBusMessage *msg, void *data)
  3. {
  4. …………
  5. err = start_discovery(adapter);
  6. if (err < 0 && err != -EINPROGRESS)
  7. return btd_error_failed(msg, strerror(-err));
  8. done:
  9. req = create_session(adapter, conn, msg, 0,
  10. session_owner_exit);
  11. adapter->disc_sessions = g_slist_append(adapter->disc_sessions, req);
  12. return dbus_message_new_method_return(msg);
  13. }

3、 start_discovery调用
idh.code\external\bluetooth\bluez\src\adapter.c

[html] view plaincopy

  1. const struct btd_adapter_ops *adapter_ops = NULL;
  2. static int start_discovery(struct btd_adapter *adapter)
  3. {
  4. …………
  5. pending_remote_name_cancel(adapter);
  6. return adapter_ops->start_discovery(adapter->dev_id);
  7. }

adapter_ops对应结构体btd_adapter_ops中对应函数,如下:上面部分就对应到btd_adapter_ops中的hci_ops结构体。
4、btd_adapter_ops中的hci_ops结构体
idh.code\external\bluetooth\bluez\plugins\hciops.c

[html] view plaincopy

  1. static struct btd_adapter_ops hci_ops = {
  2. …………
  3. .set_powered = hciops_set_powered,
  4. .set_discoverable = hciops_set_discoverable,
  5. .set_pairable = hciops_set_pairable,
  6. .set_limited_discoverable = hciops_set_limited_discoverable,
  7. .start_discovery = hciops_start_discovery,
  8. .stop_discovery = hciops_stop_discovery,
  9. ………………
  10. .create_bonding = hciops_create_bonding,
  11. .cancel_bonding = hciops_cancel_bonding,
  12. .read_local_oob_data = hciops_read_local_oob_data,
  13. .add_remote_oob_data = hciops_add_remote_oob_data,
  14. .remove_remote_oob_data = hciops_remove_remote_oob_data,
  15. .set_link_timeout = hciops_set_link_timeout,
  16. .retry_authentication = hciops_retry_authentication,
  17. };

5、hciops_start_discovery函数的实现
idh.code\external\bluetooth\bluez\plugins\hciops.c

[html] view plaincopy

  1. static int hciops_start_discovery(int index)
  2. {
  3. int adapter_type = get_adapter_type(index);
  4. switch (adapter_type) {
  5. case BR_EDR_LE:
  6. return hciops_start_inquiry(index, LENGTH_BR_LE_INQ);
  7. case BR_EDR: //蓝牙芯片为2.1+EDR的
  8. return hciops_start_inquiry(index, LENGTH_BR_INQ);
  9. case LE_ONLY:
  10. return hciops_start_scanning(index, TIMEOUT_LE_SCAN);
  11. default:
  12. return -EINVAL;
  13. }
  14. }

6、hciops_start_inquiry
idh.code\external\bluetooth\bluez\plugins\hciops.c

[html] view plaincopy

  1. static int hciops_start_inquiry(int index, uint8_t length)
  2. {
  3. struct dev_info *dev = &devs[index];
  4. uint8_t lap[3] = { 0x33, 0x8b, 0x9e };
  5. inquiry_cp inq_cp;
  6. DBG("hci%d length %u", index, length);
  7. memset(&inq_cp, 0, sizeof(inq_cp));
  8. memcpy(&inq_cp.lap, lap, 3);
  9. inq_cp.length = length;
  10. inq_cp.num_rsp = 0x00;
  11. if (hci_send_cmd(dev->sk, OGF_LINK_CTL,
  12. OCF_INQUIRY, INQUIRY_CP_SIZE, &inq_cp) < 0)
  13. return -errno;
  14. return 0;
  15. }

7、idh.code\external\bluetooth\bluez\lib\hci.c

[html] view plaincopy

  1. /* HCI functions that require open device
  2. * dd - Device descriptor returned by hci_open_dev. */
  3. dd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
  4. int hci_send_cmd(int dd, uint16_t ogf, uint16_t ocf, uint8_t plen, void *param)
  5. {
  6. ………………
  7. if (plen) {
  8. iv[2].iov_base = param;
  9. iv[2].iov_len  = plen;
  10. ivn = 3;
  11. }
  12. while (writev(dd, iv, ivn) < 0) {//writev这里把数据写到socket里面。
  13. if (errno == EAGAIN || errno == EINTR)
  14. continue;
  15. return -1;
  16. }
  17. return 0;
  18. }

(四)、内核部分:
1、HCI FILTER的设置
HCIsocket的类型为BTPROTO_HCI。上层调用setsockopt的时候,触发了内核的hci_sock_setsockopt函数的执行,在这里面设置了socket的filter特性,包括包类型,包括事件类型

当上层调用setsockopt(sock, SOL_HCI, HCI_FILTER,&flt, sizeof(flt))时,触发相应的内核路径。
idh.code\kernel\net\bluetooth\hci_sock.c

[html] view plaincopy

  1. static const struct proto_ops hci_sock_ops = {
  2. .family     = PF_BLUETOOTH,
  3. .owner      = THIS_MODULE,
  4. …………
  5. .shutdown   = sock_no_shutdown,
  6. .setsockopt = hci_sock_setsockopt,
  7. .getsockopt = hci_sock_getsockopt,
  8. .connect    = sock_no_connect,
  9. …………
  10. };

idh.code\kernel\net\bluetooth\hci_sock.c

[html] view plaincopy

  1. static int hci_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int len)
  2. {
  3. ………………
  4. case HCI_FILTER:
  5. {
  6. struct hci_filter *f = &hci_pi(sk)->filter;
  7. uf.type_mask = f->type_mask;
  8. uf.opcode    = f->opcode;
  9. uf.event_mask[0] = *((u32 *) f->event_mask + 0);
  10. uf.event_mask[1] = *((u32 *) f->event_mask + 1);
  11. }
  12. ………………
  13. }<span style="font-weight: bold;">
  14. </span>

内核这部分就比较统一的数据,通过hci_send_cmd把命令发出去,HCI_FILTER这个地方的处理还没理解,后面补充
Writev函数通过socket把数据写下去,经过VFS层,调用到内核空间的sendmsg函数。

(五)、EVENT返回状态

Controller收到查询命令后,返回一个命令状态
1、cmd_status
idh.code\external\bluetooth\bluez\plugins\hciops.c

[html] view plaincopy

  1. switch (eh->evt) {
  2. case EVT_CMD_STATUS:
  3. cmd_status(index, ptr);
  4. break;
  5. static inline void cmd_status(int index, void *ptr)
  6. {
  7. evt_cmd_status *evt = ptr;
  8. uint16_t opcode = btohs(evt->opcode);
  9. if (opcode == cmd_opcode_pack(OGF_LINK_CTL, OCF_INQUIRY))//如果是inquriy做特殊处理;
  10. cs_inquiry_evt(index, evt->status);
  11. }

2、cs_inquiry_evt的实现 idh.code\external\bluetooth\bluez\plugins\hciops.c

[html] view plaincopy

  1. static inline void cs_inquiry_evt(int index, uint8_t status)
  2. {
  3. if (status) {//错误信息
  4. error("Inquiry Failed with status 0x%02x", status);
  5. return;
  6. }
  7. set_state(index, DISCOV_INQ);//设置状态为INQ,向上层回复discoverying的property change
  8. }

3、设置不同的DISCOV 状态 idh.code\external\bluetooth\bluez\plugins\hciops.c

[html] view plaincopy

  1. static void set_state(int index, int state)
  2. {
  3. ………………
  4. switch (dev->discov_state) {
  5. case DISCOV_HALTED://停止发现;
  6. if (adapter_get_state(adapter) == STATE_SUSPENDED)
  7. return;
  8. if (is_resolvname_enabled() &&
  9. adapter_has_discov_sessions(adapter))
  10. adapter_set_state(adapter, STATE_RESOLVNAME);
  11. else
  12. adapter_set_state(adapter, STATE_IDLE);
  13. break;
  14. case DISCOV_INQ:
  15. case DISCOV_SCAN://扫描发现;
  16. adapter_set_state(adapter, STATE_DISCOV);
  17. break;
  18. }
  19. }

4、设置adapter的状态 idh.code\external\bluetooth\bluez\src\adapter.c

[html] view plaincopy

  1. idh.code\external\bluetooth\bluez\src\adapter.c
  2. #define ADAPTER_INTERFACE   "org.bluez.Adapter"
  3. void adapter_set_state(struct btd_adapter *adapter, int state)
  4. {
  5. …………
  6. case STATE_DISCOV:
  7. discov_active = TRUE;
  8. //向上层回复discovering的property change
  9. emit_property_changed(connection, path,
  10. ADAPTER_INTERFACE, "Discovering",
  11. DBUS_TYPE_BOOLEAN, &discov_active);
  12. break;
  13. …………
  14. }

emit_property_changed发送PropertyChanged的消息,消息内容为Discovering。通知上层BluetoothEventLoop进行Discovering。
5、emit_property_changed发送Discovering消息的实现 idh.code\external\bluetooth\bluez\src\dbus-common.c
这部分涉及到DBUS内容

[html] view plaincopy

  1. dbus_bool_t emit_property_changed(DBusConnection *conn,
  2. const char *path,
  3. const char *interface,
  4. const char *name,
  5. int type, void *value)
  6. {
  7. DBusMessage *signal;
  8. DBusMessageIter iter;
  9. signal = dbus_message_new_signal(path, interface, "PropertyChanged"); // 创建消息对象并标识路径
  10. if (!signal) {
  11. error("Unable to allocate new %s.PropertyChanged signal",
  12. interface);
  13. return FALSE;
  14. }
  15. dbus_message_iter_init_append(signal, &iter);//把信号相对应的参数压进去
  16. dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);//申请一个首地址,把前面压入的参数传入这个首地址
  17. append_variant(&iter, type, value);//
  18. return g_dbus_send_message(conn, signal);//启动发送调用,并释放发送相关消息信息
  19. }

6、DBUS消息接收的实现 idh.code\frameworks\base\core\jni\android_server_BluetoothEventLoop.cpp

[html] view plaincopy

  1. // Called by dbus during WaitForAndDispatchEventNative()
  2. static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg,
  3. void *data) {
  4. …………
  5. else if (dbus_message_is_signal(msg, "org.bluez.Adapter", "PropertyChanged")) {
  6. jobjectArray str_array = parse_adapter_property_change(env, msg);//(1)、对收到消息的解析
  7. if (str_array != NULL) {
  8. /* Check if bluetoothd has (re)started, if so update the path. */
  9. jstring property =(jstring) env->GetObjectArrayElement(str_array, 0);
  10. const char *c_property = env->GetStringUTFChars(property, NULL);
  11. if (!strncmp(c_property, "Powered", strlen("Powered"))) {
  12. jstring value =
  13. (jstring) env->GetObjectArrayElement(str_array, 1);
  14. const char *c_value = env->GetStringUTFChars(value, NULL);
  15. if (!strncmp(c_value, "true", strlen("true")))
  16. nat->adapter = get_adapter_path(nat->conn);
  17. env->ReleaseStringUTFChars(value, c_value);
  18. }
  19. env->ReleaseStringUTFChars(property, c_property);
  20. env->CallVoidMethod(nat->me,
  21. method_onPropertyChanged,//(2)、
  22. method_onPropertyChanged NATVIE函数的实现
  23. str_array);
  24. } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
  25. goto success;
  26. }

(1)、对收到消息的解析 idh.code\frameworks\base\core\jni\android_bluetooth_common.cpp

[html] view plaincopy

  1. jobjectArray parse_adapter_property_change(JNIEnv *env, DBusMessage *msg) {
  2. return parse_property_change(env, msg, (Properties *) &adapter_properties,
  3. sizeof(adapter_properties) / sizeof(Properties));
  4. }

针对org.bluez.Adapter不同的消息类型
idh.code\frameworks\base\core\jni\android_bluetooth_common.cpp

[html] view plaincopy

  1. static Properties adapter_properties[] = {
  2. {"Address", DBUS_TYPE_STRING},
  3. {"Name", DBUS_TYPE_STRING},
  4. {"Class", DBUS_TYPE_UINT32},
  5. {"Powered", DBUS_TYPE_BOOLEAN},
  6. {"Discoverable", DBUS_TYPE_BOOLEAN},
  7. {"DiscoverableTimeout", DBUS_TYPE_UINT32},
  8. {"Pairable", DBUS_TYPE_BOOLEAN},
  9. {"PairableTimeout", DBUS_TYPE_UINT32},
  10. {"Discovering", DBUS_TYPE_BOOLEAN},
  11. {"Devices", DBUS_TYPE_ARRAY},
  12. {"UUIDs", DBUS_TYPE_ARRAY},
  13. };

(2)、method_onPropertyChanged NATVIE函数的实现 idh.code\frameworks\base\core\jni\android_server_BluetoothEventLoop.cpp

[html] view plaincopy

  1. static void classInitNative(JNIEnv* env, jclass clazz) {
  2. ALOGV("%s", __FUNCTION__);
  3. #ifdef HAVE_BLUETOOTH
  4. method_onPropertyChanged = env->GetMethodID(clazz, "onPropertyChanged",
  5. "([Ljava/lang/String;)V");
  6. method_onDevicePropertyChanged = env->GetMethodID(clazz,
  7. "onDevicePropertyChanged","(Ljava/lang/String;[Ljava/lang/String;)V");
  8. …………
  9. }

7、JNI调用onPropertyChanged对应JAVA的实现,在BluetoothEventLoop.java
idh.code\frameworks\base\core\java\android\server\BluetoothEventLoop.java中

[html] view plaincopy

  1. <pre code_snippet_id="452489" snippet_file_name="blog_20140817_26_3867453" name="code" class="html">   private static native void classInitNative();
  2. /*package*/ void onPropertyChanged(String[] propValues) {
  3. ………………
  4. log("Property Changed: " + propValues[0] + " : " + propValues[1]);
  5. String name = propValues[0];
  6. if (name.equals("Name")) {//获取蓝牙名字;
  7. …………
  8. } else if (name.equals("Pairable") || name.equals("Discoverable")) {//配对;
  9. ………………
  10. } else if (name.equals("Discovering")) {//扫描查询;
  11. Intent intent;
  12. adapterProperties.setProperty(name, propValues[1]);
  13. if (propValues[1].equals("true")) {
  14. intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
  15. } else {
  16. // Stop the discovery.
  17. mBluetoothService.cancelDiscovery();
  18. intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
  19. }
  20. mContext.sendBroadcast(intent, BLUETOOTH_PERM);
  21. } else if (name.equals("Devices") || name.equals("UUIDs")) {//Devices、UUID的获取;
  22. ………………
  23. } else if (name.equals("Powered")) {//蓝牙打开、关闭;
  24. mBluetoothState.sendMessage(BluetoothAdapterStateMachine.POWER_STATE_CHANGED,
  25. propValues[1].equals("true") ? new Boolean(true) : new Boolean(false));
  26. } else if (name.equals("DiscoverableTimeout")) {
  27. adapterProperties.setProperty(name, propValues[1]);
  28. }
  29. } </pre>

(1)、看到这份log我们也许会更明白其他功能的由来:

[html] view plaincopy

  1. D BluetoothEventLoop: Property Changed: Powered : true
  2. D BluetoothEventLoop: Property Changed: Pairable : true
  3. D BluetoothEventLoop: Property Changed: Class : 5898764
  4. D BluetoothEventLoop: Property Changed: Pairable : true
  5. D BluetoothEventLoop: Property Changed: Discoverable : false
  6. D BluetoothEventLoop: Property Changed: Discovering : true
  7. D BluetoothEventLoop: Property Changed: Discovering : false
  8. D BluetoothEventLoop: Property Changed: Devices : 1
  9. D BluetoothEventLoop: Device property changed: 94:20:53:01:15:90 property: Connected value: true
  10. D BluetoothEventLoop: Device property changed: 94:20:53:01:15:90 property: Paired value: true
  11. D BluetoothEventLoop: Device property changed: 94:20:53:01:15:90 property: UUIDs value: 4

(2)、下面我们重点分析Discovering这部分
idh.code\frameworks\base\core\java\android\server\BluetoothEventLoop.java

[html] view plaincopy

  1. else if (name.equals("Discovering")) {
  2. Intent intent;
  3. adapterProperties.setProperty(name, propValues[1]);
  4. if (propValues[1].equals("true")) {//开始扫描
  5. intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_STARTED);//
  6. } else {
  7. // Stop the discovery. //停止扫描
  8. mBluetoothService.cancelDiscovery();
  9. intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
  10. }
  11. mContext.sendBroadcast(intent, BLUETOOTH_PERM);
  12. }
  13. 这样就可以通过broadcast发送ACTION_DISCOVERY_STARTED广播,注册的receiver来响应了。

8、ACTION_DISCOVERY_STARTED\ACTION_DISCOVERY_FINISHED的receiver分析
从代码中我们可以看到这个action一共有两个receiver,一个是静态注册的BluetoothDiscoveryReceiver,一个是动态注册是ScanningStateChangedHandler。
(1)、BluetoothDiscoveryReceiver:
这个receiver是在settings中的Androidmanifest中静态注册的。用途:主要用于获取扫描开始和终止的时间。
idh.code\packages\apps\Settings\AndroidManifest.xml

[html] view plaincopy

  1. <receiver
  2. android:name=".bluetooth.BluetoothDiscoveryReceiver">
  3. <intent-filter>
  4. <action android:name="android.bluetooth.adapter.action.DISCOVERY_STARTED" />
  5. <action android:name="android.bluetooth.adapter.action.DISCOVERY_FINISHED" />
  6. <category android:name="android.intent.category.DEFAULT" />
  7. </intent-filter>
  8. </receiver>

1)、ACTION_DISCOVERY_STARTED、ACTION_DISCOVERY_FINISHED和AndroidManifest.xml文件的联系
idh.code\frameworks\base\core\java\android\bluetooth\BluetoothAdapter.java

[html] view plaincopy

  1. public final class BluetoothAdapter {
  2. private static final String TAG = "BluetoothAdapter";
  3. private static final boolean DBG = false;
  4. …………
  5. public static final String ACTION_DISCOVERY_STARTED =
  6. "android.bluetooth.adapter.action.DISCOVERY_STARTED";
  7. public static final String ACTION_DISCOVERY_FINISHED =
  8. "android.bluetooth.adapter.action.DISCOVERY_FINISHED";
  9. …………
  10. }

2)、BluetoothAdapter,蓝牙适配器,直到我们建立bluetoothSocket连接之前,都要不断操作它。
BluetoothAdapter中的动作常量


ACTION_DISCOVERY_FINISHED


已完成蓝牙搜索


ACTION_DISCOVERY_STARTED


已经开始搜索蓝牙设备


ACTION_LOCAL_NAME_CHANGED


更改蓝牙的名字


ACTION_REQUEST_DISCOVERABLE


请求能够被搜索


ACTION_REQUEST_ENABLE


请求启动蓝牙


ACTION_SCAN_MODE_CHANGED


扫描模式已经改变


ACTION_STATE_CHANGED


状态已改变


ACTION_CONNECTION_STATE_CHANGED

3)、收到广播后函数实现,开始扫描
Main log中显示的log为DISCOVERY_STARTED
D BluetoothDiscoveryReceiver: Received:android.bluetooth.adapter.action.DISCOVERY_STARTED
HCI log 中:

idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\BluetoothDiscoveryReceiver.java这个文件中就一个函数,还是比简单

[html] view plaincopy

  1. public final class BluetoothDiscoveryReceiver extends BroadcastReceiver {
  2. private static final String TAG = "BluetoothDiscoveryReceiver";
  3. private static final boolean DEBUG = Debug.isDebug();
  4. @Override
  5. public void onReceive(Context context, Intent intent) {
  6. String action = intent.getAction();
  7. if (DEBUG) Log.d(TAG, "Received: " + action);
  8. if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_STARTED) ||
  9. action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {
  10. //共享时间戳,扫描开始和结束的时间。
  11. LocalBluetoothPreferences.persistDiscoveringTimestamp(context);
  12. }
  13. }
  14. }

ScanningStateChangedHandler的注册及用途,要用于开始扫描,和扫描显示界面的控制。
这个receiver是在idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\BluetoothEventManager.java动态注册的,如下:

[html] view plaincopy

  1. BluetoothEventManager(LocalBluetoothAdapter adapter,
  2. CachedBluetoothDeviceManager deviceManager, Context context) {
  3. mLocalAdapter = adapter;
  4. …………
  5. // Bluetooth on/off broadcasts
  6. addHandler(BluetoothAdapter.ACTION_STATE_CHANGED, new AdapterStateChangedHandler());
  7. // Discovery broadcastsaddHandler(BluetoothAdapter.ACTION_DISCOVERY_STARTED, new ScanningStateChangedHandler(true));
  8. addHandler(BluetoothAdapter.ACTION_DISCOVERY_FINISHED, new ScanningStateChangedHandler(false));
  9. …………
  10. }

(1)、ScanningStateChangedHandler函数实现如下:idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\BluetoothEventManager.java

[html] view plaincopy

  1. private class ScanningStateChangedHandler implements Handler {
  2. private final boolean mStarted;
  3. ScanningStateChangedHandler(boolean started) {
  4. mStarted = started;
  5. }
  6. public void onReceive(Context context, Intent intent,
  7. BluetoothDevice device) {
  8. synchronized (mCallbacks) {//1)、调用注册的callback
  9. 中的onScanningStateChanged函数。
  10. for (BluetoothCallback callback : mCallbacks) {
  11. callback.onScanningStateChanged(mStarted);
  12. }
  13. }
  14. //2)、这个函数就是把上次扫描到设备、和之前的设备做相应处理;
  15. mDeviceManager.onScanningStateChanged(mStarted);
  16. LocalBluetoothPreferences.persistDiscoveringTimestamp(context);
  17. }
  18. }

1)、调用注册的callback中的callback.onScanningStateChanged(mStarted)函数。
idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\DeviceListPreferenceFragment.java

[html] view plaincopy

  1. public void onScanningStateChanged(boolean started) {
  2. if (started == false) {//《1》、如果扫描结束;
  3. removeOutOfRangeDevices();
  4. }
  5. updateProgressUi(started);// 《2》、UI显示小圆圈扫描;

《1》、如果扫描结束;removeOutOfRangeDevices();
idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\DeviceListPreferenceFragment.java

[html] view plaincopy

  1. private void removeOutOfRangeDevices() {
  2. Collection<CachedBluetoothDevice> cachedDevices =
  3. mLocalManager.getCachedDeviceManager().getCachedDevicesCopy();
  4. for (CachedBluetoothDevice cachedDevice : cachedDevices) {
  5. if (cachedDevice.getBondState() == BluetoothDevice.BOND_NONE &&
  6. cachedDevice.isVisible() == false) {
  7. BluetoothDevicePreference preference = mDevicePreferenceMap.get(cachedDevice);
  8. if (preference != null) {
  9. mDeviceListGroup.removePreference(preference);
  10. }
  11. mDevicePreferenceMap.remove(cachedDevice);
  12. }
  13. }
  14. }

《2》、UI显示小圆圈扫描,updateProgressUi(started);如下图所示:

idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\DeviceListPreferenceFragment.java

[html] view plaincopy

  1. private void updateProgressUi(boolean start) {
  2. if (mDeviceListGroup instanceof ProgressCategory) {
  3. ((ProgressCategory) mDeviceListGroup).setProgress(start);
  4. }
  5. }

2)、这部分的作用,开始扫描,不显示列表中内容,或把之前列表中没扫描到的设备清除
mDeviceManager.onScanningStateChanged(mStarted);
idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\CachedBluetoothDevice.java

[html] view plaincopy

  1. private void updateProgressUi(boolean start) {
  2. if (mDeviceListGroup instanceof ProgressCategory) {
  3. ((ProgressCategory) mDeviceListGroup).setProgress(start);
  4. }
  5. }
  6. 2)、这部分的作用,开始扫描,不显示列表中内容,或把之前列表中没扫描到的设备清除
  7. mDeviceManager.onScanningStateChanged(mStarted);
  8. idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\ CachedBluetoothDevice.java
  9. public synchronized void onScanningStateChanged(boolean started) {
  10. // If starting a new scan, clear old visibility
  11. // Iterate in reverse order since devices may be removed.
  12. //如果开始新的扫描,清除旧的能见设备,迭代反序因为有的设备可能被删除
  13. for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
  14. CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
  15. if (started) {//如果扫描开始就不显示;
  16. cachedDevice.setVisible(false);
  17. } else {//对扫描的结果作出判断,如果之前扫描过,这次没有扫描到,就移除列表。
  18. if (cachedDevice.getBondState() == BluetoothDevice.BOND_NONE &&
  19. cachedDevice.isVisible() == false) {
  20. mCachedDevices.remove(cachedDevice);
  21. }
  22. }
  23. }
  24. }
时间: 2024-11-16 09:34:23

【转】Android bluetooth介绍(三): 蓝牙扫描(scan)设备分析的相关文章

Android bluetooth介绍(三): 蓝牙扫描(scan)设备分析

关键词:蓝牙blueZ  A2DP.SINK.sink_connect.sink_disconnect.sink_suspend.sink_resume.sink_is_connected.sink_get_properties.AUDIO.DBUS版本号:基于android4.2之前版本号 bluez内核:linux/linux3.08系统:android/android4.1.3.4作者:xubin341719(欢迎转载.请注明作者.请尊重版权谢谢)欢迎指正错误.共同学习.共同进步!! 參考

Android bluetooth介绍(两): android 蓝牙源架构和uart 至rfcomm过程

关键词:蓝牙blueZ  UART  HCI_UART H4  HCI  L2CAP RFCOMM  版本号:基于android4.2先前版本 bluez内核:linux/linux3.08系统:android/android4.1.3.4作者:xubin341719(欢迎转载,请注明作者.请尊重版权谢谢)欢迎指正错误,共同学习.共同进步!! Android bluetooth介绍(一):基本概念及硬件接口Android bluetooth介绍(二): android 蓝牙代码架构及其uart

Android bluetooth介绍(四): a2dp connect流程分析

关键词:蓝牙blueZ  A2DP.SINK.sink_connect.sink_disconnect.sink_suspend.sink_resume.sink_is_connected.sink_get_properties.AUDIO.DBUS版本:基于android4.2之前版本 bluez内核:linux/linux3.08系统:android/android4.1.3.4作者:xubin341719(欢迎转载,请注明作者,请尊重版权谢谢)欢迎指正错误,共同学习.共同进步!! Andr

Android bluetooth介绍(二): android 蓝牙代码架构及其uart 到rfcomm流程

关键词:蓝牙blueZ  UART  HCI_UART H4  HCI  L2CAP RFCOMM  版本:基于android4.2之前版本 bluez内核:linux/linux3.08系统:android/android4.1.3.4作者:xubin341719(欢迎转载,请注明作者,请尊重版权谢谢)欢迎指正错误,共同学习.共同进步!!一.Android Bluetooth Architecture蓝牙代码架构部分(google 官方蓝牙框架) Android的蓝牙系统,自下而上包括以下一些

Android bluetooth介绍(一):基本概念及硬件接口

关键词:蓝牙硬件接口 UART  PCM  blueZ 版本:基于android4.2之前版本 bluez内核:linux/linux3.08系统:android/android4.1.3.4作者:xubin341719(欢迎转载,请注明作者,请尊重版权谢谢)欢迎指正错误,共同学习.共同进步!! 一.基本概念补充 1.AP:ApplicationProcessor应用处理器 采用ARM架构的CPU,通常负责运行OS和一些特定设置和载入开机预设.比如一个没有电话功能的平板电脑,只跑android或

android -- 蓝牙 bluetooth (三)搜索蓝牙

接上篇打开蓝牙继续,来一起看下蓝牙搜索的流程,触发蓝牙搜索的条件形式上有两种,一是在蓝牙设置界面开启蓝牙会直接开始搜索,另一个是先打开蓝牙开关在进入蓝牙设置界面也会触发搜索,也可能还有其它触发方式,但最后都要来到BluetoothSettngs.java的startScanning(),我们分析的起点也从这里开始,起步代码如下 [cpp] view plaincopy private void updateContent(int bluetoothState, boolean scanState

android bluetooth蓝牙移植

http://blog.csdn.net/zhengmeifu/article/details/7705172 前段时间移植神念系统需要使用到bluetooth功能,现将移植过程中碰到的问题简要列一下,备忘而已. 1.内核增加配置make menuconfig -> Networking support -> Bluetooth subsystem support -->进行选择BT选项,至少选择如下几项:CONFIG_BT=yCONFIG_BT_RFCOMM=y## Bluetooth

Bluetooth LE(低功耗蓝牙) - 第三部分

回顾 在本系列的前两篇文章中,我们已经了解了一些关于Bluetooth LE的背景并建立一个简单的Activity / Service框架.   在这篇文章中,我们将探讨Bluetooth LE的细节以及蓝牙设备查找的一些问题. 扫描并发现蓝牙设备 蓝牙设备的发现是十分简单的,它是一个在蓝牙可见范围内查找设备的过程.首先我们要做的就是在Manifest中添加必要的权限,否则我们将在一开始就碰壁.我们需要的权限是android.permission.BLUETOOTH(一般蓝牙使用)和androi

Android Studio第三十三期 - 蓝牙开发初识~

遇见到坑爹的队友只有接受现实并且解决问题~ 首先介绍一下网上几乎所有的能搜到的方法: 1.首先,要操作蓝牙,先要在AndroidManifest.xml里加入权限 <uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permissionandroid:name="android.permission.BLUETOOTH" /> 2.在androi