当前位置: 首页 > news >正文

Android车载开发基础学习——蓝牙通信是如何实现的?

概述

蓝牙是一种工作在免费的ISM频段的短距离无线通信技术,在各种设备之间实现灵活、安全、低成本、低功耗的语音和数据通信。它采用自适应跳频技术,可以和多种无线通信共存于ISM频段,与同用于短距离无线通信的Zigbee和UWB相比,蓝牙协议和标准更加完善,设备间一致性和互连通性好,而且以Profile的形式定义了具体应用的实现方式,从而保证了兼容性。

蓝牙通讯原理

蓝牙通讯分为三个阶段:

  • 1)设备发现和连接
  • 2)鉴权
  • 3)应用层信息服务

第一阶段,设备发现与连接。

在这个阶段,TBox定期广播蓝牙设备的地址。主设备搜索到此前用户通过TSP绑定的TBox蓝牙MAC地址列表中的其中一个后,主动请求蓝牙设备进行连接。蓝牙主设备根据信号TBox广播的信号强度,连接TBox并且建立数据通道。

第二阶段, 鉴权。

这个阶段,使主设备与TBox相互确认身份。

第三阶段,请求服务。

这个阶段,允许通过蓝牙通信请求服务。

TBOX有两路数据通道:通道1用于主设备与TBOX蓝牙模块的一级鉴权,通道2用于主设备与TBOX的MCU通讯。

Tbox的BLE模组和主设备交互流程图:

蓝牙广播的内容为蓝牙名称和服务的UUID,广播周期为2s。

主设备通过通道1发送Booking信息,TBox收到通道1主设备的BookingID后,如果ID号在白名单列表中,则认为通道一级鉴权,需要在通道1回复主设备信息并开始执行二级鉴权。

TBox收到通道1主设备的BookingID后,如果ID号在不在白名单中列表中,则认为一级鉴权失败,需要在通道1回复主设备信息,然后断开连接。

车载蓝牙通信实现

蓝牙电话

蓝牙电话主要用到BluetoothHeadsetClient这个类里面定义了很多广播意图,最有用的是这个action

/**

\* Intent sent whenever state of a call changes.

*

*

It includes:

\* {@link #EXTRA_CALL},

\* with value of {@link BluetoothHeadsetClientCall} instance,

\* representing actual call state.

*/

public static final String ACTION_CALL_CHANGED =

"android.bluetooth.headsetclient.profile.action.AG_CALL_CHANGED";

它监听来电,接听来电,去电,通话中等状态,要想在车载设备中操作电话需要知道这些状态。

private final BroadcastReceiver mReceiver = new BroadcastReceiver() {

@Override

public void onReceive(Context context, Intent intent) {

if (null != intent) {

String action = intent.getAction();

Log.i(TAG, "BTService receiver action == "+action);

//监听来电

if (BluetoothHeadsetClient.ACTION_CALL_CHANGED.equals(action)) {

BluetoothHeadsetClientCall mCall = (BluetoothHeadsetClientCall) intent.getExtra(BluetoothHeadsetClient.EXTRA_CALL, null);

if (mCall != null) {

int callState = mCall.getState();

Log.d(TAG, "when call status changes: mConnStat is " + mConnStat+" number == "+mCall.getNumber());

if (callState == BluetoothHeadsetClientCall.CALL_STATE_INCOMING) {

//来电

} else if (callState == BluetoothHeadsetClientCall.CALL_STATE_DIALING) {

//去电

} else if (callState == BluetoothHeadsetClientCall.CALL_STATE_ACTIVE) {

//接听中

} else if (callState == BluetoothHeadsetClientCall.CALL_STATE_TERMINATED) {

//结束

}

}

}

看下它的构造方法

BluetoothHeadsetClient(Context context, ServiceListener l) {

mContext = context;

mServiceListener = l;

mAdapter = BluetoothAdapter.getDefaultAdapter();

IBluetoothManager mgr = mAdapter.getBluetoothManager();

if (mgr != null) {

try {

mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);

} catch (RemoteException e) {

Log.e(TAG,"",e);

}

}

doBind();

}

mBluetoothStateChangeCallback监听蓝牙打开或关闭状态,重点看下doBind方法

boolean doBind() {

Intent intent = new Intent(IBluetoothHeadsetClient.class.getName());

ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);

intent.setComponent(comp);

if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,

android.os.Process.myUserHandle())) {

Log.e(TAG, "Could not bind to Bluetooth Headset Client Service with " + intent);

return false;

}

