CarService 深度解析 —— 基于 Android 14

CarService 深度解析 ------ 基于 Android 14

上一篇聊了 AAOS 整体架构,这篇我们接着往下拆,聚焦到整个系统里最核心的一个进程------CarService。理解它基本就理解了车载 Android 的一半。这次从 Android 14 的源码出发,把 CarService 从启动到运行的完整链路过一遍。

CarService 在整个系统里是什么角色

先说定位。AAOS 系统里有三个关键进程:
#mermaid-svg-54MdV19y8qv6TlS9{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-54MdV19y8qv6TlS9 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-54MdV19y8qv6TlS9 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-54MdV19y8qv6TlS9 .error-icon{fill:#552222;}#mermaid-svg-54MdV19y8qv6TlS9 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-54MdV19y8qv6TlS9 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-54MdV19y8qv6TlS9 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-54MdV19y8qv6TlS9 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-54MdV19y8qv6TlS9 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-54MdV19y8qv6TlS9 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-54MdV19y8qv6TlS9 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-54MdV19y8qv6TlS9 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-54MdV19y8qv6TlS9 .marker.cross{stroke:#333333;}#mermaid-svg-54MdV19y8qv6TlS9 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-54MdV19y8qv6TlS9 p{margin:0;}#mermaid-svg-54MdV19y8qv6TlS9 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-54MdV19y8qv6TlS9 .cluster-label text{fill:#333;}#mermaid-svg-54MdV19y8qv6TlS9 .cluster-label span{color:#333;}#mermaid-svg-54MdV19y8qv6TlS9 .cluster-label span p{background-color:transparent;}#mermaid-svg-54MdV19y8qv6TlS9 .label text,#mermaid-svg-54MdV19y8qv6TlS9 span{fill:#333;color:#333;}#mermaid-svg-54MdV19y8qv6TlS9 .node rect,#mermaid-svg-54MdV19y8qv6TlS9 .node circle,#mermaid-svg-54MdV19y8qv6TlS9 .node ellipse,#mermaid-svg-54MdV19y8qv6TlS9 .node polygon,#mermaid-svg-54MdV19y8qv6TlS9 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-54MdV19y8qv6TlS9 .rough-node .label text,#mermaid-svg-54MdV19y8qv6TlS9 .node .label text,#mermaid-svg-54MdV19y8qv6TlS9 .image-shape .label,#mermaid-svg-54MdV19y8qv6TlS9 .icon-shape .label{text-anchor:middle;}#mermaid-svg-54MdV19y8qv6TlS9 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-54MdV19y8qv6TlS9 .rough-node .label,#mermaid-svg-54MdV19y8qv6TlS9 .node .label,#mermaid-svg-54MdV19y8qv6TlS9 .image-shape .label,#mermaid-svg-54MdV19y8qv6TlS9 .icon-shape .label{text-align:center;}#mermaid-svg-54MdV19y8qv6TlS9 .node.clickable{cursor:pointer;}#mermaid-svg-54MdV19y8qv6TlS9 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-54MdV19y8qv6TlS9 .arrowheadPath{fill:#333333;}#mermaid-svg-54MdV19y8qv6TlS9 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-54MdV19y8qv6TlS9 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-54MdV19y8qv6TlS9 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-54MdV19y8qv6TlS9 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-54MdV19y8qv6TlS9 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-54MdV19y8qv6TlS9 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-54MdV19y8qv6TlS9 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-54MdV19y8qv6TlS9 .cluster text{fill:#333;}#mermaid-svg-54MdV19y8qv6TlS9 .cluster span{color:#333;}#mermaid-svg-54MdV19y8qv6TlS9 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-54MdV19y8qv6TlS9 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-54MdV19y8qv6TlS9 rect.text{fill:none;stroke-width:0;}#mermaid-svg-54MdV19y8qv6TlS9 .icon-shape,#mermaid-svg-54MdV19y8qv6TlS9 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-54MdV19y8qv6TlS9 .icon-shape p,#mermaid-svg-54MdV19y8qv6TlS9 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-54MdV19y8qv6TlS9 .icon-shape .label rect,#mermaid-svg-54MdV19y8qv6TlS9 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-54MdV19y8qv6TlS9 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-54MdV19y8qv6TlS9 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-54MdV19y8qv6TlS9 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} OEM Native 进程
com.android.car 进程
system_server 进程
bind
AIDL IPC
ActivityManagerService
WindowManagerService
PackageManagerService
CarServiceHelperService

桥接层
CarService

车载业务中枢
VehicleHal
CarPropertyService
CarAudioService
CarPowerManagementService
...其他子 Service
VHAL 实现
CAN/ETH 总线
ECU

SystemServer 管 Android 通用的事,CarService 管车载专用的事,VHAL 管跟车通信的事。CarService 夹在中间,是承上启下的那一层。

