Android蓝牙服务查找附近设备分析探索

目录
  • 一、APP端调用
    • 1、注册广播监听查找结果
    • 2、开始查找附近设备
    • 3、异常处理
  • 二、查找设备源码分析
    • 1、BluetoothAdapter.startDiscovery()
    • 2、AdapterService.startDiscovery()
    • 3、startDiscoveryNative()
    • 4、start_discovery() 扫描入口
    • 5、btif_dm_start_discovery() 配置参数
    • 6、BTA_DmSearch() 搜索对等蓝牙设备
    • 7、bta_sys_sendmsg() 发送扫描消息

一、APP端调用

1、注册广播监听查找结果

//蓝牙发现设备和查找结束广播
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(btReceiver, intentFilter);
BroadcastReceiver btReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (BluetoothDevice.ACTION_FOUND.equals(intent.getAction())) {
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            if (device != null) {
                //这里收到的是单条设备信息,可以放到List中进行刷新列表
                //设备名称:device.getName()
                //设备地址:device.getAddress()
                if(device.getBondState() == BluetoothDevice.BOND_BONDED) {
                    //已配对设备
                }
            }
        } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(intent.getAction())) {
                //查找结束
        }
    }
};

2、开始查找附近设备

btAdapter.startDiscovery();

3、异常处理

上面代码无法接收到 BluetoothDevice.ACTION_FOUND 广播,查找资料发现 Android 6.0 后需要增加两个权限并且需要动态申请。

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.M
         && checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)!= PackageManager.PERMISSION_GRANTED) {//请求权限
    requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION},1);
}

这里的权限动态申请就简单处理一下,不做详细介绍了。

另一个问题是收到发现设备广播数据,很多数据的 getName() 为 null,这里还需要做一些判空处理。

二、查找设备源码分析

1、BluetoothAdapter.startDiscovery()

private IBluetooth mService;
public boolean startDiscovery() {
    if (getState() != STATE_ON) {
        return false;
    }
    try {
        mServiceLock.readLock().lock();
        if (mService != null) {
            return mService.startDiscovery(mAttributionSource);
        }
    } catch (RemoteException e) {
        Log.e(TAG, "", e);
    } finally {
        mServiceLock.readLock().unlock();
    }
    return false;
}

可以看到这里直接调用 mService.startDiscovery(),IBluetooth 的实现类为 AdapterService,相较于蓝牙开关功能,省去了调用 BluetoothManagerService 的部分。

2、AdapterService.startDiscovery()

public boolean startDiscovery(AttributionSource attributionSource) {
    AdapterService service = getService();
    if (service == null || !callerIsSystemOrActiveUser(TAG, "startDiscovery")) {
        return false;
    }
    if (!Utils.checkScanPermissionForDataDelivery(
        service, attributionSource, "Starting discovery.")) {
        return false;
    }
    return service.startDiscovery(attributionSource);
}
boolean startDiscovery(AttributionSource attributionSource) {
    UserHandle callingUser = UserHandle.of(UserHandle.getCallingUserId());
    debugLog("startDiscovery");
    String callingPackage = attributionSource.getPackageName();
    mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
    boolean isQApp = Utils.isQApp(this, callingPackage);
    boolean hasDisavowedLocation = Utils.hasDisavowedLocationForScan(this, attributionSource, mTestModeEnabled);
    String permission = null;
    if (Utils.checkCallerHasNetworkSettingsPermission(this)) {
        permission = android.Manifest.permission.NETWORK_SETTINGS;
    } else if (Utils.checkCallerHasNetworkSetupWizardPermission(this)) {
        permission = android.Manifest.permission.NETWORK_SETUP_WIZARD;
    } else if (!hasDisavowedLocation) {
        if (isQApp) {
            if (!Utils.checkCallerHasFineLocation(this, attributionSource, callingUser)) {
                return false;
            }
            permission = android.Manifest.permission.ACCESS_FINE_LOCATION;
        } else {
            if (!Utils.checkCallerHasCoarseLocation(this, attributionSource, callingUser)) {
                return false;
            }
            permission = android.Manifest.permission.ACCESS_COARSE_LOCATION;
        }
    }
    synchronized (mDiscoveringPackages) {
        mDiscoveringPackages.add(new DiscoveringPackage(callingPackage, permission, hasDisavowedLocation));
    }
    return startDiscoveryNative();
}

