跳转至

Android 15 CarService源码08-CarPropertyService

ICarProperty.aidl

// packages/services/Car/car-lib/src/android/car/hardware/property/ICarProperty.aidl

/**
 * @hide
 */
interface ICarProperty {

    /**
     * 注册一个监听器,用于监听指定的汽车属性事件。
     *
     * @param carSubscription 包含要订阅的汽车属性的列表
     * @param callback 用于处理事件的回调接口
     */
    void registerListener(in List<CarSubscription> carSubscription,
                in ICarPropertyEventListener callback);

    /**
     * 注销一个监听器,停止监听指定属性的事件。
     *
     * @param propId 要停止监听的属性ID
     * @param callback 用于处理事件的回调接口
     */
    void unregisterListener(int propId, in ICarPropertyEventListener callback);

    /**
     * 获取所有支持的汽车属性的配置列表。
     *
     * @return 包含汽车属性配置的列表
     */
    CarPropertyConfigList getPropertyList();

    /**
     * 获取指定属性和区域的属性值。
     *
     * @param prop 属性ID
     * @param zone 区域ID
     * @return 指定属性和区域的属性值
     */
    CarPropertyValue getProperty(int prop, int zone);

    /**
     * 设置指定的属性值。
     *
     * @param prop 要设置的属性值
     * @param callback 用于处理事件的回调接口
     */
    void setProperty(in CarPropertyValue prop, in ICarPropertyEventListener callback);

    /**
     * 获取指定属性的读取权限。
     *
     * @param propId 属性ID
     * @return 读取权限的字符串表示
     */
    String getReadPermission(int propId);

    /**
     * 获取指定属性的写入权限。
     *
     * @param propId 属性ID
     * @return 写入权限的字符串表示
     */
    String getWritePermission(int propId);

    /**
     * 获取指定属性ID列表的属性配置列表。
     *
     * @param propIds 属性ID数组
     * @return 包含属性配置的结果列表
     */
    GetPropertyConfigListResult getPropertyConfigList(in int[] propIds);

    /**
     * 异步获取汽车属性值。
     *
     * @param asyncPropertyServiceRequests 异步请求列表
     * @param asyncPropertyResultCallback 异步结果回调接口
     * @param timeoutInMs 超时时间(毫秒)
     */
    void getPropertiesAsync(in AsyncPropertyServiceRequestList asyncPropertyServiceRequests,
                in IAsyncPropertyResultCallback asyncPropertyResultCallback,
                long timeoutInMs);

    /**
     * 取消正在进行的异步请求。
     *
     * @param serviceRequestIds 异步获取/设置属性请求ID的列表
     */
    void cancelRequests(in int[] serviceRequestIds);

    /**
     * 异步设置汽车属性值。
     *
     * @param asyncPropertyServiceRequests 异步请求列表
     * @param asyncPropertyResultCallback 异步结果回调接口
     * @param timeoutInMs 超时时间(毫秒)
     */
    void setPropertiesAsync(in AsyncPropertyServiceRequestList asyncPropertyServiceRequests,
                in IAsyncPropertyResultCallback asyncPropertyResultCallback,
                long timeoutInMs);

    /**
     * 返回支持但调用者没有读取权限的属性ID。
     *
     * @param propIds 属性ID数组
     * @return 没有读取权限的支持属性ID数组
     */
    int[] getSupportedNoReadPermPropIds(in int[] propIds);

    /**
     * 返回属性是否支持且调用者仅有写入权限,但没有读取权限。
     *
     * @param propId 属性ID
     * @return 如果属性支持且仅有写入权限,则返回true;否则返回false。
     */
    boolean isSupportedAndHasWritePermissionOnly(int propId);
}

ICarProperty接口用于管理和操作汽车属性。提供了一系列方法,用于注册和注销监听器、获取和设置属性值、管理权限等。

  • registerListener():注册一个监听器,用于监听指定的汽车属性事件。
  • unregisterListener():注销一个监听器,停止监听指定属性的事件。
  • getPropertyList():获取所有支持的汽车属性的配置列表。
  • getProperty():获取指定属性和区域的属性值。
  • setProperty():设置指定的属性值。
  • getReadPermission():获取指定属性的读取权限。
  • getWritePermission():获取指定属性的写入权限。
  • getPropertyConfigList():获取指定属性ID列表的属性配置列表。
  • getPropertiesAsync():异步获取汽车属性值。
  • cancelRequests():取消正在进行的异步请求。
  • setPropertiesAsync():异步设置汽车属性值。
  • getSupportedNoReadPermPropIds():返回支持但调用者没有读取权限的属性ID。
  • isSupportedAndHasWritePermissionOnly():返回属性是否支持且调用者仅有写入权限,但没有读取权限。

ICarPropertyEventListener.aidl

// packages/services/Car/car-lib/src/android/car/hardware/property/ICarPropertyEventListener.aidl

/**
 * 用于汽车属性事件回调的Binder接口。
 * 该接口为每个CarClient生成。
 *
 * @hide
 */
oneway interface ICarPropertyEventListener {
    /**
     * 当事件被触发时调用的方法。事件可以是响应某个调用(例如调谐)触发的,
     * 也可以是异步触发的(例如公告)。
     *
     * @param events 包含触发事件的列表
     */
    void onEvent(in List<CarPropertyEvent> events) = 0;
}

ICarPropertyEventListener接口用于处理汽车属性事件的回调。当汽车属性事件被触发时调用 onEvent() 。参数events是一个包含触发事件的列表。

ICarPropertyEventListenerICarProperty 中用到了 CarSubscriptionCarPropertyConfigListCarPropertyValueGetPropertyConfigListResultAsyncPropertyServiceRequestListIAsyncPropertyResultCallbackCarPropertyEvent 等,所以我们再看一看这些类(数据结构)到底是什么。

其中: GetPropertyConfigListResultCarPropertyConfigList 只需要看 CarPropertyConfig 即可。

CarSubscription.aidl

// packages/services/Car/car-lib/src/com/android/car/internal/property/CarSubscription.aidl

// 使用 @JavaDerive 注解自动生成 equals 和 toString 方法
@JavaDerive(equals = true, toString = true)
// 定义 CarSubscription 类,并实现 Parcelable 接口以便支持序列化和反序列化
parcelable CarSubscription {
    // 属性:propertyId,表示订阅的属性 ID
    int propertyId;

    // 属性:areaIds,表示订阅的区域 ID 数组
    int[] areaIds;

    // 属性:updateRateHz,表示数据更新的频率(单位:赫兹)
    float updateRateHz;

    // 属性:enableVariableUpdateRate,表示是否启用可变更新频率
    boolean enableVariableUpdateRate;

    // 属性:resolution,表示数据的分辨率,默认值为 0.0f
    float resolution = 0.0f;
}
  • propertyId:标识特定的车辆属性。例如速度、油耗等。
  • areaIds:这是一个整数数组,表示该订阅适用的区域。区域ID可能用于标识车辆的不同部分或系统,例如发动机、车身等。通过指定多个区域ID,可以订阅多个区域的数据。
  • updateRateHz:这是一个浮点数,表示数据更新的频率,以赫兹(Hz)为单位。这个值决定了数据更新的速率,例如每秒更新多少次。
  • enableVariableUpdateRate:这是一个布尔值,表示是否启用可变更新速率。如果为true,则表示可以根据需要动态调整更新速率;如果为false,则更新速率是固定的。
  • resolution:这是一个浮点数,表示数据的分辨率或精度。默认值为0.0f,这意味着在没有特别指定的情况下,使用最低的分辨率。例如,可以用于表示传感器数据的精度。

CarPropertyConfig

// packages/services/Car/car-lib/src/android/car/hardware/CarPropertyConfig.java

/**  
 * 表示汽车属性的通用信息,例如数据类型和汽车区域的最小/最大范围(如果适用)。  
 * 该类是不可变的、可序列化的,并且可以在Android组件之间传递。  
 *  
 * @param <T> 参见 {@link Parcel#writeValue(java.lang.Object)} 以获取所有支持类型的列表。  
 *            由于使用了默认的类加载器,因此该类应该对框架可见。  
 */  
public final class CarPropertyConfig<T> implements Parcelable {  
    // 属性访问权限  
    private final int mAccess;  
    // 区域类型  
    private final int mAreaType;  
    // 属性变化模式  
    private final int mChangeMode;  
    // 配置数组  
    private final ArrayList<Integer> mConfigArray;  
    // 配置字符串  
    private final String mConfigString;  
    // 最大采样率  
    private final float mMaxSampleRate;  
    // 最小采样率  
    private final float mMinSampleRate;  
    // 属性ID  
    private final int mPropertyId;  
    // 区域ID配置列表  
    private final List<AreaIdConfig<T>> mAreaIdConfigs;  
    // 区域ID到区域ID配置的映射  
    private final SparseArray<AreaIdConfig<T>> mAreaIdToAreaIdConfig;  
    // 属性类型  
    private final Class<T> mType;  

    // 私有构造函数,用于初始化所有字段  
    private CarPropertyConfig(int access, int areaType, int changeMode,  
                              ArrayList<Integer> configArray, String configString,  
                              float maxSampleRate, float minSampleRate, int propertyId,  
                              List<AreaIdConfig<T>> areaIdConfigs, Class<T> type) {  
        mAccess = access;
        mAreaType = areaType;
        mChangeMode = changeMode;
        mConfigArray = configArray;
        mConfigString = configString;  
        mMaxSampleRate = maxSampleRate;  
        mMinSampleRate = minSampleRate;  
        mPropertyId = propertyId;  
        mAreaIdConfigs = areaIdConfigs;  
        // 生成区域ID到区域ID配置的映射  
        mAreaIdToAreaIdConfig = generateAreaIdToAreaIdConfig(areaIdConfigs);  
        mType = type;  
    }  

    /** @hide */  
    // 定义车辆属性访问类型的注解  
    @IntDef(prefix = {"VEHICLE_PROPERTY_ACCESS"}, value = {  
            VEHICLE_PROPERTY_ACCESS_NONE,  
            VEHICLE_PROPERTY_ACCESS_READ,  
            VEHICLE_PROPERTY_ACCESS_WRITE,  
            VEHICLE_PROPERTY_ACCESS_READ_WRITE  
    })  
    @Retention(RetentionPolicy.SOURCE)  
    public @interface VehiclePropertyAccessType {}  

    /** 属性访问未知 */  
    public static final int VEHICLE_PROPERTY_ACCESS_NONE = 0;  
    /** 属性可读 */  
    public static final int VEHICLE_PROPERTY_ACCESS_READ = 1;  
    /** 属性可写 */  
    public static final int VEHICLE_PROPERTY_ACCESS_WRITE = 2;  
    /** 属性可读写 */  
    public static final int VEHICLE_PROPERTY_ACCESS_READ_WRITE = 3;  

    /** @hide */  
    // 定义车辆属性变化模式的注解  
    @IntDef(prefix = {"VEHICLE_PROPERTY_CHANGE_MODE"}, value = {  
            VEHICLE_PROPERTY_CHANGE_MODE_STATIC,  
            VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE,  
            VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS,  
    })  
    @Retention(RetentionPolicy.SOURCE)  
    public @interface VehiclePropertyChangeModeType {}  

    /** 此类型的属性绝不能更改。 */  
    public static final int VEHICLE_PROPERTY_CHANGE_MODE_STATIC = 0;  
    /** 此类型的属性在发生变化时必须报告。 */  
    public static final int VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE = 1;  
    /** 此类型的属性连续变化。 */  
    public static final int VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS = 2;  

    // 返回汽车属性的访问类型  
    public @VehiclePropertyAccessType int getAccess() {  
        return mAccess;  
    }  

    // 返回汽车属性的区域类型  
    public @VehicleAreaTypeValue int getAreaType() {  
        return mAreaType;  
    }  

    // 返回汽车属性的变化模式  
    public @VehiclePropertyChangeModeType int getChangeMode() {  
        return mChangeMode;  
    }  

    // 返回可选的附加配置参数  
    @NonNull  
    public List<Integer> getConfigArray() {  
        return Collections.unmodifiableList(mConfigArray);  
    }  

    // 返回可选的附加配置信息  
    public String getConfigString() {  
        return mConfigString;  
    }  

    // 返回最大采样率(以赫兹为单位)  
    public float getMaxSampleRate() {  
        return mMaxSampleRate;  
    }  

    // 返回最小采样率(以赫兹为单位)  
    public float getMinSampleRate() {  
        return mMinSampleRate;  
    }  

    // 返回属性标识符  
    public int getPropertyId() {  
        return mPropertyId;  
    }  

    // 返回车辆属性的值类型  
    @NonNull  
    public Class<T> getPropertyType() {  
        return mType;  
    }  

    // 返回此属性的AreaIdConfig列表  
    @NonNull  
    public List<AreaIdConfig<T>> getAreaIdConfigs() {  
        return Collections.unmodifiableList(mAreaIdConfigs);  
    }  

    // 返回指定areaId的AreaIdConfig  
    @NonNull  
    public AreaIdConfig<T> getAreaIdConfig(int areaId) {  
        if (!mAreaIdToAreaIdConfig.contains(areaId)) {  
            throw new IllegalArgumentException("Area ID: " + Integer.toHexString(areaId)  
                    + " is not supported for property ID: " + VehiclePropertyIds.toString(  
                    mPropertyId));  
        }  
        return mAreaIdToAreaIdConfig.get(areaId);  
    }  

    // 检查此属性是否为全局区域类型  
    public boolean isGlobalProperty() {  
        return mAreaType == VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;  
    }  

    // 返回属性的区域ID数量  
    public int getAreaCount() {  
        return mAreaIdConfigs.size();  
    }  

    // 返回支持的车辆属性的区域ID列表  
    @NonNull  
    public int[] getAreaIds() {  
        int[] areaIds = new int[mAreaIdConfigs.size()];  
        for (int i = 0; i < areaIds.length; i++) {  
            areaIds[i] = mAreaIdConfigs.get(i).getAreaId();  
        }  
        return areaIds;  
    }  

    // 返回第一个也是唯一的区域ID  
    public int getFirstAndOnlyAreaId() {  
        if (mAreaIdConfigs.size() != 1) {  
            throw new IllegalStateException("Expected one and only area in this property. PropId: "  
                    + VehiclePropertyIds.toString(mPropertyId));  
        }  
        return mAreaIdConfigs.get(0).getAreaId();  
    }  

    // 验证传入的areaId是否存在  
    public boolean hasArea(int areaId) {  
        return mAreaIdToAreaIdConfig.indexOfKey(areaId) >= 0;  
    }  

    // 准备一个CarPropertyConfig实例  
    public static <T> Builder<T> newBuilder(Class<T> type, int propertyId, int areaType) {  
        return new Builder<>(areaType, propertyId, type);  
    }  

    // 生成区域ID到区域ID配置的映射  
    private static <U> SparseArray<AreaIdConfig<U>> generateAreaIdToAreaIdConfig(  
            List<AreaIdConfig<U>> areaIdConfigs) {  
        SparseArray<AreaIdConfig<U>> areaIdToAreaIdConfig = new SparseArray<>(areaIdConfigs.size());  
        for (int i = 0; i < areaIdConfigs.size(); i++) {  
            AreaIdConfig<U> areaIdConfig = areaIdConfigs.get(i);  
            areaIdToAreaIdConfig.put(areaIdConfig.getAreaId(), areaIdConfig);  
        }  
        return areaIdToAreaIdConfig;  
    }  
}

CarPropertyConfig 类用于描述车辆属性的通用信息,包括访问权限、区域类型、变化模式、采样率等。 - 通过 getAreaIdConfig() 和 getAreaIds() 等方法,可以获取特定区域的配置信息和支持的区域 ID 列表。 - isGlobalProperty() 方法用于判断属性是否为全局区域类型。 - generateAreaIdToAreaIdConfig() 方法用于生成区域 ID 到区域配置的映射,便于快速查找。

CarPropertyValue

// packages/services/Car/car-lib/src/android/car/hardware/CarPropertyValue.java

/**
 * 存储车辆属性ID和区域ID组合的值。
 *
 * 客户端在处理属性ID、区域ID或属性值时应使用{@code android.car.*}类型,且不得直接使用
 * {@code android.hardware.automotive.vehicle.*}类型。
 *
 * @param <T> 参见 {@link Parcel#writeValue(java.lang.Object)} 以获取所有支持类型的列表。
 *            由于使用了默认的类加载器,因此该类应该对框架可见。
 */
public final class CarPropertyValue<T> implements Parcelable {
    private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;

    // 属性ID
    private final int mPropertyId;
    // 区域ID
    private final int mAreaId;
    // 属性状态
    private final int mStatus;
    // 时间戳(纳秒)
    private final long mTimestampNanos;
    // 属性值
    private final T mValue;

    /** @removed accidentally exposed previously */
    @IntDef({
            STATUS_AVAILABLE,
            STATUS_UNAVAILABLE,
            STATUS_ERROR
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface PropertyStatus {}

    /**
     * {@code CarPropertyValue} 可用。
     */
    public static final int STATUS_AVAILABLE = 0;

    /**
     * {@code CarPropertyValue} 不可用。
     */
    public static final int STATUS_UNAVAILABLE = 1;

    /**
     * {@code CarPropertyValue} 出现错误。
     */
    public static final int STATUS_ERROR = 2;

    /**
     * 创建{@code CarPropertyValue}的实例。
     *
     * @param propertyId 属性标识符,参见{@link android.car.VehiclePropertyIds}中的常量。
     * @param areaId     区域标识符。如果属性是{@link android.car.VehicleAreaType#VEHICLE_AREA_TYPE_GLOBAL},
     *                   则必须为{@code 0}。否则,它必须是该属性的{@link android.car.VehicleAreaType}的一个或多个常量的按位或。
     * @param value      属性的值
     * @hide
     */
    public CarPropertyValue(int propertyId, int areaId, T value) {
        this(propertyId, areaId, /* timestampNanos= */ 0, value);
    }

    /**
     * 创建{@code CarPropertyValue}的实例。{@code timestampNanos}是事件发生的时间(以纳秒为单位)。
     * 对于给定的汽车属性,每个新的{@code CarPropertyValue}应使用与{@link android.os.SystemClock#elapsedRealtimeNanos()}相同的时间基准单调递增。
     *
     * @param propertyId 属性标识符,参见{@link android.car.VehiclePropertyIds}中的常量。
     * @param areaId     区域标识符。如果属性是{@link android.car.VehicleAreaType#VEHICLE_AREA_TYPE_GLOBAL},
     *                   则必须为{@code 0}。否则,它必须是该属性的{@link android.car.VehicleAreaType}的一个或多个常量的按位或。
     * @param timestampNanos 自启动以来的时间(纳秒)
     * @param value      属性的值
     * @hide
     */
    public CarPropertyValue(int propertyId, int areaId, long timestampNanos, T value) {
        this(propertyId, areaId, CarPropertyValue.STATUS_AVAILABLE, timestampNanos, value);
    }

    /**
     * 创建{@code CarPropertyValue}的实例。
     *
     * @param in Parcel读取
     * @hide
     */
    @SuppressWarnings("unchecked")
    public CarPropertyValue(Parcel in) {
        mPropertyId = in.readInt();
        mAreaId = in.readInt();
        mStatus = in.readInt();
        mTimestampNanos = in.readLong();
        String valueClassName = in.readString();
        Class<?> valueClass;
        try {
            valueClass = Class.forName(valueClassName);
        } catch (ClassNotFoundException e) {
            throw new IllegalArgumentException("Class not found: " + valueClassName, e);
        }

        if (String.class.equals(valueClass)) {
            byte[] bytes = ParcelHelper.readBlob(in);
            mValue = (T) new String(bytes, DEFAULT_CHARSET);
        } else if (byte[].class.equals(valueClass)) {
            mValue = (T) ParcelHelper.readBlob(in);
        } else {
            mValue = (T) in.readValue(valueClass.getClassLoader());
        }
    }

    /**
     * 返回属性标识符。
     *
     * @return {@code CarPropertyValue}的属性标识符。参见{@link android.car.VehiclePropertyIds}中的常量以获取一些系统定义的可能值。
     */
    public int getPropertyId() {
        return mPropertyId;
    }

    /**
     * 返回区域标识符。
     *
     * @return {@code CarPropertyValue}的区域标识符。如果属性是{@link android.car.VehicleAreaType#VEHICLE_AREA_TYPE_GLOBAL},
     *         则为{@code 0}。否则,它将是该属性的{@link android.car.VehicleAreaType}的一个或多个常量的按位或。
     */
    public int getAreaId() {
        return mAreaId;
    }

    /**
     * 返回{@code CarPropertyValue}的属性状态。
     */
    @PropertyStatus
    public int getPropertyStatus() {
        return mStatus;
    }

    /**
     * 返回{@code CarPropertyValue}发生的时间戳(纳秒)。
     * 对于给定的汽车属性,每个新的{@code CarPropertyValue}应使用与{@link android.os.SystemClock#elapsedRealtimeNanos()}相同的时间基准单调递增。
     *
     * <p>注意:时间戳应与平台的其他信号同步(例如{@link android.location.Location}和{@link android.hardware.SensorEvent}实例)。
     * 理想情况下,时间戳同步误差应低于1毫秒。
     */
    public long getTimestamp() {
        return mTimestampNanos;
    }

    /**
     * 返回{@code CarPropertyValue}的值。
     *
     * <p>
     * <b>注意:</b>调用者必须检查{@link #getStatus()}的值。仅当{@link #getStatus()}为{@link #STATUS_AVAILABLE}时才使用{@link #getValue()}。
     * 否则,{@link #getValue()}是没有意义的。
     */
    @NonNull
    public T getValue() {
        return mValue;
    }
}

CarPropertyValue类用于存储车辆属性ID和区域ID的值。实现了Parcelable接口,以便在Android组件之间传递。

  • mPropertyId:属性ID,标识特定的车辆属性。
  • mAreaId:区域ID,标识属性适用的区域。
  • mStatus:属性状态,表示属性的可用性或错误状态。
  • mTimestampNanos:时间戳,记录属性值的时间(以纳秒为单位)。
  • mValue:属性的实际值。

AsyncPropertyServiceRequest

// packages/services/Car/car-lib/src/com/android/car/internal/property/AsyncPropertyServiceRequest.java

/**
 * 表示一个用于异步获取或设置车辆属性的请求。
 * 该类主要用于 {@link CarPropertyService.getPropertiesAsync} 或
 * {@link CarPropertyService.setPropertiesAsync} 方法。
 *
 * @hide
 */
@DataClass(genConstructor = false) // 使用注解生成数据类,但不生成构造函数
@ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE) // 排除样板代码的覆盖率报告
public final class AsyncPropertyServiceRequest implements Parcelable {
    // 请求的唯一标识符
    private final int mRequestId;
    // 属性的标识符
    private final int mPropertyId;
    // 区域的标识符
    private final int mAreaId;
    // 要设置的属性值,仅在设置请求中使用,在获取请求中被忽略
    @Nullable
    private final CarPropertyValue mCarPropertyValue;
    // 更新频率(以赫兹为单位),用于监听新的属性更新事件,仅在设置请求中使用
    private float mUpdateRateHz;
    // 是否在调用异步设置的成功回调之前监听属性更新事件,仅在设置请求中使用
    private boolean mWaitForPropertyUpdate;

    /**
     * 创建一个异步获取请求。
     *
     * @param getPropertyRequest 包含获取请求信息的对象
     * @return 异步获取请求实例
     */
    public static AsyncPropertyServiceRequest newGetAsyncRequest(
            GetPropertyRequest getPropertyRequest) {
        return new AsyncPropertyServiceRequest(getPropertyRequest.getRequestId(),
                getPropertyRequest.getPropertyId(), getPropertyRequest.getAreaId());
    }

    /**
     * 创建一个异步设置请求。
     *
     * @param setPropertyRequest 包含设置请求信息的对象
     * @return 异步设置请求实例
     */
    public static AsyncPropertyServiceRequest newSetAsyncRequest(
            SetPropertyRequest setPropertyRequest) {
        int propertyId = setPropertyRequest.getPropertyId();
        int areaId = setPropertyRequest.getAreaId();
        // 创建一个新的异步设置请求
        AsyncPropertyServiceRequest request = new AsyncPropertyServiceRequest(
                setPropertyRequest.getRequestId(), propertyId, areaId,
                new CarPropertyValue(propertyId, areaId, setPropertyRequest.getValue()));
        // 设置更新频率
        request.setUpdateRateHz(setPropertyRequest.getUpdateRateHz());
        // 设置是否等待属性更新
        request.setWaitForPropertyUpdate(setPropertyRequest.isWaitForPropertyUpdate());
        return request;
    }

    /**
     * 创建一个异步获取请求,仅用于测试。
     *
     * @param requestId 请求ID
     * @param propertyId 属性ID
     * @param areaId 区域ID
     */
    public AsyncPropertyServiceRequest(int requestId, int propertyId, int areaId) {
        // 调用另一个构造函数,设置carPropertyValue为null
        this(requestId, propertyId, areaId, /* carPropertyValue= */ null);
    }

    /**
     * 创建一个异步设置请求,仅用于测试。
     *
     * @param requestId 请求ID
     * @param propertyId 属性ID
     * @param areaId 区域ID
     * @param carPropertyValue 要设置的属性值
     */
    public AsyncPropertyServiceRequest(int requestId, int propertyId, int areaId,
                                       @Nullable CarPropertyValue carPropertyValue) {
        mRequestId = requestId;
        mPropertyId = propertyId;
        mAreaId = areaId;
        mCarPropertyValue = carPropertyValue;
        // 默认设置为等待属性更新
        mWaitForPropertyUpdate = true;
    }

    /**
     * 设置监听新属性更新事件的更新频率(以赫兹为单位)。
     *
     * @param updateRateHz 更新频率
     */
    public void setUpdateRateHz(float updateRateHz) {
        mUpdateRateHz = updateRateHz;
    }

    /**
     * 设置是否在调用异步设置的成功回调之前等待属性更新。
     *
     * @param waitForPropertyUpdate 是否等待属性更新
     */
    public void setWaitForPropertyUpdate(boolean waitForPropertyUpdate) {
        mWaitForPropertyUpdate = waitForPropertyUpdate;
    }
}

AsyncPropertyServiceRequest类用于表示异步请求,可以是获取属性值的请求,也可以是设置属性值的请求。主要用于CarPropertyService服务的异步方法,例如getPropertiesAsyncsetPropertiesAsync

