蓝牙是一种无线技术标准,可实现设备间短距离数据交换 蓝牙可以以一定的周期发送广播,手机端接收到广播后,解析广播包,可做设备识别、配对,事件通知以及指令控制等低精度定位根据设备的信号强度,可以估算出大概方位和距离,接下来我们就来聊聊关于安卓车载系统的源码方案?以下内容大家不妨参考一二希望能帮到您!

安卓车载系统的源码方案(Android车载技术之蓝牙通讯)

安卓车载系统的源码方案

蓝牙是一种无线技术标准,可实现设备间短距离数据交换。 蓝牙可以以一定的周期发送广播,手机端接收到广播后,解析广播包,可做设备识别、配对,事件通知以及指令控制等。低精度定位根据设备的信号强度,可以估算出大概方位和距离。

蓝牙通信前置条件:

首先,要操作蓝牙,先要在AndroidManifest.xml里加入权限 <uses-permissionandroid:name="android.permission.Bluetooth_ADMIN" /> <uses-permissionandroid:name="android.permission.BLUETOOTH" />

除了蓝牙权限外,如果需要ble feature则还需要声明uses-feature:

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/> 按时required为true时,则应用只能在支持BLE的Android设备上安装运行;required为false时,Android设备均可正常安装运行,需要在代码运行时判断设备是否支持BLE feature:

通信流程:

发现设备->配对/绑定设备->建立连接->数据通信

发现设备

经典蓝牙设备发现其它经典蓝牙设备的方式是调用BluetoothAdapter的startDiscovery()方法,这个方法只能够发现经典蓝牙设备。

低功耗蓝牙中则有一个主设备(Central)和从设备(Peripheral,也叫外围设备)的概念。主设备作为发现方(一般为手机)调用发现设备的方法,通过BluetoothAdapter的startLeScan()方法实现。从设备则作为被发现方(穿戴设备,便携设备等),发出广播,以供发现。同样,这个startLeScan()方法也仅能够发现低功耗蓝牙从设备,在整个搜索过程中,功耗是比较大的,应尽快结束搜索。

发现设备比较重要,实践中有2个比较重要的点:

1 、经典蓝牙配对前,如果没有扫描过程,通过蓝牙地址直接配对,配对对话框可能不出现,以消息通知栏的方式呈现。如果扫描过,则会弹出配对对话框。 2 、ble连接前,如果没有扫描过程,通过蓝牙地址直接连接,可能连不上。如果扫描并发现外围设备,则会连接成功。

配对/绑定

配对指的是BLE设备的配对。配对的作用在于和设备做相互确认,一方面是确定要操作的设备,另一方面是考虑到安全因素。 经典蓝牙可通过createRfcommSocketToServiceRecord 和 Method creMethod = BluetoothDevice.class.getMethod("createBond"); creMethod.invoke(mBluetoothDevice);

ble蓝牙在连接的过程中 自动配对,但不弹出配对对话框。

建立连接

在建立连接的方式上,两者就千差万别了。

蓝牙小知识

在蓝牙设备中,存在着物理地址,我们也叫作蓝牙的MAC地址,这个地址是唯一的,就像咱们网络上的IP地址。同时还存在着一个叫做UUID的东西,可以把它理解为是IP地址中的端口号。正如知道了IP地址和端口号,就知道了怎么链接到目标网络服务器位置,知道了蓝牙设备的MAC地址和UUID也就能够确定到具体是哪一台蓝牙设备了,这两者合起来就是蓝牙的唯一身份标识。BLE扫描的过程,设备会生成随机地址。根据随机地址也能进行连接。

经典蓝牙建立连接的方式实际上就是Socket的连接的建立。只不过这里不是直接用Socket,而是BluetoothSocket。获取BluetoothSocket的方式也很简单,利用搜索找到的BluetoothDevice,调用其方法createRfcommSocketToServiceRecord(UUID)。最后,使用获取到的BluetoothDevice调用其方法connect()就建立了经典蓝牙设备之间的连接通道。