CarService 跟 SystemServer 之间通过一个叫 CarServiceHelperService 的轻量级 Service 做桥接。这个 Helper 跑在 system_server 进程里,职责非常简单------就是帮 system_server 启动 CarService 进程,以及在 CarService 崩溃时重启它。除此之外它不做任何车载业务逻辑。
#mermaid-svg-f5OV9jwqtVg7aRWH{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-f5OV9jwqtVg7aRWH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-f5OV9jwqtVg7aRWH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-f5OV9jwqtVg7aRWH .error-icon{fill:#552222;}#mermaid-svg-f5OV9jwqtVg7aRWH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-f5OV9jwqtVg7aRWH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-f5OV9jwqtVg7aRWH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-f5OV9jwqtVg7aRWH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-f5OV9jwqtVg7aRWH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-f5OV9jwqtVg7aRWH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-f5OV9jwqtVg7aRWH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-f5OV9jwqtVg7aRWH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-f5OV9jwqtVg7aRWH .marker.cross{stroke:#333333;}#mermaid-svg-f5OV9jwqtVg7aRWH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-f5OV9jwqtVg7aRWH p{margin:0;}#mermaid-svg-f5OV9jwqtVg7aRWH .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-f5OV9jwqtVg7aRWH .cluster-label text{fill:#333;}#mermaid-svg-f5OV9jwqtVg7aRWH .cluster-label span{color:#333;}#mermaid-svg-f5OV9jwqtVg7aRWH .cluster-label span p{background-color:transparent;}#mermaid-svg-f5OV9jwqtVg7aRWH .label text,#mermaid-svg-f5OV9jwqtVg7aRWH span{fill:#333;color:#333;}#mermaid-svg-f5OV9jwqtVg7aRWH .node rect,#mermaid-svg-f5OV9jwqtVg7aRWH .node circle,#mermaid-svg-f5OV9jwqtVg7aRWH .node ellipse,#mermaid-svg-f5OV9jwqtVg7aRWH .node polygon,#mermaid-svg-f5OV9jwqtVg7aRWH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-f5OV9jwqtVg7aRWH .rough-node .label text,#mermaid-svg-f5OV9jwqtVg7aRWH .node .label text,#mermaid-svg-f5OV9jwqtVg7aRWH .image-shape .label,#mermaid-svg-f5OV9jwqtVg7aRWH .icon-shape .label{text-anchor:middle;}#mermaid-svg-f5OV9jwqtVg7aRWH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-f5OV9jwqtVg7aRWH .rough-node .label,#mermaid-svg-f5OV9jwqtVg7aRWH .node .label,#mermaid-svg-f5OV9jwqtVg7aRWH .image-shape .label,#mermaid-svg-f5OV9jwqtVg7aRWH .icon-shape .label{text-align:center;}#mermaid-svg-f5OV9jwqtVg7aRWH .node.clickable{cursor:pointer;}#mermaid-svg-f5OV9jwqtVg7aRWH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-f5OV9jwqtVg7aRWH .arrowheadPath{fill:#333333;}#mermaid-svg-f5OV9jwqtVg7aRWH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-f5OV9jwqtVg7aRWH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-f5OV9jwqtVg7aRWH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-f5OV9jwqtVg7aRWH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-f5OV9jwqtVg7aRWH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-f5OV9jwqtVg7aRWH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-f5OV9jwqtVg7aRWH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-f5OV9jwqtVg7aRWH .cluster text{fill:#333;}#mermaid-svg-f5OV9jwqtVg7aRWH .cluster span{color:#333;}#mermaid-svg-f5OV9jwqtVg7aRWH div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-f5OV9jwqtVg7aRWH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-f5OV9jwqtVg7aRWH rect.text{fill:none;stroke-width:0;}#mermaid-svg-f5OV9jwqtVg7aRWH .icon-shape,#mermaid-svg-f5OV9jwqtVg7aRWH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-f5OV9jwqtVg7aRWH .icon-shape p,#mermaid-svg-f5OV9jwqtVg7aRWH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-f5OV9jwqtVg7aRWH .icon-shape .label rect,#mermaid-svg-f5OV9jwqtVg7aRWH .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-f5OV9jwqtVg7aRWH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-f5OV9jwqtVg7aRWH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-f5OV9jwqtVg7aRWH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Binder IPC
com.android.car 进程内
CarService
VehicleHal
CarPropertyService
CarAudioService
...
system_server 进程内
AMS / PMS / WMS
CarServiceHelper

仅桥接+重启

启动流程 ------ CarService 是什么时候起来的

