跳转至

Android 15 CarService源码04-CarService与Vehicle HAL交互

背景

Android 15 CarService源码02-服务初始化 中,我们仅简单提到通过调用 vehicle.newSubscriptionClient(this) 来订阅事件回调,但并未详细说明其操作过程。此外,在 CarServiceImpl.onCreate() 中创建了 VehicleStub,即 AidlVehicleStub,而在 ICarImpl中又创建了VehicleHal。这些组件之间的关系是什么?在这篇文章中,我们将详细解释 CarService 如何调用 Vehicle HAL 以及如何接收 Vehicle HAL 的回调,同时梳理VehicleStubAidlVehicleStubVehicleHal 等之间的关系。

接口

VehicleStub 是一个抽象类,其具体实现是 AidlVehicleStubAidlVehicleStub 用于与车辆 HAL(硬件抽象层)进行通信。在分析 AidlVehicleStub 之前,我们需要先了解 VehicleStub 抽象类中定义了哪些接口。

我们回顾 Android 15 CarService源码02-服务初始化 中,我们讨论了 IVehicle.aidl 和 IVehicleCallback.aidl 中定义的方法。这些方法构成了 Vehicle HAL(硬件抽象层)为其他客户端(如 CarService)提供的接口。

IVehicle.aidl

// hardware/interfaces/automotive/vehicle/aidl/android/hardware/automotive/vehicle/IVehicle.aidl

// Vehicle HAL 接口,定义了与车辆硬件抽象层(Vehicle HAL)进行通信的接口。  
@VintfStability  
interface IVehicle {  
    /* 一个无效的内存 ID,用于标识无效的共享内存。 */
    const long INVALID_MEMORY_ID = 0;

    /* 每个订阅客户端的最大共享内存文件数,限制了每个客户端可以使用的共享内存文件数量。 */
    const int MAX_SHARED_MEMORY_FILES_PER_CLIENT = 3;  

    /**  
     * 返回车辆 HAL 支持的所有属性配置的列表。  
     *  
     * @return 一个可打包的对象,可能包含配置列表或一个共享内存文件(如果配置列表超出了 binder 内存限制)。  
     * 必须使用 {@code android-automotive-large-parcelable} 库来解析返回的对象。  
     */  
    VehiclePropConfigs getAllPropConfigs();  

    /**  
     * 返回指定属性的配置列表。  
     *  
     * 如果请求的属性中有未找到的,必须返回 {@link StatusCode#INVALID_ARG},  
     * 否则返回包含 {@link StatusCode#OK} 的车辆属性配置列表。  
     *  
     * @param props 要获取配置的属性 ID 列表。  
     * @return 一个可打包的对象,可能包含配置列表或一个共享内存文件(如果配置列表超出了 binder 内存限制)。  
     * 必须使用 {@code android-automotive-large-parcelable} 库来解析返回的对象。  
     */  
    VehiclePropConfigs getPropConfigs(in int[] props);  

    /**  
     * 异步获取车辆属性值。  
     *  
     * 当值被获取时,将调用 {@link IVehicleCallback#onGetValues} 方法。  
     * 该方法可能会被多次调用,每次调用时传递已获取的属性的子集。  
     * 例如,如果请求属性 [A, B, C],回调可能会被调用两次,分别传递 [A, C] 和 [B]。  
     * 调用者不应期望 {@link IVehicleCallback#onGetValues} 的调用顺序。  
     *  
     * 如果此方法返回错误,则表示我们无法获取所有属性。  
     * 如果此方法返回 OK,仍然有可能无法获取某些属性,这些属性的状态由 {@link GetValueResult#status} 指示。  
     *  
     * 对于 {@link VehiclePropertyChangeMode#STATIC} 属性,此方法必须始终返回相同的值。  
     * 对于 {@link VehiclePropertyChangeMode#ON_CHANGE} 属性,必须返回最新可用的值。  
     * 对于可缓存的属性,将返回缓存中的值,而无需与实际的车辆总线通信。  
     *  
     * 某些属性(如 {@code RADIO_PRESET})需要在 {@link VehiclePropValue} 对象中传递附加数据。  
     *  
     * 如果尚无可用数据(这可能发生在初始阶段),{@link GetValueResult#status} 包含 {@link StatusCode#TRY_AGAIN}。  
     *  
     * 调用者必须为每个请求传递一个唯一的 RequestID,如果任何给定的请求 ID 与挂起的请求 ID 重复,  
     * 此函数必须返回 {@link StatusCode#INVALID_ARG}。  
     *  
     * 为防止混淆,单个调用中不允许重复属性(相同的属性 ID 和相同的区域 ID)。  
     * 此函数必须为重复属性返回 {@link StatusCode#INVALID_ARG}。  
     *  
     * 请求中的 {@link VehiclePropValue#timestamp} 字段将被忽略。  
     * {@link GetValueResult} 中的 {@link VehiclePropValue#timestamp} 字段必须是自启动以来的系统运行时间,  
     * 当值发生变化时适用于 ON_CHANGE 属性,或根据轮询率检查值时适用于 CONTINUOUS 属性。  
     * 请注意,对于 CONTINUOUS 属性,VHAL 客户端在轮询间隔内多次读取属性将获得相同的时间戳。  
     *  
     * @param callback 一个回调接口,在值被获取后调用其 'onGetValues'。  
     * 调用者应使用 {@code android-automotive-large-parcelable} 库来解析返回的 {@link GetValueResult} 对象。  
     * @param requests 一个对象,包含请求的属性列表或包含属性的共享内存文件。  
     * 该对象必须使用发送方和接收方的辅助库进行解析。  
     */  
    void getValues(IVehicleCallback callback, in GetValueRequests requests);  

    /**  
     * 设置车辆属性值。  
     *  
     * 在值设置请求通过车辆总线发送或设置失败后,将调用 {@link IVehicleCallback#onSetValues} 函数。  
     * 如果总线协议支持确认,则在收到确认后调用回调。  
     *  
     * 对于某些不支持确认的车辆总线(如 CAN 总线),OnSetValues 不一定意味着值的变化会立即反映在 {@link #getValues} 中。  
     *  
     * 如果输出状态包含错误,则表示我们无法设置所有属性。  
     * 如果我们无法设置某些值,它们将反映为非 OK 的 {@link SetValueResult#status}。  
     *  
     * 请求中每个属性的设置顺序不保证。如果调用者需要确保设置值的顺序,调用者应设置一个值,等待其回调,然后设置另一个值。  
     *  
     * 数据的时间戳在设置操作中必须被忽略。  
     *  
     * 设置某些属性需要有可用的初始状态。如果初始数据尚不可用,{@link SetValueResult#status} 必须是 {@link StatusCode#TRY_AGAIN}。  
     * 对于具有独立电源控制的属性,如果属性未通电,{@link SetValueResult#status} 必须是 {@link StatusCode#NOT_AVAILABLE}。  
     *  
     * 调用者必须为每个请求传递一个唯一的 RequestID,如果任何给定的请求 ID 与挂起的请求 ID 重复,  
     * 此函数必须返回 {@link StatusCode#INVALID_ARG}。  
     *  
     * 为防止混淆,单个调用中不允许重复属性(相同的属性 ID 和相同的区域 ID)。  
     * 此函数必须为重复属性返回 {@link StatusCode#INVALID_ARG}。  
     *  
     * @param callback 回调,在设置值请求发送到总线后调用其 'onSetValues'。  
     * @param requests 一个对象,包含 {@link SetValueRequest} 列表或存储请求列表的共享内存文件(如果它们超过 binder 内存限制),  
     * 必须使用发送方和接收方的辅助库进行解析。  
     */  
    void setValues(IVehicleCallback callback, in SetValueRequests requests);  

    /**  
     * 订阅属性事件。  
     *  
     * 客户端必须能够根据 options 参数中提供的数据一次订阅多个属性。  
     *  
     * 对于一个回调,一个属性只有一个订阅。具有不同采样率的新订阅将覆盖旧订阅。  
     * 一个属性可以为不同的回调多次订阅。  
     *  
     * 如果返回错误,则表示某些属性订阅失败。  
     * 调用者可以安全地重试,因为订阅已订阅的属性是可以的。  
     *  
     * 指定的采样率只是一个指导。根据轮询刷新率,无法保证采样率是可实现的。  
     * 实际的属性事件率可能高于或低于指定的 sampleRate。  
     * 例如,如果轮询率可以是每秒 5 次或 10 次,订阅 7 的采样率可能会使用每秒 5 次的轮询率,从而生成每秒 5 次事件。  
     * 我们只要求平均而言,{@code minSampleRate} 和 {@code maxSampleRate} 可以实现,  
     * 所有在最小和最大之间的 sampleRate 平均而言生成事件的速率 >= {@code minSampleRate} 且 <= {@code maxSampleRate}。  
     *  
     * 每个属性事件的 {@link VehiclePropValue#timestamp} 字段必须是自启动以来的系统运行时间,  
     * 当值发生变化时适用于 ON_CHANGE 属性,或根据轮询率检查值时适用于 CONTINUOUS 属性。  
     * 请注意,对于 CONTINUOUS 属性,VHAL 客户端在轮询间隔内多次读取属性将获得相同的时间戳。  
     * 例如,如果属性的轮询率是每秒 10 次,无论 {@code options} 中指定的 sampleRate 是什么,时间戳每秒更新 10 次。  
     *  
     * 如果由于某些电源状态关闭而无法读取属性,则在属性可用之前可能不会生成属性更改事件。  
     * 对于 ON_CHANGE 属性,如果属性从 NOT_AVAILABLE 更改为 OKAY 以读取某些或所有区域,  
     * 对于每个可供读取的区域,必须生成一个属性更改事件。事件必须包含该区域的当前值并具有 {@code AVAILABLE} 状态。  
     *  
     * @param callback 订阅回调。  
     *    当新属性事件到达时,将调用 {@link IVehicleCallback#onPropertyEvent}。  
     *    当属性设置请求异步失败时,将调用 {@link IVehicleCallback#onPropertySetError}。  
     *    这通常是由车辆总线发送的属性设置失败消息引起的。  
     * @param options 订阅选项列表。SubscribeOption 包含属性 ID、区域 ID、采样率等信息。  
     *    对于连续属性,必须提供采样率。如果采样率小于 {@link VehiclePropConfig#minSampleRate},  
     *    则采样率将为 minSampleRate。如果采样率大于 {@link VehiclePropValue#maxSampleRate},  
     *    则采样率将为 maxSampleRate。  
     * @param maxSharedMemoryFileCount 为此订阅在 VHAL 中分配的最大共享内存文件数。  
     *    当内存文件返回给客户端时,在调用 returnSharedMemory 将缓冲区返回给 VHAL 之前,  
     *    VHAL 不能使用该内存文件来传递另一个事件。较大的 maxSharedMemoryFileCount 意味着在处理大量数据时性能更好,  
     *    但也意味着更大的内存占用。如果不期望事件非常频繁地到达,建议值为 2。值为 0 表示对于每个新属性,  
     *    将创建一个新的共享内存文件,并且不会重用任何共享内存文件。这仅应为不频繁的事件或内存有限的设备配置。  
     *    此值必须 >=0 且 < {@link MAX_SHARED_MEMORY_FILES_PER_CLIENT}。  
     */  
    void subscribe(in IVehicleCallback callback, in SubscribeOptions[] options,  
                   int maxSharedMemoryFileCount);  

    /**  
     * 取消订阅属性事件。  
     *  
     * 如果 'callback' 无效,此方法必须返回 {@link StatusCode#INVALID_ARG}。  
     * 如果指定的 propId 之前未订阅,此方法必须忽略该 propId。  
     *  
     * 如果返回错误,则表示某些属性取消订阅失败。  
     * 调用者可以安全地重试,因为取消订阅已取消订阅的属性是可以的。  
     *  
     * @param callback 在先前订阅中使用的回调。  
     * @param propIds 要取消订阅的属性的 ID。  
     */  
    void unsubscribe(in IVehicleCallback callback, in int[] propIds);  

    /**  
     * 将共享内存文件返回给 VHAL 以供回收。  
     *  
     * 在客户端不再使用由 {@link IVehicleCallback#onPropertyEvent} 返回的共享内存文件后,必须调用此方法。  
     * 通常在 'onPropertyEvent' 结束时调用此方法。  
     *  
     * 如果 'callback' 无效或 'sharedMemoryId' 与传递给 {@link IVehicleCallback#onPropertyEvent} 的  
     * 'VehiclePropValues' 中的任何 SharedMemoryId 不匹配,此方法必须返回 {@link StatusCode#INVALID_ARG}。  
     *  
     * @param callback 在订阅中使用的回调。  
     * @param sharedMemoryId 由 'onPropertyEvent' 返回的 ID,表示要返回的已使用共享内存文件。  
     */  
    void returnSharedMemory(in IVehicleCallback callback, long sharedMemoryId);  
}

以上是 IVehicle.aidl 代码,从其接口可以看出,其主要是与车辆 HAL 交互的核心接口,支持获取和设置车辆属性,处理异步操作和资源管理。

  • getAllPropConfigs() 返回车辆 HAL 支持的所有属性配置的列表。

  • getPropConfigs(): 返回指定属性的配置列表。

  • getValues(): 异步获取车辆属性值。通过回调函数返回结果,可能多次调用以返回属性的子集。支持不同的属性变化模式(静态、变化、连续),并处理缓存和时间戳。

  • setValues(): 设置车辆属性值。通过回调函数通知设置结果。处理设置顺序、确认机制、初始状态需求和重复请求 ID 等问题。

  • subscribe(): 订阅属性事件。

  • unsubscribe 方法: 取消订阅指定属性的事件。

IVehicleCallback.aidl

// hardware/interfaces/automotive/vehicle/aidl/android/hardware/automotive/vehicle/IVehicleCallback.aidl

// IVehicleCallback 接口,定义了与车辆硬件抽象层(Vehicle HAL)进行通信时的回调接口。
@VintfStability
interface IVehicleCallback {

    /**
     * {@link IVehicle#getValues} 函数的回调。
     *
     * 当一些要获取的值准备好时调用。对于一个 'getValues' 请求,这个方法可能被调用一次或多次。
     * 每个回调包含部分请求的值。保证所有请求的值都会在某个回调中返回,但每个值准备好的顺序不保证。
     *
     * @param responses 一个对象,包含 {@link GetValueResult} 的列表(如果它们符合 binder 内存限制)或包含响应的共享内存文件。
     * 每个 {@link GetValueResult} 要么包含属性值,要么包含获取值时发生的错误。
     *
     * {@link GetValueResult} 还包含一个 requestId,指示该响应是针对哪个请求的。
     * 响应对象应由 {@code android-automotive-large-parcelable} 库解析。
     */
    oneway void onGetValues(in GetValueResults responses);

    /**
     * {@link IVehicle#setValues} 函数的回调。
     *
     * 当 VHAL 完成处理一些属性设置请求时调用。对于一个 'setValues' 请求,这个方法可能被调用一次或多次。
     * 每个回调包含部分请求的值。保证所有设置的值状态都会在某个回调中返回,但每个值设置的顺序不保证。
     *
     * @param responses {@link SetValueResult} 的列表。每个 SetValueResult 包含一个状态,指示设置特定属性的状态。
     * requestId 指示该响应是针对哪个请求的。
     */
    oneway void onSetValues(in SetValueResults responses);

    /**
     * 当 API 用户订阅的一个或多个变量需要报告时发生的事件回调。
     * 这可能纯粹基于阈值和频率(常规订阅,参见 subscribe 调用的参数)或当调用 {@link IVehicle#setValues} 方法并需要报告实际更改时。
     *
     * @param propValues 包含更新的属性值的对象。
     * 如果属性在 binder 限制内,它们将位于 {@code propValues.payloads} 中,否则,它们将位于共享内存文件 {@code propValues.sharedMemoryFd} 中。
     * 共享内存文件由 VHAL 创建,使用后必须使用 {@link IVehicle#returnSharedMemory} 返回给 VHAL。
     * 为每个订阅创建的内存文件数量有限,如果客户端不返回共享内存,客户端可能无法在未来获得事件。
     * @param sharedMemoryFileCount 为此订阅分配的共享内存文件数量。
     * 该值可用于调整 {@link IVehicle#subscribe} 中的 {@code maxSharedMemoryFileCount}。
     * 例如,如果您通常看到 sharedMemoryFileCount 是您设置的 maxSharedMemoryFileCount,这意味着您可能需要增加 maxSharedMemoryFileCount。
     */
    oneway void onPropertyEvent(in VehiclePropValues propValues, int sharedMemoryFileCount);