return true;

}

其实就是去绑定一个service

private final ServiceConnection mConnection = new ServiceConnection() {

@Override

public void onServiceConnected(ComponentName className, IBinder service) {

if (DBG) Log.d(TAG, "Proxy object connected");

mService = IBluetoothHeadsetClient.Stub.asInterface(Binder.allowBlocking(service));

if (mServiceListener != null) {

mServiceListener.onServiceConnected(BluetoothProfile.HEADSET_CLIENT,

BluetoothHeadsetClient.this);

}

}

@Override

public void onServiceDisconnected(ComponentName className) {

if (DBG) Log.d(TAG, "Proxy object disconnected");

mService = null;

if (mServiceListener != null) {

mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET_CLIENT);

}

}

};

当服务连接时返回IBluetoothHeadsetClient并通知协议请求成功,这是aidl的客户端,不了解aidl的去查看一下android跨进程通信相关知识。

/**

\* Connects to remote device.

*

\* Currently, the system supports only 1 connection. So, in case of the

\* second connection, this implementation will disconnect already connected

\* device automatically and will process the new one.

*

\* @param device a remote device we want connect to

\* @return true if command has been issued successfully;

\* false otherwise;

\* upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED}

\* intent.

*/

public boolean connect(BluetoothDevice device) {

if (DBG) log("connect(" + device + ")");

final IBluetoothHeadsetClient service = mService;

if (service != null && isEnabled() && isValidDevice(device)) {

try {

return service.connect(device);

} catch (RemoteException e) {

Log.e(TAG, Log.getStackTraceString(new Throwable()));

return false;

}

}

if (service == null) Log.w(TAG, "Proxy not attached to service");

return false;

}

连接设备就是调用服务端的connect方法。服务端的实现在packages\apps\Bluetooth\src\com\android\bluetooth\hfpclient\HeadsetClientService.java

接听电话

/**

\* Accepts a call

*

\* @param device remote device

\* @param flag action policy while accepting a call. Possible values

\* {@link #CALL_ACCEPT_NONE}, {@link #CALL_ACCEPT_HOLD},

\* {@link #CALL_ACCEPT_TERMINATE}

\* @return true if command has been issued successfully;

\* false otherwise;

\* upon completion HFP sends {@link #ACTION_CALL_CHANGED}

\* intent.

*/

public boolean acceptCall(BluetoothDevice device, int flag)

拨打电话

/**

\* Places a call with specified number.

*

\* @param device remote device

\* @param number valid phone number

\* @return {@link BluetoothHeadsetClientCall} call if command has been

\* issued successfully;

\* {@link null} otherwise;

\* upon completion HFP sends {@link #ACTION_CALL_CHANGED}

\* intent in case of success; {@link #ACTION_RESULT} is sent

\* otherwise;

*/

public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number)

拒接接听

/**

\* Rejects a call.

*

\* @param device remote device

\* @return true if command has been issued successfully;

\* false otherwise;

\* upon completion HFP sends {@link #ACTION_CALL_CHANGED}

\* intent.

*

*

Feature required for successful execution is being reported by:

\* {@link #EXTRA_AG_FEATURE_REJECT_CALL}.

\* This method invocation will fail silently when feature is not supported.

*/

public boolean rejectCall(BluetoothDevice device)

挂断电话

/**

\* Rejects a call.

*

\* @param device remote device

\* @return true if command has been issued successfully;

\* false otherwise;

\* upon completion HFP sends {@link #ACTION_CALL_CHANGED}

\* intent.

*

*

Feature required for successful execution is being reported by:

\* {@link #EXTRA_AG_FEATURE_REJECT_CALL}.

\* This method invocation will fail silently when feature is not supported.

*/

public boolean rejectCall(BluetoothDevice device)

发送DTMF编码

