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

  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

  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

  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

  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

  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

  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

  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

  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

  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

  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

  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

  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

  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

  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

  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

  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

  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

  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

  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

  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内容

  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

  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

  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

  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

  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中

  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我们也许会更明白其他功能的由来:

  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

  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

  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

  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这个文件中就一个函数,还是比简单

  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动态注册的,如下:

  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

  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

  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

  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

  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

  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介绍(三): 蓝牙扫描(scan)设备分析

    关键词:蓝牙blueZ  A2DP.SINK.sink_connect.sink_disconnect.sink_suspend.sink_resume.sink_is_connected.sink_ ...

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

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

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

    关键词:蓝牙blueZ  A2DP.SINK.sink_connect.sink_disconnect.sink_suspend.sink_resume.sink_is_connected.sink_ ...

  4. Android bluetooth介绍

    Android bluetooth介绍(一):基本概念及硬件接口Android bluetooth介绍(二): android 蓝牙代码架构及其uart 到rfcomm流程Android blueto ...

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

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

  6. 【转】Android bluetooth介绍(二): android blueZ蓝牙代码架构及其uart 到rfcomm流程

    原文网址:http://blog.sina.com.cn/s/blog_602c72c50102uzoj.html 关键词:蓝牙blueZ  UART  HCI_UART H4  HCI  L2CAP ...

  7. Android 6.0 Kotlin 蓝牙扫描

    package com.arci.myapplication import android.app.Activityimport android.os.Bundleimport android.sup ...

  8. android Bluetooth(官方翻译)

    Bluetooth Using the Bluetooth APIs, an Android application can perform the following: 使用蓝牙APIs,一个And ...

  9. android bluetooth

    Android bluetooth介绍(一):基本概念及硬件接口 BlueZ为调试和与蓝牙子系统通信提供很多设置命令行工具,包含下面这些: hciconfig hcitool hcidump sdpt ...

随机推荐

  1. mysql的分区技术

    一.概述 当 MySQL的总记录数超过了100万后,会出现性能的大幅度下降吗?答案是肯定的,但是,性 能下降>的比率不一而同,要看系统的架构.应用程序.还有>包括索引.服务器硬件等多种因素 ...

  2. weblogic9.2重置密码

    1.删除DefaultAuthenticatorInit.ldift 2.执行命令:java -cp /home/weblogic/bea/weblogic92/server/lib/weblogic ...

  3. RS485接线 - 为什么要给2线制RS485接3根线?

    http://www.chipkin.com/rs485-cables-why-you-need-3-wires-for-2-two-wire-rs485/ RS485needs 3 conducto ...

  4. C#2.0至4.0 的一些特性

    罗列清单备查 一.C#2.0 1. Partial class 分部类 file1.cs using System; public partial class MyClass { public voi ...

  5. Java 的自动装箱拆箱

    Java 是面向对象的语言,其基本数据类型也就有了相对应的类,称为包装类.以下是基本数据类型对应的包装类: 基本数据类型 包装类 byte(1字节) Byte short(2字节) Short int ...

  6. TFTPD32, 3CDaemon, FlashFxp

    TFTPD32, 3CDaemon, FlashFxp ——各种网络传输下载工具简介—— 一.将3CDaemon.exe作为TFTP服务端,开发板作为TFTP客户端 1.如上图所示,设置好3CDaem ...

  7. Java Lambda简明教程(一)

    Lambda表达式背景 许多热门的编程语言如今都有一个叫做lambda或者闭包的语言特性,包括比较经典的函数式编程语言Lisp,Scheme,也有稍微年轻的语言比如JavaScript,Python, ...

  8. c#NPOI导出

    按行列导出数据: HSSFWorkbook hssfworkbook = new HSSFWorkbook(); //命名空间:using NPOI.HSSF.UserModel; Sheet she ...

  9. Dom操作--跑马灯效果

    这里给园友们演示的是Dom操作实现跑马灯效果,相信我们很多人都用Winform实现过跑马灯效果,其中的关键就是Tirm控件,那么在Dom操作中是用setInterval方法来实现隔一段时间执行一段代码 ...

  10. Mac苹果电脑加密视频播放器使用教程

    1.   下载文件 https://pan.baidu.com/s/1slhFYuL 2.    操作流程 温馨提示 播放时,请务必保证播放设备联网(原因:用户名权限验证需要网络,播放后10秒即可关闭 ...