Android 蓝牙开发——基础开发(三)

news2024/11/24 11:14:57

         蓝牙开发这部分主要以 APP 端调用功能接口为开始,到 Framework 端的调用流程,最后到调用状态机结束,不涉及蓝牙协议栈的开发分析。

一、BluetoothAdapter

1、APP获取适配器

蓝牙权限

<mainifest>
    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
</mainifest>

获取蓝牙适配器

        所有的蓝牙 Activity 都是需要 BluetoothAdapter 的。获取 BluetoothAdapter 调用BluetoothAdapter 的静态方法 getDefaultAdapter() 方法。会返回一个表示设备自身的蓝牙适配器(蓝牙无线装置)的 BluetoothAdapter。如果返回 null 则说明该设备不支持蓝牙。

BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();

        此时可以调用 BluetoothAdapter 中的各种方法实现蓝牙的基本操作了。 

2、源码分析

BluetoothAdapter 创建及获取

public static final String BLUETOOTH_MANAGER_SERVICE = "bluetooth_manager";
private final IBluetoothManager mManagerService;
 
public static synchronized BluetoothAdapter getDefaultAdapter() {
    if (sAdapter == null) {
        sAdapter = createAdapter(BluetoothManager.resolveAttributionSource(null));
    }
    return sAdapter;
}
 
public static BluetoothAdapter createAdapter(AttributionSource attributionSource) {
    IBinder binder = ServiceManager.getService(BLUETOOTH_MANAGER_SERVICE);
    if (binder != null) {
        return new BluetoothAdapter(IBluetoothManager.Stub.asInterface(binder), attributionSource);
    } else {
        Log.e(TAG, "Bluetooth binder is null");
        return null;
    }
}
 
BluetoothAdapter(IBluetoothManager managerService, AttributionSource attributionSource) {
    mManagerService = Objects.requireNonNull(managerService);
    mAttributionSource = Objects.requireNonNull(attributionSource);
    synchronized (mServiceLock.writeLock()) {
        mService = getBluetoothService(mManagerCallback);
    }
    mLeScanClients = new HashMap<LeScanCallback, ScanCallback>();
    mToken = new Binder(DESCRIPTOR);
}

        可以看到首先在我们调用的 getDefaultAdapter 方法里去创建 Adapter,然后在 BluetoothAdapter 的构造方法里去调用 getBluetoothService 方法得到 mService,且 mService 的类型为 IBluetooth,这是一个 aidl 的接口,他的实现实在 AdapterService.java 中。

源码位置:

/system/bt/service/common/android/bluetooth/IBluetooth.aidl

/packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService.java

        继续查看 BluetoothAdapter 源码,很多功能都是通过 mService 去调用 AdapterService 中的方法实现的。

常用方法

方法名描述
enable打开蓝牙功能
disable关闭蓝牙功能
isEnable判断蓝牙功能是否打开
startDiscovery开始搜索周围的蓝牙设备
cancelDiscovery取消搜索操作
isDiscovering判断当前是否正在搜索设备
getBondedDevices获取己绑定的设备列表
setName设置本机的蓝牙名称
getName获取本机的蓝牙名称
getAddress获取本机的蓝牙地址
getRemoteDevice根据蓝牙地址获取远程的蓝牙设备
getState获取本地蓝牙适配器的状态

Adapter属性变更

        协议栈蓝牙属性发生变化,通过 AdapterProperties.java 中 adapterPropertyChangedCallback 回调函数通知上来。(此变更为车机侧属性变更)

static final int BT_PROPERTY_BDNAME = 0x01; // 车机蓝牙设备名称修改

static final int BT_PROPERTY_BDADDR = 0x02; // 车机蓝牙设备地址修改

static final int BT_PROPERTY_UUIDS = 0x03; // 车机蓝牙设备支持的协议更新

static final int BT_PROPERTY_CLASS_OF_DEVICE = 0x04; // 不常用

static final int BT_PROPERTY_ADAPTER_SCAN_MODE = 0x07; // 车机蓝牙设备可见性更新

