当前位置:编程学堂 > 【转】Android蓝牙介绍(三):蓝牙扫描(scan)设备分析

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

  • 发布:2023-10-06 06:13

-->

原文网址:http://www.sychzs.cn/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://www.sychzs.cn/u011960402/article/details/17216563
http://www.sychzs.cn/fityme/archive/2013/04/13/3019471 .html套接字相关
http://www.sychzs.cn/wwwkljoel/item/a35e5745d14e02e6bcf45170setsockopt

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

1。蓝牙扫描常用的方法:
蓝牙扫描可以通过两种方式实现: 命令行 hciitool 扫描; Android界面触发,通过JNI和DUBS发出命令。 ?结果

2。 Android 界面触发,通过 JNI 和 DUBS 发出命令:通过 Android 界面点击搜索设备

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

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

(1) 应用部分:
1。 idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\www.sychzs.cn

  1. @覆盖
  2. public boolean onPreferenceTreeClick(PreferenceScreen首选项Screen,
  3. 偏好偏好){
  4. …………
  5. mLocalAdapter.startScanning(true);
  6. 返回true;
  7. }

2、 idh.code\packages\apps\Settings\src\com\android\settings\bluetooth\www.sychzs.cn

  1. 私有最终蓝牙适配器 mAdapter;
  2. void 开始扫描(布尔力) {
  3. //仅当我们尚未扫描时才开始
  4. if(!mAdapter.isDiscovering()){
  5. if(!强制){
  6. //扫描频率不要超过 SCAN_EXPIRATION_MS,
  7. //除非被迫
  8. if(mLastScan + SCAN_EXPIRATION_MS > System.currentTimeMillis()){
  9. 返回;
  10. }
  11. //如果我们正在播放音乐,除非被迫,否则不要扫描。
  12. A2dpProfile a2dp = mProfileManager.getA2dpProfile();
  13. if(a2dp!= null && a2dp.isA2dpPlaying()){
  14. 返回;
  15. }
  16. }
  17. if(mAdapter.startDiscovery()){
  18. mLastScan = System.currentTimeMillis();
  19. }
  20. }
  21. }

3、idh.code\frameworks\base\core\java\android\bluetooth\蓝牙适配器.java

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

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

  1. private native boolean startDiscoveryNative();//Native函数声明
  2. 公共类 BluetoothService 扩展了 IBluetooth.Stub {
  3. 私有静态最终字符串TAG =“蓝牙服务”;
  4. 私有静态最终布尔值 DBG = true;
  5. …………
  6. 公共同步布尔startDiscovery(){
  7. mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
  8. “需要 BLUETOOTH_ADMIN 权限”);
  9. if (!isEnabledInternal()) 返回 false;
  10. 返回startDiscoveryNative();
  11. }
  12. ………………
  13. }

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

  1. 静态 JNINativeMethod sMethods[] = {
  2. /* 姓名、签名、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 对象) {
  4. ………………
  5. /* 编写命令 */
  6. msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC,
  7. get_adapter_path(env,对象),
  8. DBUS_ADAPTER_IFACE,“开始发现”);
  9. …………
  10. }
  11. 原生函数startDiscoveryNative对应于字符串StartDiscovery。

(3)、DBUS部分