    /**
     * 设置属性值通常是异步操作。因此,即使客户端从 {@link IVehicle#setValues} 收到 {@link StatusCode#OK},
     * 或在 {@link #onSetValues} 中收到 {@link StatusCode#OK},这并不保证值已成功传播到车辆网络。
     * 如果发生这种罕见事件,则必须调用此方法。
     *
     * @param errors 属性设置错误的列表。如果 VHAL 实现不批量处理错误,这可能只包含一个错误。
     */
    oneway void onPropertySetError(in VehiclePropErrors errors);
}
  • onGetValues(): 用于处理 IVehicle#getValues 请求的回调。当请求的属性值准备好时调用,可能会多次调用以返回部分结果。

  • onSetValues(): 用于处理 IVehicle#setValues 请求的回调。当请求的属性设置完成时调用,可能会多次调用以返回部分结果。

  • onPropertyEvent(): 当订阅的属性事件发生时调用。可能基于订阅的阈值和频率或属性设置的变化。

  • onPropertySetError(): 当属性设置过程中发生错误时调用。即使之前的操作返回成功状态,也可能出现错误。

现在我们知道了跟 Vehicle HAL 交互的主要接口了,我们返回到VehicleStub.newVehicleStub()中,其获取 Stable AIDL 版本就是创建一个AidlVehicleStub实例。

VehicleStub

在讨论 IVehicle.aidl 和 IVehicleCallback.aidl 时,我们提到这些是 Vehicle HAL 提供给外部的接口。然而,在 CarService 中具体使用了哪些接口,可以通过查看 VehicleStub 来了解。

VehicleStub 是 CarService 中的一个关键组件,它实现了 IVehicle 接口,并负责处理来自车辆硬件的请求和回调。通过分析 VehicleStub 的实现,我们可以明确 CarService 实际上调用了哪些接口方法,以及这些方法是如何被利用的。

// packages/services/Car/service/src/com/android/car/VehicleStub.java

/**
 * VehicleStub 表示一个 IVehicle 服务接口,可以是 AIDL 或者传统的 HIDL 版本。
 * 它提供了一个通用接口,使客户端不需要关心底层 IVehicle 服务使用的是哪个版本。
 */
public abstract class VehicleStub {

    /**
     * SubscriptionClient 表示一个可以订阅/取消订阅属性的客户端。
     */
    public interface SubscriptionClient {
        /**
         * 订阅一个属性。
         *
         * @param options 订阅选项的列表。
         * @throws RemoteException 如果远程操作失败。
         * @throws ServiceSpecificException 如果 VHAL 返回服务特定错误。
         */
        void subscribe(SubscribeOptions[] options) throws RemoteException, ServiceSpecificException;

        /**
         * 取消订阅一个属性。
         *
         * @param prop 要取消订阅的属性ID。
         * @throws RemoteException 如果远程操作失败。
         * @throws ServiceSpecificException 如果 VHAL 返回服务特定错误。
         */
        void unsubscribe(int prop) throws RemoteException, ServiceSpecificException;
    }

    /**
     * 用于 {@link VehicleStub#getAsync} 或 {@link VehicleStub#setAsync} 的请求。
     */
    public static class AsyncGetSetRequest {
        private final int mServiceRequestId; // 服务请求ID
        private final HalPropValue mHalPropValue; // HAL属性值
        private final long mTimeoutUptimeMs; // 超时时间

        public int getServiceRequestId() {
            return mServiceRequestId;
        }

        public HalPropValue getHalPropValue() {
            return mHalPropValue;
        }

        public long getTimeoutUptimeMs() {
            return mTimeoutUptimeMs;
        }

        /**
         * 获取 AsyncGetSetRequest 的实例。
         */
        public AsyncGetSetRequest(int serviceRequestId, HalPropValue halPropValue,
                long timeoutUptimeMs) {
            mServiceRequestId = serviceRequestId;
            mHalPropValue = halPropValue;
            mTimeoutUptimeMs = timeoutUptimeMs;
        }
    }

    /**
     * {@link VehicleStub#getAsync} 的结果。
     */
    public static final class GetVehicleStubAsyncResult {
        private final int mServiceRequestId; // 服务请求ID
        @Nullable
        private final HalPropValue mHalPropValue; // HAL属性值
        private final CarPropertyErrorCodes mCarPropertyErrorCodes; // 汽车属性错误代码

        public int getServiceRequestId() {
            return mServiceRequestId;
        }

        @Nullable
        public HalPropValue getHalPropValue() {
            return mHalPropValue;
        }

        @CarPropMgrErrorCode
        public int getErrorCode() {
            return mCarPropertyErrorCodes.getCarPropertyManagerErrorCode();
        }

        public int getVendorErrorCode() {
            return mCarPropertyErrorCodes.getVendorErrorCode();
        }

        public int getSystemErrorCode() {
            return mCarPropertyErrorCodes.getSystemErrorCode();
        }

        public CarPropertyErrorCodes getCarPropertyErrorCodes() {
            return mCarPropertyErrorCodes;
        }

        /**
         * 构造一个成功返回结果的 GetVehicleStubAsyncResult 实例。
         */
        public GetVehicleStubAsyncResult(int serviceRequestId, HalPropValue halPropValue) {
            mServiceRequestId = serviceRequestId;
            mHalPropValue = halPropValue;
            mCarPropertyErrorCodes = CarPropertyErrorCodes.STATUS_OK_NO_ERROR;
        }

        /**
         * 构造一个错误返回结果的 GetVehicleStubAsyncResult 实例。
         */
        public GetVehicleStubAsyncResult(int serviceRequestId, CarPropertyErrorCodes errorCodes) {
            mServiceRequestId = serviceRequestId;
            mHalPropValue = null;
            mCarPropertyErrorCodes = errorCodes;
        }
    }

    /**
     * {@link VehicleStub#setAsync} 的结果。
     */
    public static final class SetVehicleStubAsyncResult {
        private final int mServiceRequestId; // 服务请求ID
        @CarPropMgrErrorCode
        private final CarPropertyErrorCodes mCarPropertyErrorCodes; // 汽车属性错误代码

        public int getServiceRequestId() {
            return mServiceRequestId;
        }

        @CarPropMgrErrorCode
        public int getErrorCode() {
            return mCarPropertyErrorCodes.getCarPropertyManagerErrorCode();
        }

        public int getVendorErrorCode() {
            return mCarPropertyErrorCodes.getVendorErrorCode();
        }

        public int getSystemErrorCode() {
            return mCarPropertyErrorCodes.getSystemErrorCode();
        }

        public CarPropertyErrorCodes getCarPropertyErrorCodes() {
            return mCarPropertyErrorCodes;
        }

        /**
         * 构造一个成功结果的实例。
         */
        public SetVehicleStubAsyncResult(int serviceRequestId) {
            mServiceRequestId = serviceRequestId;
            mCarPropertyErrorCodes = CarPropertyErrorCodes.STATUS_OK_NO_ERROR;
        }

        /**
         * 构造一个错误结果的实例。
         */
        public SetVehicleStubAsyncResult(int serviceRequestId, CarPropertyErrorCodes errorCodes) {
            mServiceRequestId = serviceRequestId;
            mCarPropertyErrorCodes = errorCodes;
        }
    }

    /**
     * 异步操作的回调接口。
     */
    public abstract static class VehicleStubCallbackInterface {
        /**
         * 当 {@link getAsync} 返回结果时调用的方法。
         */
        public abstract void onGetAsyncResults(
                List<GetVehicleStubAsyncResult> getVehicleStubAsyncResults);

        /**
         * 当 {@link setAsync} 返回结果时调用的方法。
         */
        public abstract void onSetAsyncResults(
                List<SetVehicleStubAsyncResult> setVehicleStubAsyncResults);

        /**
         * 当异步请求超时时调用的方法。
         *
         * 如果回调的绑定已经死亡,则不会调用此函数。
         */
        public abstract void onRequestsTimeout(List<Integer> serviceRequestIds);
    }

    /**
     * 异步获取属性。
     */
    public abstract void getAsync(List<AsyncGetSetRequest> getVehicleStubAsyncRequests,
            VehicleStubCallbackInterface getVehicleStubAsyncCallback);

    /**
     * 异步设置属性。
     */
    public abstract void setAsync(List<AsyncGetSetRequest> setVehicleStubAsyncRequests,
            VehicleStubCallbackInterface setVehicleStubAsyncCallback);

    /**
     * 获取一个可以用于构建 HalPropValue 的 HalPropValueBuilder。
     *
     * @return 一个用于构建 HalPropValue 的构建器。
     */
    public abstract HalPropValueBuilder getHalPropValueBuilder();

    /**
     * 获取所有属性配置。
     *
     * @return 所有的属性配置。
     * @throws RemoteException 如果远程操作失败。
     * @throws ServiceSpecificException 如果 VHAL 返回服务特定错误。
     */
    public abstract HalPropConfig[] getAllPropConfigs()
            throws RemoteException, ServiceSpecificException;

    /**
     * 获取一个新的 {@code SubscriptionClient},可以用于订阅/取消订阅。
     *
     * 调用者必须在丢弃客户端之前取消订阅所有已订阅的属性以防止资源泄漏。
     *
     * @param callback 一个可以用于接收事件的回调。
     * @return 一个可以用于订阅/取消订阅的 {@code SubscriptionClient}。
     */
    public abstract SubscriptionClient newSubscriptionClient(VehicleHalCallback callback);

    /**
     * 获取一个属性。
     *
     * @param requestedPropValue 要获取的属性。
     * @return 车辆属性值。
     * @throws RemoteException 如果远程操作失败。
     * @throws ServiceSpecificException 如果 VHAL 返回服务特定错误。
     */
    @Nullable
    public abstract HalPropValue get(HalPropValue requestedPropValue)
            throws RemoteException, ServiceSpecificException;

    /**
     * 设置一个属性。
     *
     * @param propValue 要设置的属性。
     * @throws RemoteException 如果远程操作失败。
     * @throws ServiceSpecificException 如果 VHAL 返回服务特定错误。
     */
    public abstract void set(HalPropValue propValue)
            throws RemoteException, ServiceSpecificException;

    /**
     * 取消所有正在进行的异步请求。
     *
     * @param requestIds 异步获取/设置请求ID的列表。
     */
    public void cancelRequests(List<Integer> requestIds) {}
}

VehicleStub 是一个抽象类,提供了与车辆硬件抽象层(VHAL)交互的接口。它支持异步和同步的属性获取和设置操作,并提供了订阅和取消订阅车辆属性的功能。

  • SubscriptionClient:用于订阅和取消订阅车辆属性。通过 subscribe 和 unsubscribe 方法,客户端可以监听车辆属性的变化。
  • AsyncGetSetRequest:表示异步获取或设置请求的类,包含请求ID、属性值和超时时间。
  • GetVehicleStubAsyncResult 和 SetVehicleStubAsyncResult:用于表示异步获取和设置操作的结果,包含请求ID和错误代码。
  • VehicleStubCallbackInterface:定义了异步操作的回调接口,包括获取和设置结果的处理方法,以及超时处理。
  • getAsync 和 setAsync:用于异步获取和设置车辆属性。
  • get 和 set:用于同步获取和设置车辆属性。
  • getAllPropConfigs:获取所有车辆属性的配置。
  • newSubscriptionClient:创建一个新的订阅客户端,用于订阅和取消订阅车辆属性。
  • cancelRequests:取消正在进行的异步请求。

回调

AidlVehicleStub 继自 VehicleStub,用于与车辆 HAL(硬件抽象层)进行通信。

// packages/services/Car/service/src/com/android/car/AidlVehicleStub.java

// AidlVehicleStub 类是一个最终类,继承自 VehicleStub,用于与车辆的硬件抽象层(HAL)进行通信。
final class AidlVehicleStub extends VehicleStub {

    // 定义一个常量字符串,表示 AIDL VHAL(车辆硬件抽象层)服务的名称。
    private static final String AIDL_VHAL_SERVICE =
            "android.hardware.automotive.vehicle.IVehicle/default";

    // IVehicle 接口的实例,用于与 AIDL(Android 接口定义语言)进行通信。
    private final IVehicle mAidlVehicle;

    // 默认构造函数,调用另一个构造函数,并传入通过 getAidlVehicle() 方法获取的 IVehicle 实例。
    AidlVehicleStub() {
        this(getAidlVehicle());
    }

    // 测试可见的构造函数,允许传入一个 IVehicle 实例。
    @VisibleForTesting
    AidlVehicleStub(IVehicle aidlVehicle) {
        // 调用另一个构造函数,并传入 IVehicle 实例和一个新的 HandlerThread。
        this(aidlVehicle,
                CarServiceUtils.getHandlerThread(AidlVehicleStub.class.getSimpleName()));
    }

    // 测试可见的构造函数,允许传入一个 IVehicle 实例和一个 HandlerThread。
    @VisibleForTesting
    AidlVehicleStub(IVehicle aidlVehicle, HandlerThread handlerThread) {
        // 初始化 mAidlVehicle 为传入的 aidlVehicle 实例。
        mAidlVehicle = aidlVehicle;

        // 初始化 HalPropValueBuilder,用于构建 HAL 属性值,标记为 AIDL 模式。
        mPropValueBuilder = new HalPropValueBuilder(/*isAidl=*/true);

        // 初始化 mHandlerThread 为传入的 handlerThread。
        mHandlerThread = handlerThread;

        // 使用传入的 handlerThread 的 looper 初始化 Handler。
        mHandler = new Handler(mHandlerThread.getLooper());

        // 初始化 GetSetValuesCallback,用于处理 get/set 操作的回调。
        mGetSetValuesCallback = new GetSetValuesCallback();

        // 初始化 PendingAsyncRequestPool,用于管理异步请求,使用 handler 的 looper。
        mPendingAsyncRequestPool = new PendingAsyncRequestPool(mHandler.getLooper());
    }

    // 私有静态方法,用于获取 AIDL VHAL 服务的 IVehicle 实例。
    @Nullable
    private static IVehicle getAidlVehicle() {
        try {
            // 尝试获取 AIDL VHAL 服务,并将其转换为 IVehicle 接口。
            return IVehicle.Stub.asInterface(
                    ServiceManagerHelper.waitForDeclaredService(AIDL_VHAL_SERVICE));
        } catch (RuntimeException e) {
            // 如果获取服务失败,记录警告日志。
            Slogf.w(TAG, "Failed to get \"" + AIDL_VHAL_SERVICE + "\" service", e);
        }
        // 如果获取失败,返回 null。
        return null;
    }
}
  • mAidlVehicle:这是一个 IVehicle 接口的实例,用于与 AIDL 进行通信。
  • mGetSetValuesCallback:用于处理 get / set 操作的回调(同步、异步)。

GetSetValuesCallback

// packages/services/Car/service/src/com/android/car/AidlVehicleStub.java

final class AidlVehicleStub extends VehicleStub {

    // GetSetValuesCallback 是一个内部类,继承自 IVehicleCallback.Stub,用于处理车辆属性的获取和设置回调。
    private final class GetSetValuesCallback extends IVehicleCallback.Stub {

        // 处理获取车辆属性值的回调
        @Override
        public void onGetValues(GetValueResults responses) throws RemoteException {
            // 调用外部类的 onGetValues 方法
            AidlVehicleStub.this.onGetValues(responses);
        }

        // 处理设置车辆属性值的回调
        @Override
        public void onSetValues(SetValueResults responses) throws RemoteException {
            // 调用外部类的 onSetValues 方法
            AidlVehicleStub.this.onSetValues(responses);
        }

        // 不支持的操作:处理车辆属性事件
        @Override
        public void onPropertyEvent(VehiclePropValues propValues, int sharedMemoryFileCount)
                throws RemoteException {
            throw new UnsupportedOperationException(
                    "GetSetValuesCallback only support onGetValues or onSetValues");
        }