CarService 的启动不像普通 App 那样由 AMS 调度,它走的是 system_server 里的 CarServiceHelperService 主动 bind 的路径。时序图大概是这样的:
CarService CarServiceHelperService SystemServer Zygote Kernel CarService CarServiceHelperService SystemServer Zygote Kernel #mermaid-svg-Yp226VgpEi7Xde6s{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Yp226VgpEi7Xde6s .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Yp226VgpEi7Xde6s .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Yp226VgpEi7Xde6s .error-icon{fill:#552222;}#mermaid-svg-Yp226VgpEi7Xde6s .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Yp226VgpEi7Xde6s .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Yp226VgpEi7Xde6s .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Yp226VgpEi7Xde6s .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Yp226VgpEi7Xde6s .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Yp226VgpEi7Xde6s .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Yp226VgpEi7Xde6s .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Yp226VgpEi7Xde6s .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Yp226VgpEi7Xde6s .marker.cross{stroke:#333333;}#mermaid-svg-Yp226VgpEi7Xde6s svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Yp226VgpEi7Xde6s p{margin:0;}#mermaid-svg-Yp226VgpEi7Xde6s .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Yp226VgpEi7Xde6s text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-Yp226VgpEi7Xde6s .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Yp226VgpEi7Xde6s .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-Yp226VgpEi7Xde6s .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-Yp226VgpEi7Xde6s .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-Yp226VgpEi7Xde6s #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-Yp226VgpEi7Xde6s .sequenceNumber{fill:white;}#mermaid-svg-Yp226VgpEi7Xde6s #sequencenumber{fill:#333;}#mermaid-svg-Yp226VgpEi7Xde6s #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-Yp226VgpEi7Xde6s .messageText{fill:#333;stroke:none;}#mermaid-svg-Yp226VgpEi7Xde6s .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Yp226VgpEi7Xde6s .labelText,#mermaid-svg-Yp226VgpEi7Xde6s .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-Yp226VgpEi7Xde6s .loopText,#mermaid-svg-Yp226VgpEi7Xde6s .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-Yp226VgpEi7Xde6s .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Yp226VgpEi7Xde6s .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-Yp226VgpEi7Xde6s .noteText,#mermaid-svg-Yp226VgpEi7Xde6s .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-Yp226VgpEi7Xde6s .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Yp226VgpEi7Xde6s .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Yp226VgpEi7Xde6s .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Yp226VgpEi7Xde6s .actorPopupMenu{position:absolute;}#mermaid-svg-Yp226VgpEi7Xde6s .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-Yp226VgpEi7Xde6s .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Yp226VgpEi7Xde6s .actor-man circle,#mermaid-svg-Yp226VgpEi7Xde6s line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-Yp226VgpEi7Xde6s :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 注册 CarServiceHelperService在 system_server 进程内 Zygote forkcom.android.car 进程 系统就绪 Init → fork Zygotefork SystemServerstartBootstrapServices()startCarServiceHelperService()startOtherServices()AMS.systemReady()onSystemReady()bindService(CarService)CarService.onCreate()init VehicleHalinit CarPropertyServiceinit CarAudioServiceinit CarPowerManagementServiceinit ... 其余子 ServiceonCarServiceConnected()

看下 CarServiceHelperService 的源码,其实很薄:

java 复制代码
// packages/services/Car/service/src/com/android/car/CarServiceHelperService.java
public class CarServiceHelperService extends Service {
    private static final String CAR_SERVICE_PACKAGE = "com.android.car";
    private static final String CAR_SERVICE_CLASS =
            "com.android.car.CarService";

    private ICar mCarService = null;
    private boolean mConnected = false;

    @Override
    public void onCreate() {
        // 注册一个 system_service 级别的 watch
        // 如果 CarService 挂了会自动重启
        mServiceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                mCarService = ICar.Stub.asInterface(service);
                mConnected = true;
                Log.i(TAG, "CarService connected");
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                mCarService = null;
                mConnected = false;
                Log.w(TAG, "CarService disconnected, scheduling restart");
                // 延迟 2 秒后重启
                mHandler.postDelayed(() -> bindCarService(), 2000);
            }
        };
    }

    void onSystemReady() {
        bindCarService();
    }

    private void bindCarService() {
        Intent intent = new Intent();
        intent.setClassName(CAR_SERVICE_PACKAGE, CAR_SERVICE_CLASS);
        bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
    }
}

关键点是 onServiceDisconnected 里的重启逻辑------CarService 如果 crash 了,Helper 会在 2 秒后自动重新 bind。再加上 CarService 本身是 persistent 进程(存在系统级白名单里),kill 不掉,所以正常情况下 CarService 是跟系统同生命周期的。

CarService 的内部初始化

CarService 的 onCreate() 才是真正的重头戏。它初始化了 15 个以上的子 Service,每个都有严格的顺序依赖:

java 复制代码
// packages/services/Car/service/src/com/android/car/CarService.java
public class CarService extends Service {
    private final List<CarServiceBase> mServices = new ArrayList<>();
    private VehicleHal mVehicleHal;
    private ICarImpl mICarImpl;

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG, "Starting CarService");

        // 第一步:初始化 VehicleHal ------ 必须先于所有 Service
        mVehicleHal = new VehicleHal(this);
        mServices.add(mVehicleHal);

        // 第二步:依赖 VHAL 的 Service
        mServices.add(new CarPropertyService(mVehicleHal));
        mServices.add(new CarInfoService(mVehicleHal));
        mServices.add(new CarAudioService(this, mVehicleHal));
        mServices.add(new CarPowerManagementService(this, mVehicleHal));
        mServices.add(new CarDiagnosticService(this, mVehicleHal));
        mServices.add(new CarDrivingStateService(this));
        mServices.add(new CarVendorExtensionService(mVehicleHal));

        // 第三步:不依赖 VHAL 的 Service
        mServices.add(new CarBluetoothService(this));
        mServices.add(new CarInputService(this));
        mServices.add(new CarUserService(this));
        mServices.add(new CarPackageManagerService(this));
        mServices.add(new CarProjectionService(this));
        mServices.add(new CarMediaService(this));
        mServices.add(new CarNavigationService(this));
        mServices.add(new CarOccupantZoneService(this));
        mServices.add(new CarStorageMonitoringService(this));
        mServices.add(new CarTrustedDeviceService(this));
        mServices.add(new CarWatchdogService(this));

        // 按顺序调用 init()
        for (CarServiceBase service : mServices) {
            service.init();
        }

        // 将 ICarImpl 注册为 Binder 服务
        mICarImpl = new ICarImpl(this, mServices);
        ServiceManager.addService("car_service", mICarImpl);

        Log.i(TAG, "CarService started, " + mServices.size() + " services loaded");
    }
}