这里可以看到权限验证相关的内容。最后调用 startDiscoveryNative() 进入 JNI 层,在com_android_bluetooth_btservice_AdapterService.cpp文件中,调用startDiscoveryNative方法。

3、startDiscoveryNative()

源码位置:packages/apps/Bluetooth/jni/com_android_bluetooth_btservice_AdapterService.cpp

static jboolean startDiscoveryNative(JNIEnv* env, jobject obj) {
    ALOGV("%s", __func__);
    if (!sBluetoothInterface) return JNI_FALSE;
    int ret = sBluetoothInterface->start_discovery();
    return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}

4、start_discovery() 扫描入口

源码位置:system/bt/btif/src/bluetooth.cc

static int start_discovery(void) {
    if (!interface_ready()) return BT_STATUS_NOT_READY;

    do_in_main_thread(FROM_HERE, base::BindOnce(btif_dm_start_discovery));
    return BT_STATUS_SUCCESS;
}

5、btif_dm_start_discovery() 配置参数

源码位置:/system/bt/btif/src/btif_dm.cc

设备管理(DM)相关的功能

void btif_dm_start_discovery(void) {
    ......
    /* Will be enabled to true once inquiry busy level has been received */
    btif_dm_inquiry_in_progress = false;
    /* find nearby devices */
    BTA_DmSearch(btif_dm_search_devices_evt, is_bonding_or_sdp());
}

6、BTA_DmSearch() 搜索对等蓝牙设备

源码位置:/system/bt/bta/dm/bta_dm_api.cc

它执行查询并获取设备的远程名称。

void BTA_DmSearch(tBTA_DM_SEARCH_CBACK* p_cback, bool is_bonding_or_sdp) {
    tBTA_DM_API_SEARCH* p_msg = (tBTA_DM_API_SEARCH*)osi_calloc(sizeof(tBTA_DM_API_SEARCH));
    /* Queue request if a device is bonding or performing service discovery */
    if (is_bonding_or_sdp) {
        p_msg->hdr.event = BTA_DM_API_QUEUE_SEARCH_EVT;
    } else {
        p_msg->hdr.event = BTA_DM_API_SEARCH_EVT;
    }
    p_msg->p_cback = p_cback;
    bta_sys_sendmsg(p_msg);
}

7、bta_sys_sendmsg() 发送扫描消息

源码位置:/system/bt/bta/sys/bta_sys_main.cc

向 BTU TASK 发送扫描消息,由蓝牙设备管理模块处理。

void bta_sys_sendmsg(void* p_msg) {
    if (do_in_main_thread(
            FROM_HERE,
            base::Bind(&bta_sys_event, static_cast<BT_HDR_RIGID*>(p_msg))) !=
        BT_STATUS_SUCCESS) {
      LOG(ERROR) << __func__ << ": do_in_main_thread failed";
    }
}

这里调用的是 do_in_main_thread() 方法,这个方法其实在 4 中已经调用过一次。这个方法只是返回一个状态。

位置:/system/bt/stack/btu/btu_task.cc

bt_status_t do_in_main_thread(const base::Location& from_here, base::OnceClosure task) {
    if (!main_thread.DoInThread(from_here, std::move(task))) {
      LOG(ERROR) << __func__ << ": failed from " << from_here.ToString();
      return BT_STATUS_FAIL;
    }
    return BT_STATUS_SUCCESS;
}

BTU TASK收到消息后,调用 bta_dm_main.c 的(用于DM的状态机事件处理函数) bta_dm_search_sm_execute() 执行状态切换和 inquiry 流程。这里就不往下分析了。