        // 不支持的操作:处理车辆属性设置错误
        @Override
        public void onPropertySetError(VehiclePropErrors errors) throws RemoteException {
            throw new UnsupportedOperationException(
                    "GetSetValuesCallback only support onGetValues or onSetValues");
        }

        // 获取接口的哈希值
        @Override
        public String getInterfaceHash() {
            return IVehicleCallback.HASH;
        }

        // 获取接口的版本号
        @Override
        public int getInterfaceVersion() {
            return IVehicleCallback.VERSION;
        }
    }

    // 处理获取车辆属性值的逻辑
    private void onGetValues(GetValueResults responses) {
        // 开始跟踪性能
        Trace.traceBegin(TRACE_TAG, "AidlVehicleStub#onGetValues");

        // 重新构建稳定的 AIDL Parcelable 对象
        GetValueResults origResponses = (GetValueResults)
                LargeParcelable.reconstructStableAIDLParcelable(responses,
                        /* keepSharedMemory= */ false);

        // 处理获取和设置值的通用逻辑
        onGetSetValues(origResponses.payloads, new AsyncGetResultsHandler(mPropValueBuilder),
                mPendingSyncGetValueRequestPool);

        // 结束跟踪性能
        Trace.traceEnd(TRACE_TAG);
    }

    // 处理设置车辆属性值的逻辑
    private void onSetValues(SetValueResults responses) {
        // 开始跟踪性能
        Trace.traceBegin(TRACE_TAG, "AidlVehicleStub#onSetValues");

        // 重新构建稳定的 AIDL Parcelable 对象
        SetValueResults origResponses = (SetValueResults)
                LargeParcelable.reconstructStableAIDLParcelable(responses,
                        /* keepSharedMemory= */ false);

        // 处理获取和设置值的通用逻辑
        onGetSetValues(origResponses.payloads, new AsyncSetResultsHandler(),
                mPendingSyncSetValueRequestPool);

        // 结束跟踪性能
        Trace.traceEnd(TRACE_TAG);
    }

    /**
     * 处理 onGetValues 和 onSetValues 的通用函数。
     */
    private <VhalResultType> void onGetSetValues(VhalResultType[] vhalResults,
                                                 AsyncResultsHandler asyncResultsHandler,
                                                 PendingSyncRequestPool<VhalResultType> pendingSyncRequestPool) {
        synchronized (mLock) {
            for (VhalResultType result : vhalResults) {
                long vhalRequestId = asyncResultsHandler.getVhalRequestId(result);
                if (!mPendingAsyncRequestPool.contains(vhalRequestId)) {
                    // 如果在异步请求池中找不到请求 ID,则假设这是一个同步请求。
                    completePendingSyncRequestLocked(pendingSyncRequestPool, vhalRequestId, result);
                    continue;
                }

                // 完成异步请求
                AsyncRequestInfo requestInfo = mPendingAsyncRequestPool.finishRequestIfFound(
                        vhalRequestId);
                if (requestInfo == null) {
                    Slogf.w(TAG,
                            "No pending request for ID: %s, possibly already timed out, "
                                    + "or cancelled, or the client already died", vhalRequestId);
                    continue;
                }
                // 添加车辆属性结果
                asyncResultsHandler.addVhalResult(requestInfo.getClientCallback(),
                        requestInfo.getServiceRequestId(), result);
            }
        }
        // 开始跟踪性能
        Trace.traceBegin(TRACE_TAG, "AidlVehicleStub call async result callback");
        // 调用车辆存根回调
        asyncResultsHandler.callVehicleStubCallback();
        // 结束跟踪性能
        Trace.traceEnd(TRACE_TAG);
    }

    @Override
    void callVehicleStubCallback() {
        // 遍历所有回调接口,并调用其 onSetAsyncResults 方法
        for (Map.Entry<VehicleStubCallbackInterface, List<SetVehicleStubAsyncResult>> entry :
                mCallbackToResults.entrySet()) {
            entry.getKey().onSetAsyncResults(entry.getValue());
        }
    }
}

内部类 GetSetValuesCallback 是从 IVehicleCallback.Stub 继承而来的,主要用于处理车辆属性的获取和设置回调。它实现了 onGetValues 和 onSetValues 方法。当这些方法被调用时,会触发 AidlVehicleStub 类中的相应处理逻辑,最终调用 callVehicleStubCallback() 方法。

在 callVehicleStubCallback() 方法中,会遍历所有的 VehicleStubCallbackInterface 实例,并调用它们的 onSetAsyncResults 方法。这里有一个关键的部分是对 mCallbackToResults 的遍历,这个部分目前还没有详细说明,可能会让人感到有些困惑。不过,在后续分析异步 get/set 操作时,这一部分会变得更加清晰。

AidlSubscriptionClient

// packages/services/Car/service/src/com/android/car/AidlVehicleStub.java

final class AidlVehicleStub extends VehicleStub {

    /**
     * 获取一个新的 {@code SubscriptionClient},用于订阅或取消订阅。
     *
     * @param callback 用于接收事件的回调。
     * @return 一个 {@code SubscriptionClient},用于订阅或取消订阅。
     */
    @Override
    public SubscriptionClient newSubscriptionClient(VehicleHalCallback callback) {
        return new AidlSubscriptionClient(callback, mPropValueBuilder);
    }

    // AidlSubscriptionClient 是一个内部类,实现了 IVehicleCallback.Stub 和 SubscriptionClient 接口。
    private class AidlSubscriptionClient extends IVehicleCallback.Stub
            implements SubscriptionClient {
        private final VehicleHalCallback mCallback; // 用于处理车辆属性事件的回调。
        private final HalPropValueBuilder mBuilder; // 用于构建 HAL 属性值的构建器。

        // 构造函数,初始化回调和构建器。
        AidlSubscriptionClient(VehicleHalCallback callback, HalPropValueBuilder builder) {
            mCallback = callback;
            mBuilder = builder;
        }

        // 不支持的操作:获取车辆属性值
        @Override
        public void onGetValues(GetValueResults responses) throws RemoteException {
            throw new UnsupportedOperationException(
                    "onGetValues should never be called on AidlSubscriptionClient");
        }

        // 不支持的操作:设置车辆属性值
        @Override
        public void onSetValues(SetValueResults responses) throws RemoteException {
            throw new UnsupportedOperationException(
                    "onSetValues should never be called on AidlSubscriptionClient");
        }

        // 处理车辆属性事件
        @Override
        public void onPropertyEvent(VehiclePropValues propValues, int sharedMemoryFileCount)
                throws RemoteException {
            // 重新构建稳定的 AIDL Parcelable 对象
            VehiclePropValues origPropValues = (VehiclePropValues)
                    LargeParcelable.reconstructStableAIDLParcelable(propValues,
                            /* keepSharedMemory= */ false);
            // 创建一个新的列表,用于存储 HAL 属性值
            ArrayList<HalPropValue> values = new ArrayList<>(origPropValues.payloads.length);
            for (VehiclePropValue value : origPropValues.payloads) {
                values.add(mBuilder.build(value)); // 使用构建器构建 HAL 属性值
            }
            mCallback.onPropertyEvent(values); // 调用回调处理事件
        }

        // 处理车辆属性设置错误
        @Override
        public void onPropertySetError(VehiclePropErrors errors) throws RemoteException {
            // 重新构建稳定的 AIDL Parcelable 对象
            VehiclePropErrors origErrors = (VehiclePropErrors)
                    LargeParcelable.reconstructStableAIDLParcelable(errors,
                            /* keepSharedMemory= */ false);
            // 创建一个新的列表,用于存储车辆属性错误
            ArrayList<VehiclePropError> errorList = new ArrayList<>(origErrors.payloads.length);
            for (VehiclePropError error : origErrors.payloads) {
                errorList.add(error); // 添加错误到列表
            }
            mCallback.onPropertySetError(errorList); // 调用回调处理错误
        }

        // 订阅车辆属性
        @Override
        public void subscribe(SubscribeOptions[] options)
                throws RemoteException, ServiceSpecificException {
            mAidlVehicle.subscribe(this, options, /* maxSharedMemoryFileCount= */ 2);
        }

        // 取消订阅车辆属性
        @Override
        public void unsubscribe(int prop) throws RemoteException, ServiceSpecificException {
            mAidlVehicle.unsubscribe(this, new int[]{prop});
        }

        // 获取接口的哈希值
        @Override
        public String getInterfaceHash() {
            return IVehicleCallback.HASH;
        }

        // 获取接口的版本号
        @Override
        public int getInterfaceVersion() {
            return IVehicleCallback.VERSION;
        }
    }
}

AidlSubscriptionClient 和 GetSetValuesCallback 都是 AidlVehicleStub 类的内部类,它们都继承自 IVehicleCallback.Stub,但它们支持的操作不同: - AidlSubscriptionClient 只支持 onPropertyEvent 和 onPropertySetError 方法,不支持 onGetValues 和 onSetValues 方法。 - GetSetValuesCallback 只支持 onGetValues 和 onSetValues 方法,不支持 onPropertyEvent 和 onPropertySetError 方法。

以下是一个 Markdown 表格,显示这两个内部类分别支持的接口:

IVehicleCallback.Stub回调方法 AidlSubscriptionClient GetSetValuesCallback
onGetValues 不支持 支持
onSetValues 不支持 支持
onPropertyEvent 支持 不支持
onPropertySetError 支持 不支持

VehicleHalCallback 的定义如下:

// packages/services/Car/service/src/com/android/car/hal/VehicleHalCallback.java

/**
 * VehicleHalCallback 是一个接口,定义了 VehicleHal 支持的回调函数。
 */
public interface VehicleHalCallback {
    /**
     * 当新的车辆属性事件发生时调用。
     */
    void onPropertyEvent(ArrayList<HalPropValue> values);

    /**
     * 当车辆属性设置错误发生时调用。
     */
    void onPropertySetError(ArrayList<VehiclePropError> errors);
}

在这里,我们对 AidlVehicleStub 的分析告一段落。虽然 AidlVehicleStub 类中还有很多内容,但如果仅仅对其进行全面的分析,可能会让人感到困惑。因此,我们首先关注其回调机制。为了更好地理解其功能,我们需要结合具体的使用场景进行分享如何调到 VHAL,这样才能帮助大家更容易地理解其实际应用。

VehicleHal

在深入讲解 VehicleHal 如何调用 VHAL 之前,我们需要详细说明 VehicleHal 是如何接收到 AidlSubscriptionClient 的回调,即 onPropertyEvent 和 onPropertySetError 方法。这一步骤对于理解整个流程至关重要,因此我们详细讲解这一过程。

// packages/services/Car/service/src/com/android/car/hal/VehicleHal.java

/**
 * VehicleHal 类是车辆硬件抽象层(HAL)的抽象。该类负责处理与本地 HAL 的接口,并对接收到的数据进行基本解析(类型检查)。
 * 每个事件会被发送到相应的 {@link HalServiceBase} 实现,由 {@link HalServiceBase} 负责将数据转换为对应的 Car*Service,
 * 以便通过 Car*Manager API 提供给上层应用使用。
 */
public class VehicleHal implements VehicleHalCallback, CarSystemService {

    private final SubscriptionClient mSubscriptionClient; // 订阅客户端,用于接收车辆属性事件。
    private final VehicleStub mVehicleStub; // VehicleStub 实例,用于与车辆 HAL 通信。

    private final HandlerThread mHandlerThread; // 处理线程,用于异步处理事件。
    private final Handler mHandler; // Handler,用于在处理线程上调度任务。

    /**
     * 构造一个新的 {@link VehicleHal} 对象,给定 {@link Context} 和 {@link VehicleStub}。
     */
    public VehicleHal(Context context, VehicleStub vehicle) {
        this(context, /* powerHal= */ null, /* propertyHal= */ null,
                /* inputHal= */ null, /* vmsHal= */ null, /* userHal= */ null,
                /* diagnosticHal= */ null, /* clusterHalService= */ null,
                /* timeHalService= */ null,
                CarServiceUtils.getHandlerThread(VehicleHal.class.getSimpleName()), vehicle);
    }

    /**
     * 构造一个新的 {@link VehicleHal} 对象,给定传递的服务参数。
     * 此方法仅供测试使用。
     */
    @VisibleForTesting
    VehicleHal(Context context,
               PowerHalService powerHal,
               PropertyHalService propertyHal,
               InputHalService inputHal,
               VmsHalService vmsHal,
               UserHalService userHal,
               DiagnosticHalService diagnosticHal,
               ClusterHalService clusterHalService,
               TimeHalService timeHalService,
               HandlerThread handlerThread,
               VehicleStub vehicle) {

        mHandlerThread = handlerThread;
        mHandler = new Handler(mHandlerThread.getLooper());

        mVehicleStub = vehicle;
        mSubscriptionClient = vehicle.newSubscriptionClient(this); // 创建订阅客户端,用于接收事件。
    }

    /**
     * 当新的车辆属性事件发生时调用。
     * @param propValues 包含车辆属性事件的列表。
     */
    @Override
    public void onPropertyEvent(ArrayList<HalPropValue> propValues) {
        // 将事件的处理任务提交到 Handler 的消息队列中,异步执行。
        mHandler.post(() -> handleOnPropertyEvent(propValues));
    }

    /**
     * 当车辆属性设置错误发生时调用。
     * @param errors 包含车辆属性错误的列表。
     */
    @Override
    public void onPropertySetError(ArrayList<VehiclePropError> errors) {
        // 将错误的处理任务提交到 Handler 的消息队列中,异步执行。
        mHandler.post(() -> handleOnPropertySetError(errors));
    }

    /**
     * 处理车辆属性事件。
     * @param propValues 包含车辆属性事件的列表。
     */
    private void handleOnPropertyEvent(List<HalPropValue> propValues) {
        synchronized (mLock) { // 确保线程安全,防止并发修改共享数据。
            for (int i = 0; i < propValues.size(); i++) {
                HalPropValue v = propValues.get(i); // 获取当前事件。
                int propId = v.getPropId(); // 获取事件的属性 ID。

                // 根据属性 ID 查找对应的 HalServiceBase 实例。
                HalServiceBase service = mPropertyHandlers.get(propId);
                if (service == null) {
                    // 如果找不到对应的服务,记录错误日志并跳过该事件。
                    Slogf.e(CarLog.TAG_HAL, "handleOnPropertyEvent: HalService not found for %s", v);
                    continue;
                }

                // 将事件添加到服务的分发列表中。
                service.getDispatchList().add(v);
                // 将服务添加到待分发的服务列表中。
                mServicesToDispatch.add(service);

                // 更新事件日志,用于记录该属性的事件信息。
                VehiclePropertyEventInfo info = mEventLog.get(propId);
                if (info == null) {
                    // 如果该属性的事件信息不存在,则创建新的事件信息并存储。
                    info = new VehiclePropertyEventInfo(v);
                    mEventLog.put(propId, info);
                } else {
                    // 如果事件信息已存在,则添加新的事件。
                    info.addNewEvent(v);
                }
            }
        }

        // 遍历所有待分发的服务,调用其 onHalEvents 方法处理事件。
        for (HalServiceBase s : mServicesToDispatch) {
            s.onHalEvents(s.getDispatchList()); // 处理事件。
            s.getDispatchList().clear(); // 清空服务的分发列表。
        }
        // 清空待分发的服务列表。
        mServicesToDispatch.clear();
    }