这里有个设计细节:CarServiceBase 接口定义如下:

java 复制代码
// packages/services/Car/service/src/com/android/car/systeminterface/CarServiceBase.java
public interface CarServiceBase {
    void init();
    void release();
}

所有子 Service 统一实现这两个方法。init() 在 CarService 启动时调用,release() 在 CarService 销毁时调用。这种模式比各自暴露生命周期接口要干净得多,也方便统一做异常处理------比如某个 Service 的 init 抛异常了,CarService 可以统一捕获并记录,不会让整个进程挂掉。

Binder 接口 ------ ICar 和 ICarImpl

CarService 暴露给外部的 Binder 接口是 ICar,定义在 packages/services/Car/car-lib/ 里:

aidl 复制代码
// packages/services/Car/car-lib/aidl/android/car/ICar.aidl
package android.car;

interface ICar {
    IBinder getCarManager(in String serviceName);

    boolean isCarService();

    int getCarServiceVersion();

    String getCarServiceVersionName();
}

接口不多,核心就一个 getCarManager()。应用通过这个接口拿到某个具体管理器的 Binder 引用,然后通过强转成具体的 Manager 对象。

实际实现是 ICarImpl

java 复制代码
// packages/services/Car/service/src/com/android/car/ICarImpl.java
class ICarImpl extends ICar.Stub {
    private final Map<String, IBinder> mServiceMap = new HashMap<>();

    ICarImpl(CarService carService, List<CarServiceBase> services) {
        // 注册所有 Manager 的 Binder
        registerService(CarPropertyService.SERVICE_NAME,
                new CarPropertyManagerBinder(carService));
        registerService(CarAudioService.SERVICE_NAME,
                new CarAudioManagerBinder(carService));
        registerService(CarInfoService.SERVICE_NAME,
                new CarInfoManagerBinder(carService));
        registerService(CarBluetoothService.SERVICE_NAME,
                new CarBluetoothManagerBinder(carService));
        // ... 注册全部子 Service 的 Binder
    }

    private void registerService(String name, IBinder binder) {
        mServiceMap.put(name, binder);
    }

    @Override
    public IBinder getCarManager(String serviceName) {
        // 检查调用方权限
        enforceCallingPermission(serviceName);
        return mServiceMap.get(serviceName);
    }
}

应用侧的使用方式:

java 复制代码
// 应用侧 ------ 获取 CarPropertyManager 的完整链路
Car car = Car.createCar(context);
// 这一步会发起 Binder 调用绑定 com.android.car 进程
// car.connect() 成功后
CarPropertyManager pm = (CarPropertyManager) car.getCarManager(
        Car.PROPERTY_SERVICE);
// getCarManager 内部会调用 ICar.getCarManager(SERVICE_NAME)
// 返回 CarPropertyManagerBinder 的 IBinder
// CarPropertyManager 再封装成上层友好的 API

// 最终调用
float speed = pm.getFloat(VehiclePropertyId.VEHICLE_SPEED, 0);