/**

\* Sends DTMF code.

*

\* Possible code values : 0,1,2,3,4,5,6,7,8,9,A,B,C,D,*,#

*

\* @param device remote device

\* @param code ASCII code

\* @return true if command has been issued successfully;

\* false otherwise;

\* upon completion HFP sends {@link #ACTION_RESULT} intent;

*/

public boolean sendDTMF(BluetoothDevice device, byte code)

根据自己的需求直接调用对应的方法,这里只列举了常用的.

本篇主要介绍车载开发中的基础,了解到车载开发基础的蓝牙通信原理以及举例蓝牙电话通信的示例实现。

Android车载的开发现在很吃香,想入行车载行业也是不错的选择。想学习更多的Android车载技术可以前往《Android车载技术手册》领取;由BYD高级车载开发整理制定的。我这里免费分享出来,能够刷到我这篇文章的可谓是赚到了一笔。

文末

现在的小轿车越来越多,几乎人人都拥有。以至于现在新能源的倡导;以后更换的车辆也会逐渐增多。现在入行车载系统开发来看;确实是很不错的前景。

相关文章:

  • 智慧政务、数字化优先与数字机器人,政务领域正在开启“政务新视界”
  • DES算法是对称算法吗,能否通过在线工具进行DES解密?
  • 【车间调度】基于GA/PSO/SA/ACO/TS优化算法的车间调度比较(Matlab代码实现)
  • springBoot 的默认线程池-ThreadPoolTaskExecutor
  • 暑期结束为你的新学期立下Flag吧
  • 大数字符串加法
  • ROS1云课→28机器人代价地图配置
  • 设计新鲜事(News Feed)系统
  • 【气动学】基于matlab GUI弹道问题(含初始角度、速度、空气阻力、水平风)【含Matlab源码 2117期】
  • 力扣leetcode 1619. 删除某些元素后的数组均值
  • 嵌入式入门-交叉编译、bootloader、kernel、根文件系统关系
  • Google Earth Engine(GEE)——快速建立一个10km的格网
  • 关于微信学习的网站
  • 微服务项目:尚融宝(38)(核心业务流程:申请借款额度(1))
  • 八股文复习
  • __proto__ 和 prototype的关系
  • Laravel核心解读--Facades
  • MySQL数据库运维之数据恢复
  • nodejs实现webservice问题总结
  • Python连接Oracle
  • Python语法速览与机器学习开发环境搭建
  • 经典排序算法及其 Java 实现
  • 前端临床手札——文件上传
  • 微服务入门【系列视频课程】
  • 为视图添加丝滑的水波纹
  • 怎样选择前端框架
  • 转载:[译] 内容加速黑科技趣谈
  • 第二十章:异步和文件I/O.(二十三)
  • 说说我为什么看好Spring Cloud Alibaba
  • ​虚拟化系列介绍(十)
  • #大学#套接字
  • (13):Silverlight 2 数据与通信之WebRequest
  • (9)STL算法之逆转旋转
  • (第二周)效能测试
  • (附源码)spring boot网络空间安全实验教学示范中心网站 毕业设计 111454
  • (论文阅读32/100)Flowing convnets for human pose estimation in videos
  • (论文阅读笔记)Network planning with deep reinforcement learning
  • (亲测成功)在centos7.5上安装kvm,通过VNC远程连接并创建多台ubuntu虚拟机(ubuntu server版本)...
  • (未解决)macOS matplotlib 中文是方框
  • (转)Android学习系列(31)--App自动化之使用Ant编译项目多渠道打包
  • (转)一些感悟
  • *setTimeout实现text输入在用户停顿时才调用事件!*
  • .net core 6 集成和使用 mongodb
  • .NET Windows:删除文件夹后立即判断,有可能依然存在
  • .NET 常见的偏门问题
  • .NET 命令行参数包含应用程序路径吗?
  • .NET/C# 判断某个类是否是泛型类型或泛型接口的子类型
  • .NET开源项目介绍及资源推荐:数据持久层 (微软MVP写作)
  • /etc/sudoer文件配置简析
  • @ModelAttribute使用详解
  • @RequestMapping 的作用是什么?
  • @selector(..)警告提示
  • @vue/cli 3.x+引入jQuery
  • [ C++ ] template 模板进阶 (特化,分离编译)
  • []我的函数库