    /**
     * 处理车辆属性设置错误。
     * @param errors 包含车辆属性错误的列表。
     */
    private void handleOnPropertySetError(List<VehiclePropError> errors) {
        // 按属性 ID 对错误进行分组。
        SparseArray<ArrayList<VehiclePropError>> errorsByPropId =
                new SparseArray<ArrayList<VehiclePropError>>();
        for (int i = 0; i < errors.size(); i++) {
            VehiclePropError error = errors.get(i); // 获取当前错误。
            int errorCode = error.errorCode; // 获取错误代码。
            int propId = error.propId; // 获取属性 ID。
            int areaId = error.areaId; // 获取区域 ID。

            // 记录警告日志。
            Slogf.w(CarLog.TAG_HAL, "onPropertySetError, errorCode: %d, prop: 0x%x, area: 0x%x",
                    errorCode, propId, areaId);

            // 如果属性 ID 无效,则跳过该错误。
            if (propId == VehicleProperty.INVALID) {
                continue;
            }

            // 获取或创建该属性 ID 对应的错误列表。
            ArrayList<VehiclePropError> propErrors;
            if (errorsByPropId.get(propId) == null) {
                propErrors = new ArrayList<VehiclePropError>();
                errorsByPropId.put(propId, propErrors);
            } else {
                propErrors = errorsByPropId.get(propId);
            }
            // 将错误添加到错误列表中。
            propErrors.add(error);
        }

        // 遍历每个属性 ID 的错误列表,并调用对应服务的 onPropertySetError 方法。
        for (int i = 0; i < errorsByPropId.size(); i++) {
            int propId = errorsByPropId.keyAt(i); // 获取属性 ID。
            HalServiceBase service;
            synchronized (mLock) { // 确保线程安全。
                service = mPropertyHandlers.get(propId); // 获取对应的服务。
            }
            if (service == null) {
                // 如果找不到对应的服务,记录错误日志并跳过。
                Slogf.e(CarLog.TAG_HAL,
                        "handleOnPropertySetError: HalService not found for prop: 0x%x", propId);
                continue;
            }

            // 获取该属性 ID 的错误列表,并调用服务的 onPropertySetError 方法处理错误。
            ArrayList<VehiclePropError> propErrors = errorsByPropId.get(propId);
            service.onPropertySetError(propErrors);
        }
    }
}

这里的流程其实很简单:VehicleHal 接收到来自 AidlVehicleStub 的回调后,会根据事件找到对应的服务,将事件添加到该服务的分发列表中,并将服务本身添加到待分发的服务列表中。接着,VehicleHal 会遍历待分发的服务列表,调用每个服务的 onHalEvents 方法来处理这些事件。

这个服务列表是在 Android 15 CarService源码02-服务初始化 中 VehicleHal.priorityInit() 有分析到,最终是会派发到真正的服务中,如:

public class InputHalService extends HalServiceBase {  
    @Override  
    public void onHalEvents(List<HalPropValue> values) {  
        // 忽略代码  
    }  
}

在这里,有些同学可能会产生疑问:如果我是 PowerHalService,而我不需要处理输入事件,那么在 PowerHalService 的 onHalEvents() 方法中是否会接收到输入事件呢?答案是否定的。在 Android 15 CarService源码02-服务初始化 中,我们分析了 VehicleHal.priorityInit() 方法,了解到系统会查询每个服务支持的属性,并将这些属性与相应的服务关联起来。当时我们只是简单提到这一点,因为仅凭那段代码可能还不太能理解这种设计思路的深意。直到现在,我们看到事件回调给每个服务时,这种设计的合理性才变得更加清晰。通过这种方式,每个服务只会接收到它所关心的事件,从而提高了系统的效率和模块化程度。

现在,背景知识已经介绍完毕,我们可以正式进入主题。

同步获取属性

VehicleHal.get()

// packages/services/Car/service/src/com/android/car/hal/VehicleHal.java

public class VehicleHal implements VehicleHalCallback, CarSystemService {

    /**
     * 返回请求的车辆属性值对应的 {@link HalPropValue}。
     *
     * @param requestedPropValue 请求的属性值。
     * @return 请求的车辆属性值。
     * @throws IllegalArgumentException 如果参数无效。
     * @throws ServiceSpecificException 如果车辆硬件抽象层(VHAL)返回错误。
     */
    public HalPropValue get(HalPropValue requestedPropValue)
            throws IllegalArgumentException, ServiceSpecificException {
        return getValueWithRetry(requestedPropValue);
    }

    /**
     * 尝试获取车辆属性值,支持重试机制。
     *
     * @param value 请求的属性值。
     * @return 获取到的车辆属性值。
     */
    private HalPropValue getValueWithRetry(HalPropValue value) {
        return getValueWithRetry(value, /* maxRetries= */ 0);
    }

    /**
     * 尝试获取车辆属性值,支持指定次数的重试机制。
     *
     * @param value 请求的属性值。
     * @param maxRetries 最大重试次数。
     * @return 获取到的车辆属性值。
     */
    private HalPropValue getValueWithRetry(HalPropValue value, int maxRetries) {
        HalPropValue result;
        // 开始跟踪性能,标记为 "VehicleStub#getValueWithRetry"
        Trace.traceBegin(TRACE_TAG, "VehicleStub#getValueWithRetry");
        try {
            // 调用可重试的获取方法
            result = invokeRetriable((requestValue) -> {
                // 开始跟踪性能,标记为 "VehicleStub#get"
                Trace.traceBegin(TRACE_TAG, "VehicleStub#get");
                try {
                    // 从车辆存根获取请求的属性值
                    return mVehicleStub.get(requestValue);
                } finally {
                    // 结束跟踪性能
                    Trace.traceEnd(TRACE_TAG);
                }
            }, "get", value, mMaxDurationForRetryMs, mSleepBetweenRetryMs, maxRetries);
        } finally {
            // 结束跟踪性能
            Trace.traceEnd(TRACE_TAG);
        }

        // 如果结果为 null,且状态为 OKAY,则抛出异常,表示属性值不可用
        if (result == null) {
            throw new ServiceSpecificException(StatusCode.NOT_AVAILABLE,
                    errorMessage("get", value, "VHAL returns null for property value"));
        }
        return result;
    }
}

这个代码很简单,就是通过 get() 方法获取车辆属性值,最终是调到了 mVehicleStub.get()。我们继续看这个 invokeRetriable() 方法是干什么的。

VehicleHal.invokeRetriable()

// packages/services/Car/service/src/com/android/car/hal/VehicleHal.java

public class VehicleHal implements VehicleHalCallback, CarSystemService {

    /**
     * 调用一个可重试的操作。
     *
     * @param action 可重试的操作接口。
     * @param operation 操作名称,用于日志记录。
     * @param requestValue 请求的属性值。
     * @param maxDurationForRetryMs 最大重试持续时间(毫秒)。
     * @param sleepBetweenRetryMs 每次重试之间的休眠时间(毫秒)。
     * @param maxRetries 最大重试次数。
     * @return 操作结果的 {@link HalPropValue}。
     * @throws ServiceSpecificException 如果服务返回特定错误。
     * @throws IllegalArgumentException 如果参数无效。
     */
    private static HalPropValue invokeRetriable(RetriableAction action,
                                                String operation, HalPropValue requestValue, long maxDurationForRetryMs,
                                                long sleepBetweenRetryMs, int maxRetries)
            throws ServiceSpecificException, IllegalArgumentException {
        // 创建 Retrier 实例,用于处理重试逻辑。
        Retrier retrier = new Retrier(action, operation, requestValue, maxDurationForRetryMs,
                sleepBetweenRetryMs, maxRetries);
        // 调用 Retrier 的 invokeAction 方法执行操作。
        HalPropValue result = retrier.invokeAction();
        if (DBG) {
            // 如果启用了调试日志,则记录操作信息。
            Slogf.d(CarLog.TAG_HAL,
                    "Invoked retriable action for %s - RequestValue: %s - ResultValue: %s, for "
                            + "retrier: %s",
                    operation, requestValue, result, retrier);
        }
        return result;
    }

    /**
     * RetriableAction 接口定义了一个可重试的操作。
     */
    interface RetriableAction {
        /**
         * 执行操作。
         *
         * @param requestValue 请求的属性值。
         * @return 操作结果的 {@link HalPropValue}。
         * @throws ServiceSpecificException 如果服务返回特定错误。
         * @throws RemoteException 如果远程调用失败。
         */
        @Nullable HalPropValue run(HalPropValue requestValue)
                throws ServiceSpecificException, RemoteException;
    }

    /**
     * Retrier 类用于处理可重试的操作。
     */
    private static final class Retrier {
        private final RetriableAction mAction; // 可重试的操作。
        private final String mOperation; // 操作名称。
        private final HalPropValue mRequestValue; // 请求的属性值。
        private final long mMaxDurationForRetryMs; // 最大重试持续时间(毫秒)。
        private final long mSleepBetweenRetryMs; // 每次重试之间的休眠时间(毫秒)。
        private final int mMaxRetries; // 最大重试次数。
        private final long mStartTime; // 开始时间,用于计算重试持续时间。
        private int mRetryCount = 0; // 当前重试次数。

        /**
         * 构造 Retrier 实例。
         */
        Retrier(RetriableAction action,
                String operation, HalPropValue requestValue, long maxDurationForRetryMs,
                long sleepBetweenRetryMs, int maxRetries) {
            mAction = action;
            mOperation = operation;
            mRequestValue = requestValue;
            mMaxDurationForRetryMs = maxDurationForRetryMs;
            mSleepBetweenRetryMs = sleepBetweenRetryMs;
            mMaxRetries = maxRetries;
            mStartTime = uptimeMillis(); // 获取当前系统启动时间。
        }

        /**
         * 执行操作。
         *
         * @return 操作结果的 {@link HalPropValue}。
         * @throws ServiceSpecificException 如果服务返回特定错误。
         * @throws IllegalArgumentException 如果参数无效。
         */
        HalPropValue invokeAction()
                throws ServiceSpecificException, IllegalArgumentException {
            mRetryCount++; // 增加重试计数。

            try {
                // 尝试执行操作。
                return mAction.run(mRequestValue);
            } catch (ServiceSpecificException e) {
                // 根据错误代码处理异常。
                switch (e.errorCode) {
                    case StatusCode.INVALID_ARG:
                        // 如果是无效参数错误,抛出 IllegalArgumentException。
                        throw new IllegalArgumentException(errorMessage(mOperation, mRequestValue,
                                e.toString()));
                    case StatusCode.TRY_AGAIN:
                        // 如果是重试错误,调用 sleepAndTryAgain 方法。
                        return sleepAndTryAgain(e);
                    default:
                        // 其他错误,直接抛出异常。
                        throw e;
                }
            } catch (RemoteException e) {
                // 处理远程异常,调用 sleepAndTryAgain 方法。
                return sleepAndTryAgain(e);
            }
        }

        /**
         * 休眠并重试操作。
         *
         * @param e 引发重试的异常。
         * @return 操作结果的 {@link HalPropValue}。
         * @throws ServiceSpecificException 如果服务返回特定错误。
         * @throws IllegalArgumentException 如果参数无效。
         */
        private HalPropValue sleepAndTryAgain(Exception e)
                throws ServiceSpecificException, IllegalArgumentException {
            // 记录重试日志。
            Slogf.d(CarLog.TAG_HAL, "trying the request: "
                    + toPropertyIdString(mRequestValue.getPropId()) + ", "
                    + toAreaIdString(mRequestValue.getPropId(), mRequestValue.getAreaId())
                    + " again...");
            try {
                // 线程休眠指定时间。
                Thread.sleep(mSleepBetweenRetryMs);
            } catch (InterruptedException interruptedException) {
                // 处理线程中断异常。
                Thread.currentThread().interrupt();
                Slogf.w(CarLog.TAG_HAL, "Thread was interrupted while waiting for vehicle HAL.",
                        interruptedException);
                throw new ServiceSpecificException(StatusCode.INTERNAL_ERROR,
                        errorMessage(mOperation, mRequestValue, interruptedException.toString()));
            }

            // 检查是否达到最大重试次数。
            if (mMaxRetries != 0) {
                if (mMaxRetries == mRetryCount) {
                    throw new ServiceSpecificException(StatusCode.TRY_AGAIN,
                            errorMessage(mOperation, mRequestValue,
                                    "cannot get property after " + mRetryCount + " retries, "
                                            + "last exception: " + e));
                }
            } else if ((uptimeMillis() - mStartTime) >= mMaxDurationForRetryMs) {
                // 检查是否达到最大重试持续时间。
                throw new ServiceSpecificException(StatusCode.TRY_AGAIN,
                        errorMessage(mOperation, mRequestValue,
                                "cannot get property within " + mMaxDurationForRetryMs
                                        + "ms, last exception: " + e));
            }
            // 递归调用 invokeAction 方法进行重试。
            return invokeAction();
        }
    }
}

invokeRetriable() 方法 接受一个 RetriableAction 接口的实例、操作名称、请求的属性值、最大重试持续时间、每次重试之间的休眠时间和最大重试次数作为参数。

也就是说 invokeRetriable() 方法主要是执行一个可重试的操作,并在操作失败时根据特定条件进行重试。

好的我们回顾到主线任务,getValueWithRetry() 里的 mVehicleStub 就是 AidlVehicleStub

AidlVehicleStub.get()

// packages/services/Car/service/src/com/android/car/AidlVehicleStub.java

final class AidlVehicleStub extends VehicleStub {

    /**
     * 获取车辆属性。
     *
     * @param requestedPropValue 要获取的属性。
     * @return 车辆属性值。
     * @throws RemoteException 如果远程操作失败。
     * @throws ServiceSpecificException 如果车辆硬件抽象层(VHAL)返回特定的服务错误。
     */
    @Override
    @Nullable
    public HalPropValue get(HalPropValue requestedPropValue)
            throws RemoteException, ServiceSpecificException {
        long currentTime = System.currentTimeMillis(); // 获取当前时间,用于计算操作延迟。

        // 调用 getOrSetSync 方法同步获取属性值。
        HalPropValue halPropValue = getOrSetSync(requestedPropValue,
                mPendingSyncGetValueRequestPool, // 同步请求池。
                new AsyncGetRequestsHandler(), // 异步请求处理器。
                (result) -> { // 处理获取结果的回调函数。
                    if (result.status != StatusCode.OK) {
                        // 如果结果状态不是 OK,抛出 ServiceSpecificException 异常。
                        throw new ServiceSpecificException(result.status,
                                "failed to get value for " + printPropIdAreaId(requestedPropValue));
                    }
                    if (result.prop == null) {
                        // 如果结果属性为空,返回 null。
                        return null;
                    }
                    // 使用属性值构建器构建并返回 HalPropValue。
                    return mPropValueBuilder.build(result.prop);
                });

        // 记录同步获取操作的延迟时间。
        sVehicleHalGetSyncLatencyHistogram.logSample((float)
                (System.currentTimeMillis() - currentTime));

        return halPropValue; // 返回获取到的车辆属性值。
    }
}

调用 getOrSetSync() 方法同步获取属性值。

AidlVehicleStub.getOrSetSync()

// packages/services/Car/service/src/com/android/car/AidlVehicleStub.java

final class AidlVehicleStub extends VehicleStub {

    /**
     * 用于 {@link get} 或 {@link set} 操作的通用函数。
     *
     * @param requestedPropValue 请求的属性值。
     * @param pendingSyncRequestPool 同步请求池,用于管理同步请求。
     * @param requestsHandler 异步请求处理器,用于处理请求。
     * @param resultHandler 结果处理函数,用于处理返回的结果。
     * @return 车辆属性值。
     * @throws RemoteException 如果远程操作失败。
     * @throws ServiceSpecificException 如果发生特定的服务错误。
     */
    private <VhalResultType> HalPropValue getOrSetSync(
            HalPropValue requestedPropValue,
            PendingSyncRequestPool<VhalResultType> pendingSyncRequestPool,
            AsyncRequestsHandler requestsHandler,
            Function<VhalResultType, HalPropValue> resultHandler)
            throws RemoteException, ServiceSpecificException {
        // 开始跟踪性能,标记为 "AidlVehicleStub#getOrSetSync"
        Trace.traceBegin(TRACE_TAG, "AidlVehicleStub#getOrSetSync");

        // 获取一个唯一的请求 ID。
        long vhalRequestId = mRequestId.getAndIncrement();

        // 将请求添加到同步请求池中,并获取一个 AndroidFuture 对象。
        AndroidFuture<VhalResultType> resultFuture = pendingSyncRequestPool.addRequest(
                vhalRequestId);

        // 分配 VHAL 请求大小。
        requestsHandler.allocateVhalRequestSize(1);
        // 添加 VHAL 请求。
        requestsHandler.addVhalRequest(vhalRequestId, requestedPropValue);
        // 发送请求到 VHAL。
        requestsHandler.sendRequestsToVhal(mAidlVehicle, mGetSetValuesCallback);

        boolean gotResult = false; // 标记是否成功获取结果。

        try {
            // 开始跟踪性能,标记为 "AidlVehicleStub#waitingForSyncResult"
            Trace.traceBegin(TRACE_TAG, "AidlVehicleStub#waitingForSyncResult");

            // 等待结果返回,超时时间为 mSyncOpTimeoutInMs 毫秒。
            VhalResultType result = resultFuture.get(mSyncOpTimeoutInMs,
                    TimeUnit.MILLISECONDS);
            gotResult = true; // 成功获取结果。
            return resultHandler.apply(result); // 使用结果处理函数处理结果并返回。
        } catch (InterruptedException e) {
            // 如果线程被中断,恢复中断状态并抛出异常。
            Thread.currentThread().interrupt();
            throw new ServiceSpecificException(StatusCode.INTERNAL_ERROR,
                    "thread interrupted, possibly exiting the thread");
        } catch (ExecutionException e) {
            // 如果执行失败,抛出异常。
            throw new ServiceSpecificException(StatusCode.INTERNAL_ERROR,
                    "failed to resolve future, error: " + e);
        } catch (TimeoutException e) {
            // 如果超时,抛出异常。
            throw new ServiceSpecificException(StatusCode.INTERNAL_ERROR,
                    "get/set value request timeout for: " + printPropIdAreaId(requestedPropValue));
        } finally {
            // 结束跟踪性能。
            Trace.traceEnd(TRACE_TAG);
            if (!gotResult) {
                // 如果没有成功获取结果,从同步请求池中完成请求。
                resultFuture = pendingSyncRequestPool.finishRequestIfFound(vhalRequestId);
                // 取消未使用的 future。
                resultFuture.cancel(/* mayInterruptIfRunning= */ false);
            }
            // 结束跟踪性能。
            Trace.traceEnd(TRACE_TAG);
        }
    }
}

