【android bluetooth 案例分析 04】【Carplay 详解 2】【Carplay 连接之手机主动连车机】

1. 背景

【android bluetooth 案例分析 04】【Carplay 详解 1】【CarPlay 在车机侧的蓝牙通信原理与角色划分详解】中我们从整理上介绍了车机中 carplay 相关基础概念。 本节 将详细分析 iphone手机主动 连接 车机carplay 这一过程。

先回顾一下 上一节, carplay 整个流程:

  1. EIR 广播识别

    • iPhone 开启 EIR 广播,包含 UUID_DEVICE_CARPLAY_EIR 2d8d2466-e14d-451c-88bc-7301abea291a
    • 车机通过蓝牙扫描识别支持 CarPlay 的 iPhone
  2. 蓝牙连接

    • 没有配对,需要先配对
    • iPhone 主动连接车机的 SPP Server UUID (UUID_IAP_ACCESSORY 00000000-deca-fade-deca-deafdecacaff
      • 车机需要先 listenUsingRfcommWithServiceRecord
    • 车机作为 Client 主动连接 iPhone 暴露的 Server UUID
      • (需 iPhone 开启EIR 广播)
  3. IAP2 协议交互

    • 交换设备信息、认证令牌、能力参数(支持哪种 Wi-Fi 架构)
  4. Wi-Fi 建链

    • 手机连接车机热点,或车机连接手机热点,完成 IP 建立
  5. TCP & mDNS 发现 CarPlay 服务

    • 建立 TCP 通信,寻找 _carplay._tcp.local 服务,启动 CarPlay Session
  6. 启动投屏/音频/导航服务

上面总过分为 6 步: 但是涉及到蓝牙的只有 1 、 2、 3 步。 那我们就来分别来介绍一下 在当前 手机主动连车机的清晰。这三步是如何具体实操的。

2. 手机主动连车机

1. EIR 广播

EIR 广播识别:

  1. 车机开启 EIR 广播, 包含 00000000-deca-fade-deca-deafdecacaffec884348-cd41-40a2-9727-575d50bf1fd3
    • 手机就可以扫描到 车机支持 carplay
  2. iPhone 开启 EIR 广播,包含 00000000-deca-fade-deca-deafdecacafe2d8d2466-e14d-451c-88bc-7301abea291a
    • 车机通过蓝牙扫描识别支持 CarPlay 的 iPhone

1. 手机 开启 EIR 广播

当 iphone 手机进入 设置 -> 通用 -> Carplay 车载 界面时, 此时,车机如果开启 扫描。此时就能从 扫描到的 EIR 中看到如下的广播信息。

c 复制代码
300	2025-01-01 19:40:57.096358	controller	host	HCI_EVT	258	Rcvd Extended Inquiry Result

Bluetooth HCI Event - Extended Inquiry Result
    Event Code: Extended Inquiry Result (0x2f)
    Parameter Total Length: 255
    Number of responses: 1
    BD_ADDR: Apple_7c:81:36 (84:ad:8d:7c:81:36)
    Page Scan Repetition Mode: R1 (0x01)
    Reserved: 0x00
    Class of Device: 0x7a020c (Phone:Smartphone - services: Networking Capturing ObjectTransfer Audio Telephony)
    .110 1101 0010 1011 = Clock Offset: 0x6d2b
    RSSI: -38 dBm
    Extended Inquiry Response Data
        Device Name: Jxl
            Length: 4
            Type: Device Name (0x09)
            Device Name: Jxl
        16-bit Service Class UUIDs
            Length: 15
            Type: 16-bit Service Class UUIDs (0x03)
            UUID 16: PnP Information (0x1200)
            UUID 16: Handsfree Audio Gateway (0x111f)
            UUID 16: Phonebook Access Server (0x112f)
            UUID 16: Audio Source (0x110a)
            UUID 16: A/V Remote Control Target (0x110c)
            UUID 16: Message Access Server (0x1132)
            UUID 16: Generic Attribute Profile (0x1801)
        32-bit Service Class UUIDs
            Length: 1
            Type: 32-bit Service Class UUIDs (0x05)
        128-bit Service Class UUIDs
            Length: 49
            Type: 128-bit Service Class UUIDs (0x07)
            Custom UUID: 00000000-deca-fade-deca-deafdecacafe (Unknown)
            Custom UUID: 02030302-1d19-415f-86f2-22a2106a0a77 (Unknown)
            Custom UUID: 2d8d2466-e14d-451c-88bc-7301abea291a (Unknown)  // 此时手机 回复的 uuid 中就包含, 识别手机支持 carplay 的 uuid
        Manufacturer Specific
            Length: 39
            Type: Manufacturer Specific (0xff)
            Company ID: Unknown (0x4c00)
            Data: 022402000000000000000000000000000000000000000000000000000000000000000000
                [Expert Info (Note/Undecoded): Undecoded]
                    [Undecoded]
                    [Severity level: Note]
                    [Group: Undecoded]
        Unused

2d8d2466-e14d-451c-88bc-7301abea291a 这个 uuid 就是帮助车机筛选 那些手机支持 carplay 功能的。

在协议栈中 我们会将 该 uuid 上报 到 应用侧。如果不明白 如果上报,可以看一下这篇文章
【android bluetooth 协议分析 03】【蓝牙扫描详解 1】【扫描关键函数 btif_dm_search_devices_evt 分析】

此时在车机 carplay 连接 界面就可以看到 当前 iphone 手机。

2. 车机开启EIR 广播

分析到这里, 有人疑惑,车机是 可以看到 手机支持 carplay. 但是此时 手机 应该看不到 车机支持carplay 功能吧。确实是这样子?

我们可以参照手机的操作。 将 carplay 的 uuid 写入 车机的 EIR 中。 这样手机在扫描时 , 就可以扫描 到车机的 EIR. 这块手机就可以发现 车机支持 carplay.

如下是 app 侧的操作 流程:

java 复制代码
    /*我们代码添加IAP的uuid,车机的BT模块里自动添加CP的uuid*/
    //IAP2 uuid 使用此uuid,手机才能发起IAP连接
    private static final UUID SERVER_SPP_UUID = UUID.fromString("00000000-deca-fade-deca-deafdecacaff");
    //CP uuid 使用此uuid,手机CarPlay车载画面能够扫描出来,但是在手机CarPlay车载画面点击车机时,无法连接上
    private static final UUID SERVER_SPP_UUID_CP = UUID.fromString("EC884348-CD41-40A2-9727-575D50BF1FD3");


	BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();

	BluetoothServerSocket mServerSocket = adapter.listenUsingRfcommWithServiceRecord("com.running.android.carplay", SERVER_SPP_UUID);

	BluetoothSocket mClientSocket = mServerSocket.accept();
  1. 先调用 listenUsingRfcommWithServiceRecord 方法,在 该 listenUsingRfcommWithServiceRecord 中,就会将 00000000-deca-fade-deca-deafdecacaff 和 EC884348-CD41-40A2-9727-575D50BF1FD3 两个 uuid. 写入到我们的 eir 中。 并创建 一个 spp server.
  2. mServerSocket.accept(); 等待 手机来连接。 手机连接后,将会返回一个新的 socket.
  3. 拿到这个 socket. 手机和车机就可以 交互 carplay 连接需要的信息。例如 wifi 热点名字,密码等。
c 复制代码
105	2025-01-01 19:40:31.420798	host	controller	HCI_CMD	245	Sent Write Extended Inquiry Response


Frame 105: 245 bytes on wire (1960 bits), 245 bytes captured (1960 bits)
Bluetooth
Bluetooth HCI H4
Bluetooth HCI Command - Write Extended Inquiry Response
    Command Opcode: Write Extended Inquiry Response (0x0c52)
    Parameter Total Length: 241
    FEC Required: true (1)
    Extended Inquiry Response Data
        Device Name: xxxxxx
        16-bit Service Class UUIDs
            Length: 13
            Type: 16-bit Service Class UUIDs (0x03)
            UUID 16: Audio Sink (0x110b)
            UUID 16: A/V Remote Control Target (0x110c)
            UUID 16: A/V Remote Control (0x110e)
            UUID 16: Handsfree (0x111e)
            UUID 16: Phonebook Access Client (0x112e)
            UUID 16: PnP Information (0x1200)
        32-bit Service Class UUIDs
            Length: 1
            Type: 32-bit Service Class UUIDs (0x05)
        128-bit Service Class UUIDs
            Length: 129
            Type: 128-bit Service Class UUIDs (0x07)
            Custom UUID: 00000000-deca-fade-deca-deafdecacaff (Unknown) // 这个是 app 侧下发的 uuid
            Custom UUID: ec884348-cd41-40a2-9727-575d50bf1fd3 (Unknown) // 这个我们根据 app 下发的uuid 在协议栈中我们任务加的
            Custom UUID: 00000000-0000-0000-0000-000000000000 (Unknown)
            Custom UUID: 00000000-0000-0000-0000-000000000000 (Unknown)
            Custom UUID: 00000000-0000-0000-0000-000000000000 (Unknown)
            Custom UUID: 00000000-0000-0000-0000-000000000000 (Unknown)
            Custom UUID: 00000000-0000-0000-0000-000000000000 (Unknown)
            Custom UUID: 00000000-0000-0000-0000-000000000000 (Unknown)
        Tx Power Level
            Length: 2
            Type: Tx Power Level (0x0a)
            Power Level (dBm): 8
        Unused
    [Response in frame: 106]
    [Command-Response Delta: 28.115ms]
  • 此时 我们的手机就可以正确的扫描到 我们的车机也支持 carplay。

2. 蓝牙连接 iap 并通信

没有配对,需要先配对 : 这个是常规操作, 这里不分享了。

这里主要分析一下。 手机 主动连接 车机 iap 的过程:

  • 手机向 车机发起了 sdp , 这里手机向我们查询的 uuid. 正是我们前面注册的 00000000-deca-fade-deca-deafdecacaff
  • 车机回复了 对应的 rfcomm 通道为 3

手机继续 主动查询 车机之前注册的 UUID ec884348-cd41-40a2-9727-575d50bf1fd3

这里车机回复了该uuid 对应的 rfcomm 通道为 4

手机 主动 请求连接车机的 rfcomm channel 3

接下来 车机和手机 通过 channel 3 开始将 后面 carplay 连接需要的 wifi 相关的 热点名字,密码发送给手机。

3. 总结

手机 如果要主动连接车机carplay

车机需要 完成如下步骤:

  1. 车机 app 侧需要 通过如下代码:
    • 开启 车机 EIR 广播:广播 00000000-deca-fade-deca-deafdecacaffec884348-cd41-40a2-9727-575d50bf1fd3 这样 在手机carplay 界面才可以看到车机。
    • 向 sdp 数据库中注册 上述 两个uuid. 这样 手机主动发起 上述两个服务的uuid 时, 车机才知道如何回复。
c 复制代码
	BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();

	// 开启 EIR 和 注册 SDP 数据库,都是通过该调用实现的
	BluetoothServerSocket mServerSocket = adapter.listenUsingRfcommWithServiceRecord("com.running.android.carplay", SERVER_SPP_UUID);
  1. 车机carplay app. 等待 手机连接
c 复制代码
BluetoothSocket mClientSocket = mServerSocket.accept(); // 当手机主动连接时, 这里将返回,一个新的 socket. 用于 iap 内容交互
  1. 此时 手机 carplay 界面就可以扫描到 车机, 点击界面发起 对车机的主动连接:

    • 此时会看到 手机主动向 车机发起 00000000-deca-fade-deca-deafdecacaffec884348-cd41-40a2-9727-575d50bf1fd3 两个服务的 SDP 查询。
    • 车机返回对应的 rfcomm 通道号。 例如 00000000-deca-fade-deca-deafdecacaff iap协议, rfcomm channel= 3
  2. 手机主动发起 rfcomm channel 3 , iap 协议的连接。

  3. 车机和手机,此时就可以通过 iap 协议交互,后续 carplay 连接所需要的 wifi 热点信息。

  4. 交互完成后,断开rfcomm channel3

  5. 发起 真正的 carplay 连接, 此时就可以看到 手机将画面 投屏到车机上。

这里的重点是 调用 listenUsingRfcommWithServiceRecord 函数。

该函数的讲解,将在另外的 文章中单独讲解。

相关推荐
yan123685 小时前
Linux 驱动之设备树
android·linux·驱动开发·linux驱动
aningxiaoxixi7 小时前
android stdio 的布局属性
android
CYRUS STUDIO8 小时前
FART 自动化脱壳框架一些 bug 修复记录
android·bug·逆向·fart·脱壳
寻找优秀的自己9 小时前
Cocos 打包 APK 兼容环境表(Android API Level 10~15)
android·cocos2d
大胃粥10 小时前
WMS& SF& IMS: 焦点窗口更新框架
android
QING61810 小时前
Gradle 核心配置属性详解 - 新手指南(二)
android·前端·gradle
QING61810 小时前
Gradle 核心配置属性详解 - 新手指南(一)
android·前端·gradle
_一条咸鱼_13 小时前
Android Runtime内存管理子系统启动流程原理(13)
android·面试·android jetpack
法迪13 小时前
Android的uid~package~pid的关系
android