蓝牙连接耳机播放音乐

A2dp的连接过程,在蓝牙搜索结果列表连接一个蓝牙耳机,既然是从设备列表开始,所以起步代码自然是这个了

DevicePickerFragment.java (settings\src\com\android\settings\bluetooth) 3884 2013-6-26 void onClicked() { int bondState = mCachedDevice.getBondState(); if (mCachedDevice.isConnected()) { askDisconnect(); } else if (bondState == BluetoothDevice.BOND_BONDED) { mCachedDevice.connect(true); } ....... } void connect(boolean connectAllProfiles) { if (!ensurePaired()) { //要先确保配对 return; } mConnectAttempted = SystemClock.elapsedRealtime(); connectWithoutResettingTimer(connectAllProfiles);//没别的了,只能看到这里 }

代码路径这里packages/apps/Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java,具体代码看下面

// Try to initialize the profiles if they were not. ........... // Reset the only-show-one-error-dialog tracking variable mIsConnectingErrorPossible = true; int preferredProfiles = 0; for (LocalBluetoothProfile profile : mProfiles) { if (connectAllProfiles ? profile.isConnectable() : profile.isAutoConnectable()) { if (profile.isPreferred(mDevice)) { preferredProfiles; connectInt(profile);//连接在这里, } } } .............

connectInt的实现很简单,直接跳过看里面的profile.connect(mDevice),这里的profile是指A2dpProfile,所以connet()方法的具体实现在

public boolean connect(BluetoothDevice device) { if (mService == null) return false; List<BluetoothDevice> sinks = getConnectedDevices(); if (sinks != null) { for (BluetoothDevice sink : sinks) { mService.disconnect(sink); }} return mService.connect(device); }

下面是 BluetoothA2dp.java (frameworks\base\core\java\android\bluetooth) ,为什么是这样看下这个private BluetoothA2dp mService;就知道了

public boolean connect(BluetoothDevice device) { if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.connect(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" Log.getStackTraceString(new Throwable())); return false; } }........... return false; Binder跳转 public boolean connect(BluetoothDevice device) { A2dpService service = getService(); if (service == null) return false; return service.connect(device); } }

之后的跳转和第一部分蓝牙接听电话跳转过程类似,就不重复了,最后会来到packages/apps/Bluetooth/jni/com_android_bluetooth_a2dp.cpp的connectA2dpNative,同样到下面的代码,我们能看到的开放的代码也就是这些,再下面要看vendor的具体实现了。

static jboolean connectA2dpNative(JNIEnv *env, jobject object, jbyteArray address) { jbyte *addr; bt_bdaddr_t * btAddr; bt_status_t status; ALOGI("%s: sBluetoothA2dpInterface: %p", __FUNCTION__, sBluetoothA2dpInterface); if (!sBluetoothA2dpInterface) return JNI_FALSE; addr = env->GetByteArrayElements(address, NULL); btAddr = (bt_bdaddr_t *) addr; if (!addr) { JNIThrowIOException(env, EINVAL); return JNI_FALSE; } if ((status = sBluetoothA2dpInterface->connect((bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) { ALOGE("Failed HF connection, status: %d", status); } env->ReleaseByteArrayElements(address, addr, 0); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; }

本文主要讲了车载中的蓝牙通信基础,如何连接蓝牙耳机代码实现。车载除了这有许多要学习的。车载学习资料可以私信“手册” 获取方式;有BYD车载高级工程师拟制。初步学习感觉内容讲的很细致。这里推荐一下。

【私信获取】

比喻:
  • 串口

  • DLNA

  • Automotive

  • 车载进程通信

  • CarLauncher

  • 车载多媒体

  • 车载空调系统

  • 车载系统开发

    等等。。。

    具体学习路线图:

    Android车载是未来10年的黄金赛道,把握住这次机会就能稳住程序员的饭碗,所以学习是必要的。