static final int BT_PROPERTY_ADAPTER_BONDED_DEVICES = 0x08; // 返回车机蓝牙设备当前有多少配对成功的设备

static final int BT_PROPERTY_ADAPTER_DISCOVERABLE_TIMEOUT = 0x09; // 不常用

二、蓝牙开关On/Off

1、APP调用流程

 1、BtSettings App:蓝牙配置和连接管理应用,包括蓝牙界面加载、蓝牙搜索、蓝牙连接、蓝牙重命名等管理功能。

2、BluetoothAdapter:车机侧蓝牙适配器,App 调用 FW 的主要接口文件,提供基本的蓝牙任务接口,例如:启动/取消设备扫描、查询/绑定/移除配对设备、实例化 BluetoothDevice等。

3、BluetoothManagerService:管理蓝牙开关和服务的状态,例如:屏蔽多次点击开关,同步蓝牙状态变更等。

4、AdapterService:BluetoothAdapter 的实现类,FW核心业务逻辑类。实现提供给App的接口。

2、代码流程解析

        通过上面的分析,蓝牙 Activity 都是通过调用 BluetoothAdapter 中的方法实现对应功能,所以我们直接从 BluetoothAdapter 开始分析。

1)BluetoothAdapter.enable()

        通过 BluetoothAdapter 调用蓝牙开关接口,实现蓝牙开关功能,这里以打开蓝牙的代码为例。

public boolean enable() {
    if (isEnabled()) {
        if (DBG) {
            Log.d(TAG, "enable(): BT already enabled!");
        }
        return true;
    }
    try {
        return mManagerService.enable(mAttributionSource);
    } catch (RemoteException e) {
        Log.e(TAG, "", e);
    }
     return false;
}

         可以看到最后调用的是 mManagerService.enable(),通过代码可以看到mManagerService为IBluetoothManager.aidl 接口,所以实际调用的是 BluetoothManagerService.java 的 enable()。

2)BluetoothManagerService.enable()

class BluetoothManagerService extends IBluetoothManager.Stub {
 
    public boolean enable(AttributionSource attributionSource) throws RemoteException {
        final String packageName = attributionSource.getPackageName();
        if (!checkBluetoothPermissions(attributionSource, "enable", true)) {
            return false;
        }
 
        final int callingUid = Binder.getCallingUid();
        final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID;
        if (!callerSystem && !isEnabled() && mWirelessConsentRequired
                && startConsentUiIfNeeded(packageName,
            callingUid, BluetoothAdapter.ACTION_REQUEST_ENABLE)) {
            return false;
        }
 
        synchronized (mReceiver) {
            mQuietEnableExternal = false;
            mEnableExternal = true;
            // waive WRITE_SECURE_SETTINGS permission check
            sendEnableMsg(false,
            BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, packageName);
        }
        return true;
    }
}

        这里通过 sendEnableMsg 最终会走到 handleEnable 方法,而 handleEnable 方法我们在蓝牙服务启动流程 中分析最终会调用 AdapterService 中对应的方法。

3)AdapterService.enable()

@Override
public boolean enable(boolean quietMode, AttributionSource attributionSource) {
    AdapterService service = getService();
    if (service == null || !callerIsSystemOrActiveUser(TAG, "enable") || !Utils.checkConnectPermissionForDataDelivery(
        service, attributionSource, "AdapterService enable")) {
        return false;
    }
    return service.enable(quietMode);
}
 
public synchronized boolean enable(boolean quietMode) {
    mQuietmode = quietMode;
    mAdapterStateMachine.sendMessage(AdapterState.BLE_TURN_ON);
    return true;
}

        可以看到 AdapterService enable() 最后调用了 AdapterState 状态机。

4)AdapterState 状态机迁移流程

源码位置:packages\apps\Bluetooth\src\com\android\bluetooth\btservice\AdapterState.java

蓝牙On时,状态机迁移流程: off -> TurningBleOn -> BleOn -> TurningOn -> On

蓝牙Off时,状态机迁移流程:On -> TurningOff -> BleOn -> TurningBleOff -> Off

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/168467.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

3.ESP32-S2/S3 USB 挂载SPI-SD,当作U盘使用,无线U盘

