原文网址: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. }

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

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

    关键词:蓝牙blueZ  UART  HCI_UART H4  HCI  L2CAP RFCOMM  版本:基于android4.2之前版本 bluez内核:linux/linux3.08 系统:an ...

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

    关键词:蓝牙blueZ  UART  HCI_UART H4  HCI  L2CAP RFCOMM  版本号:基于android4.2先前版本 bluez内核:linux/linux3.08 系统:a ...

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

    关键词:蓝牙硬件接口 UART  PCM  blueZ  版本:基于android4.2之前版本 bluez 内核:linux/linux3.08 系统:android/android4.1.3.4 ...

  4. Android连接、断开蓝牙扫描枪时屏幕刷新

    场景:使用蓝牙扫描枪连接安卓设备,用于扫描条码. 问题:连接和断开扫描枪时,屏幕都会刷新,造成临时加载的数据丢失. 原因: 扫描枪连接时是将其作为物理输入设备,即物理键盘,而安卓在改变屏幕方向.弹出隐 ...

  5. android 蓝牙不停扫描,android – BluetoothAdapter不会停止扫描BLE设备

    在我的应用程序中我有启动和停止按钮,当用户按下启动时我调用startScan方法 bluetoothAdapter.getBluetoothLeScanner().startScan(getLeSca ...

  6. php扫描蓝牙设备接口,微信硬件蓝牙扫描某个设备接口onScanWXDeviceResult

    好久没更新微信硬件蓝牙开发的技术文章了,最近闲来无事,翻了翻系列文章以及微信硬件jsapi接口,发现onScanWXDeviceResult扫描某个设备接口并没有做案例demo给大家分享出来,所以接着 ...

  7. 【CSRMesh2.1蓝牙开发】-Android demo介绍【二】之代码分析

    怎么介绍呢,一直考虑,还是从业务流程开始,1.Meshservice初始化-->2.扫描设备--->3.连接设备-->4.扫描设备-->5.设备组网 这样分析多初学者,或者刚接 ...

  8. Android init第三、四部分详细分析

    本文一定要在详细阅读了,系列的第二篇文章时候,再来阅读. /init程序第三部分 action_for_each_trigger("early-init", action_add_ ...

  9. Android 11:bluetooth@1.0蓝牙架构分析

    介绍结 参考:蓝牙  |  Android 开源项目  |  Android Open Source Projecthttps://source.android.com/docs/core/conne ...

最新文章

  1. 成为表情包大帝,仅需几十行python代码,简单易学
  2. xhprof 安装使用(windows linux混版)
  3. matlab导向滤波磨皮,图像处理(七)导向滤波磨皮
  4. http的请求方法 GET、HEAD、POST、PUT、DELETE、CONNECT、OPTIONS、TRACE 说明
  5. python【进阶】5.一等函数(注销)
  6. 计算机考研408试题及答案,2015年计算机专业408考研试题及答案
  7. webService简单入门实战(一)
  8. http 使用curl发起https请求 error 60 51
  9. mysql 事务 不同库_MYSQL数据库重点:事务与锁机制
  10. android缓存垃圾扫描功能吗,Android 系统缓存扫描与清理方法分析
  11. PDF破解FileOpenPlugin加密的方法
  12. 论文精读- The Evaluation of the Urban Road Network Based on the Complex Network
  13. 动态面板数据模型及Eviews实现
  14. 二维数组冒泡排序 java
  15. Excel显示公式栏/编辑栏
  16. 物流查询方法,一键导入单号自动识别快递公司
  17. 电子邮箱号码大全,至尊邮为你打开邮箱的正确格式
  18. 可视化讲解:什么是宠物收养所问题?
  19. charing animation
  20. CSS font-size属性

热门文章

  1. mysql常量求和_Mysql之:count(*)、count(常量)、count(字段)的区别
  2. react月份选择控件_一款很实用的ReactJS日期范围选择控件
  3. 类似clover的软件_Clover 我的电脑里的书签栏
  4. 小米路由器4a刷第三方固件_小米路由器4A的断网问题初探
  5. oracle xp安装详细步骤
  6. 风变编程Python9 函数的学习
  7. mysql可以装到其他端口吗_linux下怎么在另一个端口安装高版本mysql
  8. Nacos教程_3 整合SpringCloud(配置中心+服务发现)
  9. 如何对第一个值相同的列表中的元组求和
  10. C语言中变量的静态分配(Static)和动态分配(StackHeap)