这里我们重点关注 AsyncRequestsHandler 异步请求处理器。在上一步传进来的异步请求处理器是 AsyncGetRequestsHandler

AsyncGetRequestsHandler

// packages/services/Car/service/src/com/android/car/AidlVehicleStub.java

final class AidlVehicleStub extends VehicleStub {

    /**
     * AsyncGetRequestsHandler 是一个异步请求处理器,用于处理获取车辆属性值的请求。
     * 它继承自 AsyncRequestsHandler,并实现了对 GetValueRequest 和 GetValueRequests 的处理。
     */
    private static final class AsyncGetRequestsHandler
            extends AsyncRequestsHandler<GetValueRequest, GetValueRequests> {
        private GetValueRequest[] mVhalRequestItems; // 存储 VHAL 请求项的数组。
        private int mIndex; // 当前请求项的索引。

        /**
         * 分配 VHAL 请求项的数组大小。
         * @param size 请求项的数量。
         */
        @Override
        public void allocateVhalRequestSize(int size) {
            mVhalRequestItems = new GetValueRequest[size];
        }

        /**
         * 添加一个 VHAL 请求到请求项数组中。
         * @param vhalRequestId 请求的唯一标识符。
         * @param halPropValue 请求的车辆属性值。
         */
        @Override
        public void addVhalRequest(long vhalRequestId, HalPropValue halPropValue) {
            mVhalRequestItems[mIndex] = new GetValueRequest();
            mVhalRequestItems[mIndex].requestId = vhalRequestId;
            mVhalRequestItems[mIndex].prop = (VehiclePropValue) halPropValue.toVehiclePropValue();
            mIndex++;
        }

        /**
         * 获取所有的 VHAL 请求项。
         * @return 请求项数组。
         */
        @Override
        public GetValueRequest[] getRequestItems() {
            return mVhalRequestItems;
        }

        /**
         * 发送请求到车辆硬件抽象层(VHAL)。
         * @param iVehicle IVehicle 接口实例,用于与 VHAL 通信。
         * @param callbackForVhal 用于 VHAL 的回调。
         * @throws RemoteException 如果远程调用失败。
         * @throws ServiceSpecificException 如果发生特定的服务错误。
         */
        @Override
        public void sendRequestsToVhal(IVehicle iVehicle, GetSetValuesCallback callbackForVhal)
                throws RemoteException, ServiceSpecificException {
            // 开始跟踪性能,标记为 "Prepare LargeParcelable"
            Trace.traceBegin(TRACE_TAG, "Prepare LargeParcelable");

            // 创建一个 GetValueRequests 对象,并将请求项数组作为其负载。
            GetValueRequests largeParcelableRequest = new GetValueRequests();
            largeParcelableRequest.payloads = mVhalRequestItems;

            // 将请求转换为大对象可打包的形式。
            // TODO: 如果请求大小太小,不要尝试使用大对象可打包。
            largeParcelableRequest = (GetValueRequests) LargeParcelable.toLargeParcelable(
                    largeParcelableRequest, () -> {
                        GetValueRequests newRequests = new GetValueRequests();
                        newRequests.payloads = new GetValueRequest[0];
                        return newRequests;
                    });

            // 结束跟踪性能。
            Trace.traceEnd(TRACE_TAG);

            // 开始跟踪性能,标记为 "IVehicle#getValues"
            Trace.traceBegin(TRACE_TAG, "IVehicle#getValues");

            // 调用 IVehicle 接口的 getValues 方法,发送请求到 VHAL。
            iVehicle.getValues(callbackForVhal, largeParcelableRequest);

            // 结束跟踪性能。
            Trace.traceEnd(TRACE_TAG);
        }

        /**
         * 获取 VHAL 请求的唯一标识符。
         * @param request 请求对象。
         * @return 请求的唯一标识符。
         */
        @Override
        public long getVhalRequestId(GetValueRequest request) {
            return request.requestId;
        }
    }
}

AsyncGetRequestsHandler 是一个静态内部类,继承自 AsyncRequestsHandler,用于处理获取车辆属性值的异步请求。主要是处理 GetValueRequest 和 GetValueRequests 类型的请求。

这里也看到了其真正的调用了 VHAL 的接口:

iVehicle.getValues(callbackForVhal, largeParcelableRequest);

PendingSyncRequestPool

好的,我们再回到 AidlVehicleStub.get(),调用 getOrSetSync() 传进来的同步请求池 mPendingSyncGetValueRequestPool。

其初始化:

// packages/services/Car/service/src/com/android/car/AidlVehicleStub.java

// 定义一个用于管理同步获取请求的池,类型为 GetValueResult
// 这个池用于存储和管理待处理的同步获取请求,确保线程安全和请求的有序处理
private final PendingSyncRequestPool<GetValueResult> mPendingSyncGetValueRequestPool =  
        new PendingSyncRequestPool<>();

PendingSyncRequestPool 类的实现:

// packages/services/Car/service/src/com/android/car/AidlVehicleStub.java

final class AidlVehicleStub extends VehicleStub {

    /**
     * 一个线程安全的待处理同步请求池。
     * 这个类用于管理和处理待处理的同步请求,确保在多线程环境下的安全性。
     */
    private static final class PendingSyncRequestPool<VhalResultType> {
        // 用于同步的锁对象,确保线程安全
        private final Object mSyncRequestPoolLock = new Object();

        // 使用注解@GuardedBy来指明mPendingRequestsByVhalRequestId的访问需要持有mSyncRequestPoolLock锁
        @GuardedBy("mSyncRequestPoolLock")
        // 存储待处理请求的集合,使用LongSparseArray来高效存储长整型键值对
        private final LongSparseArray<AndroidFuture<VhalResultType>>
                mPendingRequestsByVhalRequestId = new LongSparseArray();

        /**
         * 添加一个请求到待处理请求池中。
         * @param vhalRequestId 请求的唯一标识符
         * @return 返回一个AndroidFuture对象,用于获取请求的结果
         */
        AndroidFuture<VhalResultType> addRequest(long vhalRequestId) {
            synchronized (mSyncRequestPoolLock) {
                // 创建一个新的AndroidFuture对象用于存储请求结果
                AndroidFuture<VhalResultType> resultFuture = new AndroidFuture();
                // 将请求ID和结果对象存储到集合中
                mPendingRequestsByVhalRequestId.put(vhalRequestId, resultFuture);
                return resultFuture;
            }
        }

        /**
         * 如果找到请求,则完成该请求并从池中移除。
         * @param vhalRequestId 请求的唯一标识符
         * @return 返回找到的请求对应的AndroidFuture对象,如果未找到则返回null
         */
        @Nullable AndroidFuture<VhalResultType> finishRequestIfFound(long vhalRequestId) {
            synchronized (mSyncRequestPoolLock) {
                // 获取待处理请求
                AndroidFuture<VhalResultType> pendingRequest =
                        mPendingRequestsByVhalRequestId.get(vhalRequestId);
                // 从集合中移除该请求
                mPendingRequestsByVhalRequestId.remove(vhalRequestId);
                return pendingRequest;
            }
        }

        /**
         * 获取当前待处理请求池中的请求数量。
         * @return 返回请求池中的请求数量
         */
        int size() {
            synchronized (mSyncRequestPoolLock) {
                return mPendingRequestsByVhalRequestId.size();
            }
        }
    }
}
  • LongSparseArray:用于存储请求的集合,键是请求的唯一标识符(vhalRequestId),值是请求的结果占位符(AndroidFuture<VhalResultType>)。
  • mSyncRequestPoolLock:锁对象,所有对 mPendingRequestsByVhalRequestId 的操作都必须在同步块中进行,确保线程安全。
  • addRequest:将一个新的请求添加到同步请求池中,并返回一个 AndroidFuture 对象,供调用者等待结果。
  • finishRequestIfFound:根据请求的唯一标识符查找对应的请求,如果找到则从池中移除并返回结果占位符。

同步设置属性

VehicleHal.set()

// packages/services/Car/service/src/com/android/car/hal/VehicleHal.java

public class VehicleHal implements VehicleHalCallback, CarSystemService {

    /**
     * 设置属性。
     *
     * @param propValue 要设置的属性值
     * @throws IllegalArgumentException 如果参数无效
     * @throws ServiceSpecificException 如果车辆硬件抽象层(VHAL)返回错误
     */
    public void set(HalPropValue propValue)
            throws IllegalArgumentException, ServiceSpecificException {
        // 调用带重试机制的设置方法
        setValueWithRetry(propValue);
    }

    /**
     * 带重试机制的设置属性方法。
     *
     * @param value 要设置的属性值
     */
    private void setValueWithRetry(HalPropValue value) {
        // 调用可重试的操作
        invokeRetriable((requestValue) -> {
            // 开始追踪,用于性能分析
            Trace.traceBegin(TRACE_TAG, "VehicleStub#set");
            // 调用 mVehicleStub 的 set 方法设置属性值
            mVehicleStub.set(requestValue);
            // 结束追踪
            Trace.traceEnd(TRACE_TAG);
            return null;
        }, "set", value, mMaxDurationForRetryMs, mSleepBetweenRetryMs, /* maxRetries= */ 0);
    }
}

这段代码的核心功能是通过 set() 方法来设置车辆的属性值,最终调用 mVehicleStub.set() 来执行具体的设置操作。关于 invokeRetriable() 方法的详细分析已在 同步获取属性 部分进行过,这里不再重复。

AidlVehicleStub.set()

// packages/services/Car/service/src/com/android/car/AidlVehicleStub.java

final class AidlVehicleStub extends VehicleStub {

    /**
     * 设置一个属性。
     *
     * @param requestedPropValue 要设置的属性值。
     * @throws RemoteException 如果远程操作失败。
     * @throws ServiceSpecificException 如果车辆硬件抽象层(VHAL)返回特定的服务错误。
     */
    @Override
    public void set(HalPropValue requestedPropValue) throws RemoteException,
            ServiceSpecificException {
        // 获取当前时间,用于计算操作延迟
        long currentTime = System.currentTimeMillis();

        // 调用同步设置或获取方法
        getOrSetSync(requestedPropValue, 
                     mPendingSyncSetValueRequestPool, // 同步请求池
                     new AsyncSetRequestsHandler(),   // 异步请求处理器
                     (result) -> {                    // 结果处理回调
                         // 如果结果状态不是OK,抛出服务特定异常
                         if (result.status != StatusCode.OK) {
                             throw new ServiceSpecificException(result.status,
                                     "failed to set value for " + printPropIdAreaId(requestedPropValue));
                         }
                         return null;
                     });

        // 记录设置操作的同步延迟时间
        sVehicleHalSetSyncLatencyHistogram.logSample((float)
                (System.currentTimeMillis() - currentTime));
    }
}

与之前分析的 同步获取属性 类似,这里也是通过调用 getOrSetSync 方法来同步设置属性值。不同之处在于传入的参数:mPendingSyncSetValueRequestPool 和 new AsyncSetRequestsHandler()

其中 mPendingSyncSetValueRequestPool 初始化:

// packages/services/Car/service/src/com/android/car/AidlVehicleStub.java

// 定义一个用于管理同步设置请求的池,类型为 SetValueResult
// 这个池用于存储和管理待处理的同步设置请求,确保线程安全和请求的有序处理
private final PendingSyncRequestPool<SetValueResult> mPendingSyncSetValueRequestPool =
        new PendingSyncRequestPool<>();

AsyncSetRequestsHandler

// packages/services/Car/service/src/com/android/car/AidlVehicleStub.java

final class AidlVehicleStub extends VehicleStub {

    /**
     * 异步设置请求处理器。
     * 负责管理和处理异步设置请求。
     */
    private static final class AsyncSetRequestsHandler
            extends AsyncRequestsHandler<SetValueRequest, SetValueRequests> {

        // 存储待处理的设置请求数组
        private SetValueRequest[] mVhalRequestItems;
        // 当前请求的索引
        private int mIndex;

        /**
         * 分配VHAL请求的大小。
         * @param size 请求的数量
         */
        @Override
        public void allocateVhalRequestSize(int size) {
            // 初始化请求数组,大小为指定的请求数量
            mVhalRequestItems = new SetValueRequest[size];
        }

        /**
         * 添加一个VHAL请求。
         * @param vhalRequestId 请求的唯一标识符
         * @param halPropValue 要设置的属性值
         */
        @Override
        public void addVhalRequest(long vhalRequestId, HalPropValue halPropValue) {
            // 创建新的设置请求对象并赋值
            mVhalRequestItems[mIndex] = new SetValueRequest();
            mVhalRequestItems[mIndex].requestId = vhalRequestId;
            mVhalRequestItems[mIndex].value = (VehiclePropValue) halPropValue.toVehiclePropValue();
            // 索引递增
            mIndex++;
        }

        /**
         * 获取所有请求项。
         * @return 返回存储的请求数组
         */
        @Override
        public SetValueRequest[] getRequestItems() {
            return mVhalRequestItems;
        }

        /**
         * 发送请求到VHAL。
         * @param iVehicle 车辆接口
         * @param callbackForVhal VHAL的回调接口
         * @throws RemoteException 如果远程调用失败
         * @throws ServiceSpecificException 如果VHAL返回特定的服务错误
         */
        @Override
        public void sendRequestsToVhal(IVehicle iVehicle, GetSetValuesCallback callbackForVhal)
                throws RemoteException, ServiceSpecificException {
            // 创建一个大的可序列化请求对象
            SetValueRequests largeParcelableRequest = new SetValueRequests();
            largeParcelableRequest.payloads = mVhalRequestItems;
            // 将请求对象转换为大可序列化对象
            largeParcelableRequest = (SetValueRequests) LargeParcelable.toLargeParcelable(
                    largeParcelableRequest, () -> {
                        SetValueRequests newRequests = new SetValueRequests();
                        newRequests.payloads = new SetValueRequest[0];
                        return newRequests;
                    });
            // 通过车辆接口发送请求
            iVehicle.setValues(callbackForVhal, largeParcelableRequest);
        }

        /**
         * 获取VHAL请求的唯一标识符。
         * @param request 设置请求
         * @return 返回请求的唯一标识符
         */
        @Override
        public long getVhalRequestId(SetValueRequest request) {
            return request.requestId;
        }
    }
}

AsyncSetRequestsHandler 的功能与 AsyncGetRequestsHandler 类似。它继承自 AsyncRequestsHandler<SetValueRequest, SetValueRequests>,其中泛型参数分别代表单个请求和请求集合的类型。其主要职责是管理异步设置请求。

这里也看到了其真正的调用了 VHAL 的接口:

iVehicle.setValues(callbackForVhal, largeParcelableRequest);

同步之回调