com.android.car 进程 App 进程 com.android.car 进程 App 进程 #mermaid-svg-XovHqM5PU6RUzFWp{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-XovHqM5PU6RUzFWp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-XovHqM5PU6RUzFWp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-XovHqM5PU6RUzFWp .error-icon{fill:#552222;}#mermaid-svg-XovHqM5PU6RUzFWp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XovHqM5PU6RUzFWp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-XovHqM5PU6RUzFWp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XovHqM5PU6RUzFWp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XovHqM5PU6RUzFWp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-XovHqM5PU6RUzFWp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XovHqM5PU6RUzFWp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XovHqM5PU6RUzFWp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XovHqM5PU6RUzFWp .marker.cross{stroke:#333333;}#mermaid-svg-XovHqM5PU6RUzFWp svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XovHqM5PU6RUzFWp p{margin:0;}#mermaid-svg-XovHqM5PU6RUzFWp .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-XovHqM5PU6RUzFWp text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-XovHqM5PU6RUzFWp .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-XovHqM5PU6RUzFWp .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-XovHqM5PU6RUzFWp .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-XovHqM5PU6RUzFWp .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-XovHqM5PU6RUzFWp #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-XovHqM5PU6RUzFWp .sequenceNumber{fill:white;}#mermaid-svg-XovHqM5PU6RUzFWp #sequencenumber{fill:#333;}#mermaid-svg-XovHqM5PU6RUzFWp #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-XovHqM5PU6RUzFWp .messageText{fill:#333;stroke:none;}#mermaid-svg-XovHqM5PU6RUzFWp .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-XovHqM5PU6RUzFWp .labelText,#mermaid-svg-XovHqM5PU6RUzFWp .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-XovHqM5PU6RUzFWp .loopText,#mermaid-svg-XovHqM5PU6RUzFWp .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-XovHqM5PU6RUzFWp .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-XovHqM5PU6RUzFWp .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-XovHqM5PU6RUzFWp .noteText,#mermaid-svg-XovHqM5PU6RUzFWp .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-XovHqM5PU6RUzFWp .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-XovHqM5PU6RUzFWp .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-XovHqM5PU6RUzFWp .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-XovHqM5PU6RUzFWp .actorPopupMenu{position:absolute;}#mermaid-svg-XovHqM5PU6RUzFWp .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-XovHqM5PU6RUzFWp .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-XovHqM5PU6RUzFWp .actor-man circle,#mermaid-svg-XovHqM5PU6RUzFWp line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-XovHqM5PU6RUzFWp :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Binder IPC CarPropertyManager 封装成上层友好 API ICar.getCarManager(serviceName)ICarImpl.enforceCallingPermission()mServiceMap.get(serviceName)返回 CarXxxManagerBinder (IBinder)pm.getFloat(VEHICLE_SPEED, 0)

核心子 Service 逐个看

CarPropertyService ------ 最通用的属性接口

CarPropertyService 是所有车载属性的通用访问入口。它可以读写任何 VehicleProperty,不论这个属性是车速、电量还是空调温度。

java 复制代码
// packages/services/Car/service/src/com/android/car/property/CarPropertyService.java
public class CarPropertyService implements CarServiceBase {
    private final VehicleHal mVehicleHal;
    private final Map<Integer, CarPropertyConfig> mPropertyConfigMap = new ArrayMap<>();

    @Override
    public void init() {
        // 从 VHAL 获取所有支持的属性配置
        VehiclePropConfig[] configs = mVehicleHal.getAllPropConfigs();
        for (VehiclePropConfig config : configs) {
            mPropertyConfigMap.put(config.prop,
                    CarPropertyConfig.createFromVehiclePropConfig(config));
        }
        Log.i(TAG, "Loaded " + mPropertyConfigMap.size() + " vehicle properties");
    }

    // 读取属性 ------ 最终走到 VHAL
    public VehiclePropValue getProperty(int propId, int areaId) {
        enforceAccess(propId, VehiclePropertyAccess.READ);
        return mVehicleHal.get(propId, areaId);
    }

    // 设置属性 ------ 最终走到 VHAL
    public void setProperty(VehiclePropValue value) {
        enforceAccess(value.prop, VehiclePropertyAccess.WRITE);
        enforcePermission(value.prop);  // Android 14: 权限检查下沉
        mVehicleHal.set(value);
    }

    // 订阅属性变化
    public void subscribeProperty(int propId, int areaId, float sampleRate,
            CarPropertyEventListener listener) {
        // 内部维护一个 listener 表
        // 通过 VehicleHal.subscribe() 注册到 VHAL
        // 变化来的时候通过回调分发给所有注册的 listener
    }
}

Android 14 在这里有个重要改动:setProperty 里的权限检查 enforcePermission() 改成了动态的。以前是硬编码表------哪个属性需要哪个权限写死在代码里。现在是从 VHAL 返回的 VehiclePropConfig.configArray 里读取权限要求,OEM 可以动态调整。

CarPowerManagementService ------ 车载电源状态机

车载电源比手机复杂太多了。手机只有 awake/sleep/off,车里要考虑 ACC/Engine/熄火/休眠/唤醒等一堆状态。

Android 14 的 CarPowerManagementService 负责状态协调:

java 复制代码
// packages/services/Car/service/src/com/android/car/power/CarPowerManagementService.java
public class CarPowerManagementService implements CarServiceBase {
    // 底层通过 VHAL 的 AP_POWER_STATE_REQ 接收 VMCU 指令
    // 再通过 CarPowerManager 的 CarPowerStateListener 通知应用层
    // 应用层监听的状态定义在 CarPowerManager 中:
    //   STATE_ON               --- 正常运行
    //   STATE_PRE_SHUTDOWN_PREPARE --- 即将关机,音屏显示还在(片刻准备)
    //   STATE_SHUTDOWN_PREPARE --- 进入 Garage Mode,可执行延时任务
    //   STATE_SHUTDOWN_ENTER   --- 应用清理收尾
    //   STATE_POST_SHUTDOWN_ENTER --- 清理完毕,AP 准备断电
    //   STATE_SHUTDOWN_CANCELLED  --- 关机取消,回到 ON
    //   STATE_SUSPEND_ENTER    --- 准备 Suspend-to-RAM
    //   STATE_POST_SUSPEND_ENTER  --- 准备完毕,进入 Suspend
    //   STATE_SUSPEND_EXIT     --- 从 Suspend 唤醒
    //   STATE_WAIT_FOR_VHAL    --- 启动中,等待 VHAL 就绪
    //   STATE_HIBERNATION_ENTER / _EXIT --- 休眠相关

