01 · 车机互联(CarPlay · CarLink · AndroidAuto · 小米设备互联)

基类:connection/controller/BaseVehicleLinkPreferenceController.java 互联 SDK:aar com.xiaomi.phonecarlink:complex:0.1.40

1. 架构

BaseVehicleLinkPreferenceController(:21-96)抽象了手车互联入口四子类共享职责:

抽象点实现行号
入口点击统一委派onRightIconClicked/onRightTextClicked/onPreferenceClicked 全→doConnect():74-87
子类填两洞getCurrentConnectType() 返回 CONNECT_TYPE_*;doConnect() 跳目标:89-91
状态刷新updateConnectStatus() 从 MisComplexSdk.getConnectInfo() 取,type 匹配则显示 displayName:57-67
外部回调入口onConnectStateChange(int, ConnectInfo) 由 ConnectionSettingsTopFragment 调度:69-72
跳转 URIgetJumpUri(alias) = mis://complex/connect?action=<alias>:93-95
classDiagram
    class BaseVehicleLinkPreferenceController {
        #getCurrentConnectType() int
        #doConnect()
        +onConnectStateChange(type, info)
        -updateConnectStatus()}
    class CarplayLinkPreferenceController {
        +getCurrentConnectType() CONNECT_TYPE_CARPLAY
        +doConnect() carplay_connect}
    class CarLinkPreferenceController {
        +getCurrentConnectType() CONNECT_TYPE_CARLINK
        +doConnect() carlink_connect}
    class AndroidAutoPreferenceController {
        +getCurrentConnectType() CONNECT_TYPE_ANDROID_AUTO
        +doConnect() android_auto_connect}
    class MobileConnVehiclePreferenceController {
        +getCurrentConnectType() CONNECT_TYPE_MIRROR
        +doConnect() mirror_setting
        +onTitleIconClicked() mirror_guide}
    BaseVehicleLinkPreferenceController <|-- CarplayLinkPreferenceController
    BaseVehicleLinkPreferenceController <|-- CarLinkPreferenceController
    BaseVehicleLinkPreferenceController <|-- AndroidAutoPreferenceController
    BaseVehicleLinkPreferenceController <|-- MobileConnVehiclePreferenceController

四子类差异

子类CONNECT_TYPEdoConnect alias额外行为
MobileConnVehiclePreferenceController:29-31MIRRORmirror_setting(:50)onTitleIconClicked→mirror_guide(:34-43)
CarplayLinkPreferenceController:33-35CARPLAYcarplay_connect(:40)onCreateInternal 取 com.mi.car.carplay 真实图标,失败 fallback drawable(:57-64)
CarLinkPreferenceController:33-35CARLINKcarlink_connect(:40)同上,包名 com.mi.car.carlink
AndroidAutoPreferenceController:29-31ANDROID_AUTOandroid_auto_connect(:36)无图标覆盖

URI 常量 ConnectionConstants.kt:5-10mis://complex/connect?action=xxx 由目标 app 自己的 Activity 捕获,Settings 仅发 ACTION_VIEW无 fallback UIActivityNotFoundException 只打日志,:51-53)。

2. 能力判断依据(核心,对抗核实纠正笔误)

入口可见性 = 对应 app 是否预装(PackageUtils.isPkgInstalled,本质 getPackageInfo)。不是 SystemProperty/feature/config

方法位置判断包名入口文案(直抄 strings.xml)
isMiuiShareSupportedConnectionUtils.kt:35-38com.xiaomi.mitv.smartshare”小米设备互联” / “连接同账号的小米手机”
isCarPlaySupportedConnectionUtils.kt:41-47com.mi.car.carplaycom.desaysv.carplay”Apple CarPlay” / “连接 iPhone”
isCarLinkSupportedConnectionUtils.kt:50-53com.mi.car.carlink”ICCOA Carlink” / “连接 Android 手机”
isAndroidAutoSupportedConnectionUtils.kt:56-59com.mi.car.androidauto”Android Auto” / “连接 Android 手机”
isPhoneCarLinkSupportedConnectionUtils.kt:134-138四者任一整组显隐

⚠️ 对抗核实纠正:调研 Agent 曾把 CarPlay 第二包名转述为 com.desaysv.carlink实际是 com.desaysv.carplay(德赛西威 CarPlay 实现,直读 ConnectionUtils.kt:45 确认)。

by lazy 进程内只查一次 → 安装/卸载 app 后需重启 Settings 入口才更新(潜在不一致)。PackageUtils.isPkgInstalled 不带 MATCH_UNINSTALLED_PACKAGES、不做 enabled 检查,disabled 但 installed 的包仍判”支持” → 可能入口可见但点击无响应。

蓝牙侧 CarPlay 设备识别(per-device)

isCarPlaySupportDevice(device)(ConnectionUtils.kt:101-107)看蓝牙 EIR UUID 2d8d2466-e14d-451c-88bc-7301abea291a(CARPLAY_128UUID:30-31,苹果公开 CarPlay 128-bit Service UUID)。