至此,我们已经完整分析了 同步获取属性同步设置属性 的主要流程。然而,在分析 AidlVehicleStub.getOrSetSync() 时,有一个细节我们暂时跳过,以免打断主线流程。现在,我们可以回过头来深入分析这个细节。

回调 章节中,我们提到 GetSetValuesCallback 用于处理 get/set 操作的回调。当时只是简单提及,没有深入解释。现在有了具体的案例,我们可以重新回顾这个概念,更容易理解其作用。

首先,get/set 操作调的接口如下:

iVehicle.getValues(callbackForVhal, largeParcelableRequest);

iVehicle.setValues(callbackForVhal, largeParcelableRequest);

我们在 接口 章节中,我们提到 IVehicle.aidl 有两个接口:

void getValues(IVehicleCallback callback, in GetValueRequests requests);

void setValues(IVehicleCallback callback, in SetValueRequests requests);

现在我们倒推回去,看 callbackForVhal 到底是什么?

也就是在 AidlVehicleStub.getOrSetSync() 中:

requestsHandler.sendRequestsToVhal(mAidlVehicle, mGetSetValuesCallback);

mGetSetValuesCallback 也就是 GetSetValuesCallback 。

好的,我们现在重新分析 GetSetValuesCallback。根据前面的章节,我们了解到 GetSetValuesCallback.onSetValues() 会调用 AidlVehicleStub.onSetValues()

AidlVehicleStub.onSetValues()

// packages/services/Car/service/src/com/android/car/AidlVehicleStub.java

final class AidlVehicleStub extends VehicleStub {

    /**
     * 处理设置操作的结果。
     *
     * @param responses 包含设置操作结果的 SetValueResults 对象。
     */
    private void onSetValues(SetValueResults responses) {
        // 开始性能追踪,标记操作的开始,用于性能分析
        Trace.traceBegin(TRACE_TAG, "AidlVehicleStub#onSetValues");

        // 重新构建稳定的 AIDL 可序列化对象
        // 这里将传入的 responses 对象转换为一个新的 SetValueResults 对象
        // keepSharedMemory 参数为 false,表示不保留共享内存
        SetValueResults origResponses = (SetValueResults)
                LargeParcelable.reconstructStableAIDLParcelable(responses,
                        /* keepSharedMemory= */ false);

        // 调用 onGetSetValues 方法处理结果
        // 传入参数包括原始响应的有效负载、异步结果处理器和同步设置请求池
        onGetSetValues(origResponses.payloads, new AsyncSetResultsHandler(),
                mPendingSyncSetValueRequestPool);

        // 结束性能追踪,标记操作的结束
        Trace.traceEnd(TRACE_TAG);
    }
}

但是我们没有详细分析 onGetSetValues() ,请注意这里传入的 new AsyncSetResultsHandler()

AidlVehicleStub.onGetSetValues()

// packages/services/Car/service/src/com/android/car/AidlVehicleStub.java

final class AidlVehicleStub extends VehicleStub {

    /**
     * 一个通用函数,用于处理 {@link onGetValues} 和 {@link onSetValues} 的结果。
     *
     * @param vhalResults VHAL 操作返回的结果数组。
     * @param asyncResultsHandler 异步结果处理器,用于处理异步请求的结果。
     * @param pendingSyncRequestPool 同步请求池,用于管理待处理的同步请求。
     * @param <VhalResultType> VHAL 结果的类型。
     */
    private <VhalResultType> void onGetSetValues(VhalResultType[] vhalResults,
                                                 AsyncResultsHandler asyncResultsHandler,
                                                 PendingSyncRequestPool<VhalResultType> pendingSyncRequestPool) {
        // 使用同步块,确保对共享资源的访问是线程安全的
        synchronized (mLock) {
            // 遍历每个 VHAL 结果
            for (VhalResultType result : vhalResults) {
                // 获取 VHAL 请求的唯一标识符
                long vhalRequestId = asyncResultsHandler.getVhalRequestId(result);

                // 检查异步请求池中是否包含该请求 ID
                if (!mPendingAsyncRequestPool.contains(vhalRequestId)) {
                    // 如果异步请求池中找不到该请求 ID,假设它是一个同步请求
                    completePendingSyncRequestLocked(pendingSyncRequestPool, vhalRequestId, result);
                    continue;
                }

                // 完成异步请求池中的请求
                AsyncRequestInfo requestInfo = mPendingAsyncRequestPool.finishRequestIfFound(
                        vhalRequestId);

                // 如果请求信息为空,说明请求可能已经超时、被取消或客户端已终止
                if (requestInfo == null) {
                    Slogf.w(TAG,
                            "No pending request for ID: %s, possibly already timed out, "
                                    + "or cancelled, or the client already died", vhalRequestId);
                    continue;
                }

                // 添加 VHAL 结果到异步结果处理器
                asyncResultsHandler.addVhalResult(requestInfo.getClientCallback(),
                        requestInfo.getServiceRequestId(), result);
            }
        }

        // 开始性能追踪,标记异步结果回调的调用
        Trace.traceBegin(TRACE_TAG, "AidlVehicleStub call async result callback");
        // 调用异步结果处理器的回调方法
        asyncResultsHandler.callVehicleStubCallback();
        // 结束性能追踪
        Trace.traceEnd(TRACE_TAG);
    }
}

这里的 asyncResultsHandler 实际上是 AsyncSetResultsHandler,而 callVehicleStubCallback() 则是用于触发回调的方法。

AsyncResultsHandler

AsyncSetResultsHandler 是一个内部静态类,继承自 AsyncResultsHandler,所以我们先看看 AsyncResultsHandler 类。

// packages/services/Car/service/src/com/android/car/AidlVehicleStub.java

final class AidlVehicleStub extends VehicleStub {

    /**
     * 一个抽象类,用于处理来自 VHAL 的异步获取/设置值的结果。
     * 泛型参数:
     * - VhalResultType:表示 VHAL 返回的结果类型。
     * - VehicleStubResultType:表示最终返回给客户端的结果类型。
     */
    private abstract static class AsyncResultsHandler<VhalResultType, VehicleStubResultType> {

        // 存储回调接口与对应结果列表的映射关系
        protected Map<VehicleStubCallbackInterface, List<VehicleStubResultType>> mCallbackToResults;

        /**
         * 添加一个错误结果,以便稍后通过回调发送给 vehicleStub。
         *
         * @param callback 回调接口,用于通知客户端。
         * @param serviceRequestId 服务请求的唯一标识符。
         * @param errorCodes 错误代码,表示操作失败的原因。
         */
        abstract void addErrorResult(VehicleStubCallbackInterface callback, int serviceRequestId,
                                     CarPropertyErrorCodes errorCodes);

        /**
         * 添加一个 VHAL 结果,以便稍后通过回调发送给 vehicleStub。
         *
         * @param callback 回调接口,用于通知客户端。
         * @param serviceRequestId 服务请求的唯一标识符。
         * @param result VHAL 返回的结果。
         */
        abstract void addVhalResult(VehicleStubCallbackInterface callback, int serviceRequestId,
                                    VhalResultType result);

        /**
         * 将所有存储的结果发送给 vehicleStub。
         */
        abstract void callVehicleStubCallback();

        /**
         * 获取结果的请求 ID。
         *
         * @param vhalRequest VHAL 请求。
         * @return 返回请求的唯一标识符。
         */
        abstract long getVhalRequestId(VhalResultType vhalRequest);

        /**
         * 添加一个结果到回调接口的结果列表中。
         *
         * @param callback 回调接口,用于通知客户端。
         * @param vehicleStubResult 要添加的结果。
         */
        protected void addVehicleStubResult(VehicleStubCallbackInterface callback,
                                            VehicleStubResultType vehicleStubResult) {
            // 如果回调接口的结果列表不存在,则创建一个新的列表
            if (mCallbackToResults.get(callback) == null) {
                mCallbackToResults.put(callback, new ArrayList<>());
            }
            // 将结果添加到回调接口的结果列表中
            mCallbackToResults.get(callback).add(vehicleStubResult);
        }
    }
}

mCallbackToResults 用于存储回调接口与其对应结果列表之间的映射关系。在 addVehicleStubResult() 方法中,将一个结果添加到相应回调接口的结果列表中。

我们在回到 AsyncSetResultsHandler 中。

AsyncSetResultsHandler

// packages/services/Car/service/src/com/android/car/AidlVehicleStub.java


final class AidlVehicleStub extends VehicleStub {  

    /**  
     * 异步设置结果处理器。  
     * 继承自 AsyncResultsHandler,用于处理异步设置操作的结果。  
     * 泛型参数:  
     * - SetValueResult:表示单个设置操作的结果。  
     * - SetVehicleStubAsyncResult:表示最终返回给客户端的异步结果。  
     */  
    private static final class AsyncSetResultsHandler extends  
            AsyncResultsHandler<SetValueResult, SetVehicleStubAsyncResult> {  

        /**  
         * 构造方法,初始化回调接口与结果的映射关系。  
         */  
        AsyncSetResultsHandler() {  
            // 初始化一个 ArrayMap,用于存储回调接口和对应的异步结果列表。  
            mCallbackToResults = new ArrayMap<VehicleStubCallbackInterface,  
                    List<SetVehicleStubAsyncResult>>();  
        }  

        /**  
         * 添加错误结果。  
         *  
         * @param callback 回调接口,用于通知客户端。  
         * @param serviceRequestId 服务请求的唯一标识符。  
         * @param errorCodes 错误代码,表示设置操作失败的原因。  
         */  
        @Override  
        void addErrorResult(VehicleStubCallbackInterface callback, int serviceRequestId,  
                            CarPropertyErrorCodes errorCodes) {  
            // 将错误结果封装为 SetVehicleStubAsyncResult,并添加到结果列表中。  
            addVehicleStubResult(callback,  
                    new SetVehicleStubAsyncResult(serviceRequestId, errorCodes));  
        }  

        /**  
         * 添加 VHAL 返回的结果。  
         *  
         * @param callback 回调接口,用于通知客户端。  
         * @param serviceRequestId 服务请求的唯一标识符。  
         * @param result VHAL 返回的设置结果。  
         */  
        @Override  
        void addVhalResult(VehicleStubCallbackInterface callback, int serviceRequestId,  
                           SetValueResult result) {  
            // 将 VHAL 返回的结果转换为 SetVehicleStubAsyncResult,并添加到结果列表中。  
            addVehicleStubResult(callback, toVehicleStubResult(serviceRequestId, result));  
        }  

        /**  
         * 调用回调接口,将异步结果通知客户端。  
         */  
        @Override  
        void callVehicleStubCallback() {  
            // 遍历所有回调接口和对应的结果列表  
            for (Map.Entry<VehicleStubCallbackInterface, List<SetVehicleStubAsyncResult>> entry :  
                    mCallbackToResults.entrySet()) {  
                // 调用回调接口的 onSetAsyncResults 方法,将结果列表传递给客户端  
                entry.getKey().onSetAsyncResults(entry.getValue());  
            }  
        }  

        /**  
         * 获取 VHAL 请求的唯一标识符。  
         *  
         * @param result 单个设置操作的结果。  
         * @return 返回 VHAL 请求的唯一标识符。  
         */  
        @Override  
        long getVhalRequestId(SetValueResult result) {  
            return result.requestId;  
        }  

        /**  
         * 将 VHAL 返回的结果转换为 VehicleStub 的异步结果。  
         *  
         * @param serviceRequestId 服务请求的唯一标识符。  
         * @param vhalResult VHAL 返回的设置结果。  
         * @return 返回封装后的 SetVehicleStubAsyncResult 对象。  
         */  
        private SetVehicleStubAsyncResult toVehicleStubResult(int serviceRequestId,  
                                                              SetValueResult vhalResult) {  
            // 如果 VHAL 返回的状态不是 OK,则将错误代码转换为 CarPropertyErrorCodes            if (vhalResult.status != StatusCode.OK) {  
                CarPropertyErrorCodes carPropertyErrorCodes =  
                        convertVhalStatusCodeToCarPropertyManagerErrorCodes(vhalResult.status);  
                // 返回包含错误代码的异步结果对象  
                return new SetVehicleStubAsyncResult(serviceRequestId, carPropertyErrorCodes);  
            }  
            // 如果状态是 OK,则返回成功的异步结果对象  
            return new SetVehicleStubAsyncResult(serviceRequestId);  
        }  
    }  
}

callVehicleStubCallback() 方法遍历所有回调接口及其对应的结果列表,调用每个接口的 onSetAsyncResults 方法,将结果列表传递给客户端。而 mCallbackToResults 的内容是在 addVhalResult() 方法中通过调用 addVehicleStubResult() 添加的。

在 AidlVehicleStub.onGetSetValues() 中,由于请求信息为空,addVhalResult() 方法不会被调用,因此在后续调用 callVehicleStubCallback() 时,遍历不会进入 for 循环。

通过分析,我们了解到同步 get/set 操作不会触发回调,因为在这些操作中并不关注回调。那么,为什么我们要在这里详细分析这个过程呢?实际上,这是为了为后续异步 get/set 的分析做好准备。

异步获取属性

VehicleHal.getAsync()

// packages/services/Car/service/src/com/android/car/hal/VehicleHal.java

public class VehicleHal implements VehicleHalCallback, CarSystemService {

    /**
     * 使用 GetVehicleHalRequest 对象列表查询 HalPropValue。
     *
     * <p>此方法使用异步方式获取 HalPropValue。
     *
     * @param getVehicleStubAsyncRequests 包含异步请求的列表。
     * @param getVehicleStubAsyncCallback 用于处理异步请求结果的回调接口。
     */
    public void getAsync(List<VehicleStub.AsyncGetSetRequest> getVehicleStubAsyncRequests,
                         VehicleStub.VehicleStubCallbackInterface getVehicleStubAsyncCallback) {
        // 调用 mVehicleStub 的 getAsync 方法,传递异步请求列表和回调接口
        mVehicleStub.getAsync(getVehicleStubAsyncRequests, getVehicleStubAsyncCallback);
    }
}

接口 中,我们已经介绍了 AsyncGetSetRequest 和 VehicleStubCallbackInterfacegetAsync() 方法用于异步查询车辆属性值。

AidlVehicleStub.getAsync()

// packages/services/Car/service/src/com/android/car/AidlVehicleStub.java

final class AidlVehicleStub extends VehicleStub {

    /**
     * 异步获取车辆属性值。
     *
     * @param getVehicleStubAsyncRequests 包含异步请求的列表,每个请求都是一个 AsyncGetSetRequest 对象。
     * @param getCallback 用于处理异步请求结果的回调接口。
     */
    @Override
    public void getAsync(List<AsyncGetSetRequest> getVehicleStubAsyncRequests,
                         VehicleStubCallbackInterface getCallback) {
        // 调用 getOrSetAsync 方法,传递异步请求列表、回调接口、请求处理器和结果处理器
        getOrSetAsync(getVehicleStubAsyncRequests, getCallback, new AsyncGetRequestsHandler(),
                new AsyncGetResultsHandler(mPropValueBuilder));
    }
}
  • getCallback:回调接口,用于处理异步请求的结果。
  • AsyncGetRequestsHandler:处理异步获取请求的处理器。
  • AsyncGetResultsHandler:处理异步获取结果的处理器。

AidlVehicleStub.getOrSetAsync()

// packages/services/Car/service/src/com/android/car/AidlVehicleStub.java

final class AidlVehicleStub extends VehicleStub {