到此这篇关于Android蓝牙服务查找附近设备分析探索的文章就介绍到这了,更多相关Android查找附近设备内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Android学习笔记之蓝牙功能

    本文实例为大家分享了Android学习笔记之蓝牙功能的具体代码,供大家参考,具体内容如下 蓝牙:短距离无线通讯技术标准.蓝牙协议分为4层,即核心协议层.电缆替代协议层.电话控制协议层和其他协议层.其中核心协议层包括基带.链路管理.逻辑链路控制和适应协议四部分.链路管理(LMP)负责蓝牙组件间的建立.逻辑链路控制与适应协议(L2CAP)位于基带协议层上,属于数据链路层,是一个高层传输和应用层协议屏蔽基带协议的适配协议. 1).第一种打开蓝牙的方式: Intent enableIntent = ne

  • Android 8.0实现蓝牙遥控器自动配对

    本文要实现的是在 android 8.0 的平台上,蓝牙遥控器与TV自动配对,具体就是在TV端打开配对界面,TV端开始搜索远程蓝牙设备,按下遥控器按键让蓝牙遥控器进入对码模式,此时蓝牙遥控器就能作为一个远程蓝牙设备被发现,TV端扫描到这个远程蓝牙设备(蓝牙遥控器),就会自动进行配对连接. 话不多说,直接上代码分析. public class RcConnectActivity extends Activity {         private static final String TAG =

  • Android蓝牙服务启动流程分析探索

    目录 1.SystemServer 2.BluetoothService 3.BluetoothManagerService 首先我们要知道,主要系统服务都是在 SystemServer 启动的,蓝牙也是如此: 1.SystemServer 源码路径:/frameworks/base/services/java/com/android/server/SystemServer.java private void startOtherServices(@NonNull TimingsTraceAnd

  • Android12 蓝牙适配的实现步骤

    目录 前言 一.Android版本中蓝牙简介 二.新建项目 ① 配置settings.gradle和build.gradle ② 配置AndroidManifest.xml 三.打开蓝牙 ① 打开蓝牙意图 ② 请求BLUETOOTH_CONNECT权限意图 四.蓝牙扫描 ① 扫描者 ② 扫描回调 ③ 扫描方法 ④ 执行扫描 ⑤ 应用不推导物理位置 五.页面显示扫描设备 ① 蓝牙设备适配器 ② 显示列表设备 六.适配Android12.0以下设备 七.源码 前言 在我的申请下,公司终于购买了一台基

  • Android BLE 蓝牙开发之实现扫码枪基于BLESSED开发

    目录 一.蓝牙模式HID与BLE 二.BLE协议白话 三.第三方库 BLESSED for Android的使用 一.蓝牙模式HID与BLE 当扫码枪与手机连接时,通常采用的是蓝牙HID(Human Interface Device)模式.本质上是一个把扫码枪作为一个硬件键盘,按照键盘协议把扫码后的结果逐个输入到对应的控件上. 优点:无需开发集成,配对就可以立即作为键盘输入使用.可以使用输入框等组件直接接收扫码结果. 缺点:对非数字支持不佳,与输入法相关,在某些时候会触发英文联想-_-||,与虚

  • Android 蓝牙BLE开发完全指南

    目录  介绍 连接模式 GATT协议 使用过程 扫描 连接 设备连接 发现服务 数据传输 其他 断开连接 参考 总结  介绍 1.BLE 是 Bluetooth Low Energy 的缩写,意思为低功耗蓝牙.由蓝牙技术联盟(Bluetooth SIG)设计的无线通讯技术,主要用于医疗,健身,安全和家庭娱乐行业. 与传统蓝牙相比,蓝牙低功耗旨在大幅降低功耗和成本,同时也能够达到相同的通讯效果. 支持多个平台,包括 IOS,Android,Windows Phone 和 BlackBerry 以及

  • Android实现蓝牙串口通讯

    本文实例为大家分享了Android实现蓝牙串口通讯的具体代码,供大家参考,具体内容如下 最近在弄蓝牙串口,参考了不少网上的大佬,加上自己早期对C#的学习,写一个给自己的备忘录,如果有大佬看到还请多多指教. 1.简介 Android设备中提供了一整套蓝牙的API,我这边只取了其中需要的部分. 初期权限 <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission andr

  • Android 连接蓝牙扫码器无输入框的实现

    Android 的APP 需要集成一个蓝牙扫码器, 特别的是,需要扫码的地方是没有输入框的(EditText),不能通过直觉上理解的通过对EditText输入事件进行监听处理,取得扫码结果.并且设备也没有提供SDK. 细想了一下, 蓝牙扫码器本质应该是个HID设备,相当于蓝牙键盘.而后豁然开朗. 每一次扫码应该会触发按键事件,通过监听当前Activity的按键事件,应该可以实现,无输入框的情况下取得扫码结果. 重载Activity中的dispatchKeyEvent实现按键监听. @Overri

  • Android蓝牙服务查找附近设备分析探索

    目录 一.APP端调用 1.注册广播监听查找结果 2.开始查找附近设备 3.异常处理 二.查找设备源码分析 1.BluetoothAdapter.startDiscovery() 2.AdapterService.startDiscovery() 3.startDiscoveryNative() 4.start_discovery() 扫描入口 5.btif_dm_start_discovery() 配置参数 6.BTA_DmSearch() 搜索对等蓝牙设备 7.bta_sys_sendmsg

  • Android 6.0 蓝牙搜索不到设备原因,MIUI权限申请机制方法

    为提供更高的数据保护 Android6.0版本上增加了关于Wifi和蓝牙的权限. 蓝牙搜索到设备需要用到定位服务,所以在开发中 targetSdkVersion 大于等于23(6.0) 需要在代码中进行权限获取 需要在配置文件中申请两个权限: <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="an

  • Android实现蓝牙(BlueTooth)设备检测连接

    无论是WIFI还是4G网络,建立网络连接后都是访问互联网资源,并不能直接访问局域网资源.比如两个人在一起,A要把手机上的视频传给B,通常情况是打开手机QQ,通过QQ传送文件给对方.不过上传视频很耗流量,如果现场没有可用的WIFI,手机的数据流量又不足,那又该怎么办呢?为了解决这种邻近传输文件的问题,蓝牙技术应运而生.蓝牙技术是一种无线技术标准,可实现设备之间的短距离数据交换. Android为蓝牙技术提供了4个工具类,分别是蓝牙适配器BluetoothAdapter.蓝牙设备BluetoothD

  • Android判断服务是否运行及定位问题实例分析

    本文实例讲述了Android判断服务是否运行及定位问题.分享给大家供大家参考.具体如下: /** * 判断服务是否正在运行 * * @param context * @param className 判断的服务名字:包名+类名 * @return true在运行 false 不在运行 */ public static boolean isServiceRunning(Context context, String className) { boolean isRunning = false; A

  • Android 蓝牙连接 ESC/POS 热敏打印机打印实例(蓝牙连接篇)

    公司的一个手机端的 CRM 项目最近要增加小票打印的功能,就是我们点外卖的时候经常会见到的那种小票.这里主要涉及到两大块的知识: 蓝牙连接及数据传输 ESC/POS 打印指令 蓝牙连接不用说了,太常见了,这篇主要介绍这部分的内容.但ESC/POS 打印指令是个什么鬼?简单说,我们常见的热敏小票打印机都支持这样一种指令,只要按照指令的格式向打印机发送指令,哪怕是不同型号品牌的打印机也会执行相同的动作.比如打印一行文本,换行,加粗等都有对应的指令,这部分内容放在下一篇介绍. 本篇主要基于官方文档,相

  • Android蓝牙聊天开源项目

    前言 基于Android Classic Bluetooth的蓝牙聊天软件,目前仅支持一对一实时通信.文件传输.好友添加.好友分组.好友在线状态更新等功能,其中消息发送支持文本.表情等方式. 项目地址:Android蓝牙聊天项目 前景 蓝牙技术作为一种小范围无线连接技术,能够在设备间实现方便快捷.灵活安全.低成本.低功耗的数据和语音通信,是目前实现无线个人局域网的主流技术之一.同时,蓝牙系统以自组式组网的方式工作,每个蓝牙设备都可以在网络中实现路由选择的功能,可以形成移动自组网络.蓝牙的特性在许

  • 详解Android 蓝牙通信方式总结

    1.摘要 Android手机间通过蓝牙方式进行通信,有两种常见的方式,一种是socket方式,另一种是通过Gatt Server(Android 5.0以后)通信,socket方式最为简单,但是很多低功耗的蓝牙设备,如单片机上的蓝牙模块可能不支持:而Gatt方式相对比较复杂.其实无论是socket方式还是Gatt,Android设备间蓝牙通信都是一种C/S(client-server)模式. 本文基于两种通信方式,进行详细展开,并推荐了开源项目,建议配合学习. 关键词 (1)Bluetooth

  • Android蓝牙通信之搜索蓝牙设备

    一:注意事项 1:android6.0使用蓝牙时,需要开启gps定位权限,不然无法搜索其它蓝牙设备. 二:权限 1:权限配置 <!--允许程序连接到已配对的蓝牙设备--> <uses-permission android:name="android.permission.BLUETOOTH" /> <!-- 允许程序发现和配对蓝牙设备 --> <uses-permission android:name="android.permiss

随机推荐