1。 DBUS对应方法的实现与JNI部分类似,也是采用函数结构对应关系。
idh.code\external\bluetooth\bluez\src\adapter.c

  1. #define ADAPTER_INTERFACE "org.bluez.Adapter"
  2. 静态GDBusMethodTable适配器_方法[] = {
  3. ………………
  4. { "ReleaseSession", "", "", release_session },
  5. {“开始发现”,“”,“”,adapter_start_discovery },
  6. {“停止发现”,“”,“”,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. 静态 DBusMessage *adapter_start_discovery(DBusConnection *conn,
  2. DBusMessage *msg, void *数据)
  3. {
  4. …………
  5. err = start_discovery(适配器);
  6. if(错误<0&&错误!=-EINPROGRESS)
  7. 返回 btd_error_failed(msg, strerror(-err));
  8. 完成:
  9. req = create_session(适配器,conn,msg,0,
  10. session_owner_exit);
  11. 适配器->disc_sessions = g_slist_append(适配器->disc_sessions, req);
  12. 返回 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 *适配器)
  3. {
  4. …………
  5. pending_remote_name_cancel(适配器);
  6. 返回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. 静态结构 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. 静态int hciops_start_discovery(int索引)
  2. {
  3. int adapter_type = get_adapter_type(索引);
  4. 开关(适配器类型){
  5. 案例 BR_EDR_LE:
  6. 返回 hciops_start_inquiry(index, LENGTH_BR_LE_INQ);
  7. case BR_EDR: //蓝牙芯片为2.1+EDR的
  8. 返回 hciops_start_inquiry(index, LENGTH_BR_INQ);
  9. 案例 LE_ONLY:
  10. 返回 hciops_start_scanning(index, TIMEOUT_LE_SCAN);
  11. 默认:
  12. 返回-EINVAL;
  13. }
  14. }

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

  1. 静态int hciops_start_inquiry(int索引,uint8_t长度)
  2. {
  3. struct dev_info *dev = &devs[索引];
  4. uint8_t lap[3] = { 0x33, 0x8b, 0x9e};
  5. inquiry_cp inq_cp;
  6. DBG("hci%d长度%u",索引,长度);
  7. memset(&inq_cp, 0, sizeof(inq_cp));
  8. memcpy(&inq_cp.lap, lap, 3);
  9. inq_cp.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. 返回-errno;
  14. 返回0;
  15. }

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

  1. /* 需要开放设备的 HCI 功能
  2. * dd - 由 hci_open_dev 返回的设备描述符。 */
  3. dd = 套接字(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(全){
  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. 继续;
  15. 返回-1;
  16. }
  17. 返回0;
  18. }

(4) 内核部分:
1。 HCI FILTER 设置
HCIsocket 的类型为 BTPROTO_HCI。当上层调用setsockopt时,会触发内核的hci_sock_setsockopt函数的执行。这里设置了socket的过滤特性,包括数据包类型,也包括事件类型

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

  1. 静态 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. 案例 HCI_FILTER:
  5. {
  6. struct hci_filter *f = &hci_pi(sk)->filter;
  7. uf.type_mask = f->type_mask;
  8. uf.opcode = f->操作码;
  9. uf.event_mask[0] = *((u32 *) f->event_mask + 0);
  10. uf.event_mask[1] = *((u32 *) f->event_mask + 1);
  11. }
  12. ………………
  13. }

这部分内核数据比较统一。命令是通过hci_send_cmd发送出去的,HCI_FILTER这个地方的处理我还没看懂,稍后补充
Writev函数通过socket写入数据,经过VFS层,调用内核空间的sendmsg函数。

(5), EVENT 返回状态

控制器收到查询命令后,返回命令状态
1,cmd_status
idh.code\external\bluetooth\bluez\plugins\hciops.c

  1. 开关(eh->evt){
  2. 案例 EVT_CMD_STATUS:
  3. cmd_status(index, ptr);
  4. 断裂;
  5. 静态内联 void cmd_status(int index, void *ptr)
  6. {
  7. evt_cmd_status *evt = ptr;
  8. uint16_t 操作码 = btohs(evt->操作码);
  9. if (opcode == cmd_opcode_pack(OGF_LINK_CTL, OCF_INQUIRY))//如果是查询,则做特殊处理;
  10. cs_inquiry_evt(索引, evt->状态);
  11. }

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

  1. 静态内联void cs_inquiry_evt(int索引,uint8_t状态)
  2. {
  3. if (状态){//错误信息
  4. 错误(“查询失败,状态为0x%02x”,status);
  5. 返回;
  6. }
  7. set_state(index, DISCOV_INQ);//设置状态为INQ,并用发现属性变化回复上层
  8. }

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

  1. static void set_state(int索引,int状态)
  2. {
  3. ………………
  4. 开关(dev->discov_state){
  5. case DISCOV_HALTED://停止发现;
  6. if(adapter_get_state(适配器)== STATE_SUSPENDED)
  7. 返回;
  8. if(is_resolvname_enabled()&&
  9. adapter_has_discov_sessions(适配器))
  10. adapter_set_state(适配器,STATE_RESOLVNAME);
  11. 否则
  12. adapter_set_state(适配器,STATE_IDLE);
  13. 断裂;
  14. 案例DISCOV_INQ:
  15. 案例DISCOV_SCAN://扫描发现;
  16. adapter_set_state(适配器,STATE_DISCOV);
  17. 断裂;
  18. }
  19. }

4、设置适配器的状态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 *适配器,int状态)
  4. {
  5. …………
  6. 案例 STATE_DISCOV:
  7. discov_active = TRUE;
  8. //向上层回复发现属性变化
  9. emit_property_changed(连接,路径,
  10. ADAPTER_INTERFACE,“发现”,
  11. DBUS_TYPE_BOOLEAN,&discov_active);
  12. 断裂;
  13. …………
  14. }

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

  1. dbus_bool_t eager_property_changed(DBusConnection *conn,
  2. const char *路径,
  3. const char *接口,
  4. const char *名称,
  5. int类型,void *值)
  6. {
  7. DBusMessage *信号;
  8. DBusMessageIter iter;
  9. signal = dbus_message_new_signal(路径, 接口, "PropertyChanged"); // 创建消息对象并标识路径
  10. if(!信号){
  11. 错误(“无法分配新的%s.PropertyChanged信号”,
  12. 接口);
  13. 返回FALSE;
  14. }
  15. dbus_message_iter_init_append(signal, &iter);//按signal对应的参数
  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. //WaitForAndDispatchEventNative()期间由dbus调用
  2. 静态 DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg,
  3. 无效*数据){
  4. …………
  5. else if (dbus_message_is_signal(msg, "org.bluez.Adapter", "PropertyChanged")) {
  6. jobobjectArray str_array = parse_adapter_property_change(env, msg);//(1)、接收消息分析
  7. if (str_array != NULL) {
  8. /* 检查蓝牙是否已(重新)启动,如果是则更新路径。 */
  9. jstring 属性 =(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值=
  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->适配器= 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的实现,在www.sychzs.cn
idh.code\frameworks\base\core\java\android\server\www.sychzs.cn中

   private static native void classInitNative();
  • /*package*/ void onPropertyChanged(String[] propValues) {
  • ………………
  • log("Property Changed: " + propValues[0] + " : " + propValues[1]);
  • String name = propValues[0];
  • if (name.equals("Name")) {//获取蓝牙名字;
  • …………
  • } else if (name.equals("Pairable") || name.equals("Discoverable")) {//配对;
  • ………………
  • } else if (name.equals("Discovering")) {//扫描查询;
  • Intent intent;
  • adapterProperties.setProperty(name, propValues[1]);
  • if (propValues[1].equals("true")) {
  • intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
  • } else {
  • // Stop the discovery.
  • mBluetoothService.cancelDiscovery();
  • intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
  • }
  • mContext.sendBroadcast(intent, BLUETOOTH_PERM);
  • } else if (name.equals("Devices") || name.equals("UUIDs")) {//Devices、UUID的获取;
  • ………………
  • } else if (name.equals("Powered")) {//蓝牙打开、关闭;
  • mBluetoothState.sendMessage(BluetoothAdapterStateMachine.POWER_STATE_CHANGED,
  • propValues[1].equals("true") ? new Boolean(true) : new Boolean(false));
  • } else if (name.equals("DiscoverableTimeout")) {
  • adapterProperties.setProperty(name, propValues[1]);
  • }
  • (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\www.sychzs.cn

    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. android:name=".bluetooth.BluetoothDiscoveryReceiver">

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

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

    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\www.sychzs.cn动态注册的,如下:

    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\www.sychzs.cn

    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\www.sychzs.cn

    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\www.sychzs.cn

    1. private void removeOutOfRangeDevices() {
    2. Collection cachedDevices =
    3. mLocalManager.getCachedDeviceManager().getCachedDevicesCopy();
    4. for (CachedBluetoothDevice cachedDevice : cachedDevices) {
    5. if (cachedDevice.getBondState() == www.sychzs.cn_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\www.sychzs.cn

    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\www.sychzs.cn

    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\ www.sychzs.cn
    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() == www.sychzs.cn_NONE &&
    19. cachedDevice.isVisible() == false) {
    20. mCachedDevices.remove(cachedDevice);
    21. }
    22. }
    23. }
    24. }
    -->

    相关文章

    最新资讯