使用的 IDF_4.4 C语言开发 1.ESP32-S2/S3 USB烧录 输出日志 2.ESP32-S2/S3 USB 挂载内部Flash&#xff0c;当作U盘使用&#xff0c;无线U盘 3.ESP32-S2/S3 USB 挂载SPI-SD&#xff0c;当作U盘使用&#xff0c;无线U盘 目录1.打开 usb_msc_wireless_disk 工程 Confinguration2.…

数学建模-数学规划(Matlab)

目录 一、线性规划求解 二、非线性规划问题 三、整数规划&#xff08;包括0-1规划&#xff09; 四、最大最小化模型 五、多目标规划模型 注意&#xff1a;代码文件仅供参考&#xff0c;一定不要直接用于自己的数模论文中国赛对于论文的查重要求非常严格&#xff0c;代码雷同…

微服务Spring Boot 整合 Redis 实现 UV 数据统计

文章目录⛄引言一、HyperLoglog基础用法⛅HyperLoglog 基本语法、命令⚡HyperLoglog 命令完成功能实现二、UV统计 测试百万数据的统计☁️什么是UV统计⚡使用SpringBoot单元测试进行测试百万数据统计⛵小结⛄引言 本文参考黑马 点评项目 在各个项目中&#xff0c;我们都可能需…

DaVinci:限定器 - HSL

调色页面&#xff1a;限定器Color&#xff1a;Qualifier限定器 - HSL Qualifier - HSL根据色相、饱和度和亮度等来选择画面上的对应区域&#xff0c;从而将二级调色与修饰限制在一定的范围。选择范围Selection Range拾取器Picker在检视器画面上按住并拖动&#xff0c;可以选择相…

Linux FHS结构

FHS是Filesystem Hierarchy Standard&#xff08;文件系统层次化标准&#xff09;的缩写&#xff0c;多数Linux版本采用这种文件组织形式&#xff0c;类似于Windows操作系统中c盘的文件目录&#xff0c;FHS采用树形结构组织文件。FHS定义了系统中每个区域的用途、所需要的最小构…

格式化输出

1、golang不同输出语句的区别&#xff1a; 特点PrintPrintlnPrintf输出内容到控制台&#xff08;终端输出&#xff09;SprintSprintlnSprintf输出内容为字符串FprintFprintlnFprintf输出内容到文件特点 输出内容不会换行。 不能格式化字符串。 输出内容换行,。 不能格式化字符…

【C语言进阶】枚举与联合体

目录一&#xff1a;枚举1.1&#xff1a;枚举类型的定义&#xff1a;1.1&#xff1a;枚举的优点&#xff1a;1.2&#xff1a;枚举的使用&#xff1a;二&#xff1a;联合&#xff08;共用体&#xff09;2.1&#xff1a;联合类型的定义&#xff1a;2.2&#xff1a;联合类型的特点&…

cin、cin.getline(arr, size)、getline(cin, str)

一、cin使用空白&#xff08;空格、制表符、换行符&#xff09;来确定字符串的截止位置 注意下方这段代码 使用cin来接收姓名和甜点名&#xff0c;当我的名字长度大于一个单词长度时&#xff0c;cin直接按空格进行接收&#xff0c;将我输入的xiao接收到name中&#xff0c;wei接…

swiftUI coreml deeplabv3去除背景

现在手机的性能越来越好&#xff0c;好多深度学习的框架都能能够跑在手机上。因此就集成一下一个官方的深度学习model试一下。 其他的框架生成的模型都能通过相应的工具转换成mlmodel用&#xff0c;转换也比较简单。 下面以替换图像去背景为例&#xff0c;不过官方模型这个效果…

概论_第3章_二维随机变量__边缘概率密度

边缘概率密度是二维随机变量中的重点内容&#xff0c; 经常作为一个重要的考点&#xff0c; 必须掌握。一 定义对二维随机变量(X, Y) ,分量X, 或者Y的概率密度称为 (X, Y)的边缘概率密度&#xff0c;简称边缘密度&#xff0c;记为 或者 。边缘密度 或者 可由 二维随机变量的密…

