01 · 车机互联(CarPlay · CarLink · AndroidAuto · 小米设备互联)
基类:
connection/controller/BaseVehicleLinkPreferenceController.java互联 SDK:aarcom.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 |
| 跳转 URI | getJumpUri(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_TYPE | doConnect alias | 额外行为 |
|---|---|---|---|
| MobileConnVehiclePreferenceController:29-31 | MIRROR | mirror_setting(:50) | onTitleIconClicked→mirror_guide(:34-43) |
| CarplayLinkPreferenceController:33-35 | CARPLAY | carplay_connect(:40) | onCreateInternal 取 com.mi.car.carplay 真实图标,失败 fallback drawable(:57-64) |
| CarLinkPreferenceController:33-35 | CARLINK | carlink_connect(:40) | 同上,包名 com.mi.car.carlink |
| AndroidAutoPreferenceController:29-31 | ANDROID_AUTO | android_auto_connect(:36) | 无图标覆盖 |
URI 常量 ConnectionConstants.kt:5-10。mis://complex/connect?action=xxx 由目标 app 自己的 Activity 捕获,Settings 仅发 ACTION_VIEW,无 fallback UI(ActivityNotFoundException 只打日志,:51-53)。
2. 能力判断依据(核心,对抗核实纠正笔误)
入口可见性 = 对应 app 是否预装(PackageUtils.isPkgInstalled,本质 getPackageInfo)。不是 SystemProperty/feature/config。
| 方法 | 位置 | 判断包名 | 入口文案(直抄 strings.xml) |
|---|---|---|---|
| isMiuiShareSupported | ConnectionUtils.kt:35-38 | com.xiaomi.mitv.smartshare | ”小米设备互联” / “连接同账号的小米手机” |
| isCarPlaySupported | ConnectionUtils.kt:41-47 | com.mi.car.carplay 或 com.desaysv.carplay | ”Apple CarPlay” / “连接 iPhone” |
| isCarLinkSupported | ConnectionUtils.kt:50-53 | com.mi.car.carlink | ”ICCOA Carlink” / “连接 Android 手机” |
| isAndroidAutoSupported | ConnectionUtils.kt:56-59 | com.mi.car.androidauto | ”Android Auto” / “连接 Android 手机” |
| isPhoneCarLinkSupported | ConnectionUtils.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_DEFAULT | 0 | 无连接 |
| CONNECT_TYPE_MIRROR | 1 | 小米设备互联 |
| CONNECT_TYPE_CARPLAY | 2 | CarPlay |
| CONNECT_TYPE_CARLINK | 3 | CarLink |
| CONNECT_TYPE_ANDROID_AUTO | 4 | AndroidAuto |
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 | 同设备 CarPlay | MiuiShare | AndroidAuto |
|---|---|---|---|---|
| 配对/连接 | 影响 | 影响 | 不影响 | 不影响 |
| 删除设备 | 不影响 | 影响 | 影响 | 影响(且主动断开防回连) |
| 断开设备 | — | — | 影响 | 影响 |
- CarLink 不依赖具体蓝牙设备(任何蓝牙操作都视为”冲替”)
- CarPlay 按蓝牙 mac 匹配
- MiuiShare/AndroidAuto 与蓝牙解耦
- Settings 可反向控制:
MisComplexSdk.disconnectByBluetooth(connInfo.type)(如 :208,246,320,383)
7. 设计要点与坑
- 外部 app 依赖导致动态能力:能力=包名预装;卸载 app 入口移除(lazy 不刷新,需重启 Settings);跳转无 fallback。
- MisComplexSdk 跨进程 AIDL:存在 onBinderDied/onServiceDisconnected,断线清空;快速连续连接/断开 UI 可能滞后(mUIHandler.post 异步)。
- init 参数:Settings 不接管 mirror/carwith 业务,UI 状态完全取决于 aar 通知及时性。
- 状态分发用全局 info:假设同时仅一种互联 active,若多种并存可能显示错乱。