    /**
     * 用于 {@link getAsync} 或 {@link setAsync} 的通用函数。
     *
     * @param vehicleStubAsyncRequests 包含异步请求的列表,每个请求都是一个 AsyncGetSetRequest 对象。
     * @param vehicleStubCallback 用于处理异步请求结果的回调接口。
     * @param asyncRequestsHandler 异步请求处理器,用于处理和发送请求。
     * @param asyncResultsHandler 异步结果处理器,用于处理请求的结果。
     * @param <VhalRequestType> VHAL 请求的类型。
     * @param <VhalRequestsType> VHAL 请求列表的类型。
     */
    private <VhalRequestType, VhalRequestsType> void getOrSetAsync(
            List<AsyncGetSetRequest> vehicleStubAsyncRequests,
            VehicleStubCallbackInterface vehicleStubCallback,
            AsyncRequestsHandler<VhalRequestType, VhalRequestsType> asyncRequestsHandler,
            AsyncResultsHandler asyncResultsHandler) {

        // 准备并转换异步请求,将请求和回调接口传递给请求处理器
        prepareAndConvertAsyncRequests(vehicleStubAsyncRequests, vehicleStubCallback,
                asyncRequestsHandler);

        try {
            // 通过请求处理器将请求发送到 VHAL
            asyncRequestsHandler.sendRequestsToVhal(mAidlVehicle, mGetSetValuesCallback);
        } catch (RemoteException e) {
            // 处理来自 VHAL 的远程异常
            // 创建一个内部错误代码对象
            handleAsyncExceptionFromVhal(
                    asyncRequestsHandler,
                    vehicleStubCallback,
                    new CarPropertyErrorCodes(
                            CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR,
                            /* vendorErrorCode= */ 0,
                            /* systemErrorCode= */ 0),
                    asyncResultsHandler);
            return;
        } catch (ServiceSpecificException e) {
            // 处理服务特定异常,将 VHAL 状态码转换为 CarPropertyManager 错误代码
            CarPropertyErrorCodes carPropertyErrorCodes =
                    convertVhalStatusCodeToCarPropertyManagerErrorCodes(e.errorCode);
            handleAsyncExceptionFromVhal(asyncRequestsHandler, vehicleStubCallback,
                    carPropertyErrorCodes, asyncResultsHandler);
            return;
        }
    }
}

调用 prepareAndConvertAsyncRequests() 方法,准备并转换异步请求。将请求和回调接口传递给 asyncRequestsHandler 进行处理。

AidlVehicleStub.prepareAndConvertAsyncRequests()

// packages/services/Car/service/src/com/android/car/AidlVehicleStub.java

final class AidlVehicleStub extends VehicleStub {  

    /**  
     * 准备来自客户端的异步 get/set 请求,并将其转换为 VHAL 请求。  
     *  
     * <p> 该方法执行以下操作:  
     * <ul>  
     * <li> 添加一个客户端回调死亡监听器,当客户端死亡时清除挂起的请求。  
     * <li> 将异步请求存储到挂起请求映射中。  
     * <li> 为每个客户端请求生成一个唯一的 VHAL 请求 ID,并将请求转换为 VHAL 请求类型。  
     * <li> 将每个请求的超时信息存储到映射中,以便稍后注册超时处理程序。  
     * <li> 将 VHAL 请求项转换为一个大型可序列化类。  
     * </ul>  
     *  
     * @param vehicleStubRequests 包含异步请求的列表。  
     * @param clientCallback 用于处理异步请求结果的客户端回调接口。  
     * @param asyncRequestsHandler 异步请求处理器,用于处理和发送请求。  
     * @param <VhalRequestType> VHAL 请求的类型。  
     * @param <VhalRequestsType> VHAL 请求列表的类型。  
     */  
    private <VhalRequestType, VhalRequestsType> void prepareAndConvertAsyncRequests(  
            List<AsyncGetSetRequest> vehicleStubRequests,  
            VehicleStubCallbackInterface clientCallback,  
            AsyncRequestsHandler<VhalRequestType, VhalRequestsType> asyncRequestsHandler) {  

        // 为 VHAL 请求分配大小,准备处理指定数量的请求  
        asyncRequestsHandler.allocateVhalRequestSize(vehicleStubRequests.size());  

        synchronized (mLock) {  
            // 添加死亡接收者,以便在回调死亡时清除所有客户端信息。  
            // 注意,这必须与存储客户端信息到映射的代码在同一个临界区内。  
            // 这确保即使客户端在添加客户端信息时中途死亡,也会等待所有客户端被添加后再移除它们。  
            try {  
                clientCallback.linkToDeath(() -> {  
                    // 该函数将在不同的线程中调用。需要通过锁进行保护,以确保在移除回调之前  
                    // 'prepareAndConvertAsyncRequests' 完成。  
                    synchronized (mLock) {  
                        mPendingAsyncRequestPool.removeRequestsForCallback(clientCallback);  
                    }  
                });  
            } catch (RemoteException e) {  
                // Binder 已经死亡。  
                throw new IllegalStateException("无法将回调链接到死亡接收者,客户端可能已经死亡");  
            }  

            List<AsyncRequestInfo> requestInfoList = new ArrayList<>();  
            for (int i = 0; i < vehicleStubRequests.size(); i++) {  
                AsyncGetSetRequest vehicleStubRequest = vehicleStubRequests.get(i);  
                // 为每个请求生成一个唯一的 VHAL 请求 ID                long vhalRequestId = mRequestId.getAndIncrement();  
                // 将请求添加到 VHAL 请求处理器中  
                asyncRequestsHandler.addVhalRequest(vhalRequestId,  
                        vehicleStubRequest.getHalPropValue());  
                // 创建请求信息对象,并添加到请求信息列表中  
                requestInfoList.add(new AsyncRequestInfo(  
                        vhalRequestId, vehicleStubRequest.getServiceRequestId(), clientCallback,  
                        vehicleStubRequest.getTimeoutUptimeMs()));  
            }  
            // 将请求信息列表添加到挂起的异步请求池中  
            mPendingAsyncRequestPool.addRequests(requestInfoList);  
        }  
    }  
}

对于每个请求,即 vehicleStubRequests.size(),我们遍历并调用 AsyncGetRequestsHandler.addVhalRequest 方法,将请求添加到 VHAL 请求处理器中。这部分不再详细分析,我们重点关注创建 AsyncRequestInfo 对象,并将其添加到 requestInfoList 列表中。最后,将 requestInfoList 列表添加到 mPendingAsyncRequestPool 中。

AsyncRequestInfo

// packages/services/Car/service/src/com/android/car/AidlVehicleStub.java

final class AidlVehicleStub extends VehicleStub {

    /**
     * 异步请求信息类,用于存储异步请求的详细信息。
     * 实现了 LongRequestIdWithTimeout 接口,提供请求 ID 和超时时间的访问方法。
     */
    private static class AsyncRequestInfo implements LongRequestIdWithTimeout {

        // 服务请求的唯一标识符
        private final int mServiceRequestId;
        // 客户端回调接口,用于处理请求结果
        private final VehicleStubCallbackInterface mClientCallback;
        // 请求的超时时间,以系统启动时间为基准的毫秒数
        private final long mTimeoutUptimeMs;
        // VHAL 请求的唯一标识符
        private final long mVhalRequestId;

        /**
         * 构造方法,初始化异步请求信息对象。
         *
         * @param vhalRequestId VHAL 请求的唯一标识符。
         * @param serviceRequestId 服务请求的唯一标识符。
         * @param clientCallback 客户端回调接口。
         * @param timeoutUptimeMs 请求的超时时间。
         */
        private AsyncRequestInfo(
                long vhalRequestId,
                int serviceRequestId,
                VehicleStubCallbackInterface clientCallback,
                long timeoutUptimeMs) {
            mVhalRequestId = vhalRequestId;
            mServiceRequestId = serviceRequestId;
            mClientCallback = clientCallback;
            mTimeoutUptimeMs = timeoutUptimeMs;
        }

        /**
         * 获取 VHAL 请求的唯一标识符。
         *
         * @return VHAL 请求的唯一标识符。
         */
        @Override
        public long getRequestId() {
            return mVhalRequestId;
        }

        /**
         * 获取请求的超时时间。
         *
         * @return 请求的超时时间,以系统启动时间为基准的毫秒数。
         */
        @Override
        public long getTimeoutUptimeMs() {
            return mTimeoutUptimeMs;
        }

        /**
         * 获取服务请求的唯一标识符。
         *
         * @return 服务请求的唯一标识符。
         */
        public int getServiceRequestId() {
            return mServiceRequestId;
        }

        /**
         * 获取客户端回调接口。
         *
         * @return 客户端回调接口。
         */
        public VehicleStubCallbackInterface getClientCallback() {
            return mClientCallback;
        }
    }
}

这里的 mClientCallback 其实就是前面 VehicleHal.getAsync() 传进来的 VehicleStubCallbackInterface 。

回到 AidlVehicleStub.prepareAndConvertAsyncRequests() 函数中,将 requestInfoList 列表添加到 mPendingAsyncRequestPool 中我们不展开分析了。

那就继续返回到 AidlVehicleStub.getOrSetAsync(),调用了:

asyncRequestsHandler.sendRequestsToVhal(mAidlVehicle, mGetSetValuesCallback);

根据前面的分析,asyncRequestsHandler 就是AsyncGetRequestsHandler,这里我们又看到了 mGetSetValuesCallback。 关于 mGetSetValuesCallback 回顾 同步之回调 即可。

AsyncGetRequestsHandler.sendRequestsToVhal()

// packages/services/Car/service/src/com/android/car/AidlVehicleStub.java

final class AidlVehicleStub extends VehicleStub {

    /**
     * 异步获取请求处理器类。
     * 继承自 AsyncRequestsHandler,用于处理获取车辆属性值的异步请求。
     */
    private static final class AsyncGetRequestsHandler
            extends AsyncRequestsHandler<GetValueRequest, GetValueRequests> {

        /**
         * 将请求发送到 VHAL(车辆硬件抽象层)。
         *
         * @param iVehicle VHAL 接口,用于与车辆硬件进行通信。
         * @param callbackForVhal 用于处理 VHAL 响应的回调接口。
         * @throws RemoteException 如果与 VHAL 的通信发生远程异常。
         * @throws ServiceSpecificException 如果发生服务特定的异常。
         */
        @Override
        public void sendRequestsToVhal(IVehicle iVehicle, GetSetValuesCallback callbackForVhal)
                throws RemoteException, ServiceSpecificException {
            // 开始跟踪 "Prepare LargeParcelable" 操作
            Trace.traceBegin(TRACE_TAG, "Prepare LargeParcelable");

            // 创建一个新的 GetValueRequests 对象,用于存储请求的有效负载
            GetValueRequests largeParcelableRequest = new GetValueRequests();
            largeParcelableRequest.payloads = mVhalRequestItems;

            // TODO: 如果请求大小太小,不要尝试使用大型可序列化对象。
            largeParcelableRequest = (GetValueRequests) LargeParcelable.toLargeParcelable(
                    largeParcelableRequest, () -> {
                        // 创建一个新的 GetValueRequests 对象,初始化为空的有效负载数组
                        GetValueRequests newRequests = new GetValueRequests();
                        newRequests.payloads = new GetValueRequest[0];
                        return newRequests;
                    });

            // 结束跟踪 "Prepare LargeParcelable" 操作
            Trace.traceEnd(TRACE_TAG);

            // 开始跟踪 "IVehicle#getValues" 操作
            Trace.traceBegin(TRACE_TAG, "IVehicle#getValues");

            // 调用 VHAL 的 getValues 方法,传递回调接口和请求对象
            iVehicle.getValues(callbackForVhal, largeParcelableRequest);

            // 结束跟踪 "IVehicle#getValues" 操作
            Trace.traceEnd(TRACE_TAG);
        }
    }
}

看到了,这里跟前面 同步获取属性 是一样的,唯一不同的是在 AidlVehicleStub.prepareAndConvertAsyncRequests() 时设置了回调接口 VehicleStubCallbackInterface 给 mPendingAsyncRequestPool。

iVehicle.getValues(callbackForVhal, largeParcelableRequest);

因此,接下来将按顺序依次调用以下方法:

  1. AidlVehicleStub.onSetValues()
  2. AidlVehicleStub.onGetSetValues()
  3. AsyncGetResultsHandler.callVehicleStubCallback()
  4. VehicleStubCallbackInterface.onGetAsyncResults()

由于之前已经设置了 VehicleStubCallbackInterface,因此在 callVehicleStubCallback() 遍历时能够找到客户端,从而成功回调到 onGetAsyncResults()

详情可以直接参考 同步之回调 这里不再赘述。

异步设置属性

VehicleHal.setAsync()

// packages/services/Car/service/src/com/android/car/hal/VehicleHal.java

public class VehicleHal implements VehicleHalCallback, CarSystemService {

    /**
     * 异步设置车辆属性值。
     *
     * @param setVehicleStubAsyncRequests 包含异步设置请求的列表,每个请求都是一个 AsyncGetSetRequest 对象。
     * @param setVehicleStubAsyncCallback 用于处理异步设置请求结果的回调接口。
     */
    public void setAsync(List<VehicleStub.AsyncGetSetRequest> setVehicleStubAsyncRequests,
                         VehicleStub.VehicleStubCallbackInterface setVehicleStubAsyncCallback) {
        // 调用 mVehicleStub 的 setAsync 方法,传递异步设置请求列表和回调接口
        mVehicleStub.setAsync(setVehicleStubAsyncRequests, setVehicleStubAsyncCallback);
    }
}

同 VehicleHal.getAsync() ,这里依然传了一个回调进去。

AidlVehicleStub.setAsync()

// packages/services/Car/service/src/com/android/car/AidlVehicleStub.java

final class AidlVehicleStub extends VehicleStub {

    /**
     * 异步设置车辆属性值。
     *
     * @param setVehicleStubAsyncRequests 包含异步设置请求的列表,每个请求都是一个 AsyncGetSetRequest 对象。
     * @param setCallback 用于处理异步设置请求结果的回调接口。
     */
    @Override
    public void setAsync(List<AsyncGetSetRequest> setVehicleStubAsyncRequests,
                         VehicleStubCallbackInterface setCallback) {
        // 调用 getOrSetAsync 方法,传递异步设置请求列表、回调接口、设置请求处理器和结果处理器
        getOrSetAsync(setVehicleStubAsyncRequests, setCallback, new AsyncSetRequestsHandler(),
                new AsyncSetResultsHandler());
    }
}

跟 AidlVehicleStub.getAsync() 一样调用同样的接口 AidlVehicleStub.getOrSetAsync()。但传的参数不太一样。 - setCallback:回调接口,用于处理异步请求的结果。 - AsyncSetRequestsHandler:用于处理异步设置请求的处理器。 - AsyncSetResultsHandler:用于处理异步设置结果的处理器。

接下来的流程跟 异步获取属性 大同小异,最终会通过回调调到 VehicleStubCallbackInterface.onSetAsyncResults()

订阅

在前面的分析中,我们没有详细介绍如何在VehicleHal中订阅车辆属性。现在,我们将对此进行补充说明。

VehicleHal.subscribePropertySafe()

// packages/services/Car/service/src/com/android/car/hal/VehicleHal.java

public class VehicleHal implements VehicleHalCallback, CarSystemService {

    /**
     * 安全订阅车辆属性的方法。
     * 该方法类似于 {@link #subscribeProperty(HalServiceBase, int)},但捕获并记录所有异常。
     * 
     * @param service 实现了 HalServiceBase 的服务对象,用于处理订阅的属性。
     * @param property 要订阅的车辆属性的标识符。
     */
    public void subscribePropertySafe(HalServiceBase service, int property) {
        try {
            // 尝试订阅指定的车辆属性
            subscribeProperty(service, property);
        } catch (IllegalArgumentException | ServiceSpecificException e) {
            // 捕获 IllegalArgumentException 和 ServiceSpecificException 异常
            // 记录警告日志,包含失败的属性标识符和异常信息
            Slogf.w(CarLog.TAG_HAL, "Failed to subscribe for property: "
                    + VehiclePropertyIds.toString(property), e);
        }
    }
}

subscribePropertySafe() 方法用于安全地订阅车辆属性。与subscribeProperty() 方法类似,但增加了异常处理机制,以确保在订阅过程中出现异常时不会导致程序崩溃。

VehicleHal.subscribeProperty()

// packages/services/Car/service/src/com/android/car/hal/VehicleHal.java

public class VehicleHal implements VehicleHalCallback, CarSystemService {

    /**
     * 订阅给定的属性,采样率默认为0,没有提供特殊标志。
     *
     * @param service 拥有该属性的HalService
     * @param property 要订阅的属性ID(VehicleProperty)
     * @throws IllegalArgumentException 如果属性不受VHAL支持时抛出
     * @throws ServiceSpecificException 如果VHAL返回错误或与VHAL失去连接时抛出
     * @see #subscribeProperty(HalServiceBase, int, float)
     */
    public void subscribeProperty(HalServiceBase service, int property)
            throws IllegalArgumentException, ServiceSpecificException {
        // 调用重载方法,设置采样率为0
        subscribeProperty(service, property, /* samplingRateHz= */ 0f);
    }