什么是pod(容器组)

pod&#xff08;容器组&#xff09; 术语中英文对照&#xff1a; 英文全称英文缩写中文翻译PodPod容器组ContainerContainer容器ControllerController控制器 什么是 Pod 容器组&#xff1f; Pod&#xff08;容器组&#xff09;是 Kubernetes 中最小的可部署单元。一个 Pod&a…

基于FPGA的UDP 通信(六)

引言 前文链接&#xff1a; 基于FPGA的UDP 通信&#xff08;一&#xff09; 基于FPGA的UDP 通信&#xff08;二&#xff09; 基于FPGA的UDP 通信&#xff08;三&#xff09; 基于FPGA的UDP 通信&#xff08;四&#xff09; 基于FPGA的UDP 通信&#xff08;五&#xff09;…

【Spring6源码・AOP】AOP源码解析

上一篇《【Spring6源码・AOP】代理对象的创建》&#xff0c;我们知道了代理是如何创建的&#xff0c;那么它又是如何工作的呢&#xff1f; 创建完代理对象之后&#xff0c;最终&#xff0c;会真正的执行我们的目标方法&#xff0c;但是步入该方法&#xff0c;会进入cglib代理类…

ET框架关于opCode的理解

因为所有的网络消息在发送时候格式都是这样 对于用Protobuf定义的每一消息类型class&#xff0c;都需要定义一个对应消息头code在发送的时候&#xff0c;先将消息体进行序列化&#xff0c;再将code进行序列化&#xff0c;进行组装发送 //这个代码没有进行过优化&#xff0c;会产…

大衣哥给儿媳买回来烟花,是准备加入河南炮击山东大战吗

自从取得抗疫阶段性胜利后&#xff0c;国人就再也难以按捺激动的心情&#xff0c;都想通过放烟花以示庆祝。河南山东属于搭界的两个省&#xff0c;最近就因为放烟花&#xff0c;闹出来不小的笑话&#xff0c;有人甚至戏称炮击事件。 事情的起因是这样的&#xff0c;河南因为地处…

maven的build节点配置

虽然一直在使用maven&#xff0c;但是对于maven的配置还没有深入的了解过。本文以build节点为切入点&#xff0c;主要解释相关maven打包使用到的一些基础配置。 文章目录build节点常用插件spring-boot-maven-pluginmaven-jar-pluginmaven-dependency-plugin注意事项build节点 …

基于javaweb的会议管理系统源码+数据库,javaEE会议管理系统源码

guihaiyidao_git 介绍 javaEE工程 普通的javaEE工程&#xff0c;用idea打开工程即可运行 服务器用的是Tomcat 8.5.722 数据库用的Oracle xe版 数据库可视化工具使用的是PLSQL 相关软件 需要可从百度网盘中获取 链接&#xff1a;https://pan.baidu.com/s/1ZrmfsvQEA4dIP0GF_p…

CSS 布局 - 水平 垂直对齐

CSS 布局 - 水平 & 垂直对齐 那么怎样设置居中对齐呢? 我们可以用margin: auto来设置水平居中对其元素。auto可以防止 元素的宽高溢出&#xff0c;而且也可以平均分配两边的空白。 举例说明: .center {padding: 23px;border: 5px solid red;width: 41%;margin: auto; }那…

【数据结构】单向链表的原理及实现

1.什么是单链表 链表里的数据是以节点的方式表示的&#xff0c;每一个结点的组成是由&#xff1a;元素指针来组成的&#xff0c;元素就是存储数据里的存储单元&#xff0c;指针就是用来连接每一个结点的地址数据。这个以结点的序列来表示线性表被称作为单链表。 单链表是一种…

牛客寒假算法集训营1 补题

标题迷惑大赏 A、World Final? World Cup! 题目描述 众所周知&#xff0c;2022年是四年一度的世界杯年&#xff0c;那么当然要整点足球题。本题需要你模拟一次点球大战。 假设对战双方为A和B&#xff0c;则点球大战中双方会按照ABABABABAB方式来罚点球&#xff0c;即两队交…