  • mRequestId:请求的唯一标识符,用于区分不同的请求。
  • mPropertyId:属性的标识符,表示请求涉及的车辆属性。
  • mAreaId:区域的标识符,表示请求涉及的车辆区域。
  • mCarPropertyValue:要设置的属性值,仅在设置请求中使用,在获取请求中被忽略。
  • mUpdateRateHz:更新频率,用于监听新的属性更新事件,仅在设置请求中使用。
  • mWaitForPropertyUpdate:是否在调用异步设置的成功回调之前监听属性更新事件,仅在设置请求中使用。

IAsyncPropertyResultCallback.aidl

// packages/services/Car/car-lib/src/com/android/car/internal/property/IAsyncPropertyResultCallback.aidl

/**
 * 异步属性服务请求成功时的回调接口。
 * 该接口用于处理{@link CarPropertyService#getPropertiesAsync}的成功结果。
 *
 * @hide
 */
oneway interface IAsyncPropertyResultCallback {
    /**
     * 当{@link com.android.car.getPropertiesAsync}返回结果时调用的方法。
     *
     * @param getValueResults 包含获取结果的对象列表
     */
    void onGetValueResults(in GetSetValueResultList getValueResults);

    /**
     * 当{@link com.android.car.setPropertiesAsync}返回结果时调用的方法。
     *
     * 如果结果成功,则表示属性已成功更新。
     * 如果结果不成功,则表示请求未能通过或属性未在超时时间内更新到目标值。
     *
     * @param setValueResults 包含设置结果的对象列表
     */
    void onSetValueResults(in GetSetValueResultList setValueResults);
}

IAsyncPropertyResultCallback接口用于处理异步属性服务请求的回调结果。主要用于CarPropertyService的异步方法,例如getPropertiesAsyncsetPropertiesAsync

回调及内部类

CarPropertyManager 继承自CarManagerBase,用于提供与车辆特定属性交互的应用程序接口。其代码比较多,我们拆开了分析。先分析其所有公开接口,再分析其私用接口。

CarPropertyEventCallback

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

/**
 * 提供一个应用程序接口,用于与车辆特定属性进行交互。
 * 有关各个属性的详细信息,请参阅{@link VehiclePropertyIds}中的描述。
 */
public class CarPropertyManager extends CarManagerBase {
    /**
     * 应用程序注册{@link CarPropertyEventCallback}对象以接收更新和更改
     * 到订阅的车辆特定属性。
     */
    public interface CarPropertyEventCallback {
        /**
         * 当属性更新时调用。
         *
         * <p>对于一个变化属性或启用了可变更新速率(默认)的连续属性,
         * 当属性的值或状态发生变化时调用。
         *
         * <p>对于禁用了可变更新速率的连续属性,将根据更新速率定期调用。
         *
         * <p>这也将在每个新订阅中调用一次,以传递初始值(或状态不可用或错误的值)。
         *
         * @param value 属性的新值
         */
        void onChangeEvent(CarPropertyValue value);

        /**
         * 当最近的{@link CarPropertyManager#setProperty}发生错误时调用。
         *
         * @param propertyId 检测到错误的属性ID
         * @param areaId 检测到错误的区域ID
         *
         * <p>建议客户端重写
         * {@link CarPropertyEventCallback#onErrorEvent(int, int, int)} 并将此方法重写为无操作。
         *
         * <p>对于遗留客户端,{@link CarPropertyEventCallback#onErrorEvent(int, int, int)}
         * 应使用默认实现,该实现将内部调用此方法。
         *
         * @see CarPropertyEventCallback#onErrorEvent(int, int, int)
         */
        void onErrorEvent(int propertyId, int areaId);

        /**
         * 当最近的{@link CarPropertyManager#setProperty}发生错误时调用。
         *
         * <p>注意,{@link CarPropertyManager#setPropertiesAsync} 不会触发此方法。在失败的情况下,
         * 将调用{@link CarPropertyManager.SetPropertyCallback#onFailure}。
         *
         * <p>最近更改了areaId中属性值的客户端将收到此回调。
         * 如果多个客户端同时为同一区域ID设置属性,优先级未定义。通常,最后的设置操作
         * (按照它们发送到汽车ECU的顺序)会覆盖之前的设置操作。
         * 传递的错误反映了最后一次设置操作中发生的错误。
         *
         * <p>如果客户端重写此方法,实现不必在此函数内调用
         * {@link CarPropertyEventCallback#onErrorEvent(int, int)}。
         * {@link CarPropertyEventCallback#onErrorEvent(int, int)} 应重写为无操作。
         *
         * @param propertyId 检测到错误的属性ID
         * @param areaId 检测到错误的区域ID
         * @param errorCode 在汽车中引发的错误代码
         */
        default void onErrorEvent(int propertyId, int areaId,
                                  @CarSetPropertyErrorCode int errorCode) {
            if (DBG) {
                Slog.d(TAG, "onErrorEvent propertyId: " + VehiclePropertyIds.toString(propertyId)
                        + " areaId: 0x" + toHexString(areaId) + " ErrorCode: " + errorCode);
            }
            onErrorEvent(propertyId, areaId);
        }
    }
}

CarPropertyEventCallback:是一个内部接口,定义了处理属性更新和错误事件的回调方法。

  • onChangeEvent(CarPropertyValue value):当属性更新时调用。根据属性类型和更新设置,可能在属性值或状态变化时调用,也可能根据更新速率定期调用。
  • onErrorEvent(int propertyId, int areaId):当最近的setProperty操作发生错误时调用。建议客户端重写onErrorEvent(int, int, int)并将此方法重写为无操作。
  • onErrorEvent(int propertyId, int areaId, @CarSetPropertyErrorCode int errorCode):默认实现,当最近的setProperty操作发生错误时调用。记录错误信息并调用onErrorEvent(int, int)

GetPropertyCallback

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {
    /**
     * 一个回调接口,用于处理{@link CarPropertyManager#getPropertiesAsync}操作的成功或失败。
     */
    public interface GetPropertyCallback {
        /**
         * 当{@link GetPropertyRequest}成功获取结果时调用的方法。
         *
         * @param getPropertyResult 获取属性操作的结果对象
         */
        void onSuccess(@NonNull GetPropertyResult<?> getPropertyResult);

        /**
         * 当{@link GetPropertyRequest}返回错误时调用的方法。
         *
         * @param propertyAsyncError 异步获取属性操作的错误对象
         */
        void onFailure(@NonNull PropertyAsyncError propertyAsyncError);
    }
}

GetPropertyCallback:是一个内部接口,定义了处理异步获取属性操作的回调方法。主要用于getPropertiesAsync方法的回调处理。

  • onSuccess(GetPropertyResult<?> getPropertyResult):当异步获取属性操作成功时调用。
  • onFailure(PropertyAsyncError propertyAsyncError):当异步获取属性操作返回错误时调用。

SetPropertyCallback

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {
    /**
     * 一个回调接口 {@link CarPropertyManager#setPropertiesAsync} 在设置操作成功或失败时被调用。
     */
    public interface SetPropertyCallback {

        /**
         * 当 {@link SetPropertyRequest} 成功设置属性值时调用此方法。
         *
         * <p>这意味着:设置请求已成功发送到车辆系统,并且
         *
         * <p>要么当前的属性值已经是目标值,要么我们已经收到了一个属性更新事件,表示属性值已经更新为目标值。
         *
         * <p>如果多个客户端同时为同一个区域ID设置不同的值,回调顺序是未定义的。一种可能的情况是,两个请求都被发送到车辆总线,导致两个属性更新事件。因此,两个客户端的成功回调都会被调用,但顺序不确定。这意味着即使成功回调被调用,也不一定表示你设置的属性值与当前获取的值相同,因为其他客户端可能在你设置值之后又更改了该值。
         *
         * <p>如果只有一个请求被成功处理并覆盖了另一个请求,则只有一个客户端会收到成功回调。另一个客户端将收到失败回调,并带有 {@link CarPropertyManager#STATUS_ERROR_TIMEOUT} 错误码。
         *
         * <p>如果多个客户端同时为同一个区域ID设置相同的值,则这两个客户端的成功回调将在未定义的顺序下被触发。
         */
        void onSuccess(@NonNull SetPropertyResult setPropertyResult);

        /**
         * 当 {@link SetPropertyRequest} 返回错误时调用此方法。
         *
         * <p>此方法在属性设置操作出现错误时被调用,参数 {@link PropertyAsyncError} 提供了错误的详细信息。
         */
        void onFailure(@NonNull PropertyAsyncError propertyAsyncError);
    }
}

SetPropertyCallback:这是一个内部接口,定义了处理异步设置属性操作的回调方法。主要用于setPropertiesAsync方法的回调处理。

  • onSuccess(SetPropertyResult setPropertyResult):当异步设置属性操作成功时调用。参数setPropertyResult是设置操作的结果对象,包含成功设置的属性值和相关信息。
    • 说明了在多个客户端同时设置同一区域ID的属性时,可能出现的情况和回调行为。
    • 如果多个客户端同时设置不同的值,顺序未定义,可能导致两个更新事件。
    • 如果只有一个请求成功处理,另一个请求会收到超时错误。
    • 如果设置相同的值,成功回调会以未定义的顺序被调用。
  • onFailure(PropertyAsyncError propertyAsyncError):当异步设置属性操作返回错误时调用。

AsyncPropertyRequest

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {
    /**
     * 异步获取/设置属性的请求接口。
     * 这个接口表示一个异步请求,用于获取或设置车辆属性的请求。
     */
    public interface AsyncPropertyRequest {

        /**
         * 返回此请求的唯一 ID。
         *
         * <p>每个请求都必须有一个唯一的请求 ID,这样可以确保不同的响应能够被正确区分。
         * 通过这个 ID,系统能够识别是哪一个请求发起的响应。
         */
        int getRequestId();

        /**
         * 返回此请求所涉及的属性 ID。
         *
         * <p>属性 ID 必须是 {@link VehiclePropertyIds} 中定义的标准属性 ID 或者厂商自定义的属性 ID。
         * 这些 ID 唯一地标识了车辆中的每一个属性,例如车速、油量、座椅加热等。
         */
        int getPropertyId();

        /**
         * 返回此请求的属性所在的区域 ID。
         *
         * <p>区域 ID 用于标识属性所属的区域。例如,车辆的座椅区域、空调区域等。
         * 通过区域 ID,可以区分同一车辆中不同区域的属性。
         */
        int getAreaId();
    }

}

AsyncPropertyRequest:是一个内部接口,定义了异步获取或设置属性请求的基本结构。主要用于表示一个异步操作的请求信息。

  • getRequestId():返回请求的唯一ID。每个请求必须有一个唯一的ID,以便在响应时能够正确匹配请求和结果。
  • getPropertyId():返回请求的属性ID。属性ID用于标识车辆的具体属性,例如速度、燃油水平等。属性ID必须是VehiclePropertyIds中的一个,或者是供应商自定义的属性ID。
  • getAreaId():返回请求的属性区域ID。区域ID用于标识属性适用的车辆区域,例如座椅、车窗、车门等。

GetPropertyRequest

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {
    /**
     * 表示一个用于{@link CarPropertyManager#getPropertiesAsync(List, long, CancellationSignal,
     * Executor, GetPropertyCallback)}的请求。
     */
    public static final class GetPropertyRequest implements AsyncPropertyRequest {
        // 请求的唯一ID
        private final int mRequestId;
        // 请求的属性ID
        private final int mPropertyId;
        // 请求的属性区域ID
        private final int mAreaId;

        /**
         * 返回此请求的唯一ID。
         * 
         * @see AsyncPropertyRequest#getRequestId
         * @return 请求的唯一ID
         */
        @Override
        public int getRequestId() {
            return mRequestId;
        }

        /**
         * 返回此请求的属性ID。
         * 
         * @see AsyncPropertyRequest#getPropertyId
         * @return 请求的属性ID
         */
        @Override
        public int getPropertyId() {
            return mPropertyId;
        }

        /**
         * 返回此请求的属性区域ID。
         * 
         * @see AsyncPropertyRequest#getAreaId
         * @return 请求的属性区域ID
         */
        @Override
        public int getAreaId() {
            return mAreaId;
        }

        /**
         * 仅供内部使用。用户应使用{@link #generateGetPropertyRequest(int, int)}。
         *
         * @param requestId 请求的唯一ID
         * @param propertyId 请求的属性ID
         * @param areaId 请求的属性区域ID
         */
        private GetPropertyRequest(int requestId, int propertyId, int areaId) {
            mRequestId = requestId;
            mPropertyId = propertyId;
            mAreaId = areaId;
        }
    }
}

GetPropertyRequest类为异步获取属性操作提供了一个标准化的请求结构。通过实现AsyncPropertyRequest接口,该类提供了获取请求ID、属性ID和区域ID的方法。

  • mRequestId:请求的唯一ID,用于区分不同的请求。
  • mPropertyId:请求的属性ID,标识要获取的车辆属性。
  • mAreaId:请求的属性区域ID,标识属性适用的车辆区域。

SetPropertyRequest

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {
    /**
     * 表示一个用于{@link CarPropertyManager#setPropertiesAsync(List, long, CancellationSignal,
     * Executor, SetPropertyCallback)}的请求。
     *
     * @param <T> 属性值的类型,必须是以下类型之一:Object, Boolean, Float, Integer,
     *      Long, Float[], Integer[], Long[], String, byte[], Object[]
     */
    public static final class SetPropertyRequest<T> implements AsyncPropertyRequest {
        // 请求的唯一ID
        private final int mRequestId;
        // 请求的属性ID
        private final int mPropertyId;
        // 请求的属性区域ID
        private final int mAreaId;
        // 更新频率(以赫兹为单位),用于监听连续属性的更新
        private float mUpdateRateHz = 0.f;
        // 默认情况下,异步设置操作将在属性更新到目标值后调用成功回调
        private boolean mWaitForPropertyUpdate = true;

        /**
         * 要设置的属性值。
         */
        private final T mValue;

        /**
         * 设置连续属性的更新频率(以赫兹为单位)。
         *
         * <p>如果{@code waitForPropertyUpdate}设置为{@code true}(默认)且属性被设置为与当前值不同的值,
         * 则当新值的属性更新事件到达时,将调用成功回调。此选项控制连续属性的属性更新事件应报告的频率。
         * 这类似于{@link CarPropertyManager#registerCallback}中的{@code updateRateHz}。
         *
         * <p>对于非连续属性,此设置将被忽略。
         *
         * <p>如果{@code waitForPropertyUpdate}设置为{@code false},此设置将被忽略。
         */
        public void setUpdateRateHz(float updateRateHz) {
            mUpdateRateHz = updateRateHz;
        }

        /**
         * 设置是否在调用成功回调之前等待属性更新事件。
         *
         * <p>此参数控制在何种条件下操作被视为成功,并将调用成功回调。
         *
         * <p>如果设置为{@code true}(默认),则在满足以下两个条件时调用成功回调:
         *
         * <ul>
         * <li>设置操作成功传递到车辆总线。
         * <li>{@code mPropertyId}+{@code mAreaId}的值已经等于{@code mValue}或通过设置操作成功更新为{@code mValue}。
         * </ul>
         *
         * <p>即使目标值与当前值相同,我们仍会将设置操作发送到车辆总线。如果调用者希望减少不必要的开销,
         * 必须在发出请求之前检查现有值。
         *
         * <p>如果第一个条件失败,将调用错误回调。如果第二个条件失败,即在指定超时时间内未看到属性更新到目标值,
         * 将调用带有{@link #STATUS_ERROR_TIMEOUT}错误代码的错误回调。
         *
         * <p>如果设置为{@code false},则在设置操作成功传递到车辆总线后调用成功回调。
         *
         * <p>在大多数情况下,客户端应等待属性更新以验证设置操作实际成功。
         *
         * <p>对于属性是只写的(无法获取属性更新事件)或属性表示某种操作而不是实际状态的情况,
         * 例如按键操作,属性的当前值没有意义,调用者应将{@code waitForPropertyUpdate}设置为{@code false}。
         *
         * <p>对于{@code HVAC_TEMPERATURE_VALUE_SUGGESTION},必须设置为{@code false},
         * 因为更新的属性值不会与要设置的值相同。
         *
         * <p>请注意,即使设置为{@code true},也只能保证在没有其他客户端同时更改属性的情况下,
         * 成功回调被调用后属性值是目标值。始终有可能在属性更新到目标值后但在客户端成功回调运行之前,
         * 另一个客户端更改了属性值。我们只能保证在客户端发出请求后和成功回调被调用之前的某个时间点,
         * 属性值被设置为目标值。
         */
        public void setWaitForPropertyUpdate(boolean waitForPropertyUpdate) {
            mWaitForPropertyUpdate = waitForPropertyUpdate;
        }

        /**
         * @see AsyncPropertyRequest#getRequestId
         */
        @Override
        public int getRequestId() {
            return mRequestId;
        }

        /**
         * @see AsyncPropertyRequest#getPropertyId
         */
        @Override
        public int getPropertyId() {
            return mPropertyId;
        }

        /**
         * @see AsyncPropertyRequest#getAreaId
         */
        @Override
        public int getAreaId() {
            return mAreaId;
        }

        /**
         * 获取要设置的属性值。
         */
        public T getValue() {
            return mValue;
        }

        /**
         * 获取用于监听属性更新的更新频率。
         */
        public float getUpdateRateHz() {
            return mUpdateRateHz;
        }

        /**
         * 获取是否在调用成功回调之前等待属性更新事件。
         */
        public boolean isWaitForPropertyUpdate() {
            return mWaitForPropertyUpdate;
        }

        /**
         * 仅供内部使用。用户应使用{@link #generateSetPropertyRequest(int, int, T)}。
         *
         * @param requestId 请求的唯一ID
         * @param propertyId 请求的属性ID
         * @param areaId 请求的属性区域ID
         * @param value 要设置的属性值
         */
        private SetPropertyRequest(int requestId, int propertyId, int areaId, T value) {
            mRequestId = requestId;
            mPropertyId = propertyId;
            mAreaId = areaId;
            mValue = value;
        }
    }
}

SetPropertyRequest 实现了 AsyncPropertyRequest接口,提供获取请求ID、属性ID、区域ID和属性值的方法。

  • mRequestId:请求的唯一ID,用于区分不同的请求。
  • mPropertyId:请求的属性ID,标识要设置的车辆属性。
  • mAreaId:请求的属性区域ID,标识属性适用的车辆区域。
  • mUpdateRateHz:更新频率(以赫兹为单位),用于监听连续属性的更新。
  • mWaitForPropertyUpdate:是否在调用成功回调之前等待属性更新事件。
  • mValue:要设置的属性值。

PropertyAsyncError

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {
    /**
     * 表示异步属性操作(如{@link GetPropertyCallback}或{@link SetPropertyCallback})的错误结果。
     */
    public static final class PropertyAsyncError {
        // 请求的唯一ID,用于标识该错误属于哪个请求
        private final int mRequestId;
        // 属性ID,标识发生错误的具体属性
        private final int mPropertyId;
        // 区域ID,标识发生错误的属性所属的区域
        private final int mAreaId;
        // 包含错误代码的对象,提供详细的错误信息
        private final CarPropertyErrorCodes mCarPropertyErrorCodes;

        /**
         * 获取请求的唯一ID。
         *
         * @return 请求ID
         */
        public int getRequestId() {
            return mRequestId;
        }

        /**
         * 获取发生错误的属性ID。
         *
         * @return 属性ID
         */
        public int getPropertyId() {
            return mPropertyId;
        }

        /**
         * 获取发生错误的区域ID。
         *
         * @return 区域ID
         */
        public int getAreaId() {
            return mAreaId;
        }

        /**
         * 获取异步属性操作的错误代码。
         *
         * @return 错误代码,表示异步操作中发生的错误类型
         */
        public @CarPropertyAsyncErrorCode int getErrorCode() {
            return getCarPropertyAsyncErrorCodeFromCarPropertyManagerErrorCode(
                    mCarPropertyErrorCodes.getCarPropertyManagerErrorCode());
        }

        /**
         * 获取供应商定义的错误代码,以提供更详细的错误信息。
         *
         * @return 如果设置了供应商错误代码,则返回该代码;否则返回0。
         *         供应商错误代码的范围为0x0000到0xffff。
         *
         * @hide
         */
        @SystemApi
        public int getVendorErrorCode() {
            return mCarPropertyErrorCodes.getVendorErrorCode();
        }

        /**
         * 获取详细的系统错误代码。
         *
         * 这些值必须是{@link DetailedErrorCode}中定义的值。
         * {@link DetailedErrorCode}中的值可能会在未来扩展以包括其他错误代码。
         *
         * @return 如果设置了详细错误代码,则返回该代码;否则返回0。
         */
        @FlaggedApi(Flags.FLAG_CAR_PROPERTY_DETAILED_ERROR_CODES)
        public int getDetailedErrorCode() {
            return getDetailedErrorCodeFromSystemErrorCode(
                    mCarPropertyErrorCodes.getSystemErrorCode());
        }

        /**
         * 创建一个新的异步属性请求错误结果。
         *
         * @param requestId 请求的唯一ID
         * @param propertyId 请求中的属性ID
         * @param areaId 请求中属性的区域ID
         * @param carPropertyErrorCodes 指示错误的代码对象
         */
        PropertyAsyncError(int requestId, int propertyId, int areaId,
                           CarPropertyErrorCodes carPropertyErrorCodes) {
            mRequestId = requestId;
            mPropertyId = propertyId;
            mAreaId = areaId;
            mCarPropertyErrorCodes = carPropertyErrorCodes;
        }
    }
}

PropertyAsyncError 表示异步获取或设置属性操作中的错误结果,包括请求ID、属性ID、区域ID以及错误代码。

  • mRequestId:请求的唯一ID,用于标识该错误属于哪个请求。
  • mPropertyId:属性ID,标识发生错误的具体属性。
  • mAreaId:区域ID,标识发生错误的属性所属的区域。
  • mCarPropertyErrorCodes:包含错误代码的对象,提供详细的错误信息。

GetPropertyResult

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {
    /**
     * 表示{@link GetPropertyCallback}的成功结果。
     *
     * @param <T> 属性值的类型,必须是以下类型之一:Object, Boolean, Float, Integer,
     *      Long, Float[], Integer[], Long[], String, byte[], Object[]
     */
    public static final class GetPropertyResult<T> {
        // 请求的唯一ID,用于标识该结果属于哪个请求
        private final int mRequestId;
        // 属性ID,标识获取的具体属性
        private final int mPropertyId;
        // 区域ID,标识获取的属性所属的区域
        private final int mAreaId;
        // 属性值更新的时间戳(纳秒)
        private final long mTimestampNanos;
        // 属性的实际值
        private final T mValue;

        /**
         * 返回此结果对应的{@link GetPropertyRequest}的唯一ID。
         *
         * @return 请求ID
         */
        public int getRequestId() {
            return mRequestId;
        }

        /**
         * 返回此结果的属性ID。
         *
         * @return 属性ID
         */
        public int getPropertyId() {
            return mPropertyId;
        }

        /**
         * 返回此结果的区域ID。
         *
         * @return 区域ID
         */
        public int getAreaId() {
            return mAreaId;
        }

        /**
         * 返回属性的值。
         *
         * @return 属性值
         */
        @NonNull
        public T getValue() {
            return mValue;
        }

        /**
         * 返回属性值发生的时间戳(纳秒)。
         * 对于给定的车辆属性,每个新的时间戳应使用与{@link SystemClock#elapsedRealtimeNanos()}相同的时间基准单调递增。
         *
         * <p>注意:时间戳应与平台的其他信号同步(例如{@link android.location.Location}和{@link android.hardware.SensorEvent}实例)。
         * 理想情况下,时间戳同步误差应低于1毫秒。
         *
         * @return 时间戳(纳秒)
         */
        public long getTimestampNanos() {
            return mTimestampNanos;
        }

        /**
         * 创建一个新的异步GetProperty请求的值结果。
         *
         * @param requestId 请求的唯一ID
         * @param propertyId 请求中的属性ID
         * @param areaId 请求中属性的区域ID
         * @param timestampNanos 属性更新时的时间戳(纳秒)
         * @param value 属性的值
         */
        GetPropertyResult(int requestId, int propertyId, int areaId, long timestampNanos,
                          @NonNull T value) {
            mRequestId = requestId;
            mPropertyId = propertyId;
            mAreaId = areaId;
            mTimestampNanos = timestampNanos;
            mValue = value;
        }
    }
}

GetPropertyResult 表示异步获取属性操作的成功结果,提供了获取请求ID、属性ID、区域ID、时间戳和属性值的方法。

  • mRequestId:请求的唯一ID,用于标识该结果属于哪个请求。
  • mPropertyId:属性ID,标识获取的具体属性。
  • mAreaId:区域ID,标识获取的属性所属的区域。
  • mTimestampNanos:属性值更新的时间戳(纳秒)。
  • mValue:属性的实际值。

SetPropertyResult

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {
    /**
     * 表示{@link SetPropertyCallback}的成功结果。
     */
    public static final class SetPropertyResult {
        // 请求的唯一ID,用于标识该结果属于哪个请求
        private final int mRequestId;
        // 属性ID,标识设置的具体属性
        private final int mPropertyId;
        // 区域ID,标识设置的属性所属的区域
        private final int mAreaId;
        // 属性更新为目标值的时间戳(纳秒)
        private final long mUpdateTimestampNanos;

        /**
         * 获取此结果对应的请求ID。
         *
         * @return 请求ID
         */
        public int getRequestId() {
            return mRequestId;
        }

        /**
         * 获取此结果的属性ID。
         *
         * @return 属性ID
         */
        public int getPropertyId() {
            return mPropertyId;
        }

        /**
         * 获取此结果的区域ID。
         *
         * @return 区域ID
         */
        public int getAreaId() {
            return mAreaId;
        }

        /**
         * 获取属性更新为目标值的时间戳(纳秒)。
         *
         * <p>时间戳将使用与{@link SystemClock#elapsedRealtimeNanos()}相同的时间基准。
         *
         * <p>注意:如果请求的{@code waitForPropertyUpdate}设置为{@code false},则此值将是异步设置请求成功发送到车辆总线的时间,
         * 而不是属性更新的时间,因为我们无法得知属性何时更新。
         *
         * <p>注意:时间戳应与平台的其他信号同步(例如{@link android.location.Location}和{@link android.hardware.SensorEvent}实例)。
         * 理想情况下,时间戳同步误差应低于1毫秒。
         *
         * @return 更新时间戳(纳秒)
         */
        public long getUpdateTimestampNanos() {
            return mUpdateTimestampNanos;
        }

        /**
         * 创建一个新的设置属性结果对象。
         *
         * @param requestId 请求的唯一ID
         * @param propertyId 请求中的属性ID
         * @param areaId 请求中属性的区域ID
         * @param updateTimestampNanos 属性更新时的时间戳(纳秒)
         */
        SetPropertyResult(int requestId, int propertyId, int areaId, long updateTimestampNanos) {
            mRequestId = requestId;
            mPropertyId = propertyId;
            mAreaId = areaId;
            mUpdateTimestampNanos = updateTimestampNanos;
        }
    }
}

SetPropertyResult 是一个静态内部类,用于表示异步设置属性操作的成功结果。包含请求ID、属性ID、区域ID和属性更新时间戳。