    /**
     * 订阅给定的属性。只有拥有该属性的Hal服务可以订阅它。
     *
     * @param service 拥有该属性的HalService
     * @param property 要订阅的属性ID(VehicleProperty)
     * @param samplingRateHz 连续属性的采样率,以赫兹为单位
     * @throws IllegalArgumentException 如果属性不受VHAL支持时抛出
     * @throws ServiceSpecificException 如果VHAL返回错误或与VHAL失去连接时抛出
     */
    public void subscribeProperty(HalServiceBase service, int property, float samplingRateHz)
            throws IllegalArgumentException, ServiceSpecificException {
        // 创建订阅选项对象,包含属性ID和采样率
        HalSubscribeOptions options = new HalSubscribeOptions(property, new int[0], samplingRateHz);
        // 调用另一个重载方法,传递订阅选项列表
        subscribeProperty(service, List.of(options));
    }

    /**
     * 订阅给定的属性。只有拥有该属性的Hal服务可以订阅它。
     *
     * @param service 拥有该属性的HalService
     * @param halSubscribeOptions 订阅VHAL所需的信息
     * @throws IllegalArgumentException 如果属性不受VHAL支持时抛出
     * @throws ServiceSpecificException 如果VHAL返回错误或与VHAL失去连接时抛出
     */
    public void subscribeProperty(HalServiceBase service, List<HalSubscribeOptions>
            halSubscribeOptions) throws IllegalArgumentException, ServiceSpecificException {
        // 同步块,确保线程安全
        synchronized (mLock) {
            // 克隆当前的订阅状态,以便在失败时恢复
            PairSparseArray<RateInfo> previousState = cloneState(mRateInfoByPropIdAreaId);
            // 创建VHAL订阅选项
            SubscribeOptions[] subscribeOptions = createVhalSubscribeOptionsLocked(
                    service, halSubscribeOptions);
            // 如果没有有效的订阅选项,记录调试日志并返回
            if (subscribeOptions.length == 0) {
                if (DBG) {
                    Slogf.d(CarLog.TAG_HAL,
                            "Ignore the subscribeProperty request, SubscribeOptions is length 0");
                }
                return;
            }
            try {
                // 尝试订阅属性
                mSubscriptionClient.subscribe(subscribeOptions);
            } catch (RemoteException e) {
                // 如果发生远程异常,恢复之前的状态并记录警告日志
                mRateInfoByPropIdAreaId = previousState;
                Slogf.w(CarLog.TAG_HAL, "Failed to subscribe, connection to VHAL failed", e);
                // 将RemoteException转换为ServiceSpecificException,以便传递给客户端
                throw new ServiceSpecificException(StatusCode.INTERNAL_ERROR,
                        "Failed to subscribe, connection to VHAL failed, error: " + e);
            } catch (ServiceSpecificException e) {
                // 如果发生特定服务异常,恢复之前的状态并记录警告日志
                mRateInfoByPropIdAreaId = previousState;
                Slogf.w(CarLog.TAG_HAL, "Failed to subscribe, received error from VHAL", e);
                throw e;
            }
        }
    }
}

subscribeProperty() 提供了多种重载方法,以支持不同的订阅需求(例如不同的采样率)。

VehicleHal.createVhalSubscribeOptionsLocked()

// packages/services/Car/service/src/com/android/car/hal/VehicleHal.java

public class VehicleHal implements VehicleHalCallback, CarSystemService {

    /**
     * 将 {@link HalSubscribeOptions} 转换为 {@link SubscribeOptions},后者是 VHAL 使用的数据结构。
     * 
     * @param service 拥有该属性的 HalService
     * @param halSubscribeOptions 包含订阅信息的 HalSubscribeOptions 列表
     * @return 转换后的 SubscribeOptions 数组
     * @throws IllegalArgumentException 如果属性不受支持或无法订阅时抛出
     */
    @GuardedBy("mLock")
    private SubscribeOptions[] createVhalSubscribeOptionsLocked(HalServiceBase service,
                                                                List<HalSubscribeOptions> halSubscribeOptions) throws IllegalArgumentException {
        // 如果调试模式开启,记录创建订阅选项的日志
        if (DBG) {
            Slogf.d(CarLog.TAG_HAL, "creating subscribeOptions from HalSubscribeOptions of size: "
                    + halSubscribeOptions.size());
        }
        // 创建一个用于存储订阅选项的列表
        List<SubscribeOptions> subscribeOptionsList = new ArrayList<>();
        for (int i = 0; i < halSubscribeOptions.size(); i++) {
            // 获取当前的 HalSubscribeOptions
            HalSubscribeOptions halSubscribeOption = halSubscribeOptions.get(i);
            int property = halSubscribeOption.getHalPropId(); // 获取属性ID
            int[] areaIds = halSubscribeOption.getAreaId(); // 获取区域ID
            float samplingRateHz = halSubscribeOption.getUpdateRateHz(); // 获取采样率
            boolean enableVariableUpdateRate = halSubscribeOption.isVariableUpdateRateEnabled(); // 是否启用可变更新率
            float resolution = halSubscribeOption.getResolution(); // 获取分辨率

            // 从所有属性中获取当前属性的配置
            HalPropConfig config = mAllProperties.get(property);

            // 如果配置为空,抛出异常
            if (config == null) {
                throw new IllegalArgumentException("subscribe error: "
                        + toPropertyIdString(property) + " is not supported");
            }

            // 检查是否启用可变更新率
            if (enableVariableUpdateRate) {
                // 如果属性不是连续的,禁用可变更新率
                if (config.getChangeMode() != VehiclePropertyChangeMode.CONTINUOUS) {
                    enableVariableUpdateRate = false;
                    Slogf.w(CarLog.TAG_HAL, "VUR is always off for non-continuous property: "
                            + toPropertyIdString(property));
                }
                // 如果特性标志不支持可变更新率,禁用可变更新率
                if (!mFeatureFlags.variableUpdateRate()) {
                    enableVariableUpdateRate = false;
                    Slogf.w(CarLog.TAG_HAL, "VUR feature is not enabled, VUR is always off");
                }
            }

            // 检查分辨率
            if (resolution != 0.0f) {
                // 如果属性不是连续的,设置分辨率为0
                if (config.getChangeMode() != VehiclePropertyChangeMode.CONTINUOUS) {
                    resolution = 0.0f;
                    Slogf.w(CarLog.TAG_HAL, "resolution is always 0 for non-continuous property: "
                            + toPropertyIdString(property));
                }
                // 如果特性标志不支持分辨率,设置分辨率为0
                if (!mFeatureFlags.subscriptionWithResolution()) {
                    resolution = 0.0f;
                    Slogf.w(CarLog.TAG_HAL,
                            "Resolution feature is not enabled, resolution is always 0");
                }
            }

            // 如果属性是静态的,忽略订阅请求
            if (isStaticProperty(config)) {
                Slogf.w(CarLog.TAG_HAL, "Ignore subscribing to static property: "
                        + toPropertyIdString(property));
                continue;
            }

            // 检查区域ID
            if (areaIds.length == 0) {
                // 如果属性不可订阅,抛出异常
                if (!isPropertySubscribable(config)) {
                    throw new IllegalArgumentException("Property: " + toPropertyIdString(property)
                            + " is not subscribable");
                }
                // 获取所有区域ID
                areaIds = getAllAreaIdsFromPropertyId(config);
            } else {
                // 检查每个区域ID的可访问性
                for (int j = 0; j < areaIds.length; j++) {
                    Integer access = mAccessByPropIdAreaId.get(config.getPropId(), areaIds[j]);
                    if (access == null) {
                        throw new IllegalArgumentException(
                                "Cannot subscribe to " + toPropertyIdString(property)
                                        + " at areaId " + toAreaIdString(property, areaIds[j])
                                        + " the property does not have the requested areaId");
                    }
                    if (!isPropIdAreaIdReadable(config, access.intValue())) {
                        throw new IllegalArgumentException(
                                "Cannot subscribe to " + toPropertyIdString(property)
                                        + " at areaId " + toAreaIdString(property, areaIds[j])
                                        + " the property's access mode does not contain READ");
                    }
                }
            }

            // 创建订阅选项对象
            SubscribeOptions opts = new SubscribeOptions();
            opts.propId = property;
            opts.sampleRate = samplingRateHz;
            opts.enableVariableUpdateRate = enableVariableUpdateRate;
            opts.resolution = resolution;

            // 创建速率信息对象
            RateInfo rateInfo = new RateInfo(samplingRateHz, enableVariableUpdateRate, resolution);
            // 过滤具有相同速率信息的区域ID
            int[] filteredAreaIds = filterAreaIdsWithSameRateInfo(property, areaIds, rateInfo);
            opts.areaIds = filteredAreaIds;

            // 如果没有有效的区域ID,记录调试日志并继续
            if (opts.areaIds.length == 0) {
                if (DBG) {
                    Slogf.d(CarLog.TAG_HAL, "property: " + VehiclePropertyIds.toString(property)
                            + " is already subscribed at rate: " + samplingRateHz + " hz");
                }
                continue;
            }

            // 确保服务是属性的所有者
            assertServiceOwnerLocked(service, property);

            // 更新每个区域ID的订阅速率信息
            for (int j = 0; j < filteredAreaIds.length; j++) {
                if (DBG) {
                    Slogf.d(CarLog.TAG_HAL, "Update subscription rate for propertyId:"
                                    + " %s, areaId: %d, SampleRateHz: %f, enableVur: %b,"
                                    + " resolution: %f",
                            VehiclePropertyIds.toString(opts.propId), filteredAreaIds[j],
                            samplingRateHz, enableVariableUpdateRate, resolution);
                }
                mRateInfoByPropIdAreaId.put(property, filteredAreaIds[j], rateInfo);
            }

            // 将订阅选项添加到列表中
            subscribeOptionsList.add(opts);
        }

        // 返回订阅选项数组
        return subscribeOptionsList.toArray(new SubscribeOptions[0]);
    }
}

createVhalSubscribeOptionsLocked() 方法用于将 HalSubscribeOptions 转换为 SubscribeOptions ,后者是VHAL使用的数据结构。

回到 VehicleHal.subscribeProperty() 中,调用 mSubscriptionClient.subscribe(subscribeOptions) 订阅属性,接收AidlSubscriptionClient的onPropertyEvent()回调。关于其接收机制以及如何回调到 HalServiceBase 的详细分析,请参见前面的 VehicleHal 章节。

取消订阅

VehicleHal.unsubscribePropertySafe()

// packages/services/Car/service/src/com/android/car/hal/VehicleHal.java

public class VehicleHal implements VehicleHalCallback, CarSystemService {

    /**
     * 安全地取消订阅车辆属性的方法。
     * 该方法类似于 {@link unsubscribeProperty},但会记录发生的异常。
     * 
     * @param service 拥有该属性的 HalService
     * @param property 要取消订阅的属性ID(VehicleProperty)
     */
    public void unsubscribePropertySafe(HalServiceBase service, int property) {
        try {
            // 尝试取消订阅指定的车辆属性
            unsubscribeProperty(service, property);
        } catch (ServiceSpecificException e) {
            // 捕获 ServiceSpecificException 异常
            // 记录警告日志,包含失败的属性标识符和异常信息
            Slogf.w(CarLog.TAG_SERVICE, "Failed to unsubscribe: "
                    + toPropertyIdString(property), e);
        }
    }
}

unsubscribePropertySafe() 方法用于安全地取消订阅车辆属性。与unsubscribeProperty() 方法类似,但增加了异常处理机制,以确保在取消订阅过程中出现异常时不会导致程序崩溃。

VehicleHal.unsubscribeProperty()

// packages/services/Car/service/src/com/android/car/hal/VehicleHal.java

public class VehicleHal implements VehicleHalCallback, CarSystemService {

    /**
     * 取消订阅指定属性的通知,并传入相关的 HAL 服务。
     * 
     * @param service 拥有该属性的 HalService
     * @param property 要取消订阅的属性ID(VehicleProperty)
     * @throws ServiceSpecificException 如果与VHAL的连接失败或VHAL返回错误时抛出
     */
    public void unsubscribeProperty(HalServiceBase service, int property)
            throws ServiceSpecificException {
        // 如果调试模式开启,记录取消订阅的日志
        if (DBG) {
            Slogf.d(CarLog.TAG_HAL, "unsubscribeProperty, service:" + service
                    + ", " + toPropertyIdString(property));
        }

        // 同步块,确保线程安全
        synchronized (mLock) {
            // 获取属性的配置
            HalPropConfig config = mAllProperties.get(property);
            // 如果配置不存在,记录警告日志并返回
            if (config == null) {
                Slogf.w(CarLog.TAG_HAL, "unsubscribeProperty " + toPropertyIdString(property)
                        + " does not exist");
                return;
            }
            // 如果属性是静态的,记录警告日志并返回
            if (isStaticProperty(config)) {
                Slogf.w(CarLog.TAG_HAL, "Unsubscribe to a static property: "
                        + toPropertyIdString(property) + ", do nothing");
                return;
            }

            // 确保服务是属性的所有者
            assertServiceOwnerLocked(service, property);

            // 获取属性的区域配置
            HalAreaConfig[] halAreaConfigs = config.getAreaConfigs();
            boolean isSubscribed = false; // 标记是否已订阅
            // 克隆当前的订阅状态,以便在失败时恢复
            PairSparseArray<RateInfo> previousState = cloneState(mRateInfoByPropIdAreaId);

            // 如果没有区域配置,检查全局属性的订阅状态
            if (halAreaConfigs.length == 0) {
                int index = mRateInfoByPropIdAreaId.indexOfKeyPair(property, 0);
                if (hasReadAccess(config.getAccess()) && index >= 0) {
                    mRateInfoByPropIdAreaId.removeAt(index);
                    isSubscribed = true;
                }
            } else {
                // 检查每个区域的订阅状态
                for (int i = 0; i < halAreaConfigs.length; i++) {
                    // 如果区域不可读,记录警告日志并继续
                    if (!isPropIdAreaIdReadable(config, halAreaConfigs[i].getAccess())) {
                        Slogf.w(CarLog.TAG_HAL,
                                "Cannot unsubscribe to " + toPropertyIdString(property)
                                        + " at areaId " + toAreaIdString(property,
                                        halAreaConfigs[i].getAreaId())
                                        + " the property's access mode does not contain READ");
                        continue;
                    }
                    // 检查并移除订阅
                    int index = mRateInfoByPropIdAreaId.indexOfKeyPair(property,
                            halAreaConfigs[i].getAreaId());
                    if (index >= 0) {
                        mRateInfoByPropIdAreaId.removeAt(index);
                        isSubscribed = true;
                    }
                }
            }

            // 如果没有订阅,记录调试日志并返回
            if (!isSubscribed) {
                if (DBG) {
                    Slogf.d(CarLog.TAG_HAL, "Property " + toPropertyIdString(property)
                            + " was not subscribed, do nothing");
                }
                return;
            }

            try {
                // 尝试取消订阅属性
                mSubscriptionClient.unsubscribe(property);
            } catch (RemoteException e) {
                // 如果发生远程异常,恢复之前的状态并记录警告日志
                mRateInfoByPropIdAreaId = previousState;
                Slogf.w(CarLog.TAG_HAL, "Failed to unsubscribe, connection to VHAL failed", e);
                // 将RemoteException转换为ServiceSpecificException,以便传递给客户端
                throw new ServiceSpecificException(StatusCode.INTERNAL_ERROR,
                        "Failed to unsubscribe, connection to VHAL failed, error: " + e);
            } catch (ServiceSpecificException e) {
                // 如果发生特定服务异常,恢复之前的状态并记录警告日志
                mRateInfoByPropIdAreaId = previousState;
                Slogf.w(CarLog.TAG_HAL, "Failed to unsubscribe, received error from VHAL", e);
                throw e;
            }
        }
    }
}

unsubscribeProperty() 方法用于取消订阅指定的车辆属性通知。

  • 方法首先检查属性的配置是否存在以及属性是否为静态。
  • 确保服务是属性的所有者。
  • 检查属性的区域配置,并根据配置移除订阅。
  • 如果属性没有被订阅,记录调试日志并返回。
  • 如果属性已被订阅,尝试通过mSubscriptionClient取消订阅。

评论