    private int mCurrentState = CarPowerManager.STATE_ON;

    @Override
    public void init() {
        // 监听 VHAL 的 AP_POWER_STATE_REQ
        // VMCU 通过这个属性下发点火/熄火指令
        mVehicleHal.subscribeProperty(
            VehiclePropertyId.AP_POWER_STATE_REQ,
            0,
            0,
            (VehiclePropValue value) -> {
                int reqState = value.value.int32Values[0];
                int param = value.value.int32Values[1];
                handlePowerRequest(reqState, param);
            }
        );
    }

    private void handlePowerRequest(int reqState, int param) {
        switch (reqState) {
            case VehicleApPowerStateReq.ON:
                transitionToOn();
                break;
            case VehicleApPowerStateReq.SHUTDOWN_PREPARE:
                // param 取值: CAN_SLEEP / CAN_HIBERNATE / SHUTDOWN_ONLY
                // SHUTDOWN_ONLY = 不睡只关,OTA 场景用
                startShutdownPreparation(param);
                break;
            case VehicleApPowerStateReq.CANCEL_SHUTDOWN:
                cancelShutdown();
                break;
            case VehicleApPowerStateReq.FINISHED:
                // VMCU 确认 AP 可以断电了
                enterSuspendOrShutdown();
                break;
        }
    }

    private void startShutdownPreparation(int param) {
        // 1. 广播 STATE_PRE_SHUTDOWN_PREPARE
        notifyListener(CarPowerManager.STATE_PRE_SHUTDOWN_PREPARE);
        // 2. 关闭屏幕和音频
        // 3. 等待应用异步清理(有超时)
        waitForAppShutdown(SHUTDOWN_TIMEOUT_MS);
        // 4. 广播 STATE_SHUTDOWN_ENTER
        notifyListener(CarPowerManager.STATE_SHUTDOWN_ENTER);
        // 5. 通过 VHAL 上报 SHUTDOWN_START
        mVehicleHal.set(createPowerProperty(
                VehiclePropertyId.AP_POWER_STATE_REPORT,
                VehicleApPowerStateReport.SHUTDOWN_START));
    }
}

VHAL 收到 AP_POWER_STATE_REPORT 后通知 VMCU,VMCU 确认后下发 FINISHED,CPMS 再往 /sys/power/statemem(Suspend)或 disk(Hibernate),或者直接断电。

这里需要区分一下:SHUTDOWN_ONLY 不是独立状态,而是 SHUTDOWN_PREPARE 请求携带的参数(VehicleApPowerStateShutdownParam),告诉 AP 这次熄火不能休眠只能关机。这个参数是给 OTA 场景用的------普通熄火可能走 Suspend 快速恢复,但 OTA 升级要求彻底关机后冷启动。

CarAudioService ------ 车载音频路由中心

前面有一篇专门讲过音频,这里只挑 CarService 里跟音频相关的那部分说。

CarAudioService 在初始化时要做一件关键的事:根据配置文件建立 Zone → Context → Bus 的映射关系:

java 复制代码
// packages/services/Car/service/src/com/android/car/audio/CarAudioService.java
public class CarAudioService implements CarServiceBase {
    private CarAudioZone[] mCarAudioZones;
    private AudioManager mAudioManager;

    @Override
    public void init() {
        // 1. 解析 car_audio_configuration.xml
        CarAudioConfiguration config = CarAudioConfiguration.load(this);

        // 2. 创建 AudioZone 对象
        mCarAudioZones = new CarAudioZone[config.getZoneCount()];
        for (int i = 0; i < config.getZoneCount(); i++) {
            mCarAudioZones[i] = new CarAudioZone(config.getZone(i));
        }

        // 3. 注册 AudioPolicy 的动态路由
        // Android 14: 用 setPreferredDeviceForZone 替代了旧的 HIDL 方式
        mAudioManager = getSystemService(AudioManager.class);
        for (CarAudioZone zone : mCarAudioZones) {
            for (CarAudioContext context : zone.getAllContexts()) {
                AudioDeviceInfo device = zone.getPreferredDevice(context);
                if (device != null) {
                    mAudioManager.setPreferredDeviceForZone(
                            zone.getId(),
                            device,
                            context.getAudioAttributes());
                }
            }
        }

        // 4. 注册 VHAL 的音量变化监听
        // 方向盘上的音量加减按键通过 VHAL 上报
        mVehicleHal.subscribeProperty(
            VehiclePropertyId.STEERING_WHEEL_VOLUME_UP, ...);
        mVehicleHal.subscribeProperty(
            VehiclePropertyId.STEERING_WHEEL_VOLUME_DOWN, ...);

        Log.i(TAG, "AudioService initialized: " + mCarAudioZones.length + " zones");
    }
}

CarOccupantZoneService ------ 座位与硬件资源映射