  • mRequestId:请求的唯一ID,用于标识该结果属于哪个请求。
  • mPropertyId:属性ID,标识设置的具体属性。
  • mAreaId:区域ID,标识设置的属性所属的区域。
  • mUpdateTimestampNanos:属性更新为目标值的时间戳(纳秒)。

PropertyResultCallback

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {
    /**
     * 一个抽象接口,用于转换异步获取/设置结果并调用客户端回调。
     *
     * @param <CallbackType> 回调的类型
     * @param <ResultType> 结果的类型
     */
    private interface PropertyResultCallback<CallbackType, ResultType> {
        /**
         * 构建一个结果对象。
         *
         * @param requestId 请求的唯一ID
         * @param propertyId 属性ID
         * @param areaId 区域ID
         * @param timestampNanos 时间戳(纳秒)
         * @param value 属性值,可能为null
         * @return 构建的结果对象
         */
        ResultType build(int requestId, int propertyId, int areaId, long timestampNanos,
                         @Nullable Object value);

        /**
         * 在成功时调用客户端的回调方法。
         *
         * @param callback 客户端的回调对象
         * @param result 成功的结果对象
         */
        void onSuccess(CallbackType callback, ResultType result);

        /**
         * 在失败时调用客户端的回调方法。
         *
         * @param callback 客户端的回调对象
         * @param error 异步操作的错误对象
         */
        void onFailure(CallbackType callback, PropertyAsyncError error);
    }
}

PropertyResultCallback 接口用于将异步获取或设置操作的结果转换为客户端回调,并调用相应的回调方法。

GetPropertyResultCallback

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {
    /**
     * 用于隐藏获取/设置回调实现细节的类。
     */
    private static final class GetPropertyResultCallback implements
            PropertyResultCallback<GetPropertyCallback, GetPropertyResult> {

        /**
         * 构建一个获取属性结果对象。
         *
         * @param requestId 请求的唯一ID
         * @param propertyId 属性ID
         * @param areaId 区域ID
         * @param timestampNanos 时间戳(纳秒)
         * @param value 属性值,可能为null
         * @return 构建的获取属性结果对象
         */
        public GetPropertyResult build(int requestId, int propertyId, int areaId,
                                       long timestampNanos, @Nullable Object value) {
            return new GetPropertyResult(requestId, propertyId, areaId, timestampNanos, value);
        }

        /**
         * 在成功时调用客户端的回调方法。
         *
         * @param callback 客户端的回调对象
         * @param result 成功的获取属性结果对象
         */
        public void onSuccess(GetPropertyCallback callback, GetPropertyResult result) {
            if (DBG) {
                Slog.d(TAG, "delivering success get property result: " + result);
            }
            callback.onSuccess(result);
        }

        /**
         * 在失败时调用客户端的回调方法。
         *
         * @param callback 客户端的回调对象
         * @param error 异步操作的错误对象
         */
        public void onFailure(GetPropertyCallback callback, PropertyAsyncError error) {
            if (DBG) {
                Slog.d(TAG, "delivering error get property result: " + error);
            }
            callback.onFailure(error);
        }
    }
}

GetPropertyResultCallback 是一个静态内部类,实现了PropertyResultCallback接口。并在成功或失败时调用客户端回调。

SetPropertyResultCallback

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {
    /**
     * 用于隐藏获取/设置回调实现细节的类。
     */
    private static final class SetPropertyResultCallback implements
            PropertyResultCallback<SetPropertyCallback, SetPropertyResult> {

        /**
         * 构建一个设置属性结果对象。
         *
         * @param requestId 请求的唯一ID
         * @param propertyId 属性ID
         * @param areaId 区域ID
         * @param timestampNanos 时间戳(纳秒)
         * @param value 属性值,可能为null
         * @return 构建的设置属性结果对象
         */
        public SetPropertyResult build(int requestId, int propertyId, int areaId,
                                       long timestampNanos, @Nullable Object value) {
            return new SetPropertyResult(requestId, propertyId, areaId, timestampNanos);
        }

        /**
         * 在成功时调用客户端的回调方法。
         *
         * @param callback 客户端的回调对象
         * @param result 成功的设置属性结果对象
         */
        public void onSuccess(SetPropertyCallback callback, SetPropertyResult result) {
            if (DBG) {
                Slog.d(TAG, "delivering success set property result: " + result);
            }
            callback.onSuccess(result);
        }

        /**
         * 在失败时调用客户端的回调方法。
         *
         * @param callback 客户端的回调对象
         * @param error 异步操作的错误对象
         */
        public void onFailure(SetPropertyCallback callback, PropertyAsyncError error) {
            if (DBG) {
                Slog.d(TAG, "delivering error set property result: " + error);
            }
            callback.onFailure(error);
        }
    }
}

SetPropertyResultCallback 是一个静态内部类,实现了PropertyResultCallback接口。并在成功或失败时调用客户端回调。

AsyncPropertyResultCallback

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {
    /**
     * 一个内部类,用于在异步属性操作(如获取或设置属性值)完成后,
     * 将结果传递给客户端的回调接口。
     * 
     * 该类继承自`IAsyncPropertyResultCallback.Stub`,实现了异步结果的处理逻辑。
     */
    private class AsyncPropertyResultCallback extends IAsyncPropertyResultCallback.Stub {

        /**
         * 返回当前对象的`IBinder`接口。
         * 
         * 这是Android Binder机制的一部分,用于将当前对象绑定到IPC通信中。
         *
         * @return 当前对象的`IBinder`接口
         */
        @Override
        public IBinder asBinder() {
            return this;
        }

        /**
         * 当异步获取属性值的结果返回时调用。
         *
         * @param getValueResults 包含获取属性值结果的列表
         */
        @Override
        public void onGetValueResults(GetSetValueResultList getValueResults) {
            // 调用通用的结果处理方法,将结果列表传递给获取属性的回调逻辑
            this.<GetPropertyRequest, GetPropertyCallback, GetPropertyResult>onResults(
                    getValueResults.getList(), mGetPropertyResultCallback);
        }

        /**
         * 当异步设置属性值的结果返回时调用。
         *
         * @param setValueResults 包含设置属性值结果的列表
         */
        @Override
        public void onSetValueResults(GetSetValueResultList setValueResults) {
            // 调用通用的结果处理方法,将结果列表传递给设置属性的回调逻辑
            this.<SetPropertyRequest<?>, SetPropertyCallback, SetPropertyResult>onResults(
                    setValueResults.getList(), mSetPropertyResultCallback);
        }

        /**
         * 通用的结果处理方法,用于处理获取或设置属性值的结果列表。
         *
         * @param results 包含操作结果的列表
         * @param propertyResultCallback 用于处理结果的回调逻辑
         * @param <RequestType> 请求类型,必须实现`AsyncPropertyRequest`接口
         * @param <CallbackType> 回调类型
         * @param <ResultType> 结果类型
         */
        @SuppressLint("WrongConstant")
        private <RequestType extends AsyncPropertyRequest, CallbackType, ResultType> void onResults(
                List<GetSetValueResult> results,
                PropertyResultCallback<CallbackType, ResultType> propertyResultCallback) {
            // 遍历结果列表,逐个处理每个结果
            for (int i = 0; i < results.size(); i++) {
                GetSetValueResult result = results.get(i); // 当前结果对象
                int requestId = result.getRequestId(); // 获取请求的唯一ID

                // 从请求ID映射中获取对应的请求信息
                AsyncPropertyRequestInfo<RequestType, CallbackType> requestInfo;
                synchronized (mLock) {
                    requestInfo =
                            (AsyncPropertyRequestInfo<RequestType, CallbackType>)
                                    mRequestIdToAsyncRequestInfo.get(requestId);
                    // 从映射中移除已处理的请求
                    mRequestIdToAsyncRequestInfo.remove(requestId);
                }

                // 如果请求信息为空,说明请求可能已经完成、被取消或发生异常
                if (requestInfo == null) {
                    Slog.w(TAG, "onResults: Request ID: " + requestId
                            + " might have been completed, cancelled or an exception might have "
                            + "been thrown");
                    continue;
                }

                // 获取回调执行器和客户端回调对象
                Executor callbackExecutor = requestInfo.getCallbackExecutor();
                CallbackType clientCallback = requestInfo.getCallback();

                // 获取错误代码,判断操作是否成功
                @CarPropertyAsyncErrorCode int errorCode =
                        result.getCarPropertyErrorCodes().getCarPropertyManagerErrorCode();
                int propertyId = requestInfo.getRequest().getPropertyId(); // 属性ID
                String propertyName = VehiclePropertyIds.toString(propertyId); // 属性名称
                int areaId = requestInfo.getRequest().getAreaId(); // 区域ID

                // 如果操作成功
                if (errorCode == STATUS_OK) {
                    CarPropertyValue<?> carPropertyValue = result.getCarPropertyValue(); // 属性值对象
                    long timestampNanos;

                    // 如果属性值对象不为空,说明是获取操作的结果
                    if (carPropertyValue != null) {
                        int valuePropertyId = carPropertyValue.getPropertyId();
                        if (propertyId != valuePropertyId) {
                            Slog.e(TAG, "onResults: Request ID: " + requestId + " received get "
                                    + "property value result, but has mismatch property ID, "
                                    + " expect: " + propertyName + ", got: "
                                    + VehiclePropertyIds.toString(valuePropertyId));
                        }
                        int valueAreaId = carPropertyValue.getAreaId();
                        if (areaId != valueAreaId) {
                            Slog.e(TAG, "onResults: Property: " + propertyName + " Request ID: "
                                    + requestId + " received get property value result, but has "
                                    + "mismatch area ID, expect: " + areaId + ", got: "
                                    + valueAreaId);
                        }
                        timestampNanos = carPropertyValue.getTimestamp(); // 获取时间戳
                    } else {
                        // 如果属性值对象为空,说明是设置操作的结果
                        timestampNanos = result.getUpdateTimestampNanos();
                    }

                    // 构建客户端结果对象
                    ResultType clientResult = propertyResultCallback.build(
                            requestId, propertyId, areaId, timestampNanos,
                            carPropertyValue == null ? null : carPropertyValue.getValue());

                    // 清除调用身份并执行成功回调
                    Binder.clearCallingIdentity();
                    callbackExecutor.execute(() -> propertyResultCallback.onSuccess(
                            clientCallback, clientResult));
                } else {
                    // 如果操作失败,执行失败回调
                    Binder.clearCallingIdentity();
                    callbackExecutor.execute(() -> propertyResultCallback.onFailure(clientCallback,
                            new PropertyAsyncError(requestId, propertyId, areaId,
                                    result.getCarPropertyErrorCodes())));
                }
            }
        }
    }
}

AsyncPropertyResultCallback继承自IAsyncPropertyResultCallback.Stub,用于处理异步属性操作(如获取或设置)的结果。

  • onGetValueResults():调用通用的onResults()方法处理结果列表,并将其传递给获取属性的回调逻辑。
  • onSetValueResults():用通用的onResults()方法处理结果列表,并将其传递给设置属性的回调逻辑。
  • onResults():通用的结果处理方法,用于处理获取或设置操作的结果列表。
    • 遍历结果列表,逐个处理每个结果。
    • 根据请求ID从映射中获取请求信息,并移除已处理的请求。
    • 如果请求信息为空,记录警告日志并跳过。
    • 根据错误代码判断操作是否成功:
      • 成功:构建结果对象并调用成功回调。
      • 失败:构建错误对象并调用失败回调。

AsyncPropertyRequestInfo

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {
    /**
     * 一个类,用于存储异步获取/设置属性请求的信息。
     *
     * @param <RequestType> 请求的类型
     * @param <CallbackType> 回调的类型
     */
    private static final class AsyncPropertyRequestInfo<RequestType, CallbackType> {
        // 请求对象,包含请求的详细信息
        private final RequestType mRequest;
        // 回调执行器,用于在特定的线程或线程池中执行回调
        private final Executor mCallbackExecutor;
        // 回调对象,用于处理请求结果
        private final CallbackType mCallback;

        /**
         * 获取请求对象。
         *
         * @return 请求对象
         */
        public RequestType getRequest() {
            return mRequest;
        }

        /**
         * 获取回调执行器。
         *
         * @return 回调执行器
         */
        public Executor getCallbackExecutor() {
            return mCallbackExecutor;
        }

        /**
         * 获取回调对象。
         *
         * @return 回调对象
         */
        public CallbackType getCallback() {
            return mCallback;
        }

        /**
         * 构造函数,用于初始化异步请求信息对象。
         *
         * @param request 请求对象
         * @param callbackExecutor 回调执行器
         * @param callback 回调对象
         */
        private AsyncPropertyRequestInfo(RequestType request, Executor callbackExecutor,
                                         CallbackType callback) {
            mRequest = request;
            mCallbackExecutor = callbackExecutor;
            mCallback = callback;
        }
    }
}

AsyncPropertyRequestInfo 是一个静态内部类,用于存储异步获取或设置属性请求的信息,包含请求对象、回调执行器和回调对象。

  • RequestType:请求的类型,用于指定请求对象的类型。
  • CallbackType:回调的类型,用于指定回调对象的类型。

  • mRequest:请求对象,包含请求的详细信息。

  • mCallbackExecutor:回调执行器,用于在特定的线程或线程池中执行回调。
  • mCallback:回调对象,用于处理请求结果。

CarPropertyEventListenerToService

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {
    /**
     * 一个内部静态类,用于将属性事件从服务端传递到客户端。
     * 该类实现了`ICarPropertyEventListener`接口,并通过AIDL机制与服务端通信。
     */
    private static class CarPropertyEventListenerToService extends ICarPropertyEventListener.Stub {
        // 使用弱引用存储CarPropertyManager实例,防止内存泄漏
        private final WeakReference<CarPropertyManager> mCarPropertyManager;

        /**
         * 构造方法,初始化弱引用。
         *
         * @param carPropertyManager CarPropertyManager实例。
         */
        CarPropertyEventListenerToService(CarPropertyManager carPropertyManager) {
            // 将CarPropertyManager实例包装为弱引用,避免强引用导致内存泄漏
            mCarPropertyManager = new WeakReference<>(carPropertyManager);
        }

        /**
         * 当服务端有属性事件发生时调用此方法。
         *
         * @param carPropertyEvents 服务端传递的属性事件列表。
         * @throws RemoteException 如果与服务端通信发生错误。
         */
        @Override
        public void onEvent(List<CarPropertyEvent> carPropertyEvents) throws RemoteException {
            // 从弱引用中获取CarPropertyManager实例
            CarPropertyManager carPropertyManager = mCarPropertyManager.get();

            // 如果CarPropertyManager实例仍然存在(未被GC回收)
            if (carPropertyManager != null) {
                // 调用CarPropertyManager的handleEvents方法处理事件
                carPropertyManager.handleEvents(carPropertyEvents);
            }
        }
    }
}

CarPropertyEventListenerToService是一个内部静态类,实现了ICarPropertyEventListener.Stub接口,用于客户端与服务端的跨进程通信。 当服务端有属性事件发生时,服务端会调用 onEvent() 方法,将事件列表carPropertyEvents传递给客户端。

CarPropertyConfigs

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {  

    /**
     * 用于包装从 {@code ICarProperty.getPropertyConfigList} 返回的结果。
     * 
     * <p>该内部类封装了获取到的车载属性配置列表,并提供一些方法来查询属性是否被支持或是否缺少必要的权限。</p>
     * 
     * <p>CarPropertyConfigs 会持有属性的配置信息、缺少权限的属性 ID 和不支持的属性 ID,并且为查询这些属性提供便捷的方法。</p>
     */
    private static final class CarPropertyConfigs {

        // 使用 SparseArray 存储属性 ID 对应的属性配置
        private final SparseArray<CarPropertyConfig<?>> mCarPropertyConfigById =
                new SparseArray<>();

        // 存储缺少权限的属性 ID
        private final ArraySet<Integer> mMissingPermissionPropIds = new ArraySet<>();

        // 存储不支持的属性 ID
        private final ArraySet<Integer> mUnsupportedPropIds = new ArraySet<>();

        // 存储获取属性配置信息的结果
        private final GetPropertyConfigListResult mResult;

        /**
         * 构造函数,用于初始化 CarPropertyConfigs 实例。
         * 
         * @param result 从车载服务获取的配置信息结果。
         * @param unsupportedPropIds 在请求获取配置之前过滤掉的、不支持的属性 ID 列表。
         */
        CarPropertyConfigs(GetPropertyConfigListResult result, IntArray unsupportedPropIds) {
            mResult = result;
            // 获取配置列表中的所有属性配置,并将它们存储在 mCarPropertyConfigById 中。
            List<CarPropertyConfig> configs = result.carPropertyConfigList.getConfigs();
            for (int i = 0; i < configs.size(); i++) {
                mCarPropertyConfigById.put(configs.get(i).getPropertyId(), configs.get(i));
            }
            // 获取缺少权限的属性 ID,并将其添加到 mMissingPermissionPropIds 中。
            for (int i = 0; i < result.missingPermissionPropIds.length; i++) {
                mMissingPermissionPropIds.add(result.missingPermissionPropIds[i]);
            }
            // 获取不支持的属性 ID,并将其添加到 mUnsupportedPropIds 中。
            for (int i = 0; i < result.unsupportedPropIds.length; i++) {
                mUnsupportedPropIds.add(result.unsupportedPropIds[i]);
            }
            // 将请求时过滤掉的不支持的属性 ID 添加到 mUnsupportedPropIds 中。
            for (int i = 0; i < unsupportedPropIds.size(); i++) {
                mUnsupportedPropIds.add(unsupportedPropIds.get(i));
            }
        }

        /**
         * 获取指定属性 ID 的配置。
         * 
         * @param propertyId 要查询的属性 ID。
         * @return 如果存在该属性的配置,则返回其配置;否则返回 null。
         */
        @Nullable
        CarPropertyConfig<?> getConfig(int propertyId) {
            return mCarPropertyConfigById.get(propertyId);
        }

        /**
         * 检查指定的属性是否不支持。
         * 
         * @param propertyId 要检查的属性 ID。
         * @return 如果该属性 ID 不被支持,返回 true;否则返回 false。
         */
        boolean isNotSupported(int propertyId) {
            return mUnsupportedPropIds.contains(propertyId);
        }

        /**
         * 检查指定的属性是否缺少权限(即缺少读/写权限)。
         * 
         * @param propertyId 要检查的属性 ID。
         * @return 如果缺少读/写权限,返回 true;否则返回 false。
         */
        boolean missingPermission(int propertyId) {
            return mMissingPermissionPropIds.contains(propertyId);
        }

        /**
         * 获取所有的属性配置列表。
         * 
         * @return 返回所有属性的配置列表。
         */
        List<CarPropertyConfig> getConfigs() {
            return mResult.carPropertyConfigList.getConfigs();
        }

        /**
         * 获取所有缺少权限的属性 ID。
         * 
         * @return 返回缺少权限的属性 ID 数组。
         */
        int[] getMissingPermissionPropIds() {
            return mResult.missingPermissionPropIds;
        }

        /**
         * 获取所有不支持的属性 ID。
         * 
         * @return 返回不支持的属性 ID 数组。
         */
        int[] getUnsupportedPropIds() {
            return mResult.unsupportedPropIds;
        }
    }
}

内部类 CarPropertyConfigs用于包装从车载服务获取的属性配置列表,并为用户提供接口来查询哪些属性不被支持、哪些属性缺少权限,或者获取特定属性的详细配置。 它包含三个集合:

  • CarPropertyConfigById:一个 SparseArray 用来存储所有属性 ID 对应的配置。
  • mMissingPermissionPropIds:一个 ArraySet 用来存储缺少读/写权限的属性 ID。
  • mUnsupportedPropIds:一个 ArraySet 用来存储不支持的属性 ID。

主要方法:

  • getConfig(int propertyId):返回指定 propertyId 对应的 CarPropertyConfig 配置,如果该配置不存在,返回 null
  • isNotSupported(int propertyId):判断该属性是否不被支持。如果该属性 ID 在 mUnsupportedPropIds 中,返回 true,否则返回 false
  • missingPermission(int propertyId):判断该属性是否缺少权限。如果该属性 ID 在 mMissingPermissionPropIds 中,返回 true,否则返回 false
  • getConfigs():返回所有的车载属性配置列表,即 mResult.carPropertyConfigList.getConfigs()
  • getMissingPermissionPropIds():返回所有缺少权限的属性 ID 列表,即 mResult.missingPermissionPropIds
  • getUnsupportedPropIds():返回所有不支持的属性 ID 列表,即 mResult.unsupportedPropIds

公共方法

subscribePropertyEvents()

registerCallback() 已经被弃用了,我们不再分析。

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {  
    /**  
     * 订阅指定属性的所有区域ID的属性事件。  
     *  
     * <p>对于连续属性,启用可变更新速率。更新速率为1Hz或支持的最大速率(如果低于1Hz)。  
     *  
     * <p>注意,回调将在提供给{@link android.car.Car}的事件处理器上执行,  
     * 如果没有提供,则在主线程上执行。  
     *  
     * @param propertyId 要订阅的属性的ID。  
     * @param carPropertyEventCallback 用于传递属性更新/错误事件的回调。  
     * @return 如果监听器成功注册,则返回{@code true}  
     * @throws SecurityException 如果调用者没有读取权限访问支持的属性之一。  
     * @throws IllegalArgumentException 如果存在重叠的区域ID,或者执行器已注册到另一个回调,  
     *                                  或者某些属性不受支持。  
     *  
     * @see #subscribePropertyEvents(int, int, float, boolean, CarPropertyEventCallback) 获取更多选项。  
     */  
    @FlaggedApi(Flags.FLAG_VARIABLE_UPDATE_RATE)  
    public boolean subscribePropertyEvents(int propertyId,  
                                           @NonNull CarPropertyEventCallback carPropertyEventCallback) {  
        // 使用默认更新速率创建订阅列表,并调用重载方法进行订阅  
        return subscribePropertyEvents(List.of(  
                new Subscription.Builder(propertyId).setUpdateRateHz(DEFAULT_UPDATE_RATE_HZ)  
                        .build()), /* callbackExecutor= */ null, carPropertyEventCallback);  
    }  

    /**  
     * 订阅指定属性的所有区域ID的属性事件。  
     *  
     * <p>对于连续属性,启用可变更新速率。  
     *  
     * <p>注意,回调将在提供给{@link android.car.Car}的事件处理器上执行,  
     * 如果没有提供,则在主线程上执行。  
     *  
     * @param propertyId 要订阅的属性的ID。  
     * @param updateRateHz 仅对连续属性有意义。更新速率(Hz)。常见值为1Hz。  
     * @param carPropertyEventCallback 用于传递属性更新/错误事件的回调。  
     * @return 如果监听器成功注册,则返回{@code true}  
     * @throws SecurityException 如果调用者没有读取权限访问支持的属性之一。  
     * @throws IllegalArgumentException 如果存在重叠的区域ID,或者执行器已注册到另一个回调,  
     *                                  或者某些属性不受支持。  
     *  
     * @see #subscribePropertyEvents(int, int, float, boolean, CarPropertyEventCallback) 获取更多选项。  
     */  
    @FlaggedApi(Flags.FLAG_VARIABLE_UPDATE_RATE)  
    public boolean subscribePropertyEvents(int propertyId,  
                                           @FloatRange(from = 0.0, to = 100.0) float updateRateHz,  
                                           @NonNull CarPropertyEventCallback carPropertyEventCallback) {  
        // 使用指定的更新速率创建订阅列表,并调用重载方法进行订阅  
        return subscribePropertyEvents(List.of(  
                        new Subscription.Builder(propertyId).setUpdateRateHz(updateRateHz).build()),  
                /* callbackExecutor= */ null, carPropertyEventCallback);  
    }  

    /**  
     * 订阅指定属性的特定区域ID的属性事件。  
     *  
     * <p>对于连续属性,启用可变更新速率。更新速率为1Hz或支持的最大速率(如果低于1Hz)。  
     *  
     * <p>注意,回调将在提供给{@link android.car.Car}的事件处理器上执行,  
     * 如果没有提供,则在主线程上执行。  
     *  
     * @param propertyId 要订阅的属性的ID。  
     * @param areaId 要订阅的区域ID。  
     * @param carPropertyEventCallback 用于传递属性更新/错误事件的回调。  
     * @return 如果监听器成功注册,则返回{@code true}  
     * @throws SecurityException 如果调用者没有读取权限访问支持的属性之一。  
     * @throws IllegalArgumentException 如果存在重叠的区域ID,或者执行器已注册到另一个回调,  
     *                                  或者某些属性不受支持。  
     *  
     * @see #subscribePropertyEvents(int, int, float, boolean, CarPropertyEventCallback) 获取更多选项。  
     */  
    @FlaggedApi(Flags.FLAG_VARIABLE_UPDATE_RATE)  
    public boolean subscribePropertyEvents(int propertyId, int areaId,  
                                           @NonNull CarPropertyEventCallback carPropertyEventCallback) {  
        // 使用默认更新速率1Hz创建订阅列表,并调用重载方法进行订阅  
        return subscribePropertyEvents(List.of(  
                        new Subscription.Builder(propertyId).addAreaId(areaId).setUpdateRateHz(1f)  
                                .build()),  
                /* callbackExecutor= */ null, carPropertyEventCallback);  
    }  

    /**  
     * 订阅指定属性的特定区域ID的属性事件。  
     *  
     * <p>对于连续属性,启用可变更新速率。  
     *  
     * <p>属性事件用于指示属性的值/状态变化(即属性更新事件),  
     * 或用于指示先前的{@link #setProperty}操作失败(即属性错误事件)。  
     *  
     * <p>允许为单个[PropertyId, areaId]注册多个{@code carPropertyEventCallback}。  
     * 所有注册的回调都将被调用。  
     *  
     * <p>对于单个[propertyId, areaId, carPropertyEventCallback]组合,只允许有一个{@code updateRateHz}。  
     * 新的{@code updateRateHz}将更新该组合的更新速率。  
     *  
     * <p>对于单个[propertyId, areaId, carPropertyEventCallback]组合,只允许有一个  
     * {@code setVariableUpdateRateEnabled}设置。新的设置将覆盖当前的设置。  
     *  
     * <p>{@code carPropertyEventCallback}将在单个默认事件处理器线程上执行。  
     *  
     * <p>  
     * <b>注意:</b>调用此函数时,如果订阅的[propertyId, areaId]当前可读取,  
     * 则回调将通过属性更改事件接收当前值。如果它们不可读取或处于错误状态,  
     * 则将生成不可用或错误状态的属性更改事件。  
     *  
     * <p>注意,回调将在提供给{@link android.car.Car}的事件处理器上执行,  
     * 如果没有提供,则在主线程上执行。  
     *  
     * @param propertyId 要订阅的属性的ID。  
     * @param areaId 要订阅的区域ID。  
     * @param updateRateHz 仅对连续属性有意义。更新速率(Hz)。常见值为1Hz。  
     * @param carPropertyEventCallback 用于传递属性更新/错误事件的回调。  
     * @return 如果监听器成功注册,则返回{@code true}  
     * @throws SecurityException 如果调用者没有读取权限访问支持的属性之一。  
     * @throws IllegalArgumentException 如果存在重叠的区域ID,或者执行器已注册到另一个回调,  
     *                                  或者某些属性不受支持。  
     *  
     * @see #subscribePropertyEvents(List, Executor, CarPropertyEventCallback) 获取有关属性订阅和批量订阅使用的更详细说明。  
     */  
    @FlaggedApi(Flags.FLAG_VARIABLE_UPDATE_RATE)  
    public boolean subscribePropertyEvents(int propertyId, int areaId,  
                                           @FloatRange(from = 0.0, to = 100.0) float updateRateHz,  
                                           @NonNull CarPropertyEventCallback carPropertyEventCallback) {  
        // 使用指定的更新速率和区域ID创建订阅对象,并调用重载方法进行订阅  
        Subscription subscription = new Subscription.Builder(propertyId).addAreaId(areaId)  
                .setUpdateRateHz(updateRateHz).setVariableUpdateRateEnabled(false).build();  
        return subscribePropertyEvents(List.of(subscription), /* callbackExecutor= */ null,  
                carPropertyEventCallback);  
    }  

    /**  
     * 订阅指定属性的所有区域ID的属性事件。  
     *  
     * <p>对于连续属性,启用可变更新速率。  
     *  
     * <p>注意,回调将在提供给{@link android.car.Car}的事件处理器上执行,  
     * 如果没有提供,则在主线程上执行。  
     *  
     * @param propertyId 要订阅的属性的ID。  
     * @param updateRateHz 仅对连续属性有意义。更新速率(Hz)。常见值为1Hz。  
     * @param carPropertyEventCallback 用于传递属性更新/错误事件的回调。  
     * @return 如果监听器成功注册,则返回{@code true}  
     * @throws SecurityException 如果调用者没有读取权限访问支持的属性之一。  
     * @throws IllegalArgumentException 如果存在重叠的区域ID,或者执行器已注册到另一个回调,  
     *                                  或者某些属性不受支持。  
     *  
     * @see #subscribePropertyEvents(int, int, float, boolean, CarPropertyEventCallback) 获取更多选项。  
     */  
    @FlaggedApi(Flags.FLAG_VARIABLE_UPDATE_RATE)  
    public boolean subscribePropertyEvents(int propertyId,  
                                           @FloatRange(from = 0.0, to = 100.0) float updateRateHz,  
                                           @NonNull CarPropertyEventCallback carPropertyEventCallback) {  
        // 使用指定的更新速率创建订阅列表,并调用重载方法进行订阅  
        return subscribePropertyEvents(List.of(  
                        new Subscription.Builder(propertyId).setUpdateRateHz(updateRateHz).build()),  
                /* callbackExecutor= */ null, carPropertyEventCallback);  
    }  

    /**  
     * 订阅指定属性的特定区域ID的属性事件。  
     *  
     * <p>对于连续属性,启用可变更新速率。更新速率为1Hz或支持的最大速率(如果低于1Hz)。  
     *  
     * <p>注意,回调将在提供给{@link android.car.Car}的事件处理器上执行,  
     * 如果没有提供,则在主线程上执行。  
     *  
     * @param propertyId 要订阅的属性的ID。  
     * @param areaId 要订阅的区域ID。  
     * @param carPropertyEventCallback 用于传递属性更新/错误事件的回调。  
     * @return 如果监听器成功注册,则返回{@code true}  
     * @throws SecurityException 如果调用者没有读取权限访问支持的属性之一。  
     * @throws IllegalArgumentException 如果存在重叠的区域ID,或者执行器已注册到另一个回调,  
     *                                  或者某些属性不受支持。  
     *  
     * @see #subscribePropertyEvents(int, int, float, boolean, CarPropertyEventCallback) 获取更多选项。  
     */  
    @FlaggedApi(Flags.FLAG_VARIABLE_UPDATE_RATE)  
    public boolean subscribePropertyEvents(int propertyId, int areaId,  
                                           @NonNull CarPropertyEventCallback carPropertyEventCallback) {  
        // 使用默认更新速率1Hz创建订阅列表,并调用重载方法进行订阅  
        return subscribePropertyEvents(List.of(  
                        new Subscription.Builder(propertyId).addAreaId(areaId).setUpdateRateHz(1f)  
                                .build()),  
                /* callbackExecutor= */ null, carPropertyEventCallback);  
    }  

    /**  
     * 订阅指定属性的特定区域ID的属性事件。  
     *  
     * <p>对于连续属性,启用可变更新速率。  
     *  
     * <p>属性事件用于指示属性的值/状态变化(即属性更新事件),  
     * 或用于指示先前的{@link #setProperty}操作失败(即属性错误事件)。  
     *  
     * <p>允许为单个[PropertyId, areaId]注册多个{@code carPropertyEventCallback}。  
     * 所有注册的回调都将被调用。  
     *  
     * <p>对于单个[propertyId, areaId, carPropertyEventCallback]组合,只允许有一个{@code updateRateHz}。  
     * 新的{@code updateRateHz}将更新该组合的更新速率。  
     *  
     * <p>对于单个[propertyId, areaId, carPropertyEventCallback]组合,只允许有一个  
     * {@code setVariableUpdateRateEnabled}设置。新的设置将覆盖当前的设置。  
     *  
     * <p>{@code carPropertyEventCallback}将在单个默认事件处理器线程上执行。  
     *  
     * <p>  
     * <b>注意:</b>调用此函数时,如果订阅的[propertyId, areaId]当前可读取,  
     * 则回调将通过属性更改事件接收当前值。如果它们不可读取或处于错误状态,  
     * 则将生成不可用或错误状态的属性更改事件。  
     *  
     * <p>注意,回调将在提供给{@link android.car.Car}的事件处理器上执行,  
     * 如果没有提供,则在主线程上执行。  
     *  
     * @param propertyId 要订阅的属性的ID。  
     * @param areaId 要订阅的区域ID。  
     * @param updateRateHz 仅对连续属性有意义。更新速率(Hz)。常见值为1Hz。  
     * @param carPropertyEventCallback 用于传递属性更新/错误事件的回调。  
     * @return 如果监听器成功注册,则返回{@code true}  
     * @throws SecurityException 如果调用者没有读取权限访问支持的属性之一。  
     * @throws IllegalArgumentException 如果存在重叠的区域ID,或者执行器已注册到另一个回调,  
     *                                  或者某些属性不受支持。  
     *  
     * @see #subscribePropertyEvents(List, Executor, CarPropertyEventCallback) 获取有关属性订阅和批量订阅使用的更详细说明。  
     */  
    @FlaggedApi(Flags.FLAG_VARIABLE_UPDATE_RATE)  
    public boolean subscribePropertyEvents(int propertyId, int areaId,  
                                           @FloatRange(from = 0.0, to = 100.0) float updateRateHz,  
                                           @NonNull CarPropertyEventCallback carPropertyEventCallback) {  
        // 使用指定的更新速率和区域ID创建订阅对象,并调用重载方法进行订阅  
        Subscription subscription = new Subscription.Builder(propertyId).addAreaId(areaId)  
                .setUpdateRateHz(updateRateHz).setVariableUpdateRateEnabled(false).build();  
        return subscribePropertyEvents(List.of(subscription), /* callbackExecutor= */ null,  
                carPropertyEventCallback);  
    }  
}

subscribePropertyEvents() 提供了多种重载方法,用于订阅汽车属性事件,支持不同的参数组合。

unsubscribePropertyEvents()

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java  

public class CarPropertyManager extends CarManagerBase {  
    /**  
     * 停止为给定的{@link CarPropertyEventCallback}获取属性更新。  
     * 如果该回调有多个注册,则所有监听都会被停止。  
     *  
     * @param carPropertyEventCallback 之前订阅的回调,用于取消订阅。  
     * @throws SecurityException 如果调用者没有读取该回调注册的属性的权限。  
     */  
    @FlaggedApi(Flags.FLAG_BATCHED_SUBSCRIPTIONS)  
    public void unsubscribePropertyEvents(  
            @NonNull CarPropertyEventCallback carPropertyEventCallback) {  
        // 确保回调不为空  
        requireNonNull(carPropertyEventCallback);  

        // 如果启用了调试模式,记录调试信息  
        if (DBG) {  
            Slog.d(TAG, "unsubscribePropertyEvents, callback: " + carPropertyEventCallback);  
        }  

        int[] propertyIds; // 用于存储该回调订阅的属性ID  

        // 使用同步块确保线程安全  
        synchronized (mLock) {  
            // 从映射中获取与回调关联的回调控制器  
            CarPropertyEventCallbackController cpeCallbackController =  
                    mCpeCallbackToCpeCallbackController.get(carPropertyEventCallback);  

            // 如果回调控制器不存在,记录警告日志并返回  
            if (cpeCallbackController == null) {  
                Slog.w(TAG, "unsubscribePropertyEvents: callback was not previously registered.");  
                return;  
            }  

            // 获取该回调控制器订阅的属性ID  
            propertyIds = cpeCallbackController.getSubscribedProperties();  
        }  

        // 将属性ID数组转换为ArrayList  
        ArrayList<Integer> propertyIdsList = new ArrayList<>(propertyIds.length);  
        for (int i = 0; i < propertyIds.length; i++) {  
            propertyIdsList.add(propertyIds[i]);  
        }  

        // 调用内部方法取消订阅属性事件  
        unsubscribePropertyEventsInternal(propertyIdsList, carPropertyEventCallback);  
    }  