3. 互联状态刷新(MisComplexSdk 回调→UI)

MisComplexSdk 是外部 aar,对 IMisComplex AIDL service 的封装,实际连接业务跑在外部 app 进程。init(ctx, false, false)app/.../AsyncThreadStartTask.java:217-220(两 bool=withMirror/withCarWith,Settings 都传 false,仅作 listener/caller——真正连接握手、设备发现都在外部 app 进程)。

ComplexConstant 类型枚举(反编译确认)

常量含义
CONNECT_TYPE_DEFAULT0无连接
CONNECT_TYPE_MIRROR1小米设备互联
CONNECT_TYPE_CARPLAY2CarPlay
CONNECT_TYPE_CARLINK3CarLink
CONNECT_TYPE_ANDROID_AUTO4AndroidAuto

LAST_COMPLEX_CONNECT_TYPE = "last_complex_connect_type"(Settings.Secure key)。

sequenceDiagram
    participant App as 外部 app 进程
    participant Aar as MisComplexSdk aar
    participant Top as ConnectionSettingsTopFragment
    participant Ctl as BaseVehicleLinkPreferenceController[4]
    participant UI as BasicPreference
    Note over App: 手机连接 CarPlay
    App->>Aar: onComplexConnected(ConnectInfo{type=CARPLAY})
    Aar->>Top: onComplexConnected(info) (:127)
    Top->>Ctl: notifyConnectStateChange 遍历 (:141-148)
    Ctl->>Aar: getConnectInfo() (:59)
    alt info.type == getCurrentConnectType()
        Ctl->>UI: updateRightText(displayName)
    else 不匹配
        Ctl->>UI: updateRightText("")
    end

关键设计updateConnectStatus全局 getConnectInfo() 而非回调 info,前提假设同一时刻只有一种互联 active。四个 controller 都收同一次回调,各自 getCurrentConnectType 做”是不是我”过滤。onBinderDied/onServiceDisconnected→notifyConnectStateChange(null)→全清空。

4. 各入口点击行为

flowchart TD
    MS["小米设备互联"] -->|点右箭头 doConnect| URI1["mis://...action=mirror_setting<br/>getHostFragment.startActivity"]
    MS -->|点标题图标| URI2["mis://...action=mirror_guide (使用引导)"]
    CP[CarPlay] --> URI3["mis://...action=carplay_connect<br/>getActivity.startActivity"]
    CL[CarLink] --> URI4["mis://...action=carlink_connect"]
    AA[AndroidAuto] --> URI5["mis://...action=android_auto_connect"]
    URI1 & URI3 & URI4 & URI5 --> Ext[外部互联 app<br/>com.mi.car.carplay 等捕获]
    Ext -. ActivityNotFoundException .-> NoFB[无 fallback, 仅日志]

点击行为完全依赖目标 app 已安装,与入口可见性(包名判断)耦合一致——理论上能看到就能跳,但用户/PM 卸载 app 会入口可见但点击无响应。

5. CarPlay 类型选择二次弹窗(蓝牙配对联动)

详见 02-配对流程 §6。要点:CarPlay 设备配对成功后弹 MiCarplayConnectTypeChooseDialogActivity 选”蓝牙连接”还是”切换到 CarPlay”。判定条件两层:isCarPlaySupported()(ROM) + isCarPlaySupportDevice(device)(UUID) + 配对成功 + 非 MIS(!isBluetoothBondingByMe)。选 CarPlay→setLastConnectTypeCarplay(写 SP=2) + requestConnectedCarplay(btMac) 发 deeplink connection://carplay&bt_mac=

6. 复合连接与蓝牙操作联动(ConnectionUtils.kt:180-403)

蓝牙操作CarLink同设备 CarPlayMiuiShareAndroidAuto
配对/连接影响影响不影响不影响
删除设备不影响影响影响影响(且主动断开防回连)
断开设备影响影响
  • CarLink 不依赖具体蓝牙设备(任何蓝牙操作都视为”冲替”)
  • CarPlay 按蓝牙 mac 匹配
  • MiuiShare/AndroidAuto 与蓝牙解耦
  • Settings 可反向控制:MisComplexSdk.disconnectByBluetooth(connInfo.type)(如 :208,246,320,383)

7. 设计要点与坑

  1. 外部 app 依赖导致动态能力:能力=包名预装;卸载 app 入口移除(lazy 不刷新,需重启 Settings);跳转无 fallback。
  2. MisComplexSdk 跨进程 AIDL:存在 onBinderDied/onServiceDisconnected,断线清空;快速连续连接/断开 UI 可能滞后(mUIHandler.post 异步)。
  3. init 参数:Settings 不接管 mirror/carwith 业务,UI 状态完全取决于 aar 通知及时性。
  4. 状态分发用全局 info:假设同时仅一种互联 active,若多种并存可能显示错乱。