车里每个座位是一个 OccupantZone,每个 Zone 关联一组硬件设备(屏幕、扬声器、麦克风、USB 口等)。Android 14 在音频多区路由(Multi-zone Audio)中深度整合了 OccupantZone:音频配置里的 occupantZoneId 直接对应 CarOccupantZoneService 定义的座位区域,实现用户登录后自动路由音频到对应区域。

java 复制代码
// packages/services/Car/service/src/com/android/car/occupant/CarOccupantZoneService.java
public class CarOccupantZoneService implements CarServiceBase {
    private final List<CarOccupantZone> mOccupantZones = new ArrayList<>();

    @Override
    public void init() {
        // 读取配置
        CarOccupantZoneConfig[] configs = loadOccupantZoneConfig();
        for (int i = 0; i < configs.length; i++) {
            CarOccupantZone zone = new CarOccupantZone(
                    i, /* zone id */
                    configs[i].getType(),  /* DRIVER / FRONT_PASSENGER / REAR_PASSENGER */
                    configs[i].getDisplayForZone(),
                    configs[i].getAudioDeviceForZone()
            );
            mOccupantZones.add(zone);
        }
    }

    // 获取某个驱动的驾驶员区域信息
    public CarOccupantZone getDriverZone() {
        for (CarOccupantZone zone : mOccupantZones) {
            if (zone.getType() == CarOccupantZoneType.DRIVER) {
                return zone;
            }
        }
        return null;
    }

    // 根据屏幕找对应的 OccupantZone
    public CarOccupantZone getZoneForDisplay(int displayId) {
        for (CarOccupantZone zone : mOccupantZones) {
            if (zone.getDisplayId() == displayId) {
                return zone;
            }
        }
        return null;
    }
}

这个 Service 解决了一个实际问题:以前音频、显示、输入都是各自独立配置 zone 的,经常出现司机在副驾屏上操作、或者音频播放到错误区域的问题。OccupantZoneService 把所有跟"座位"相关的硬件资源统一管理起来。

CarWatchdogService ------ 系统健康监控

车载系统对稳定性的要求远高于手机。CarWatchdogService 负责监控关键进程和 Service 的健康状态:

java 复制代码
// packages/services/Car/service/src/com/android/car/watchdog/CarWatchdogService.java
public class CarWatchdogService implements CarServiceBase {
    private static final long IO_WATCHDOG_TIMEOUT_MS = 5000;
    private static final long BLOCKED_THREAD_TIMEOUT_MS = 3000;

    private final Map<String, HealthCheckTarget> mTargets = new HashMap<>();

    @Override
    public void init() {
        // 注册需要监控的组件
        registerHealthCheck(new CarServiceHealthMonitor(this));
        registerHealthCheck(new SystemServerHealthMonitor(this));
        registerHealthCheck(new VHalHealthMonitor(this));
        registerHealthCheck(new MediaServerHealthMonitor(this));

        // 启动 IO 监控线程
        startIOWatchdog();
    }

    private void startIOWatchdog() {
        Thread ioThread = new Thread(() -> {
            while (true) {
                for (HealthCheckTarget target : mTargets.values()) {
                    if (!target.isAlive()) {
                        Log.w(TAG, target.getName() + " not responding");
                        // 收集 dump 信息
                        collectDumpsys(target);
                        // 如果超时则 kill 并重启
                        if (target.getElapsed() > IO_WATCHDOG_TIMEOUT_MS) {
                            recoverTarget(target);
                        }
                    }
                }
                SystemClock.sleep(1000);
            }
        }, "CarIOWatchdog");
        ioThread.setDaemon(true);
        ioThread.start();
    }
}

与 App 的交互方式

CarService 跟 App 的交互主要有三种方式:

1. Binder 直调(同步)

应用通过 Car.getCarManager() 拿到 Binder 引用,直接调用。适用于获取属性值这类需要同步结果的场景。

2. Callback 回调(异步)

应用注册 CarPropertyEventListener,VHAL 属性变化时 CarService 通过 Binder callback 通知应用。

3. Broadcast(系统级通知)