    /**  
     * 停止为给定的{@code propertyId}和{@link CarPropertyEventCallback}获取更新。  
     * 如果同一个{@link CarPropertyEventCallback}用于其他属性,这些订阅不会受到影响。  
     *  
     * @param propertyId 要取消订阅的属性ID。  
     * @param carPropertyEventCallback 之前订阅的回调,用于取消订阅。  
     * @throws SecurityException 如果调用者没有读取该属性的权限。  
     */  
    @FlaggedApi(Flags.FLAG_BATCHED_SUBSCRIPTIONS)  
    public void unsubscribePropertyEvents(int propertyId,  
                                          @NonNull CarPropertyEventCallback carPropertyEventCallback) {  
        // 确保回调不为空  
        requireNonNull(carPropertyEventCallback);  

        // 调用内部方法取消订阅属性事件  
        unsubscribePropertyEventsInternal(List.of(propertyId), carPropertyEventCallback);  
    }  
}

unsubscribePropertyEvents() 方法用于停止为指定的CarPropertyEventCallback获取属性更新。它会取消该回调的所有注册监听。主要看下 unsubscribePropertyEventsInternal() 方法。

获取属性

getProperty()

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {  

    /**  
     * 返回 {@link CarPropertyValue}  
     * <p>此方法可能需要几秒钟才能完成,因此必须在非主线程中调用。  
     * <p>注意:客户端不得使用以下任意一个作为propertyId,否则行为未定义(可能抛出异常或返回null):  
     * <ul>  
     * <li>{@code INITIAL_USER_INFO}  
     * <li>{@code SWITCH_USER}  
     * <li>{@code CREATE_USER}  
     * <li>{@code REMOVE_USER}  
     * <li>{@code USER_IDENTIFICATION_ASSOCIATION}  
     * </ul>  
     *  
     * <p>声明 {@link android.content.pm.ApplicationInfo#targetSdkVersion} 等于或大于  
     * {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE} 的客户端在请求失败时将收到以下异常:  
     * <ul>  
     *     <li>{@link CarInternalErrorException} 当车辆中检测到意外错误时  
     *     <li>{@link PropertyAccessDeniedSecurityException} 当车辆拒绝访问该属性时  
     *     <li>{@link PropertyNotAvailableAndRetryException} 当该属性暂时不可用,并且重试可能会成功时  
     *     <li>{@link PropertyNotAvailableException} 当该属性不可用,并且可能在一段时间内不可用时  
     *     <li>{@link IllegalArgumentException} 当[propertyId, areaId]不受支持或指定的类与属性类型不匹配时  
     * </ul>  
     *  
     * <p>声明 {@link android.content.pm.ApplicationInfo#targetSdkVersion} 等于或大于  
     * {@link Build.VERSION_CODES#R},但小于 {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE} 的客户端  
     * 在请求失败时将收到以下异常或 {@code null}:  
     * <ul>  
     *     <li>{@link CarInternalErrorException} 当车辆中检测到意外错误时  
     *     <li>{@link PropertyAccessDeniedSecurityException} 当车辆拒绝访问该属性时  
     *     <li>{@link PropertyNotAvailableAndRetryException} 当该属性暂时不可用,并且重试可能会成功时  
     *     <li>{@link PropertyNotAvailableException} 当该属性不可用,并且可能在一段时间内不可用时  
     *     <li>{@code null} 当[propertyId, areaId]不受支持时  
     * </ul>  
     *  
     * <p>声明 {@link android.content.pm.ApplicationInfo#targetSdkVersion} 小于  
     * {@link Build.VERSION_CODES#R} 的客户端在请求失败时将收到以下异常或 {@code null}:  
     * <ul>  
     *     <li>{@link IllegalStateException} 当车辆中检测到错误,或者车辆拒绝访问该属性,或者该属性不可用并且可能会在一段时间内不可用,或者发生了意外错误时  
     *     <li>{@link IllegalArgumentException} 当指定的类与属性类型不匹配时  
     *     <li>{@code null} 当[propertyId, areaId]不受支持或该属性暂时不可用时  
     * </ul>  
     *  
     * <p>对于较早版本的客户端,如果属性暂时不可用,返回的值可能为null。客户端应该在这种情况下重试。  
     * <p>对于较早版本的客户端,当[propertyId, areaId]不受支持时,将返回 {@code null}。  
     * <p>对于较早版本的客户端,返回的 {@link CarPropertyValue} 可能包含不可用或错误状态。客户端必须使用  
     * {@link CarPropertyValue#getStatus} 进行检查。如果返回的状态不是  
     * {@link CarPropertyValue#STATUS_AVAILABLE},则通过 {@link CarPropertyValue#getValue} 返回的值是未定义的。  
     * <p>对于 U 及更高版本的客户端,当[propertyId, areaId]不受支持时,保证抛出  
     * {@code IllegalArgumentException} 异常。该方法不会返回 {@code null}。  
     * <p>对于 U 及更高版本的客户端,如果属性的状态为  
     * {@link CarPropertyValue#STATUS_UNAVAILABLE},则会抛出 {@link PropertyNotAvailableException}。  
     * 如果属性的状态为 {@link CarPropertyValue#STATUS_ERROR},则会抛出  
     * {@link CarInternalErrorException}。如果没有抛出异常,返回的  
     * {@link CarPropertyValue#getStatus} 保证是  
     * {@link CarPropertyValue#STATUS_AVAILABLE},因此客户端无需检查。  
     *  
     * @param clazz 期望的属性类型的类对象  
     * @param propertyId 要获取的属性ID  
     * @param areaId 要获取的属性区域ID  
     * @throws CarInternalErrorException 当车辆检测到意外错误时  
     * @throws PropertyAccessDeniedSecurityException 当车辆拒绝访问该属性时  
     * @throws PropertyNotAvailableAndRetryException 当该属性暂时不可用,并且重试可能会成功时  
     * @throws PropertyNotAvailableException 当该属性不可用,并且可能在一段时间内不可用时  
     * @throws IllegalArgumentException 当[propertyId, areaId]不受支持,或者指定的类与属性类型不匹配时  
     * @return 返回属性值的 {@link CarPropertyValue} 或 {@code null}  
     */  
    @SuppressWarnings("unchecked")  
    @Nullable  
    public <E> CarPropertyValue<E> getProperty(@NonNull Class<E> clazz, int propertyId,  
                                               int areaId) {  
        // 调用不带类型检查的getProperty方法来获取CarPropertyValue
        CarPropertyValue<E> carPropertyValue = getProperty(propertyId, areaId);  

        // 如果获取的属性值为null,直接返回null
        if (carPropertyValue == null) {  
            return null;  
        }  

        // 获取返回值的实际类型
        Class<?> actualClass = carPropertyValue.getValue().getClass();  

        // 如果实际返回的类型与预期类型不一致,抛出IllegalArgumentException
        if (actualClass != clazz) {  
            throw new IllegalArgumentException(  
                    "Invalid property type. " + "Expected: " + clazz + ", but was: "  
                            + actualClass);  
        }  

        // 返回获取的CarPropertyValue对象
        return carPropertyValue;  
    }  

    /**  
     * 查询 {@link CarPropertyValue} 根据属性ID和区域ID  
     * <p>此方法可能需要几秒钟才能完成,因此必须在非主线程中调用。  
     * <p>注意:客户端不得使用以下任意一个作为propertyId,否则行为未定义(可能抛出异常或返回null):  
     * <ul>  
     * <li>{@code INITIAL_USER_INFO}  
     * <li>{@code SWITCH_USER}  
     * <li>{@code CREATE_USER}  
     * <li>{@code REMOVE_USER}  
     * <li>{@code USER_IDENTIFICATION_ASSOCIATION}  
     * </ul>  
     *  
     * <p>声明 {@link android.content.pm.ApplicationInfo#targetSdkVersion} 等于或大于  
     * {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE} 的客户端在请求失败时将收到以下异常:  
     * <ul>  
     *     <li>{@link CarInternalErrorException} 当车辆中检测到意外错误时  
     *     <li>{@link PropertyAccessDeniedSecurityException} 当车辆拒绝访问该属性时  
     *     <li>{@link PropertyNotAvailableAndRetryException} 当该属性暂时不可用,并且重试可能会成功时  
     *     <li>{@link PropertyNotAvailableException} 当该属性不可用,并且可能在一段时间内不可用时  
     *     <li>{@link IllegalArgumentException} 当[propertyId, areaId]不受支持或指定的类与属性类型不匹配时  
     * </ul>  
     *  
     * <p>声明 {@link android.content.pm.ApplicationInfo#targetSdkVersion} 等于或大于  
     * {@link Build.VERSION_CODES#R},但小于 {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE} 的客户端  
     * 在请求失败时将收到以下异常或 {@code null}:  
     * <ul>  
     *     <li>{@link CarInternalErrorException} 当车辆中检测到意外错误时  
     *     <li>{@link PropertyAccessDeniedSecurityException} 当车辆拒绝访问该属性时  
     *     <li>{@link PropertyNotAvailableAndRetryException} 当该属性暂时不可用,并且重试可能会成功时  
     *     <li>{@link PropertyNotAvailableException} 当该属性不可用,并且可能在一段时间内不可用时  
     *     <li>{@code null} 当[propertyId, areaId]不受支持时  
     * </ul>  
     *  
     * <p>声明 {@link android.content.pm.ApplicationInfo#targetSdkVersion} 小于  
     * {@link Build.VERSION_CODES#R} 的客户端在请求失败时将收到以下异常或 {@code null}:  
     * <ul>  
     *     <li>{@link IllegalStateException} 当车辆中检测到错误,或者车辆拒绝访问该属性,或者该属性不可用并且可能会在一段时间内不可用,或者发生了意外错误时  
     *     <li>{@link IllegalArgumentException} 当指定的类与属性类型不匹配时  
     *     <li>{@code null} 当[propertyId, areaId]不受支持或该属性暂时不可用时  
     * </ul>  
     *  
     * <p>对于较早版本的客户端,如果属性暂时不可用,返回的值可能为null。客户端应该在这种情况下重试。  
     * <p>对于较早版本的客户端,当[propertyId, areaId]不受支持时,将返回 {@code null}。  
     * <p>对于较早版本的客户端,返回的 {@link CarPropertyValue} 可能包含不可用或错误状态。客户端必须使用  
     * {@link CarPropertyValue#getStatus} 进行检查。如果返回的状态不是  
     * {@link CarPropertyValue#STATUS_AVAILABLE},则通过 {@link CarPropertyValue#getValue} 返回的值是未定义的。  
     * <p>对于 U 及更高版本的客户端,当[propertyId, areaId]不受支持时,保证抛出  
     * {@code IllegalArgumentException} 异常。该方法不会返回 {@code null}。  
     * <p>对于 U 及更高版本的客户端,如果属性的状态为  
     * {@link CarPropertyValue#STATUS_UNAVAILABLE},则会抛出 {@link PropertyNotAvailableException}。  
     * 如果属性的状态为 {@link CarPropertyValue#STATUS_ERROR},则会抛出  
     * {@link CarInternalErrorException}。如果没有抛出异常,返回的  
     * {@link CarPropertyValue#getStatus} 保证是  
     * {@link CarPropertyValue#STATUS_AVAILABLE},因此客户端无需检查。  
     *  
     * @param propertyId 要获取的属性ID  
     * @param areaId 要获取的属性区域ID  
     * @return 返回获取到的属性值,如果失败则返回null  
     */  
    @Nullable  
    public <E> CarPropertyValue<E> getProperty(int propertyId, int areaId) {  
        // 输出调试信息,打印请求的属性ID和区域ID
        if (DBG) {  
            Slog.d(TAG, "getProperty, propertyId: " + VehiclePropertyIds.toString(propertyId)  
                    + ", areaId: 0x" + toHexString(areaId));  
        }  

        // 确保请求的属性ID是合法的
        assertNotUserHalProperty(propertyId);  

        try {  
            // 验证该属性ID是否受支持
            assertPropertyIdIsSupported(propertyId);  
        } catch (IllegalArgumentException e) {  
            // 对于较新的SDK版本,如果属性ID不受支持,则抛出异常
            if (mAppTargetSdk >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {  
                throw e;  
            } else {  
                // 对于旧版SDK,直接返回null
                return null;  
            }  
        }  

        // 开始性能跟踪
        Trace.beginSection("getProperty-" + propertyId + "/" + areaId);  
        try {  
            // 调用同步操作,向车载服务请求属性值
            CarPropertyValue<E> carPropertyValue = (CarPropertyValue<E>) (runSyncOperation(() -> {  
                return mService.getProperty(propertyId, areaId);  
            }));  

            // 如果返回的属性值为空,直接返回null
            if (carPropertyValue == null) {  
                return null;  
            }  

            // 对于较新的SDK版本,检查返回的属性值状态
            if (mAppTargetSdk >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {  
                // 如果属性状态为不可用,抛出异常
                if (carPropertyValue.getStatus() == CarPropertyValue.STATUS_UNAVAILABLE) {  
                    throw new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE,  
                            "getProperty returned value with UNAVAILABLE status: "  
                                    + carPropertyValue);  
                } else if (carPropertyValue.getStatus() != CarPropertyValue.STATUS_AVAILABLE) {  
                    // 如果属性状态异常,抛出内部错误异常
                    throw new ServiceSpecificException(VehicleHalStatusCode.STATUS_INTERNAL_ERROR,  
                            "getProperty returned value with error or unknown status: "  
                                    + carPropertyValue);  
                }  
            }  

            // 返回获取到的属性值
            return carPropertyValue;  
        } catch (IllegalArgumentException e) {  
            // 如果属性ID或区域不合法,则返回null或抛出异常(取决于SDK版本)
            if (mAppTargetSdk >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {  
                throw e;  
            } else {  
                return null;  
            }  
        } catch (RemoteException e) {  
            // 处理车载服务的远程异常
            return handleRemoteExceptionFromCarService(e, null);  
        } catch (ServiceSpecificException e) {  
            // 处理车载服务特定异常
            if (DBG) {  
                Slog.d(TAG, "getProperty received service specific exception, code: "  
                        + e.errorCode);  
            }  
            if (mAppTargetSdk < Build.VERSION_CODES.R) {  
                // 对于旧版本SDK,检查特定错误码
                if (e.errorCode == VehicleHalStatusCode.STATUS_TRY_AGAIN) {  
                    return null;  
                } else {  
                    // 如果错误无法恢复,抛出状态异常
                    throw new IllegalStateException("Failed to get propertyId: "  
                            + VehiclePropertyIds.toString(propertyId) + " areaId: 0x"  
                            + toHexString(areaId), e);  
                }  
            }  
            // 处理车载服务的特定异常
            handleCarServiceSpecificException(e, propertyId, areaId);  

            // 代码不会到达这里
            return null;  
        } finally {  
            // 结束性能跟踪
            Trace.endSection();  
        }  
    }  
}
  • getProperty(@NonNull Class<E> clazz, int propertyId, int areaId)

    • 参数 clazz:期望返回值的类型。
    • 参数 propertyIdareaId:分别是车辆属性的ID和区域ID。
    • 返回值:返回的 CarPropertyValue<E> 包含了属性值和属性状态,或者如果请求失败则返回 null
  • getProperty(int propertyId, int areaId)

参数 propertyIdareaId:分别是车辆属性的ID和区域ID。

getPropertyList()

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {  

    /**  
     * 获取该汽车上应用程序可以访问的所有支持的属性列表
     * 
     * @return 返回一个包含所有支持属性的列表,每个属性为 CarPropertyConfig 类型
     */    
    @NonNull  
    public List<CarPropertyConfig> getPropertyList() {  
        // 如果开启了调试模式 (DBG 为 true),输出日志
        if (DBG) {  
            Slog.d(TAG, "getPropertyList");  
        }  

        // 定义一个变量来存储汽车属性配置列表
        List<CarPropertyConfig> configs;  

        try {  
            // 通过 mService 调用获取汽车属性列表的方法,获取所有支持的属性配置
            configs = mService.getPropertyList().getConfigs();  
        } catch (RemoteException e) {  
            // 如果在获取属性列表时发生远程异常 (例如通信失败),则打印错误日志
            Slog.e(TAG, "getPropertyList exception ", e);  
            // 处理远程异常并返回一个空的属性列表
            return handleRemoteExceptionFromCarService(e, new ArrayList<>());  
        }  

        // 如果开启了调试模式,输出获取到的属性列表的大小
        if (DBG) {  
            Slog.d(TAG, "getPropertyList returns " + configs.size() + " configs");  
            // 遍历打印所有属性配置的内容
            for (int i = 0; i < configs.size(); i++) {  
                Slog.v(TAG, i + ": " + configs.get(i));  
            }  
        }  

        // 返回获取到的属性列表
        return configs;  
    }  
}

通过 mService 获取所有汽车属性的配置。该方法会返回一个 CarPropertyConfig 对象的列表,包含了车辆支持的每一个属性的详细信息。

getPropertyList(ArraySet<Integer> propertyIds)

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {  

    /**  
     * 检查给定的属性 ID 列表,并返回车辆支持的属性配置列表。
     * 如果给定的属性 ID 列表中有某些属性不被支持,则它们不会被返回。
     * 
     * @param propertyIds 传入的属性 ID 列表
     * @return 返回包含所有支持属性的配置列表,每个配置是 CarPropertyConfig 类型
     */    
    @NonNull  
    public List<CarPropertyConfig> getPropertyList(@NonNull ArraySet<Integer> propertyIds) {  

        // 如果开启了调试日志 (DBG 为 true),则输出日志,记录传入的属性 ID 列表
        if (DBG) {  
            Slog.d(TAG, "getPropertyList(" + CarPropertyHelper.propertyIdsToString(propertyIds)  
                    + ")");  
        }  

        // 通过调用 getPropertyConfigsFromService 方法从服务中获取属性配置
        CarPropertyConfigs configs = getPropertyConfigsFromService(propertyIds);  

        // 如果获取的属性配置为空,返回一个空列表
        if (configs == null) {  
            return new ArrayList<>();  
        }  

        // 如果返回的配置中包含缺少权限的属性 ID,则输出警告日志
        if (configs.getMissingPermissionPropIds().length != 0) {  
            Slog.w(TAG, "Missing required permissions to access properties: "  
                    + CarPropertyHelper.propertyIdsToString(configs.getMissingPermissionPropIds()));  
        }  

        // 如果返回的配置中包含不支持的属性 ID,则输出警告日志
        if (configs.getUnsupportedPropIds().length != 0) {  
            Slog.w(TAG, "The following properties are not supported: "  
                    + CarPropertyHelper.propertyIdsToString(configs.getUnsupportedPropIds()));  
        }  

        // 获取所有有效的属性配置
        List<CarPropertyConfig> configList = configs.getConfigs();  

        // 如果开启了调试日志,输出获取到的属性配置列表的大小,并打印每个配置的详细信息
        if (DBG) {  
            Slog.d(TAG, "getPropertyList(" + CarPropertyHelper.propertyIdsToString(propertyIds)  
                    + ") returns " + configList.size() + " configs");  
            for (int i = 0; i < configList.size(); i++) {  
                Slog.v(TAG, i + ": " + configList.get(i));  
            }  
        }  

        // 返回有效的属性配置列表
        return configList;  
    }  
}

getPropertyList() 方法用于获取指定属性 ID 列表对应的所有支持的汽车属性配置。该方法返回一个 List<CarPropertyConfig> 类型的列表,每个元素是一个 CarPropertyConfig 对象,表示汽车的一项属性配置。

getBooleanProperty()

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {  

    /**  
     * 获取布尔类型的车辆属性值  
     *  
     * <p>此方法可能需要几秒钟才能完成,因此必须在非主线程中调用。</p>  
     *  
     * <p>注意:客户端不得使用以下属性ID,否则行为未定义:</p>  
     * <ul>  
     *     <li>{@code INITIAL_USER_INFO}</li>  
     *     <li>{@code SWITCH_USER}</li>  
     *     <li>{@code CREATE_USER}</li>  
     *     <li>{@code REMOVE_USER}</li>  
     *     <li>{@code USER_IDENTIFICATION_ASSOCIATION}</li>  
     * </ul>  
     *  
     * <p>声明 {@link android.content.pm.ApplicationInfo#targetSdkVersion} 大于或等于  
     * {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE} 的客户端,在请求失败时将收到以下异常:</p>  
     * <ul>  
     *     <li>{@link CarInternalErrorException} 当车辆检测到意外错误时</li>  
     *     <li>{@link PropertyAccessDeniedSecurityException} 当车辆拒绝访问该属性时</li>  
     *     <li>{@link PropertyNotAvailableAndRetryException} 当属性暂时不可用且重试可能成功时</li>  
     *     <li>{@link PropertyNotAvailableException} 当属性不可用,且可能会在一段时间内不可用时</li>  
     *     <li>{@link IllegalArgumentException} 当[propertyId, areaId]不受支持或属性类型错误时</li>  
     * </ul>  
     *  
     * <p>声明 {@link android.content.pm.ApplicationInfo#targetSdkVersion} 大于或等于  
     * {@link Build.VERSION_CODES#R} 且小于 {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE} 的客户端,  
     * 在请求失败时将收到以下异常或 {@code false}:</p>  
     * <ul>  
     *     <li>{@link CarInternalErrorException} 当车辆检测到意外错误时</li>  
     *     <li>{@link PropertyAccessDeniedSecurityException} 当车辆拒绝访问该属性时</li>  
     *     <li>{@link PropertyNotAvailableAndRetryException} 当属性暂时不可用且重试可能成功时</li>  
     *     <li>{@link PropertyNotAvailableException} 当属性不可用,且可能会在一段时间内不可用时</li>  
     *     <li>{@link IllegalArgumentException} 当属性类型错误时</li>  
     *     <li>{@code false} 当[propertyId, areaId]不受支持时</li>  
     * </ul>  
     *  
     * <p>声明 {@link android.content.pm.ApplicationInfo#targetSdkVersion} 小于 {@link Build.VERSION_CODES#R} 的客户端,  
     * 在请求失败时将收到以下异常或 {@code false}:</p>  
     * <ul>  
     *     <li>{@link IllegalStateException} 当车辆检测到错误、拒绝访问该属性、属性不可用,  
     *         或发生意外错误时</li>  
     *     <li>{@link IllegalArgumentException} 当属性类型错误时</li>  
     *     <li>{@code false} 当[propertyId, areaId]不受支持或属性暂时不可用时</li>  
     * </ul>  
     *  
     * <p>对于 R 版本之前的客户端,如果属性暂时不可用,返回值可能是 {@code false},客户端可以尝试重试。</p>  
     * <p>对于 R 版本之前的客户端,当[propertyId, areaId]不受支持时,将返回 {@code false}。</p>  
     * <p>对于 U 版本及之后的客户端,当[propertyId, areaId]不受支持时,将抛出 {@code IllegalArgumentException}。</p>  
     *  
     * @param propertyId 要获取的属性ID  
     * @param areaId 要获取的属性区域ID  
     *  
     * @throws CarInternalErrorException 当车辆检测到意外错误时  
     * @throws PropertyAccessDeniedSecurityException 当车辆拒绝访问该属性时  
     * @throws PropertyNotAvailableAndRetryException 当[propertyId, areaId]暂时不可用,重试可能成功时  
     * @throws PropertyNotAvailableException 当[propertyId, areaId]不可用,可能长时间不可用时  
     * @throws IllegalArgumentException 当[propertyId, areaId]不受支持或属性类型错误时  
     *  
     * @return 返回布尔类型的属性值,或 {@code false}。  
     */  
    public boolean getBooleanProperty(int propertyId, int areaId) {  
        // 调用 getProperty 方法获取属性值,期望返回类型为 Boolean 类型
        CarPropertyValue<Boolean> carProp = getProperty(Boolean.class, propertyId, areaId);  

        // 处理获取到的属性值(可能为 null),并根据属性状态返回结果
        return handleNullAndPropertyStatus(carProp, areaId, false);  
    }  

}

getBooleanProperty() 方法用于获取车辆属性的布尔类型值。它通过 propertyIdareaId 唯一标识需要获取的车辆属性,并返回对应的布尔值。

getFloatProperty()

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {  

    /**  
     * 获取浮动类型的车辆属性值  
     *  
     * <p>此方法可能需要几秒钟才能完成,因此必须在非主线程中调用。</p>  
     *  
     * <p>此方法的异常处理与 {@link #getBooleanProperty(int, int)} 方法相同。</p>  
     *  
     * @param propertyId 要获取的属性ID  
     * @param areaId 要获取的属性区域ID  
     *  
     * @throws CarInternalErrorException 当车辆检测到意外错误时  
     * @throws PropertyAccessDeniedSecurityException 当车辆拒绝访问该属性时  
     * @throws PropertyNotAvailableAndRetryException 当[propertyId, areaId]暂时不可用,重试可能成功时  
     * @throws PropertyNotAvailableException 当[propertyId, areaId]不可用,可能长时间不可用时  
     * @throws IllegalArgumentException 当[propertyId, areaId]不受支持或属性类型错误时  
     *  
     * @return 返回浮动类型的属性值,或 {@code 0}。  
     */  
    public float getFloatProperty(int propertyId, int areaId) {  
        // 调用 getProperty 方法获取指定属性ID和区域ID的浮动类型属性值
        CarPropertyValue<Float> carProp = getProperty(Float.class, propertyId, areaId);  

        // 处理获取到的属性值(可能为 null),并根据属性状态返回合理的值
        return handleNullAndPropertyStatus(carProp, areaId, 0f);  
    }
}

getFloatProperty() 方法用于获取车辆属性的浮动类型(float)值。它通过 propertyIdareaId 唯一标识要获取的车辆属性,并返回该属性的浮动类型值。

getIntProperty()

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {  

    /**
     * 获取整数类型的车辆属性值
     *
     * <p>此方法可能需要几秒钟才能完成,因此需要在非主线程中调用。
     *
     * <p>此方法的异常行为与 {@link #getBooleanProperty(int, int)} 方法相同。
     *
     * @param propertyId 需要获取的属性 ID
     * @param areaId 属性所属的区域 ID
     *
     * @throws CarInternalErrorException 当车辆出现意外错误时抛出
     * @throws PropertyAccessDeniedSecurityException 当车辆拒绝访问该属性时抛出
     * @throws PropertyNotAvailableAndRetryException 当属性暂时不可用,并且重试可能成功时抛出
     * @throws PropertyNotAvailableException 当属性不可用,可能会长时间不可用时抛出
     * @throws IllegalArgumentException 当 [propertyId, areaId] 不受支持,或属性类型不正确时抛出
     *
     * @return 返回整数类型的属性值,如果没有获取到有效的值,则返回 {@code 0}
     */
    public int getIntProperty(int propertyId, int areaId) {
        // 调用 getProperty 方法,获取指定属性 ID 和区域 ID 对应的 CarPropertyValue
        CarPropertyValue<Integer> carProp = getProperty(Integer.class, propertyId, areaId);

        // 调用 handleNullAndPropertyStatus 方法,处理可能的 null 值和属性状态,返回默认值 0
        return handleNullAndPropertyStatus(carProp, areaId, 0);
    }
}

getIntProperty() 方法用于获取一个指定 propertyIdareaId 对应的整数类型属性值。

getIntArrayProperty()

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {  

    /**
     * 获取整数数组类型的车辆属性值
     *
     * <p>此方法可能需要几秒钟才能完成,因此需要在非主线程中调用。
     *
     * <p>此方法的异常行为与 {@link #getBooleanProperty(int, int)} 方法相同。
     *
     * @param propertyId 需要获取的属性 ID
     * @param areaId 属性所属的区域 ID
     *
     * @throws CarInternalErrorException 当车辆出现意外错误时抛出
     * @throws PropertyAccessDeniedSecurityException 当车辆拒绝访问该属性时抛出
     * @throws PropertyNotAvailableAndRetryException 当属性暂时不可用,并且重试可能成功时抛出
     * @throws PropertyNotAvailableException 当属性不可用,可能会长时间不可用时抛出
     * @throws IllegalArgumentException 当 [propertyId, areaId] 不受支持,或属性类型不正确时抛出
     *
     * @return 返回整数数组类型的属性值,如果没有获取到有效的值,则返回空数组。
     */
    @NonNull
    public int[] getIntArrayProperty(int propertyId, int areaId) {
        // 调用 getProperty 方法,获取指定属性 ID 和区域 ID 对应的 CarPropertyValue
        CarPropertyValue<Integer[]> carProp = getProperty(Integer[].class, propertyId, areaId);

        // 调用 handleNullAndPropertyStatus 方法,处理可能的 null 值和属性状态,返回默认值为空数组
        Integer[] res = handleNullAndPropertyStatus(carProp, areaId, new Integer[0]);

        // 将 Integer[] 转换为 int[],并返回
        return toIntArray(res);
    }

    /**
     * 将 Integer 数组转换为 int 数组
     * 
     * @param input 要转换的 Integer 数组
     * @return 转换后的 int 数组
     */
    private static int[] toIntArray(Integer[] input) {
        int len = input.length;
        int[] arr = new int[len];
        // 遍历 Integer 数组,将每个元素转换为 int 类型并填充到新的数组中
        for (int i = 0; i < len; i++) {
            arr[i] = input[i];
        }
        return arr;
    }
}

getIntArrayProperty() 方法的作用是获取一个整数数组类型的车辆属性值。它通过指定 propertyIdareaId 来查询车辆的属性,然后返回一个整数数组 int[]

getPropertiesAsync()

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {  

    /**
     * 异步查询一组 {@link CarPropertyValue} 对象,通过属性 ID 和区域 ID。
     *
     * <p>此方法会在结果准备好之前立即返回。对于每个请求,如果成功,结果会通过
     * {@code resultCallback.onSuccess} 进行回调;如果失败,结果会通过
     * {@code errorCallback.onFailure} 进行回调。保证总的回调次数等于请求数量,
     * 如果该方法没有抛出异常的话。如果抛出异常,则保证不会调用任何回调函数。
     * 如果 {@code callbackExecutor} 为 {@code null},回调将在默认的事件处理线程中执行。
     * 如果回调执行较为繁重,建议传入 {@code callbackExecutor} 来指定回调执行的线程。
     *
     * <p>如果操作被取消,保证不会再调用任何回调函数。
     *
     * <p>对于某个请求,如果该属性不可用,则 {@code errorCallback.onFailure} 会
     * 被调用一次,错误码为 {@link #STATUS_ERROR_NOT_AVAILABLE}。
     *
     * <p>对于某个请求,如果该属性状态为错误,则 {@code errorCallback.onFailure} 会
     * 被调用一次,错误码为 {@link #STATUS_ERROR_INTERNAL_ERROR}。
     *
     * @param getPropertyRequests 需要查询的属性列表。
     * @param timeoutInMs 操作的超时时间(毫秒)。
     * @param cancellationSignal 可用于取消操作的信号。
     * @param callbackExecutor 执行回调的执行器。
     * @param getPropertyCallback 查询结果的回调函数。
     * @throws SecurityException 如果没有读取某个属性的权限。
     * @throws IllegalArgumentException 如果要读取的属性不受支持。
     */
    public void getPropertiesAsync(
            @NonNull List<GetPropertyRequest> getPropertyRequests,
            long timeoutInMs,
            @Nullable CancellationSignal cancellationSignal,
            @Nullable @CallbackExecutor Executor callbackExecutor,
            @NonNull GetPropertyCallback getPropertyCallback) {

        // 调试模式下,打印调用信息。
        if (DBG) {
            Slog.d(TAG, "getPropertiesAsync, requests: " + getPropertyRequests + ", timeoutInMs: "
                    + timeoutInMs + ", callback: " + getPropertyCallback);
        }

        // 校验参数是否合法。
        checkAsyncArguments(getPropertyRequests, getPropertyCallback, timeoutInMs);

        // 如果未指定 callbackExecutor,则使用默认的事件处理线程。
        if (callbackExecutor == null) {
            callbackExecutor = new HandlerExecutor(getEventHandler());
        }

        // 为每个请求创建 AsyncPropertyServiceRequest 对象。
        List<AsyncPropertyServiceRequest> getPropertyServiceRequests = new ArrayList<>(
                getPropertyRequests.size());
        for (int i = 0; i < getPropertyRequests.size(); i++) {
            GetPropertyRequest getPropertyRequest = getPropertyRequests.get(i);
            int propertyId = getPropertyRequest.getPropertyId();

            // 确保该属性 ID 是支持的。
            assertPropertyIdIsSupported(propertyId);

            // 创建异步服务请求对象,并添加到列表中。
            getPropertyServiceRequests.add(AsyncPropertyServiceRequest.newGetAsyncRequest(
                    getPropertyRequest));
        }

        // 存储请求信息,并为每个请求保存回调执行器和回调函数。
        List<Integer> requestIds = storePendingRequestInfo(getPropertyRequests, callbackExecutor,
                getPropertyCallback);

        try {
            if (DBG) {
                Slog.d(TAG, "calling CarPropertyService.getPropertiesAsync");
            }

            // 调用 car service 的异步查询方法。
            mService.getPropertiesAsync(new AsyncPropertyServiceRequestList(
                    getPropertyServiceRequests), mAsyncPropertyResultCallback, timeoutInMs);

            if (DBG) {
                Slog.d(TAG, "CarPropertyService.getPropertiesAsync succeed");
            }
        } catch (RemoteException e) {
            // 如果发生远程异常,清除请求信息,并处理异常。
            clearRequestIdToAsyncRequestInfo(getPropertyRequests);
            handleRemoteExceptionFromCarService(e);
        } catch (IllegalArgumentException | SecurityException e) {
            // 如果发生非法参数异常或权限异常,清除请求信息并抛出异常。
            clearRequestIdToAsyncRequestInfo(getPropertyRequests);
            throw e;
        }

        // 如果提供了取消信号,设置取消监听器。
        if (cancellationSignal != null) {
            setOnCancelListener(cancellationSignal, requestIds);
        }
    }

    /**
     * 异步查询一组 {@link CarPropertyValue} 对象,通过属性 ID 和区域 ID。
     *
     * <p>此方法与 {@link CarPropertyManager#getPropertiesAsync(List, long, CancellationSignal,
     * Executor, GetPropertyCallback)} 相同,唯一不同的是默认超时时间为 10 秒。
     */
    public void getPropertiesAsync(
            @NonNull List<GetPropertyRequest> getPropertyRequests,
            @Nullable CancellationSignal cancellationSignal,
            @Nullable @CallbackExecutor Executor callbackExecutor,
            @NonNull GetPropertyCallback getPropertyCallback) {

        // 使用默认超时时间调用上面的 getPropertiesAsync 方法。
        getPropertiesAsync(getPropertyRequests, ASYNC_GET_DEFAULT_TIMEOUT_MS, cancellationSignal,
                callbackExecutor, getPropertyCallback);
    }
}

异步查询一组车载属性(CarPropertyValue),根据传入的属性 ID 和区域 ID 执行查询。查询操作会返回一个 GetPropertyCallback 回调,查询成功时通过 onSuccess 回调返回结果,失败时通过 onFailure 回调返回错误信息。

设置属性

setProperty()

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {  

    /**  
     * 通过 areaId 设置车辆属性的值。  
     *  
     * <p>如果多个客户端同时为同一个 areaId 设置属性,哪个设置操作会生效是不确定的。通常情况下,  
     * 最后一个设置操作(按照操作发送到汽车的 ECU 的顺序)会覆盖之前的设置操作。</p>  
     *  
     * <p>此方法可能需要几秒钟才能完成,因此必须在非主线程中调用。</p>  
     *  
     * <p>声明 {@link android.content.pm.ApplicationInfo#targetSdkVersion} 大于或等于  
     * {@link Build.VERSION_CODES#R} 的客户端,在请求失败时将收到以下异常:</p>  
     * <ul>  
     *     <li>{@link CarInternalErrorException}:当检测到意外错误时</li>  
     *     <li>{@link PropertyAccessDeniedSecurityException}:当车辆拒绝访问该属性时</li>  
     *     <li>{@link PropertyNotAvailableAndRetryException}:当属性暂时不可用且重试可能成功时</li>  
     *     <li>{@link PropertyNotAvailableException}:当属性不可用,并且可能会持续不可用时</li>  
     *     <li>{@link IllegalArgumentException}:当[propertyId, areaId]不受支持时</li>  
     * </ul>  
     *  
     * <p>声明 {@link android.content.pm.ApplicationInfo#targetSdkVersion} 小于 {@link Build.VERSION_CODES#R} 的客户端,在请求失败时将收到以下异常:</p>  
     * <ul>  
     *     <li>{@link RuntimeException}:当属性暂时不可用时</li>  
     *     <li>{@link IllegalStateException}:当车辆检测到错误、拒绝访问属性、属性暂时不可用或者发生了意外错误时</li>  
     *     <li>{@link IllegalArgumentException}:当[propertyId, areaId]不受支持时</li>  
     * </ul>  
     *  
     * <p>此方法返回并不一定意味着设置操作成功。为了确认操作是否成功,客户端应该使用  
     * {@link CarPropertyManager#registerCallback} 在设置操作之前注册该属性的回调,监听  
     * 属性更新。当 {@link CarPropertyEventCallback#onChangeEvent} 被调用时,表示设置操作成功;  
     * 当 {@link CarPropertyEventCallback#onErrorEvent} 被调用时,表示设置操作失败。</p>  
     *  
     * <p>需要注意的是,如果要设置的值与当前值相同,仍然会发送设置请求到车辆硬件,  
     * 但是不会触发新的属性变化事件。如果客户端想要避免发送设置请求,可以在调用此方法之前,  
     * 先使用 {@link getProperty} 检查当前值。</p>  
     *  
     * @param clazz 期望返回的属性值类型的类对象  
     * @param propertyId 要修改的属性ID  
     * @param areaId 要修改的区域ID  
     * @param val 要设置的属性值  
     * @param <E> 给定属性的类类型,例如,如果车辆硬件接口定义的属性类型为 `VEHICLE_VALUE_TYPE_INT32`,  
     *            客户端可以使用 {@code Integer.class} 来访问该属性  
     * @throws CarInternalErrorException 当车辆检测到意外错误时  
     * @throws PropertyAccessDeniedSecurityException 当车辆拒绝访问该属性时  
     * @throws PropertyNotAvailableException 当 [propertyId, areaId] 不可用,并且可能在一段时间内不可用时  
     * @throws PropertyNotAvailableAndRetryException 当 [propertyId, areaId] 暂时不可用,并且重试可能成功时  
     * @throws IllegalArgumentException 当 [propertyId, areaId] 不受支持时  
     */  
    public <E> void setProperty(@NonNull Class<E> clazz, int propertyId, int areaId,  
                                @NonNull E val) {  
        // 调试输出:记录正在设置的属性ID、区域ID、类类型和值
        if (DBG) {  
            Slog.d(TAG, "setProperty, propertyId: " + VehiclePropertyIds.toString(propertyId)  
                    + ", areaId: 0x" + toHexString(areaId) + ", class: " + clazz + ", val: " + val);  
        }  

        // 确保请求的属性ID不是用户自定义的属性(用户自定义属性的ID不允许使用)
        assertNotUserHalProperty(propertyId);  

        // 验证该属性ID是否受支持
        assertPropertyIdIsSupported(propertyId);  

        // 开始性能跟踪,记录设置属性操作的开始
        Trace.beginSection("setProperty-" + propertyId + "/" + areaId);  
        try {  
            // 执行同步操作,向车载服务发送设置属性请求
            runSyncOperation(() -> {  
                // 将属性值封装到 CarPropertyValue 对象中,并通过 carPropertyEventToService 发送
                mService.setProperty(new CarPropertyValue<>(propertyId, areaId, val),  
                        mCarPropertyEventToService);  
                return null;  
            });  
        } catch (RemoteException e) {  
            // 处理远程异常,当与车载服务通信时发生错误
            handleRemoteExceptionFromCarService(e);  
        } catch (ServiceSpecificException e) {  
            // 处理服务特定异常,捕获来自车载服务的具体错误
            if (DBG) {  
                Slog.d(TAG, "setProperty received service specific exception", e);  
            }  
            if (mAppTargetSdk < Build.VERSION_CODES.R) {  
                // 对于较旧的SDK版本,处理特定的错误码
                if (e.errorCode == VehicleHalStatusCode.STATUS_TRY_AGAIN) {  
                    // 如果错误是临时的,客户端可以重试
                    throw new RuntimeException("Failed to set propertyId: "  
                            + VehiclePropertyIds.toString(propertyId) + " areaId: 0x"  
                            + toHexString(areaId), e);  
                } else {  
                    // 如果错误是不可恢复的,抛出非法状态异常
                    throw new IllegalStateException("Failed to set propertyId: "  
                            + VehiclePropertyIds.toString(propertyId) + " areaId: 0x"  
                            + toHexString(areaId), e);  
                }  
            }  
            // 处理来自车载服务的具体异常,客户端可以根据需要进行更多处理
            handleCarServiceSpecificException(e, propertyId, areaId);  
        } finally {  
            // 结束性能跟踪,记录设置属性操作的结束
            Trace.endSection();  
        }  
    }  

}

setProperty() 方法用于设置车辆属性的值,主要作用是通过指定的 propertyId 和 areaId 修改车辆中某个特定属性的值。

setBooleanProperty()

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {  

    /**
     * 修改属性的值。如果属性修改没有成功,则会生成错误事件并将其传播回应用程序。
     *
     * <p>该方法可能需要几秒钟才能完成,因此需要在非主线程中调用。
     *
     * @param propertyId 需要修改的属性 ID
     * @param areaId 需要应用修改的区域 ID
     * @param val 要设置的新值,类型为 boolean
     */
    public void setBooleanProperty(int propertyId, int areaId, boolean val) {
        // 调用 setProperty 方法来设置属性的值。这里使用了 Boolean.class 来指定值的类型为布尔值。
        setProperty(Boolean.class, propertyId, areaId, val);
    }
}

setBooleanProperty() 方法用于修改一个布尔类型的属性值。

setFloatProperty()

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {  

    /**
     * 设置属性的浮动值(float 类型)
     *
     * <p>该方法可能需要几秒钟才能完成,因此需要在非主线程中调用。
     *
     * @param propertyId 需要修改的属性 ID
     * @param areaId 需要应用修改的区域 ID
     * @param val 要设置的新值,类型为 float
     */
    public void setFloatProperty(int propertyId, int areaId, float val) {
        // 调用通用的 setProperty 方法来设置属性值,这里指定值的类型是 Float.class
        setProperty(Float.class, propertyId, areaId, val);
    }
}

setFloatProperty() 方法用于修改一个浮动类型(float)的属性值。它通过调用通用的 setProperty 方法来实现,能够动态处理不同类型的属性。

setIntProperty()

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {  

    /**
     * 设置属性的整数值(int 类型)
     *
     * <p>该方法可能需要几秒钟才能完成,因此需要在非主线程中调用。
     *
     * @param propertyId 需要修改的属性 ID
     * @param areaId 需要应用修改的区域 ID
     * @param val 要设置的新值,类型为 int
     */
    public void setIntProperty(int propertyId, int areaId, int val) {
        // 调用通用的 setProperty 方法来设置属性值,这里指定值的类型是 Integer.class
        setProperty(Integer.class, propertyId, areaId, val);
    }
}

setIntProperty() 方法用于设置一个整数类型(int)的属性值。它通过调用一个通用的 setProperty 方法来实现,避免了为每种属性类型编写独立的修改方法。

setPropertiesAsync()

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {  

    /**
     * 异步设置一组车载属性值。
     *
     * <p>此方法会立即返回,在结果准备好之前不会阻塞。每个请求的结果会通过
     * {@code resultCallback.onSuccess} 进行回调(如果请求成功),或通过
     * {@code errorCallback.onFailure} 进行回调(如果请求失败)。如果没有抛出异常,
     * 则保证回调函数的调用次数与请求的数量相等。如果发生异常,则不再调用任何回调函数。
     * 如果 {@code callbackExecutor} 为 {@code null},回调将在默认的事件处理线程中执行。
     * 如果回调执行较为繁重,建议提供一个 {@code callbackExecutor} 来指定回调执行线程。
     *
     * <p>如果操作被取消,保证不会再调用任何回调函数。
     *
     * <p>如果多个客户端同时设置同一区域 ID 的属性,哪个操作优先级更高是不确定的。
     * 通常情况下,最后一个执行的设置操作会覆盖之前的操作。
     *
     * <p>成功回调的调用时机取决于每个请求的 {@code waitForPropertyUpdate} 设置。
     * 如果该值为 {@code true}(默认值),则在设置操作成功发送到车辆总线并且
     * 属性值与目标值相同或已更新时调用成功回调。
     *
     * <p>如果 {@code waitForPropertyUpdate} 设置为 {@code false},则只要设置操作
     * 成功发送到车辆总线,成功回调就会被调用。
     *
     * <p>在大多数情况下,客户端应该等待属性更新,以验证设置操作是否成功。
     *
     * <p>对于一些写入只属性(无法获取更新事件的属性)或表示某些动作的属性,
     * 比如键盘按键事件,当前的属性值没有意义,调用者必须将 {@code waitForPropertyUpdate}
     * 设置为 {@code false}。
     *
     * <p>对于 {@code HVAC_TEMPERATURE_VALUE_SUGGESTION} 属性,必须将 {@code waitForPropertyUpdate}
     * 设置为 {@code false},因为更新后的属性值将与目标值不同。
     *
     * @param setPropertyRequests 要设置的属性请求列表。
     * @param timeoutInMs 操作的超时时间(毫秒)。
     * @param cancellationSignal 可用于取消正在进行的操作。
     * @param callbackExecutor 执行回调的线程池执行器。
     * @param setPropertyCallback 用于接收设置结果的回调函数。
     * @throws SecurityException 如果没有权限写入某个属性。
     * @throws IllegalArgumentException 如果要设置的属性不受支持。
     * @throws IllegalArgumentException 如果设置的某个值不支持该属性。
     * @throws IllegalArgumentException 如果某个属性不可读且未将 {@code waitForPropertyUpdate}
     *   设置为 {@code false}。
     * @throws IllegalArgumentException 如果某个属性是 {@code HVAC_TEMPERATURE_VALUE_SUGGESTION}
     *   且未将 {@code waitForPropertyUpdate} 设置为 {@code false}。
     */
    public void setPropertiesAsync(
            @NonNull List<SetPropertyRequest<?>> setPropertyRequests,
            long timeoutInMs,
            @Nullable CancellationSignal cancellationSignal,
            @Nullable @CallbackExecutor Executor callbackExecutor,
            @NonNull SetPropertyCallback setPropertyCallback) {

        // 调试模式下打印调用参数。
        if (DBG) {
            Slog.d(TAG, "setPropertiesAsync, requests: " + setPropertyRequests + ", timeoutInMs: "
                    + timeoutInMs + ", callback: " + setPropertyCallback);
        }

        // 校验参数。
        checkAsyncArguments(setPropertyRequests, setPropertyCallback, timeoutInMs);

        // 如果 callbackExecutor 为 null,使用默认的事件处理器。
        if (callbackExecutor == null) {
            callbackExecutor = new HandlerExecutor(getEventHandler());
        }

        // 构建异步服务请求列表。
        List<AsyncPropertyServiceRequest> setPropertyServiceRequests = new ArrayList<>(
                setPropertyRequests.size());
        for (int i = 0; i < setPropertyRequests.size(); i++) {
            SetPropertyRequest setPropertyRequest = setPropertyRequests.get(i);
            int propertyId = setPropertyRequest.getPropertyId();

            // 确保请求的值不为空,并检查属性 ID 是否支持。
            requireNonNull(setPropertyRequest.getValue());
            assertPropertyIdIsSupported(propertyId);

            // 为每个请求创建异步设置请求,并加入请求列表。
            setPropertyServiceRequests.add(AsyncPropertyServiceRequest.newSetAsyncRequest(
                    setPropertyRequest));
        }

        // 存储请求 ID 和相关的回调信息。
        List<Integer> requestIds = storePendingRequestInfo(setPropertyRequests, callbackExecutor,
                setPropertyCallback);

        try {
            if (DBG) {
                Slog.d(TAG, "calling CarPropertyService.setPropertiesAsync");
            }

            // 调用 car service 进行异步属性设置。
            mService.setPropertiesAsync(new AsyncPropertyServiceRequestList(
                    setPropertyServiceRequests), mAsyncPropertyResultCallback, timeoutInMs);

            if (DBG) {
                Slog.d(TAG, "CarPropertyService.setPropertiesAsync succeed");
            }
        } catch (RemoteException e) {
            // 远程调用异常处理:清理请求信息,并处理异常。
            clearRequestIdToAsyncRequestInfo(setPropertyRequests);
            handleRemoteExceptionFromCarService(e);
        } catch (IllegalArgumentException | SecurityException e) {
            // 非法参数或安全异常:清理请求信息并抛出异常。
            clearRequestIdToAsyncRequestInfo(setPropertyRequests);
            throw e;
        }

        // 如果提供了取消信号,设置取消监听器。
        if (cancellationSignal != null) {
            setOnCancelListener(cancellationSignal, requestIds);
        }
    }

    /**
     * 异步设置一组车载属性值。
     *
     * 与 {@link CarPropertyManager#setPropertiesAsync(List, long, CancellationSignal,
     * Executor, SetPropertyCallback)} 相同,唯一不同的是默认超时时间为 10 秒。
     */
    public void setPropertiesAsync(
            @NonNull List<SetPropertyRequest<?>> setPropertyRequests,
            @Nullable CancellationSignal cancellationSignal,
            @Nullable @CallbackExecutor Executor callbackExecutor,
            @NonNull SetPropertyCallback setPropertyCallback) {

        // 使用默认超时时间(10秒)调用上面的 setPropertiesAsync 方法。
        setPropertiesAsync(setPropertyRequests, ASYNC_GET_DEFAULT_TIMEOUT_MS, cancellationSignal,
                callbackExecutor, setPropertyCallback);
    }
}

异步设置车载属性的值。不同于同步设置,调用此方法后,不会阻塞当前线程,方法会立即返回。设置的结果会通过回调函数(setPropertyCallback)异步返回,成功或失败都会有对应的回调。

isPropertyAvailable()

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {  

    /**  
     * 检查给定的属性是否可用,或者基于车辆当前状态是否被禁用。  
     * 
     * @param propertyId 属性 ID  
     * @param areaId 区域 ID  
     * @return 如果属性可用,返回 {@code true}(即 {@link CarPropertyValue#STATUS_AVAILABLE}),  
     *         否则返回 {@code false}(例如 {@link CarPropertyValue#STATUS_UNAVAILABLE})。  
     */  
    public boolean isPropertyAvailable(int propertyId, int areaId) {  

        // 如果开启调试日志(DBG),则打印方法调用的日志
        if (DBG) {  
            Slog.d(TAG, "isPropertyAvailable(propertyId = "  
                    + VehiclePropertyIds.toString(propertyId) + ", areaId = " + areaId + ")");  
        }  

        // 确保查询的属性 ID 不是用户层管理的属性
        assertNotUserHalProperty(propertyId);  

        // 如果该属性不被支持,则返回 false
        if (!CarPropertyHelper.isSupported(propertyId)) {  
            if (DBG) {  
                Slog.d(TAG, "Property: " + VehiclePropertyIds.toString(propertyId)  
                        + " is not supported");  
            }  
            return false;  
        }  

        try {  
            // 异步调用车载服务获取属性的当前状态,检查该属性是否可用
            CarPropertyValue propValue = runSyncOperation(() -> {  
                if (DBG) {  
                    Slog.d(TAG, "calling getProperty to check property's availability");  
                }  
                return mService.getProperty(propertyId, areaId);  
            });  

            // 检查获取到的属性值的状态是否为可用状态
            return (propValue != null  
                    && propValue.getStatus() == CarPropertyValue.STATUS_AVAILABLE);  
        } catch (RemoteException e) {  
            // 如果与车载服务通信时发生远程异常,处理该异常并返回 false
            return handleRemoteExceptionFromCarService(e, false);  
        } catch (ServiceSpecificException e) {  
            // 如果出现服务特定异常,记录错误日志并返回 false
            Slog.e(TAG, "unable to get property, error: " + e);  
            return false;  
        }  
    }  
}

isPropertyAvailable() 方法用于检查一个特定的车辆属性是否可用。 该方法通过查询车辆的服务层接口,判断给定属性是否在车辆当前状态下可用,返回属性的可用状态。

getAreaId()

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {  

    /**  
     * 返回指定属性在选定区域中的 areaId。  
     * 如果属性在指定区域不支持,或者调用者没有读/写该属性的权限,抛出异常。  
     * 
     * @param propertyId 属性的 ID  
     * @param area 区域枚举,参见 {@link android.car.VehicleAreaSeat} 中的枚举  
     * @throws IllegalArgumentException 如果属性在选定区域不支持,或者调用者没有权限访问该属性  
     * @return 返回包含选定区域的 areaId  
     */    
    public int getAreaId(int propertyId, int area) {  

        // 检查是否为用户层属性,确保不为用户层管理的属性
        assertNotUserHalProperty(propertyId);  

        // 将属性 ID 转换为字符串,以便输出日志时使用
        String propertyIdStr = VehiclePropertyIds.toString(propertyId);  

        // 如果启用了调试日志,记录调用日志
        if (DBG) {  
            Slog.d(TAG, "getAreaId(propertyId = " + propertyIdStr + ", area = " + area + ")");  
        }  

        // 从服务端获取与属性 ID 对应的属性配置
        CarPropertyConfigs configs = getPropertyConfigsFromService(new ArraySet<>(Set.of(propertyId)));  

        // 如果获取的配置为空,抛出异常
        if (configs == null) {  
            throw new IllegalArgumentException("Failed to getPropertyConfigList from car service");  
        }  

        // 如果当前属性缺少访问权限,抛出异常
        if (configs.missingPermission(propertyId)) {  
            throw new IllegalArgumentException("Missing required permissions to access property: "  
                    + propertyIdStr);  
        }  

        // 如果当前属性在车辆中不支持,抛出异常
        if (configs.isNotSupported(propertyId)) {  
            throw new IllegalArgumentException("The property is not supported: " + propertyIdStr);  
        }  

        // 获取当前属性配置列表中的第一个属性配置
        CarPropertyConfig<?> propConfig = configs.getConfigs().get(0);  

        // 如果该属性是全局属性,则 areaId 为 0
        if (propConfig.isGlobalProperty()) {  
            if (DBG) {  
                Slog.d(TAG, "getAreaId returns the global area ID (0)");  
            }  
            return 0;  
        }  

        // 对属性的所有区域 ID 进行迭代,如果某个区域 ID 与传入的区域匹配,则返回该区域 ID
        for (int areaId : propConfig.getAreaIds()) {  
            if ((area & areaId) == area) {  
                if (DBG) {  
                    Slog.d(TAG, "getAreaId returns " + areaId);  
                }  
                return areaId;  
            }  
        }  

        // 如果没有找到匹配的区域 ID,抛出异常
        throw new IllegalArgumentException("The propertyId: " + propertyIdStr  
                + " is not available at the area: 0x" + toHexString(area));  
    }  
}  

getAreaId() 方法的功能是根据传入的 propertyIdarea,返回对应的区域 ID (areaId)。

权限

getReadPermission()

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {  

    /**  
     * 返回给定属性 ID 的读取权限字符串。  
     * 由于该方法返回值的格式随着时间变化,因此不应过度依赖返回值的格式。  
     * 
     * @param propId 要查询的属性 ID  
     * @return 返回读取该属性所需的权限,如果该属性 ID 不可用,返回 {@code null}  
     * @hide  
     */  
    @Nullable  
    public String getReadPermission(int propId) {  

        // 确保属性 ID 不是用户层管理的属性
        assertNotUserHalProperty(propId);  

        try {  
            // 从车载服务中获取读取该属性所需的权限字符串
            String permission = mService.getReadPermission(propId);  

            // 如果调试日志(DBG)开启,输出日志记录方法调用及返回的权限信息
            if (DBG) {  
                Slog.d(TAG, "getReadPermission(propId =" + VehiclePropertyIds.toString(propId)  
                        + ") returns " + permission);  
            }  

            // 返回获取到的权限字符串
            return permission;  
        } catch (RemoteException e) {  
            // 如果获取权限时发生远程异常,处理异常并返回空字符串
            return handleRemoteExceptionFromCarService(e, "");  
        }  
    }
}  

getReadPermission() 方法的功能是查询给定属性 ID 的读取权限。此方法会返回与该属性相关的权限字符串(如果有的话),如果属性不存在,则返回 null

getWritePermission()

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {  

    /**  
     * 返回给定属性 ID 的写入权限字符串。  
     * 由于该方法返回值的格式随着时间变化,因此不应过度依赖返回值的格式。  
     * 
     * @param propId 要查询的属性 ID  
     * @return 返回写入该属性所需的权限,如果该属性 ID 不可用,返回 {@code null}  
     * @hide  
     */  
    @Nullable  
    public String getWritePermission(int propId) {  

        // 确保属性 ID 不是用户层管理的属性
        assertNotUserHalProperty(propId);  

        try {  
            // 从车载服务中获取写入该属性所需的权限字符串
            String permission = mService.getWritePermission(propId);  

            // 如果调试日志(DBG)开启,输出日志记录方法调用及返回的权限信息
            if (DBG) {  
                Slog.d(TAG, "getWritePermission(propId = " + VehiclePropertyIds.toString(propId)  
                        + ") returns " + permission);  
            }  

            // 返回获取到的权限字符串
            return permission;  
        } catch (RemoteException e) {  
            // 如果获取权限时发生远程异常,处理异常并返回空字符串
            return handleRemoteExceptionFromCarService(e, "");  
        }  
    }
}  

getWritePermission() 方法的功能是查询给定属性 ID 的写入权限。它从车载服务获取该属性所需的权限字符串,并返回给调用者。如果属性不可用,返回 null

生成ID

generateGetPropertyRequest()

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {  

    /**
     * 生成一个唯一的获取请求 ID,并将其返回给客户端。
     *
     * @param propertyId 需要获取的属性 ID。
     * @param areaId 需要获取的区域 ID。
     * @return 返回一个 GetPropertyRequest 对象,包含请求的相关信息。
     */
    @NonNull
    @SuppressWarnings("FormatString")
    public GetPropertyRequest generateGetPropertyRequest(int propertyId, int areaId) {
        // 获取并递增请求 ID 计数器,生成一个唯一的请求 ID。
        int requestIdCounter = mRequestIdCounter.getAndIncrement();

        // 如果调试模式开启,打印日志
        if (DBG) {
            Slog.d(TAG, String.format("generateGetPropertyRequest, requestId: %d, propertyId: %s, "
                    + "areaId: %d", requestIdCounter, VehiclePropertyIds.toString(propertyId),
                    areaId));
        }

        // 创建一个 GetPropertyRequest 对象,返回请求 ID、属性 ID 和区域 ID
        return new GetPropertyRequest(requestIdCounter, propertyId, areaId);
    }
}

生成一个唯一的获取属性请求 ID,并封装为一个 GetPropertyRequest 对象,供客户端使用。

generateSetPropertyRequest()

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {  

    /**
     * 生成唯一的设置请求 ID,并返回给客户端。
     *
     * <p>该方法通过给定的属性 ID 和区域 ID,生成唯一的请求 ID,并将所需设置的属性值封装为
     * {@link SetPropertyRequest} 对象返回。</p>
     *
     * @param <T> 属性值的类型,必须是以下类型之一:Object, Boolean, Float, Integer,
     *      Long, Float[], Integer[], Long[], String, byte[], Object[]。
     * @param propertyId 需要设置的属性 ID。
     * @param areaId 需要设置的区域 ID。
     * @param value 要设置的属性值。
     * @return 返回一个 {@link SetPropertyRequest} 对象,其中包含请求的相关信息。
     */
    @NonNull
    @SuppressWarnings("FormatString")
    public <T> SetPropertyRequest<T> generateSetPropertyRequest(int propertyId, int areaId,
            @NonNull T value) {
        // 确保值(value)不为 null
        requireNonNull(value);

        // 获取并递增请求 ID 计数器,生成一个唯一的请求 ID
        int requestIdCounter = mRequestIdCounter.getAndIncrement();

        // 如果调试模式开启,打印日志
        if (DBG) {
            Slog.d(TAG, String.format("generateSetPropertyRequest, requestId: %d, propertyId: %s, "
                    + "areaId: %d, value: %s", requestIdCounter,
                    VehiclePropertyIds.toString(propertyId), areaId, value));
        }

        // 创建并返回一个 SetPropertyRequest 对象,封装请求的相关信息
        return new SetPropertyRequest<>(requestIdCounter, propertyId, areaId, value);
    }
}

生成一个唯一的设置属性请求 ID,并返回一个封装了请求信息的 SetPropertyRequest 对象。客户端可以通过该对象提交设置属性的请求。

总结

CarPropertyManager 通过 ICarProperty 接口调用 CarPropertyService,并通过 ICarPropertyEventListener 接口监听 CarPropertyService 的回调。由于重点不在此,这里不再详细分析。

CarPropertyManager 如何能调用到 CarPropertyService,可以参考 Android 15 CarService源码03-服务及接口

扩展

这部分主要是分析 CarPropertyManager 属性及其私用方法。

属性

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {
    // 常量定义
    private static final int MSG_GENERIC_EVENT = 0; // 通用事件的消息类型
    private static final int SYNC_OP_RETRY_SLEEP_IN_MS = 10; // 同步操作重试的休眠时间(毫秒)
    private static final int SYNC_OP_RETRY_MAX_COUNT = 10; // 同步操作的最大重试次数
    // 当subscribePropertyEvents不包含updateRateHz作为参数时使用的默认更新率
    private static final float DEFAULT_UPDATE_RATE_HZ = 1f;

    /**
     * 异步获取属性的默认超时时间(毫秒)。
     */
    public static final long ASYNC_GET_DEFAULT_TIMEOUT_MS = 10_000;

    // 成员变量定义
    private final SingleMessageHandler<CarPropertyEvent> mHandler; // 用于处理CarPropertyEvent的消息处理器
    private final ICarProperty mService; // 车辆属性服务接口
    private final int mAppTargetSdk; // 应用程序的目标SDK版本
    private final Executor mExecutor; // 用于执行异步任务的执行器
    private final AtomicInteger mRequestIdCounter = new AtomicInteger(0); // 请求ID计数器
    @GuardedBy("mLock")
    private final SparseArray<AsyncPropertyRequestInfo<?, ?>> mRequestIdToAsyncRequestInfo =
            new SparseArray<>(); // 请求ID到异步请求信息的映射
    private final AsyncPropertyResultCallback mAsyncPropertyResultCallback =
            new AsyncPropertyResultCallback(); // 异步属性结果回调

    private final CarPropertyEventListenerToService mCarPropertyEventToService =
            new CarPropertyEventListenerToService(this); // 事件监听器服务

    // 共享锁,用于防止潜在的死锁
    private final Object mLock = new Object();
    @GuardedBy("mLock")
    private final Map<CarPropertyEventCallback, CarPropertyEventCallbackController>
            mCpeCallbackToCpeCallbackController = new ArrayMap<>(); // 回调到控制器的映射
    @GuardedBy("mLock")
    private final SparseArray<ArraySet<CarPropertyEventCallbackController>>
            mPropIdToCpeCallbackControllerList = new SparseArray<>(); // 属性ID到控制器列表的映射

    private final GetPropertyResultCallback mGetPropertyResultCallback =
            new GetPropertyResultCallback(); // 获取属性结果回调

    private final SetPropertyResultCallback mSetPropertyResultCallback =
            new SetPropertyResultCallback(); // 设置属性结果回调
    @GuardedBy("mLock")
    private final SubscriptionManager<CarPropertyEventCallback> mSubscriptionManager =
            new SubscriptionManager<>(); // 订阅管理器

    private FeatureFlags mFeatureFlags = new FeatureFlagsImpl(); // 功能标志

    /**
     * 设置用于单元测试的假功能标志。
     *
     * @hide
     */
    @VisibleForTesting
    public void setFeatureFlags(FeatureFlags fakeFeatureFlags) {
        mFeatureFlags = fakeFeatureFlags;
    }

    // 传感器读取速率常量
    /** 读取ONCHANGE传感器。 */
    public static final float SENSOR_RATE_ONCHANGE = 0f;
    /** 以1赫兹的速率读取传感器 */
    public static final float SENSOR_RATE_NORMAL = 1f;
    /** 以5赫兹的速率读取传感器 */
    public static final float SENSOR_RATE_UI = 5f;
    /** 以10赫兹的速率读取传感器 */
    public static final float SENSOR_RATE_FAST = 10f;
    /** 以100赫兹的速率读取传感器 */
    public static final float SENSOR_RATE_FASTEST = 100f;

    // 设置属性错误代码常量
    /**
     * 状态指示设置操作失败。请重试。
     */
    public static final int CAR_SET_PROPERTY_ERROR_CODE_TRY_AGAIN = 1;

    /**
     * 状态指示设置操作因无效参数而失败。
     */
    public static final int CAR_SET_PROPERTY_ERROR_CODE_INVALID_ARG = 2;

    /**
     * 状态指示设置操作因属性不可用而失败。
     */
    public static final int CAR_SET_PROPERTY_ERROR_CODE_PROPERTY_NOT_AVAILABLE = 3;

    /**
     * 状态指示设置操作因车辆拒绝访问该属性而失败。
     */
    public static final int CAR_SET_PROPERTY_ERROR_CODE_ACCESS_DENIED = 4;

    /**
     * 状态指示设置操作因车辆中的一般错误而失败。
     */
    public static final int CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN = 5;

    /** @hide */
    @IntDef(prefix = {"CAR_SET_PROPERTY_ERROR_CODE_"}, value = {
            CAR_SET_PROPERTY_ERROR_CODE_TRY_AGAIN,
            CAR_SET_PROPERTY_ERROR_CODE_INVALID_ARG,
            CAR_SET_PROPERTY_ERROR_CODE_PROPERTY_NOT_AVAILABLE,
            CAR_SET_PROPERTY_ERROR_CODE_ACCESS_DENIED,
            CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface CarSetPropertyErrorCode {}

    // 异步属性错误代码常量
    /**
     * 错误指示车辆中检测到错误。
     */
    public static final int STATUS_ERROR_INTERNAL_ERROR = 1;
    /**
     * 错误指示属性暂时不可用。
     */
    public static final int STATUS_ERROR_NOT_AVAILABLE = 2;
    /**
     * 错误指示操作已超时。
     */
    public static final int STATUS_ERROR_TIMEOUT = 3;

    /** @hide */
    @IntDef(prefix = {"STATUS_"}, value = {
            STATUS_OK,
            STATUS_ERROR_INTERNAL_ERROR,
            STATUS_ERROR_NOT_AVAILABLE,
            STATUS_ERROR_TIMEOUT
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface CarPropertyAsyncErrorCode {}
}
  • 常量定义

    • MSG_GENERIC_EVENTSYNC_OP_RETRY_SLEEP_IN_MSSYNC_OP_RETRY_MAX_COUNT等常量用于定义消息类型、同步操作的重试策略等。
    • DEFAULT_UPDATE_RATE_HZ定义了默认的属性更新频率。
    • ASYNC_GET_DEFAULT_TIMEOUT_MS定义了异步获取属性的默认超时时间。
  • 成员变量

    • mHandler:用于处理CarPropertyEvent的消息处理器。
    • mService:车辆属性服务接口。
    • mAppTargetSdk:应用程序的目标SDK版本。
    • mExecutor:用于执行异步任务的执行器。
    • mRequestIdCounter:请求ID计数器,用于生成唯一的请求ID。
    • mRequestIdToAsyncRequestInfo:请求ID到异步请求信息的映射,存储异步请求的信息。
    • mAsyncPropertyResultCallback:异步属性结果回调,用于处理异步操作的结果。
    • mCarPropertyEventToService:事件监听器服务,用于将事件传递给服务。
    • mLock:共享锁,用于防止潜在的死锁。
    • mCpeCallbackToCpeCallbackControllermPropIdToCpeCallbackControllerList:用于管理属性事件回调的映射。
    • mGetPropertyResultCallbackmSetPropertyResultCallback:获取和设置属性结果的回调。
    • mSubscriptionManager:订阅管理器,用于管理属性事件的订阅。
    • mFeatureFlags:功能标志,用于控制功能开关。
  • 传感器读取速率常量

    • 定义了不同速率下的传感器读取频率,如SENSOR_RATE_ONCHANGESENSOR_RATE_NORMAL等。
  • 设置属性错误代码常量

    • 定义了设置属性操作可能返回的错误代码,如CAR_SET_PROPERTY_ERROR_CODE_TRY_AGAINCAR_SET_PROPERTY_ERROR_CODE_INVALID_ARG等。
  • 异步属性错误代码常量

    • 定义了异步属性操作可能返回的错误代码,如STATUS_ERROR_INTERNAL_ERRORSTATUS_ERROR_NOT_AVAILABLE等。

CarPropertyManager构造函数

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {
    /**
     * 获取CarPropertyManager的实例。
     *
     * 不应直接由客户端获取,而应使用{@link Car#getCarManager(String)}。
     *
     * @param car Car实例
     * @param service ICarProperty实例
     * @hide 隐藏此构造方法,表示不对外公开
     */
    public CarPropertyManager(ICarBase car, @NonNull ICarProperty service) {
        // 调用父类的构造方法
        super(car);
        // 初始化服务接口
        mService = service;
        // 获取应用程序的目标SDK版本
        mAppTargetSdk = getContext().getApplicationInfo().targetSdkVersion;

        // 获取事件处理器
        Handler eventHandler = getEventHandler();
        if (eventHandler == null) {
            // 如果事件处理器为空,则将mHandler和mExecutor设为null并返回
            mHandler = null;
            mExecutor = null;
            return;
        }
        // 使用事件处理器初始化HandlerExecutor
        mExecutor = new HandlerExecutor(getEventHandler());
        // 初始化SingleMessageHandler,用于处理CarPropertyEvent
        mHandler = new SingleMessageHandler<>(eventHandler.getLooper(), MSG_GENERIC_EVENT) {
            @Override
            protected void handleEvent(CarPropertyEvent carPropertyEvent) {
                // 获取事件中的属性ID
                int propertyId = carPropertyEvent.getCarPropertyValue().getPropertyId();
                // 创建一个用于存储回调控制器的列表
                List<CarPropertyEventCallbackController> cpeCallbacks = new ArrayList<>();
                synchronized (mLock) {
                    // 从属性ID到回调控制器列表的映射中获取回调控制器集合
                    ArraySet<CarPropertyEventCallbackController> cpeCallbackControllerSet =
                            mPropIdToCpeCallbackControllerList.get(propertyId);
                    if (cpeCallbackControllerSet == null) {
                        // 如果找不到回调控制器集合,则记录警告日志
                        Slog.w(TAG, "handleEvent: could not find any callbacks for propertyId="
                                + VehiclePropertyIds.toString(propertyId));
                        return;
                    }
                    // 将回调控制器集合中的每个元素添加到回调列表中
                    for (int i = 0; i < cpeCallbackControllerSet.size(); i++) {
                        cpeCallbacks.add(cpeCallbackControllerSet.valueAt(i));
                    }
                }

                // 依次调用回调列表中的每个回调控制器的onEvent方法
                for (int i = 0; i < cpeCallbacks.size(); i++) {
                    cpeCallbacks.get(i).onEvent(carPropertyEvent);
                }
            }
        };
    }
}

SingleMessageHandlerhandleEvent方法用于处理CarPropertyEvent。 这里最关键的就是从 mPropIdToCpeCallbackControllerList 获取到 CarPropertyEventCallbackController,我们在后续的分析再看 mPropIdToCpeCallbackControllerList 都是怎么别添加的。

subscribePropertyEvents() 流程

前面提到了汽车订阅指定属性的所有区域ID的属性事件是通过 subscribePropertyEvents() ,其主要是调用了内部 subscribePropertyEventsInternal() ,这里我们继续分析其流程。

subscribePropertyEventsInternal()

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {
    /**
     * 内部方法,用于订阅属性事件。
     *
     * @param subscribeOptions 订阅选项列表,包含属性ID、区域ID和更新速率等信息。
     * @param callbackExecutor 执行回调的执行器,如果为null,则使用默认执行器。
     * @param carPropertyEventCallback 用于处理属性更新或错误事件的回调。
     * @return 如果监听器成功注册,则返回{@code true}。
     */
    private boolean subscribePropertyEventsInternal(List<CarSubscription> subscribeOptions,
                                                    @Nullable @CallbackExecutor Executor callbackExecutor,
                                                    CarPropertyEventCallback carPropertyEventCallback) {
        // 确保回调不为空
        requireNonNull(carPropertyEventCallback);

        // 验证订阅选项中区域ID的互斥性,确保没有重叠的区域ID
        validateAreaDisjointness(subscribeOptions);

        // 如果启用了调试模式,记录调试信息
        if (DBG) {
            Slog.d(TAG, String.format("subscribePropertyEvents, callback: %s subscribeOptions: %s",
                    carPropertyEventCallback, subscribeOptions));
        }

        // 检查调用者是否有读取权限
        int[] noReadPermPropertyIds;
        try {
            noReadPermPropertyIds = getSupportedNoReadPermPropIds(subscribeOptions);
        } catch (RemoteException e) {
            // 处理远程异常
            handleRemoteExceptionFromCarService(e);
            return false;
        }

        // 如果有属性没有读取权限,抛出安全异常
        if (noReadPermPropertyIds.length != 0) {
            List<Integer> noReadPermPropertyIdsCopy = new ArrayList<>();
            for (int i = 0; i < noReadPermPropertyIds.length; i++) {
                noReadPermPropertyIdsCopy.add(noReadPermPropertyIds[i]);
            }
            throw new SecurityException("Do not have read permissions for properties: "
                    + CarPropertyHelper.propertyIdsToString(noReadPermPropertyIdsCopy));
        }

        // 如果回调执行器为空,使用默认执行器
        if (callbackExecutor == null) {
            callbackExecutor = mExecutor;
        }

        // 清理和验证订阅选项,确保更新速率和其他参数的有效性
        List<CarSubscription> sanitizedSubscribeOptions;
        try {
            sanitizedSubscribeOptions = sanitizeSubscribeOptions(subscribeOptions);
        } catch (IllegalStateException e) {
            Slog.e(TAG, "failed to sanitize update rate", e);
            return false;
        }

        // 使用同步块确保线程安全
        synchronized (mLock) {
            // 从映射中获取与回调关联的回调控制器
            CarPropertyEventCallbackController cpeCallbackController =
                    mCpeCallbackToCpeCallbackController.get(carPropertyEventCallback);

            // 如果回调控制器存在且执行器不匹配,抛出异常
            if (cpeCallbackController != null
                    && cpeCallbackController.getExecutor() != callbackExecutor) {
                throw new IllegalArgumentException("A different executor is already associated with"
                        + " this callback, please use the same executor.");
            }

            // 将新的订阅选项暂存到订阅管理器中
            // 这一步是为了准备将要应用的订阅更改
            mSubscriptionManager.stageNewOptions(carPropertyEventCallback,
                    sanitizedSubscribeOptions);

            // 应用订阅更改,如果失败则记录错误日志
            if (!applySubscriptionChangesLocked()) {
                Slog.e(TAG, "Subscription failed: failed to apply subscription changes");
                return false;
            }

            // 如果回调控制器不存在,创建新的并添加到映射中
            if (cpeCallbackController == null) {
                cpeCallbackController =
                        new CarPropertyEventCallbackController(carPropertyEventCallback,
                                callbackExecutor);
                mCpeCallbackToCpeCallbackController.put(carPropertyEventCallback,
                        cpeCallbackController);
            }

            // 遍历清理后的订阅选项,根据更新速率将属性添加到回调控制器中
            for (int i = 0; i < sanitizedSubscribeOptions.size(); i++) {
                CarSubscription option = sanitizedSubscribeOptions.get(i);
                int propertyId = option.propertyId;
                float sanitizedUpdateRateHz = option.updateRateHz;
                int[] areaIds = option.areaIds;

                // 根据更新速率将属性添加到回调控制器中
                if (sanitizedUpdateRateHz == 0) {
                    // 如果更新速率为0,表示这是一个on-change属性
                    cpeCallbackController.addOnChangeProperty(propertyId, areaIds);
                } else {
                    // 否则,将其作为连续属性添加
                    cpeCallbackController.addContinuousProperty(propertyId, areaIds,
                            sanitizedUpdateRateHz, option.enableVariableUpdateRate,
                            option.resolution);
                }

                // 更新属性ID到回调控制器列表的映射
                ArraySet<CarPropertyEventCallbackController> cpeCallbackControllerSet =
                        mPropIdToCpeCallbackControllerList.get(propertyId);
                if (cpeCallbackControllerSet == null) {
                    cpeCallbackControllerSet = new ArraySet<>();
                    mPropIdToCpeCallbackControllerList.put(propertyId, cpeCallbackControllerSet);
                }
                cpeCallbackControllerSet.add(cpeCallbackController);
            }
        }
        return true;
    }
}

subscribePropertyEventsInternal() 是一个私有方法,用于处理属性事件的订阅。它接收订阅选项、回调执行器和属性事件回调作为参数,并返回一个布尔值,指示订阅是否成功。

  • carPropertyEventCallback 是入参,用于处理属性更新或错误事件的回调接口。
  • mCpeCallbackToCpeCallbackController是一个映射,用于将每个carPropertyEventCallback与其对应的CarPropertyEventCallbackController关联起来。这个映射确保每个回调都有一个专门的控制器来管理其事件处理。
  • cpeCallbackControllerCarPropertyEventCallbackController的实例,用于管理特定回调的属性事件处理。它负责将属性事件分发给回调,并管理属性的更新速率和区域ID。
  • mPropIdToCpeCallbackControllerList是一个映射,将属性ID与对应的回调控制器集合关联起来。它用于快速查找哪些回调控制器正在监听特定属性ID的事件。

validateAreaDisjointness()

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {
    /**
     * 检查订阅选项中是否存在重叠的[propertyId, areaId]对。
     *
     * @param subscribeOptions 要检查的订阅选项列表。
     */
    private void validateAreaDisjointness(List<CarSubscription> subscribeOptions) {
        // 创建一个PairSparseArray用于存储属性ID和区域ID的组合
        PairSparseArray<Object> propertyToAreaId = new PairSparseArray<>();
        // 创建一个占位符对象,用于填充PairSparseArray
        Object placeHolder = new Object();

        // 遍历每个订阅选项
        for (int i = 0; i < subscribeOptions.size(); i++) {
            CarSubscription option = subscribeOptions.get(i);
            int propertyId = option.propertyId; // 获取属性ID
            int[] areaIds = option.areaIds; // 获取区域ID数组

            // 遍历每个区域ID
            for (int areaId : areaIds) {
                // 检查PairSparseArray中是否已存在相同的[propertyId, areaId]对
                if (propertyToAreaId.contains(propertyId, areaId)) {
                    // 如果存在重叠,抛出IllegalArgumentException异常
                    throw new IllegalArgumentException("Subscribe options contain overlapping "
                            + "propertyId: " + VehiclePropertyIds.toString(propertyId) + " areaId: "
                            + areaId);
                }
                // 如果不存在重叠,将[propertyId, areaId]对添加到PairSparseArray中
                propertyToAreaId.append(propertyId, areaId, placeHolder);
            }
        }
    }
}

validateAreaDisjointness() 方法用于检查给定的订阅选项列表中是否存在重叠的[propertyId, areaId]对。重叠意味着同一个属性ID和区域ID组合在列表中出现多次,这在某些情况下可能是不允许的。

getSupportedNoReadPermPropIds()

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {
    /**
     * 获取调用者没有读取权限的属性ID列表。
     *
     * @param subscribeOptions 订阅选项列表,其中包含要检查的属性ID。
     * @return 一个整数数组,包含调用者没有读取权限的属性ID。
     * @throws RemoteException 如果与远程服务的通信发生错误。
     */
    private int[] getSupportedNoReadPermPropIds(List<CarSubscription> subscribeOptions)
            throws RemoteException {
        // 使用ArraySet存储属性ID,确保唯一性
        ArraySet<Integer> propertyIds = new ArraySet<>();

        // 遍历订阅选项列表,提取每个订阅选项中的属性ID
        for (int i = 0; i < subscribeOptions.size(); i++) {
            propertyIds.add(subscribeOptions.get(i).propertyId);
        }

        // 将ArraySet中的属性ID转换为整数数组
        int[] propertyIdsArray = new int[propertyIds.size()];
        for (int i = 0; i < propertyIds.size(); i++) {
            propertyIdsArray[i] = propertyIds.valueAt(i);
        }

        // 调用远程服务方法,获取没有读取权限的属性ID
        return mService.getSupportedNoReadPermPropIds(propertyIdsArray);
    }
}

getSupportedNoReadPermPropIds() 方法用于获取调用者没有读取权限的属性ID列表。它从给定的订阅选项中提取属性ID,并通过远程服务检查这些属性ID的读取权限。

sanitizeSubscribeOptions()

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {
    /**
     * 清理和验证订阅选项,确保每个选项的有效性和安全性。
     *
     * @param subscribeOptions 要清理的订阅选项列表。
     * @return 一个经过清理的CarSubscription列表。
     * @throws IllegalArgumentException 如果某个属性ID不受支持或缺少权限。
     * @throws IllegalStateException 如果无法从汽车服务获取属性配置。
     */
    private List<CarSubscription> sanitizeSubscribeOptions(List<CarSubscription> subscribeOptions)
            throws IllegalArgumentException, IllegalStateException {
        // 使用ArraySet存储属性ID,确保唯一性
        ArraySet<Integer> propertyIds = new ArraySet<>();
        for (int i = 0; i < subscribeOptions.size(); i++) {
            propertyIds.add(subscribeOptions.get(i).propertyId);
        }

        // 从汽车服务获取属性配置
        CarPropertyConfigs configs = getPropertyConfigsFromService(propertyIds);
        if (configs == null) {
            // 如果无法获取配置,抛出IllegalStateException
            throw new IllegalStateException("Failed to get property config list from car service");
        }

        // 创建一个列表用于存储清理后的CarSubscription
        List<CarSubscription> output = new ArrayList<>();
        for (int i = 0; i < subscribeOptions.size(); i++) {
            CarSubscription subscribeOption = subscribeOptions.get(i);
            int propertyId = subscribeOption.propertyId;

            // 检查属性ID是否受支持
            if (configs.isNotSupported(propertyId)) {
                String errorMessage = "propertyId is not in carPropertyConfig list: "
                        + VehiclePropertyIds.toString(propertyId);
                Slog.e(TAG, "sanitizeUpdateRate: " + errorMessage);
                throw new IllegalArgumentException(errorMessage);
            }

            // 检查是否缺少权限
            if (configs.missingPermission(propertyId)) {
                // 这不应该发生,因为我们已经通过getSupportedNoReadPermPropIds检查了权限。
                // 如果调用者没有读取或写入权限,应该在此之前抛出SecurityException。
                String errorMessage = "missing required read/write permission for: "
                        + VehiclePropertyIds.toString(propertyId);
                Slog.wtf(TAG, "sanitizeUpdateRate: " + errorMessage);
                throw new SecurityException(errorMessage);
            }

            // 获取属性配置
            CarPropertyConfig<?> carPropertyConfig = configs.getConfig(propertyId);
            CarSubscription carSubscription = new CarSubscription();
            carSubscription.propertyId = propertyId;
            carSubscription.areaIds = subscribeOption.areaIds;

            // 如果未指定区域ID,则订阅所有区域ID
            if (carSubscription.areaIds.length == 0) {
                carSubscription.areaIds = carPropertyConfig.getAreaIds();
            }

            // 设置可变更新速率
            carSubscription.enableVariableUpdateRate = subscribeOption.enableVariableUpdateRate;

            // 清理更新速率
            carSubscription.updateRateHz = InputSanitizationUtils.sanitizeUpdateRateHz(
                    carPropertyConfig, subscribeOption.updateRateHz);

            // 根据功能标志设置分辨率
            float resolution = mFeatureFlags.subscriptionWithResolution()
                    ? subscribeOption.resolution : 0.0f;
            carSubscription.resolution = InputSanitizationUtils.sanitizeResolution(mFeatureFlags,
                    carPropertyConfig, resolution);

            // 清理并添加到输出列表
            output.addAll(InputSanitizationUtils.sanitizeEnableVariableUpdateRate(
                    mFeatureFlags, carPropertyConfig, carSubscription));
        }
        return output;
    }
}

sanitizeSubscribeOptions() 方法用于清理和验证订阅选项列表,确保每个选项的有效性和安全性。它会检查属性ID的支持情况、权限、更新速率和分辨率等。

  • 使用ArraySet<Integer>存储属性ID,确保每个属性ID在集合中是唯一的。
  • 调用getPropertyConfigsFromService方法,从汽车服务获取属性配置。
  • 对于每个CarSubscription对象,提取其propertyId
  • 获取属性配置CarPropertyConfig
  • 创建新的CarSubscription对象,设置属性ID和区域ID。
  • 如果未指定区域ID,则从属性配置中获取所有区域ID。
  • 设置可变更新速率。
  • 使用InputSanitizationUtils.sanitizeUpdateRateHz清理更新速率。
  • 根据功能标志设置分辨率,并使用InputSanitizationUtils.sanitizeResolution清理分辨率。
  • 使用InputSanitizationUtils.sanitizeEnableVariableUpdateRate清理可变更新速率,并将结果添加到输出列表。

applySubscriptionChangesLocked()

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {

    /**
     * 在{@link #mService}中更新属性ID和区域ID的订阅。
     *
     * @return 如果属性已成功注册到服务,则返回{@code true}。
     * @throws SecurityException 如果缺少适当的属性访问权限。
     */
    @GuardedBy("mLock")
    private boolean applySubscriptionChangesLocked() {
        // 用于存储需要更新的订阅和需要取消订阅的属性ID
        List<CarSubscription> updatedCarSubscriptions = new ArrayList<>();
        List<Integer> propertiesToUnsubscribe = new ArrayList<>();

        // 计算当前订阅和暂存订阅之间的差异
        mSubscriptionManager.diffBetweenCurrentAndStage(updatedCarSubscriptions,
                propertiesToUnsubscribe);

        // 如果没有需要订阅或取消订阅的内容,直接提交并返回true
        if (propertiesToUnsubscribe.isEmpty() && updatedCarSubscriptions.isEmpty()) {
            Slog.d(TAG, "There is nothing to subscribe or unsubscribe to CarPropertyService");
            mSubscriptionManager.commit();
            return true;
        }

        // 如果启用了调试模式,记录调试信息
        if (DBG) {
            Slog.d(TAG, "updatedCarSubscriptions to subscribe is: "
                    + updatedCarSubscriptions + " and the list of properties to unsubscribe is: "
                    + CarPropertyHelper.propertyIdsToString(propertiesToUnsubscribe));
        }

        try {
            // 注册新的订阅
            if (!updatedCarSubscriptions.isEmpty()) {
                if (!registerLocked(updatedCarSubscriptions)) {
                    // 如果注册失败,放弃提交并返回false
                    mSubscriptionManager.dropCommit();
                    return false;
                }
            }

            // 取消订阅不再需要的属性
            if (!propertiesToUnsubscribe.isEmpty()) {
                for (int i = 0; i < propertiesToUnsubscribe.size(); i++) {
                    if (!unregisterLocked(propertiesToUnsubscribe.get(i))) {
                        // 如果取消订阅失败,记录警告日志,放弃提交并返回false
                        Slog.w(TAG, "Failed to unsubscribe to: " + VehiclePropertyIds.toString(
                                propertiesToUnsubscribe.get(i)));
                        mSubscriptionManager.dropCommit();
                        return false;
                    }
                }
            }
        } catch (SecurityException e) {
            // 如果抛出SecurityException,放弃提交并重新抛出异常
            mSubscriptionManager.dropCommit();
            throw e;
        }

        // 提交所有更改
        mSubscriptionManager.commit();
        return true;
    }

}

applySubscriptionChangesLocked() 方法用于在服务中更新属性ID和区域ID的订阅。它会根据当前的订阅状态和暂存的订阅状态之间的差异,执行相应的注册和取消注册操作。

  • 计算差异
    • 调用 SubscriptionManager.diffBetweenCurrentAndStage() 方法,计算当前订阅和暂存订阅之间的差异。
    • 将需要更新的订阅存储在 updatedCarSubscriptions 列表中,将需要取消订阅的属性ID存储在 propertiesToUnsubscribe 列表中。
  • 检查无操作情况:如果没有需要订阅或取消订阅的内容,记录调试信息,提交当前状态并返回true
  • 注册和取消注册:
    • 注册新的订阅:
      • 如果updatedCarSubscriptions不为空,调用registerLocked方法注册新的订阅。
      • 如果注册失败,放弃提交并返回false
    • 取消订阅:
      • 如果propertiesToUnsubscribe不为空,遍历每个属性ID,调用unregisterLocked方法取消订阅。
      • 如果取消订阅失败,记录警告日志,放弃提交并返回false
  • 提交更改:如果所有操作成功,调用 SubscriptionManager.commit() 方法提交所有更改,并返回true

registerLocked()

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {

    /**
     * 当需要更新{@code propertyId}的注册时调用。
     *
     * @param options 包含要注册的属性订阅选项的列表。
     * @return 如果注册成功则返回{@code true},否则返回{@code false}。
     * @throws SecurityException 如果缺少适当的属性访问权限。
     */
    @GuardedBy("mLock")
    private boolean registerLocked(List<CarSubscription> options) {
        try {
            // 调用服务的registerListener方法注册监听器
            mService.registerListener(options, mCarPropertyEventToService);
        } catch (RemoteException e) {
            // 处理来自CarService的远程异常
            handleRemoteExceptionFromCarService(e);
            return false;
        } catch (SecurityException e) {
            // 如果缺少权限,抛出SecurityException
            throw e;
        } catch (Exception e) {
            // 捕获其他异常,记录警告日志并返回false
            Slog.w(TAG, "registerLocked with options: " + options
                    + ", unexpected exception=", e);
            return false;
        }
        // 如果没有异常抛出,返回true表示注册成功
        return true;
    }

}

registerLocked() 方法调用 mService.registerListener() 方法,用于在服务中注册属性监听器。

unsubscribePropertyEvents() 流程

前面提到了汽车停止为指定的 CarPropertyEventCallback 获取属性更新是通过 unsubscribePropertyEvents() ,其主要是调用了内部 unsubscribePropertyEventsInternal() ,这里我们继续分析其流程。

unsubscribePropertyEventsInternal()

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {
    /**
     * 内部方法,用于取消指定属性ID列表的属性事件订阅。
     *
     * @param propertyIds 要取消订阅的属性ID列表。
     * @param carPropertyEventCallback 之前订阅的回调,用于取消订阅。
     */
    private void unsubscribePropertyEventsInternal(
            List<Integer> propertyIds, CarPropertyEventCallback carPropertyEventCallback) {
        // 使用同步块确保线程安全
        synchronized (mLock) {
            // 获取与回调关联的回调控制器
            CarPropertyEventCallbackController cpeCallbackController =
                    mCpeCallbackToCpeCallbackController.get(carPropertyEventCallback);

            // 如果回调控制器不存在,直接返回
            if (cpeCallbackController == null) {
                return;
            }

            // 过滤掉用户HAL属性ID,以防止getPropertyConfigsFromService抛出IllegalArgumentException
            List<Integer> filteredPropertyIds = filterOutUserHalProperty(propertyIds);

            // 从服务获取属性配置
            CarPropertyConfigs configs = getPropertyConfigsFromService(filteredPropertyIds);

            // 如果无法获取配置,记录错误日志并返回
            if (configs == null) {
                Slog.e(TAG, "failed to get property config list from car service, do nothing");
                return;
            }

            // 遍历过滤后的属性ID列表
            for (int i = 0; i < filteredPropertyIds.size(); i++) {
                int propertyId = filteredPropertyIds.get(i);

                // 如果启用了调试模式,记录调试信息
                if (DBG) {
                    Slog.d(TAG, String.format(
                            "unsubscribePropertyEvents, callback: %s, property Id: %s",
                            carPropertyEventCallback, VehiclePropertyIds.toString(propertyId)));
                }

                // 检查属性是否受支持
                if (configs.isNotSupported(propertyId)) {
                    Slog.e(TAG, "unsubscribePropertyEvents: not supported property: "
                            + VehiclePropertyIds.toString(propertyId));
                    continue;
                }

                // 检查是否缺少权限
                if (configs.missingPermission(propertyId)) {
                    Slog.e(TAG, "unsubscribePropertyEvents: missing read/write permission for "
                            + "property: " + VehiclePropertyIds.toString(propertyId));
                    continue;
                }

                // 获取属性ID对应的回调控制器集合
                ArraySet<CarPropertyEventCallbackController> cpeCallbackControllerSet =
                        mPropIdToCpeCallbackControllerList.get(propertyId);

                // 如果集合不存在或不包含当前回调控制器,记录错误日志并继续
                if (cpeCallbackControllerSet == null) {
                    Slog.e(TAG,
                            "unsubscribePropertyEvents: callback was not previously registered.");
                    continue;
                } else if (!cpeCallbackControllerSet.contains(cpeCallbackController)) {
                    Slog.e(TAG,
                            "unsubscribePropertyEvents: callback was not previously registered for"
                                    + " propertyId=" + VehiclePropertyIds.toString(propertyId));
                    continue;
                }

                // 暂存取消注册操作
                mSubscriptionManager.stageUnregister(carPropertyEventCallback,
                        new ArraySet<>(Set.of(propertyId)));

                // 应用订阅更改
                if (!applySubscriptionChangesLocked()) {
                    continue;
                }

                // 从回调控制器中移除属性ID
                boolean allPropertiesRemoved = cpeCallbackController.remove(propertyId);

                // 如果回调控制器中没有剩余属性,移除回调控制器
                if (allPropertiesRemoved) {
                    mCpeCallbackToCpeCallbackController.remove(carPropertyEventCallback);
                }

                // 从属性ID的回调控制器集合中移除当前回调控制器
                cpeCallbackControllerSet.remove(cpeCallbackController);

                // 如果集合为空,移除属性ID的映射
                if (cpeCallbackControllerSet.isEmpty()) {
                    mPropIdToCpeCallbackControllerList.remove(propertyId);
                }
            }
        }
    }
}
  • mCpeCallbackToCpeCallbackController映射中获取与carPropertyEventCallback关联的CarPropertyEventCallbackController
  • 调用filterOutUserHalProperty方法,过滤掉用户HAL属性ID,以防止后续操作抛出异常。
  • 调用getPropertyConfigsFromService方法,从服务获取属性配置。
  • 获取属性ID对应的回调控制器集合cpeCallbackControllerSet
  • cpeCallbackController中移除属性ID。
  • 如果回调控制器中没有剩余属性,移除回调控制器。
  • cpeCallbackControllerSet中移除当前回调控制器。
  • 如果集合为空,移除属性ID的映射。

也就是说 unsubscribePropertyEventsInternal() 方法通过更新mCpeCallbackToCpeCallbackControllermPropIdToCpeCallbackControllerList,确保内部数据结构与实际的订阅状态保持一致。

filterOutUserHalProperty()

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {
    /**
     * 过滤掉用户HAL相关的属性ID。
     *
     * @param propertyIds 要过滤的属性ID列表。
     * @return 过滤后的属性ID列表,不包含用户HAL相关的属性ID。
     */
    private List<Integer> filterOutUserHalProperty(List<Integer> propertyIds) {
        // 检查应用的目标SDK版本
        if (mAppTargetSdk >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
            // 如果目标SDK版本是Android U或更高版本,直接返回原始列表
            // 因为在Android U之后,这些用户HAL属性ID被视为不受支持的属性ID,不再需要特殊处理
            return propertyIds;
        }

        // 创建一个新的列表用于存储过滤后的属性ID
        List<Integer> filteredPropertyIds = new ArrayList<>();

        // 遍历传入的属性ID列表
        for (int i = 0; i < propertyIds.size(); i++) {
            // 检查当前属性ID是否是用户HAL相关的属性ID
            switch (propertyIds.get(i)) {
                case VehiclePropertyIds.INITIAL_USER_INFO:
                case VehiclePropertyIds.SWITCH_USER:
                case VehiclePropertyIds.CREATE_USER:
                case VehiclePropertyIds.REMOVE_USER:
                case VehiclePropertyIds.USER_IDENTIFICATION_ASSOCIATION:
                    // 如果是用户HAL相关的属性ID,跳过此ID,不添加到过滤后的列表中
                    continue;
            }
            // 如果不是用户HAL相关的属性ID,将其添加到过滤后的列表中
            filteredPropertyIds.add(propertyIds.get(i));
        }

        // 返回过滤后的属性ID列表
        return filteredPropertyIds;
    }
}

过滤掉用户HAL相关的属性ID。这些属性ID与用户管理相关,在某些情况下可能不需要处理。如: - INITIAL_USER_INFO - SWITCH_USER - CREATE_USER - REMOVE_USER - USER_IDENTIFICATION_ASSOCIATION

getPropertyConfigsFromService()

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {
    /**
     * 从服务中获取指定属性ID的属性配置。
     *
     * @param propertyIds 要获取配置的属性ID集合。
     * @return 包含属性配置的CarPropertyConfigs对象,如果发生异常则返回null。
     */
    @Nullable
    private CarPropertyConfigs getPropertyConfigsFromService(Iterable<Integer> propertyIds) {
        // 用于存储经过过滤的支持的属性ID
        IntArray filteredPropertyIds = new IntArray();
        // 用于存储不支持的属性ID
        IntArray unsupportedPropertyIds = new IntArray();

        // 遍历传入的属性ID集合
        for (int propertyId : propertyIds) {
            // 确保属性ID不是用户HAL相关的属性
            assertNotUserHalProperty(propertyId);

            // 检查属性ID是否受支持
            if (!CarPropertyHelper.isSupported(propertyId)) {
                // 如果不支持,将其添加到不支持的属性ID列表中
                unsupportedPropertyIds.add(propertyId);
                continue;
            }

            // 如果支持,将其添加到过滤后的属性ID列表中
            filteredPropertyIds.add(propertyId);
        }

        GetPropertyConfigListResult result;
        try {
            // 调用服务方法获取属性配置列表
            result = mService.getPropertyConfigList(filteredPropertyIds.toArray());
        } catch (RemoteException e) {
            // 处理远程异常,记录错误日志并返回null
            Slog.e(TAG, "CarPropertyService.getPropertyConfigList exception ", e);
            return handleRemoteExceptionFromCarService(e, null);
        }

        // 返回包含属性配置和不支持属性ID的CarPropertyConfigs对象
        return new CarPropertyConfigs(result, unsupportedPropertyIds);
    }
}
  • 初始化数据结构:使用IntArray存储经过过滤的支持的属性ID和不支持的属性ID。
  • 遍历属性ID
    • 遍历传入的propertyIds集合。
    • 调用assertNotUserHalProperty方法,确保属性ID不是用户HAL相关的属性。
    • 使用CarPropertyHelper.isSupported方法检查属性ID是否受支持。
    • 如果属性ID不受支持,将其添加到unsupportedPropertyIds列表中。
    • 如果属性ID受支持,将其添加到filteredPropertyIds列表中。
  • 获取属性配置
    • 调用mService.getPropertyConfigList方法,传递过滤后的属性ID数组,获取属性配置列表。
    • 捕获RemoteException,记录错误日志,并调用handleRemoteExceptionFromCarService方法处理异常,返回null
  • 返回结果:创建并返回一个CarPropertyConfigs对象,包含获取的属性配置和不支持的属性ID。

接着会调用到 applySubscriptionChangesLocked() 参考前面,我们继续看 applySubscriptionChangesLocked() 调用 unregisterLocked() 的流程。

unregisterLocked()

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {

    /**
     * 当需要取消注册{@code propertyId}时调用。
     *
     * @param propertyId 要取消注册的属性ID。
     * @return 如果取消注册成功则返回{@code true},否则返回{@code false}。
     * @throws SecurityException 如果缺少适当的属性访问权限。
     */
    @GuardedBy("mLock")
    private boolean unregisterLocked(int propertyId) {
        try {
            // 调用服务的unregisterListener方法取消监听器
            mService.unregisterListener(propertyId, mCarPropertyEventToService);
        } catch (RemoteException e) {
            // 处理来自CarService的远程异常
            handleRemoteExceptionFromCarService(e);
            return false;
        } catch (SecurityException e) {
            // 如果缺少权限,抛出SecurityException
            throw e;
        } catch (Exception e) {
            // 捕获其他异常,记录警告日志并返回false
            Slog.w(TAG, "unregisterLocked with property: "
                    + VehiclePropertyIds.toString(propertyId)
                    + ", unexpected exception=", e);
            return false;
        }
        // 如果没有异常抛出,返回true表示取消注册成功
        return true;
    }

}

unregisterLocked() 方法调用 mService.unregisterListener() 方法,用于在服务中取消监听器。

获取属性(类型转换)

handleNullAndPropertyStatus()

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {  

    /**
     * 处理车辆属性值的状态和可能为null的情况,并根据当前SDK版本抛出异常或返回默认值。
     * 
     * 该方法根据属性值的状态,决定是返回属性的值,还是返回默认值,或者抛出异常。
     * 
     * 如果属性值为空,直接返回默认值;如果在Android R版本之前,若属性值为“可用”状态则返回属性值,否则返回默认值;
     * 在Android S及之后版本,如果属性值为“错误”状态,则抛出 `CarInternalErrorException` 异常,如果是“不可用”状态则抛出 `PropertyNotAvailableException` 异常。
     *
     * @param propertyValue 车辆属性的值,包含属性状态和值
     * @param areaId 属性所在的区域ID
     * @param defaultValue 如果属性值为null或不可用时返回的默认值
     * @return 返回属性值,或者在异常情况下返回默认值
     */
    private <T> T handleNullAndPropertyStatus(CarPropertyValue<T> propertyValue, int areaId,
                                              T defaultValue) {
        // 如果属性值为null,直接返回默认值
        if (propertyValue == null) {
            return defaultValue;
        }

        // 如果当前应用的目标SDK版本小于Android S(即SDK版本小于Android 12),保持Android R(Android 11)时的行为
        if (mAppTargetSdk < Build.VERSION_CODES.S) {
            // 如果属性的状态是可用的,返回属性值;否则返回默认值
            return propertyValue.getStatus() == CarPropertyValue.STATUS_AVAILABLE
                    ? propertyValue.getValue() : defaultValue;
        }

        // 如果应用的目标SDK版本是Android S及以上版本,按照新的异常处理逻辑处理
        switch (propertyValue.getStatus()) {
            // 如果属性状态是错误,抛出CarInternalErrorException异常
            case CarPropertyValue.STATUS_ERROR:
                throw new CarInternalErrorException(propertyValue.getPropertyId(), areaId);
            // 如果属性状态是不可用,抛出PropertyNotAvailableException异常
            case CarPropertyValue.STATUS_UNAVAILABLE:
                throw new PropertyNotAvailableException(propertyValue.getPropertyId(),
                        areaId, /*vendorErrorCode=*/0);
            // 如果状态是其他状态,直接返回属性的值
            default:
                return propertyValue.getValue();
        }
    }
}

handleNullAndPropertyStatus() 方法的主要作用是根据车辆属性的状态,返回该属性的值。

  • propertyValue: CarPropertyValue<T> 类型的参数,包含车辆属性的值及其状态。
  • areaId: 属性所属的区域ID。
  • defaultValue: 默认值,用于在属性值为 null 或不可用时返回。

runSyncOperation()

获取属性、设置属性、判断属性是否 available 时调用此方法。

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {  

    // 定义一个功能接口(Functional Interface),用于定义需要远程调用的方法
    @FunctionalInterface
    private interface RemoteCallable<V> {
        // 定义一个 call 方法,返回一个泛型类型的结果,可能抛出 RemoteException 异常
        V call() throws RemoteException;
    }

    /**
     * 执行同步操作并处理可能的远程调用异常。
     * 
     * 该方法会尝试调用远程服务,如果远程服务在执行过程中返回了某些特定的错误(例如请求过于频繁),
     * 会自动重试,最多重试 `SYNC_OP_RETRY_MAX_COUNT` 次。每次重试之间,会等待 `SYNC_OP_RETRY_SLEEP_IN_MS` 毫秒。
     * 如果所有重试都失败,则抛出异常。
     *
     * @param c 远程调用的操作,需要传入一个实现了 `RemoteCallable` 接口的对象
     * @param <V> 远程调用返回的结果类型
     * @return 远程调用的返回值
     * @throws RemoteException 如果远程服务出现异常
     * @throws ServiceSpecificException 如果远程服务返回了特定的服务错误码
     */
    @Nullable
    private static <V> V runSyncOperation(RemoteCallable<V> c)
            throws RemoteException, ServiceSpecificException {

        // 重试次数计数器,最多重试 SYNC_OP_RETRY_MAX_COUNT 次
        int retryCount = 0;

        // 当重试次数小于最大重试次数时,继续执行操作
        while (retryCount < SYNC_OP_RETRY_MAX_COUNT) {
            retryCount++;
            try {
                // 调用远程操作并返回结果
                return c.call();
            } catch (ServiceSpecificException e) {
                // 如果捕获到特定的服务错误,且错误码是要求重试的情况
                if (e.errorCode != SYNC_OP_LIMIT_TRY_AGAIN) {
                    throw e;  // 如果不是需要重试的错误,则直接抛出该异常
                }

                // 如果远程服务的请求过于频繁,导致没有足够的线程处理当前请求,进行等待重试
                Slog.d(TAG, "too many sync request, sleeping for " + SYNC_OP_RETRY_SLEEP_IN_MS
                        + " ms before retry");

                // 等待指定的时间(10ms)再进行重试
                SystemClock.sleep(SYNC_OP_RETRY_SLEEP_IN_MS);
            } catch (RemoteException e) {
                // 如果是远程调用的其他异常,直接抛出
                throw e;
            }
        }

        // 如果超过最大重试次数仍然没有成功,抛出一个服务特定的异常
        throw new ServiceSpecificException(VehicleHalStatusCode.STATUS_INTERNAL_ERROR,
                "failed to call car service sync operations after " + retryCount + " retries");
    }
}
  • `RemoteCallable

这是一个函数式接口 (@FunctionalInterface),接口中只有一个方法 call(),用于定义远程调用操作。这个方法返回一个泛型类型 V 的结果,并且可能会抛出 RemoteException 异常。它是一个包含远程调用逻辑的通用接口。

  • runSyncOperation()

这是一个静态的泛型方法,用于执行一个同步的远程操作。它接受一个 RemoteCallable<V> 类型的参数 c,表示需要执行的远程操作。该方法会尝试调用远程服务并处理可能的异常,支持最大重试次数和延时重试机制。

setOnCancelListener()

异步获取属性、异步设置属性时调用此方法。也就是 getPropertiesAsyncsetPropertiesAsync

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {  

    /**
     * 为取消信号设置 {@code onCancelListener} 回调
     *
     * <p>当取消信号被触发时,汽车服务将移除指定挂起请求 ID 的存储状态,
     * 并忽略所有后续结果。
     *
     * @param cancellationSignal 取消信号对象,表示取消请求的信号。
     * @param requestIds 需要取消的请求 ID 列表。
     */
    private void setOnCancelListener(CancellationSignal cancellationSignal,
            List<Integer> requestIds) {
        // 为取消信号设置回调监听器
        cancellationSignal.setOnCancelListener(() -> {
            // 创建一个与 requestIds 列表大小相同的数组,存储请求 ID
            int[] requestIdsArray = new int[requestIds.size()];

            // 同步块,确保线程安全地处理 mRequestIdToAsyncRequestInfo(请求信息映射)
            synchronized (mLock) {
                // 遍历请求 ID 列表,将其转换成数组,并从请求 ID 映射中移除相应的请求
                for (int i = 0; i < requestIds.size(); i++) {
                    int requestId = requestIds.get(i);
                    requestIdsArray[i] = requestId;
                    // 移除与请求 ID 相关的异步请求信息
                    mRequestIdToAsyncRequestInfo.remove(requestId);
                }
            }

            // 调用汽车服务的 cancelRequests 方法,取消这些请求
            try {
                mService.cancelRequests(requestIdsArray);
            } catch (RemoteException e) {
                // 处理远程异常
                handleRemoteExceptionFromCarService(e);
            }
        });
    }
}

setOnCancelListener 的目的是为给定的取消信号(cancellationSignal)设置一个取消监听器。当取消信号被触发时,它将取消与提供的请求 ID 列表相关的所有挂起请求。

具体来说,当取消信号被触发时,回调函数会移除请求 ID 的状态信息,并通过汽车服务 (mService) 调用 cancelRequests 方法,告知服务取消这些请求。

handleCarPropertyEvents()

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {  

    /**
     * 处理一组车载属性事件。
     * 
     * <p>该方法会将多个车载属性事件按照属性 ID 进行分组,并根据注册的回调控制器将事件分发到相应的回调函数中处理。
     * 
     * <p>此方法可能会被绑定服务线程调用,因此在调用客户端回调之前,必须清除调用身份。
     *
     * @param carPropertyEvents 需要处理的车载属性事件列表。
     */
    private void handleCarPropertyEvents(List<CarPropertyEvent> carPropertyEvents) {

        // 用于存储按属性 ID 分组的车载属性事件列表。key 是属性 ID,value 是该属性 ID 对应的事件列表。
        SparseArray<List<CarPropertyEvent>> carPropertyEventsByPropertyId = new SparseArray<>();

        // 遍历事件列表,按属性 ID 对事件进行分组
        for (int i = 0; i < carPropertyEvents.size(); i++) {
            CarPropertyEvent carPropertyEvent = carPropertyEvents.get(i);
            int propertyId = carPropertyEvent.getCarPropertyValue().getPropertyId();

            // 如果该属性 ID 没有对应的事件列表,初始化一个新的 ArrayList
            if (carPropertyEventsByPropertyId.get(propertyId) == null) {
                carPropertyEventsByPropertyId.put(propertyId, new ArrayList<>());
            }

            // 将事件添加到对应的属性 ID 的事件列表中
            carPropertyEventsByPropertyId.get(propertyId).add(carPropertyEvent);
        }

        // 用于存储按回调控制器分组的事件列表。key 是回调控制器,value 是该控制器对应的事件列表。
        ArrayMap<CarPropertyEventCallbackController, List<CarPropertyEvent>> eventsByCallback =
                new ArrayMap<>();

        // 使用锁来保护对共享资源(mPropIdToCpeCallbackControllerList)的访问
        synchronized (mLock) {
            // 遍历按属性 ID 分组的事件
            for (int i = 0; i < carPropertyEventsByPropertyId.size(); i++) {
                int propertyId = carPropertyEventsByPropertyId.keyAt(i);  // 获取属性 ID
                List<CarPropertyEvent> eventsForPropertyId = carPropertyEventsByPropertyId.valueAt(i);  // 获取该属性 ID 对应的事件列表

                // 获取与该属性 ID 相关联的回调控制器列表
                ArraySet<CarPropertyEventCallbackController> cpeCallbackControllerSet =
                        mPropIdToCpeCallbackControllerList.get(propertyId);

                // 如果找不到回调控制器,打印警告并退出
                if (cpeCallbackControllerSet == null) {
                    Slog.w(TAG, "handleEvent: could not find any callbacks for propertyId="
                            + VehiclePropertyIds.toString(propertyId));
                    return;
                }

                // 遍历与该属性 ID 关联的所有回调控制器
                for (int j = 0; j < cpeCallbackControllerSet.size(); j++) {
                    var callback = cpeCallbackControllerSet.valueAt(j);  // 获取回调控制器

                    // 如果回调控制器在 eventsByCallback 中不存在,则初始化一个新的 ArrayList
                    if (eventsByCallback.get(callback) == null) {
                        eventsByCallback.put(callback, new ArrayList<>());
                    }

                    // 将当前属性 ID 对应的所有事件添加到回调控制器的事件列表中
                    eventsByCallback.get(callback).addAll(eventsForPropertyId);
                }
            }
        }

        // 可能从 Binder 线程调用(CarPropertyEventListenerToService.onEvent),因此在调用客户端回调之前清除调用身份
        Binder.clearCallingIdentity();

        // 遍历按回调控制器分组的事件列表,调用每个回调控制器的 onEvent 方法,传递事件给客户端
        for (int i = 0; i < eventsByCallback.size(); i++) {
            var callback = eventsByCallback.keyAt(i);  // 获取回调控制器
            var events = eventsByCallback.valueAt(i);  // 获取该回调控制器的事件列表
            for (int j = 0; j < events.size(); j++) {
                callback.onEvent(events.get(j));  // 触发回调,传递事件
            }
        }
    }
}

handleCarPropertyEvents() 方法用于处理一组车载属性事件。这些事件会被按属性 ID 进行分组,并最终通过回调机制分发给相应的回调控制器(CarPropertyEventCallbackController)。每个回调控制器负责处理特定属性 ID 的事件。

storePendingRequestInfo()

异步获取属性、异步设置属性时调用此方法。也就是 getPropertiesAsyncsetPropertiesAsync

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {  

    /**
     * 存储挂起的异步请求信息。
     * 
     * <p>该方法用于存储一组异步请求的请求 ID 和相关的请求信息。它将请求信息添加到 `mRequestIdToAsyncRequestInfo` 映射中,并返回请求 ID 列表。
     * 
     * <p>这个方法会先检查请求 ID 是否已经存在于请求信息映射中,如果已经存在则抛出异常。
     * 
     * @param <RequestType> 请求类型,必须是继承自 {@link AsyncPropertyRequest} 的类型。
     * @param <CallbackType> 回调类型。
     * @param requests 要存储的异步请求列表。
     * @param callbackExecutor 执行回调的执行器。
     * @param callback 回调函数。
     * @return 返回存储的请求 ID 列表。
     * @throws IllegalArgumentException 如果请求 ID 已经存在,抛出异常。
     */
    private <RequestType extends AsyncPropertyRequest, CallbackType> List<Integer>
            storePendingRequestInfo(
                    List<RequestType> requests, Executor callbackExecutor, CallbackType callback) {

        // 在调试模式下,打印正在存储的异步请求
        if (DBG) {
            Slog.d(TAG, "store pending async requests: " + requests);
        }

        // 存储请求 ID 的列表
        List<Integer> requestIds = new ArrayList<>();

        // 存储要添加的请求信息的临时映射,key 为请求 ID,value 为请求信息
        SparseArray<AsyncPropertyRequestInfo<?, ?>> requestInfoToAdd = new SparseArray<>();

        // 使用锁来保证对共享资源(mRequestIdToAsyncRequestInfo)的线程安全访问
        synchronized (mLock) {

            // 遍历请求列表,逐个处理每个请求
            for (int i = 0; i < requests.size(); i++) {
                RequestType request = requests.get(i);

                // 创建一个新的请求信息对象,用于存储请求和回调相关信息
                AsyncPropertyRequestInfo<RequestType, CallbackType> requestInfo =
                        new AsyncPropertyRequestInfo(request, callbackExecutor, callback);

                // 获取请求 ID
                int requestId = request.getRequestId();

                // 将请求 ID 添加到列表中
                requestIds.add(requestId);

                // 检查请求 ID 是否已经存在,如果存在则抛出异常
                if (mRequestIdToAsyncRequestInfo.contains(requestId)
                        || requestInfoToAdd.contains(requestId)) {
                    throw new IllegalArgumentException(
                            "Request ID: " + requestId + " already exists");
                }

                // 将新的请求信息添加到临时映射中
                requestInfoToAdd.put(requestId, requestInfo);
            }

            // 将临时映射中的请求信息添加到主映射 mRequestIdToAsyncRequestInfo 中
            for (int i = 0; i < requestInfoToAdd.size(); i++) {
                mRequestIdToAsyncRequestInfo.put(requestInfoToAdd.keyAt(i),
                        requestInfoToAdd.valueAt(i));
            }
        }

        // 返回存储的请求 ID 列表
        return requestIds;
    }
}

getPropertyConfigsFromService()

// packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

public class CarPropertyManager extends CarManagerBase {  

    /**
     * 从车载服务获取属性配置列表。
     *
     * <p>此方法根据给定的属性 ID 列表,过滤掉不支持的属性,并向车载服务请求获取对应的属性配置。</p>
     * 
     * <p>返回值是一个 {@code CarPropertyConfigs} 对象,包含有效的属性配置信息以及过滤掉的、不支持的属性 ID。</p>
     * 
     * @param propertyIds 要查询的属性 ID 列表。
     * @return 返回 {@code CarPropertyConfigs} 对象,包含有效的属性配置和不支持的属性 ID。
     * @throws RemoteException 如果从车载服务获取属性配置时发生通信异常,则抛出 {@code RemoteException}。
     */
    @Nullable
    private CarPropertyConfigs getPropertyConfigsFromService(Iterable<Integer> propertyIds) {
        // 创建两个 IntArray 用于分别存储过滤后的属性 ID 和不支持的属性 ID
        IntArray filteredPropertyIds = new IntArray();
        IntArray unsupportedPropertyIds = new IntArray();

        // 遍历传入的属性 ID 列表
        for (int propertyId : propertyIds) {
            // 确保属性 ID 不是用户自定义的 HAL 属性
            assertNotUserHalProperty(propertyId);

            // 判断属性是否被支持
            if (!CarPropertyHelper.isSupported(propertyId)) {
                // 如果不支持该属性,则将其添加到 unsupportedPropertyIds 中
                unsupportedPropertyIds.add(propertyId);
                continue;
            }

            // 如果支持该属性,则将其添加到 filteredPropertyIds 中
            filteredPropertyIds.add(propertyId);
        }

        // 声明一个变量用于存储获取的属性配置结果
        GetPropertyConfigListResult result;

        try {
            // 向车载服务请求获取属性配置列表,传入过滤后的属性 ID 列表
            result = mService.getPropertyConfigList(filteredPropertyIds.toArray());
        } catch (RemoteException e) {
            // 如果发生 RemoteException 异常,记录错误日志并处理异常
            Slog.e(TAG, "CarPropertyService.getPropertyConfigList exception ", e);
            return handleRemoteExceptionFromCarService(e, null);
        }

        // 返回一个 CarPropertyConfigs 对象,包含获取的属性配置结果和不支持的属性 ID
        return new CarPropertyConfigs(result, unsupportedPropertyIds);
    }
}

getPropertyConfigsFromService() 方法用于从车载服务获取属性配置。它接收一个包含属性 ID 的集合(propertyIds),过滤掉不支持的属性 ID,并向车载服务请求获取对应的属性配置。返回值是一个 CarPropertyConfigs 对象,包含有效的属性配置以及过滤掉的不支持的属性 ID。

评论