一些全局性的事件(电源状态变化、驾驶状态变化)通过 broadcast 发送。应用在 Manifest 里注册 receiver 或在代码里动态注册。
VHAL CarService App VHAL CarService App #mermaid-svg-pwBfsbKxYIba7d2X{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-pwBfsbKxYIba7d2X .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-pwBfsbKxYIba7d2X .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-pwBfsbKxYIba7d2X .error-icon{fill:#552222;}#mermaid-svg-pwBfsbKxYIba7d2X .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-pwBfsbKxYIba7d2X .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-pwBfsbKxYIba7d2X .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-pwBfsbKxYIba7d2X .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-pwBfsbKxYIba7d2X .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-pwBfsbKxYIba7d2X .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-pwBfsbKxYIba7d2X .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-pwBfsbKxYIba7d2X .marker{fill:#333333;stroke:#333333;}#mermaid-svg-pwBfsbKxYIba7d2X .marker.cross{stroke:#333333;}#mermaid-svg-pwBfsbKxYIba7d2X svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-pwBfsbKxYIba7d2X p{margin:0;}#mermaid-svg-pwBfsbKxYIba7d2X .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-pwBfsbKxYIba7d2X text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-pwBfsbKxYIba7d2X .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-pwBfsbKxYIba7d2X .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-pwBfsbKxYIba7d2X .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-pwBfsbKxYIba7d2X .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-pwBfsbKxYIba7d2X #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-pwBfsbKxYIba7d2X .sequenceNumber{fill:white;}#mermaid-svg-pwBfsbKxYIba7d2X #sequencenumber{fill:#333;}#mermaid-svg-pwBfsbKxYIba7d2X #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-pwBfsbKxYIba7d2X .messageText{fill:#333;stroke:none;}#mermaid-svg-pwBfsbKxYIba7d2X .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-pwBfsbKxYIba7d2X .labelText,#mermaid-svg-pwBfsbKxYIba7d2X .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-pwBfsbKxYIba7d2X .loopText,#mermaid-svg-pwBfsbKxYIba7d2X .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-pwBfsbKxYIba7d2X .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-pwBfsbKxYIba7d2X .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-pwBfsbKxYIba7d2X .noteText,#mermaid-svg-pwBfsbKxYIba7d2X .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-pwBfsbKxYIba7d2X .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-pwBfsbKxYIba7d2X .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-pwBfsbKxYIba7d2X .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-pwBfsbKxYIba7d2X .actorPopupMenu{position:absolute;}#mermaid-svg-pwBfsbKxYIba7d2X .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-pwBfsbKxYIba7d2X .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-pwBfsbKxYIba7d2X .actor-man circle,#mermaid-svg-pwBfsbKxYIba7d2X line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-pwBfsbKxYIba7d2X :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 同步调用 ------ getProperty 异步订阅 ------ subscribe 车辆行驶中... getProperty(VEHICLE_SPEED, areaId)AIDL get(VEHICLE_SPEED, areaId)VehiclePropValue (speed)float speedsubscribeProperty(VEHICLE_SPEED, listener)AIDL subscribe(VEHICLE_SPEED, callback)onPropertyEvent(VEHICLE_SPEED, 60km/h)onPropertyChanged(VEHICLE_SPEED, 60km/h)onPropertyEvent(VEHICLE_SPEED, 65km/h)onPropertyChanged(VEHICLE_SPEED, 65km/h)

Android 14 在 CarService 上的关键变化

最后总结一下 Android 14 跟 CarService 直接相关的改动:

AIDL 全面替换 HIDL ------ CarService 跟 VHAL 的通信从 hwbinder 切到了标准 binder。对 CarService 本身来说,代码改动不小但逻辑没变。好处是可以统一用 dumpsys car_service 来 debug 了。

OccupantZone 与音频深度整合 ------ OccupantZoneService 本身不是 Android 14 新增的,但 14 在多区音频路由中深度整合了它,音频 Zone 通过 occupantZoneId 关联到座位区域,登录后自动切换音频路由。

VHAL 新增 SHUTDOWN_ONLY 参数 ------ VehicleApPowerStateShutdownParam.SHUTDOWN_ONLY 是 Android 14 引入的参数,VMCU 发起关机的 SHUTDOWN_PREPARE 请求时携带,AP 收到后不进入 Suspend/Hibernate,直接走完整关机流程,给 OTA 场景用。

权限模型重构 ------ CarPropertyService.setProperty() 的权限检查从硬编码变成了动态读取 VHAL 配置。OEM 可以自定义每个属性的访问权限。

CarService 启动优化 ------ Android 14 把 CarService 的启动从 onBootPhase 回调提前到了 systemReady 阶段,比 13 大约早了 500ms。对于需要 CarService 提前就绪的场景(比如仪表盘初始化)很有帮助。

总结

CarService 是 AAOS 里代码量最大的一个进程,packages/services/Car/ 目录下光 Java 文件就超过 1500 个。它做的是把 Android 通用框架跟车载硬件之间搭一座桥------桥的一端是标准的 Android API,另一端是千奇百怪的 OEM 硬件和车载总线协议。

理解了 CarService 的初始化时序、Binder 接口设计、子 Service 的分工协作,基本就能明白 AAOS 里车载业务逻辑是怎么运转起来的。下一篇计划写 Vehicle HAL 的 AIDL 实现细节,有兴趣的可以留意。

相关推荐
爱笑鱼1 小时前
JNI(二):Java 里的一个 long,为什么能找到 C++ 对象?
android
爱笑鱼2 小时前
JNI(一):nativePollOnce() 到底去了哪里?
android
hqk4 小时前
玩Android×AI Coding:从产品到代码,完整跑通一套 AI 软件研发工作流
android·ai编程·vibecoding
安卓修改大师4 小时前
安卓修改大师实战:深入解析Android主题Theme修改与沉浸式界面实现
android
stevenzqzq7 小时前
app性能日志工具
android
DB哥讲数据库7 小时前
【最新】MySQL9.7安装教程:图文详解(附MySQL安装包)
android·mysql·adb
IT乐手7 小时前
Android 写本地日志工具类
android·java
Knight_V_Schumacher8 小时前
基于5090D服务器的NVIDIA Alpamayo实验
车载系统·自动驾驶
非典型Android程序员8 小时前
Launcher学习总结
android