当前位置: 首页 > 工具软件 > CarApi > 使用案例 >

Android Automotive (三)Car API

杭昊空
2023-12-01

Android Automotive (三)Car API

Car API 是Android系统中使用Android Automotive特性的系统接口,代码路径在:packages/services/Car/car-lib

从前面知道,Car API 提供了很多的接口类,这里介绍Android Automotive的核心接口之一的CarPropertyManager

调用方法

我们先来介绍一下怎么使用Car API

private void initCarApi() {
    if (mCarApi != null && mCarApi.isConnected()) {
        mCarApi.disconnect();
        mCarApi = null;
    }
    mCarApi = Car.createCar(this, null, Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER, (Car car, boolean ready) -> {
        if (ready) {
            initManagers(car);
        }
    });
}

private void initManagers(Car car) {
    synchronized (mPropertyManagerReady) {
        mHvacManager = (CarHvacManager) car.getCarManager(
            android.car.Car.HVAC_SERVICE);
        mPowerManager = (CarPowerManager) car.getCarManager(
            android.car.Car.POWER_SERVICE);
        mPropertyManager = (CarPropertyManager) car.getCarManager(
            android.car.Car.PROPERTY_SERVICE);
        mSensorManager = (CarSensorManager) car.getCarManager(
            android.car.Car.SENSOR_SERVICE);
        mCarAppFocusManager =
            (CarAppFocusManager) car.getCarManager(Car.APP_FOCUS_SERVICE);
        mCarProjectionManager =
            (CarProjectionManager) car.getCarManager(Car.PROJECTION_SERVICE);
        mPropertyManagerReady.notifyAll();
    }
}

使用Car API一般需要三个流程。

  1. mCarApi = Car.createCar 使用Car的静态方法获取到Car实例

    public static Car createCar(@NonNull Context context,
                @Nullable Handler handler, long waitTimeoutMs,
                @NonNull statusChangeListener)
    

    CAR_WAIT_TIMEOUT_WAIT_FOREVER表示在呼叫中永久等待,直到汽车服务CarService就绪。

  2. 当汽车服务CarService就绪后,会回调CarServiceLifecycleListenervoid onLifecycleChanged(@NonNull Car car, boolean ready)接口。

  3. onLifecycleChanged回调中,获取子Manager接口实例,调用子Manager实例接口实现功能。

Car

用于Android Automotive架构的顶级API接口,此API仅适用具有FEATURE_AUTOMOTIVE的设备。

所有应用在希望使用Android Automotive特性的时候,必须要用过这个API来实现。Car是一个总的功能入口。

Car API 的入口函数,里面含有大量的Manager,通过getCarManager(String)获取实例CarPropertyManager也需要用这种方式获取。

常用方法

  • public static Car createCar(Context context) 获取Car实例

  • public static Car createCar(Context context, @Nullable Handler handler) 获取Car实例

  • public static Car createCar(@NonNull Context context, @Nullable Handler handler, long waitTimeoutMs, @NonNull CarServiceLifecycleListener statusChangeListener) 获取Car实例

  • public Object getCarManager(String serviceName) 获取子Manager

  • public boolean isConnected() 判断是否和CarService保持连接

  • public void disconnect() 断开和CarService的连接

Car::CreateCar

CreateCar方法来获取Car实例,看看代码里都做了什么

public static Car createCar(Context context, @Nullable Handler handler) {
    assertNonNullContext(context);
    Car car = null;
    IBinder service = null;
    boolean started = false;
    int retryCount = 0;
    while (true) {
        service = ServiceManager.getService(CAR_SERVICE_BINDER_SERVICE_NAME);
        if (car == null) {
            // service can be still null. The constructor is safe for null service.
            car = new Car(context, ICar.Stub.asInterface(service),
                    null /*serviceConnectionListener*/, null /*statusChangeListener*/, handler);
        }
        if (service != null) {
            if (!started) {  // specialization for most common case.
                // Do this to crash client when car service crashes.
                car.startCarService();
                return car;
            }
            break;
        }
        if (!started) {
            car.startCarService();
            started = true;
        }
        retryCount++;
        if (retryCount > CAR_SERVICE_BINDER_POLLING_MAX_RETRY) {
            Log.e(TAG_CAR, "cannot get car_service, waited for car service (ms):"
                            + CAR_SERVICE_BINDER_POLLING_INTERVAL_MS
                            * CAR_SERVICE_BINDER_POLLING_MAX_RETRY,
                    new RuntimeException());
            return null;
        }
        try {
            Thread.sleep(CAR_SERVICE_BINDER_POLLING_INTERVAL_MS);
        } catch (InterruptedException e) {
            Log.e(CarLibLog.TAG_CAR, "interrupted while waiting for car_service",
                    new RuntimeException());
            return null;
        }
    }
    // Can be accessed from mServiceConnectionListener in main thread.
    synchronized (car) {
        if (car.mService == null) {
            car.mService = ICar.Stub.asInterface(service);
            Log.w(TAG_CAR,
                    "waited for car_service (ms):"
                            + CAR_SERVICE_BINDER_POLLING_INTERVAL_MS * retryCount,
                    new RuntimeException());
        }
        car.mConnectionState = STATE_CONNECTED;
    }
    return car;
}
  1. while (true) { API接口里有个死循环也是万万没想到的啊!

  2. service = ServiceManager.getService(CAR_SERVICE_BINDER_SERVICE_NAME);首先获取CarService的服务实例,这里直接通过ServiceManager比较方便。问题是不一定能取到。

  3. car = new Car(context, ICar.Stub.asInterface(service), null /*serviceConnectionListener*/, null /*statusChangeListener*/, handler); 这里前面方法中声明,所以判断一定是null,会new一个Car实例。

  4. car.startCarService();这里实际是去用Bind的方式启动CarService

  5. if (retryCount > CAR_SERVICE_BINDER_POLLING_MAX_RETRY) {这里会有个判断重试的次数,如果超过次数还没取到CarService服务,则会返回nullsleep的时间是50ms,重试100次,也就是获取Car实例最长时间长达5s。

    private static final long CAR_SERVICE_BINDER_POLLING_INTERVAL_MS = 50;
    private static final long CAR_SERVICE_BINDER_POLLING_MAX_RETRY = 100
    

Car::startCarService

去启动CarService,这里用到的实际是Bind的形式bindServiceAsUser

private void startCarService() {
    Intent intent = new Intent();
    intent.setPackage(CAR_SERVICE_PACKAGE);
    intent.setAction(Car.CAR_SERVICE_INTERFACE_NAME);
    boolean bound = mContext.bindServiceAsUser(intent, mServiceConnectionListener,
            Context.BIND_AUTO_CREATE, UserHandle.CURRENT_OR_SELF);
    synchronized (mLock) {
        if (!bound) {
            mConnectionRetryCount++;
            if (mConnectionRetryCount > CAR_SERVICE_BIND_MAX_RETRY) {
                Log.w(TAG_CAR, "cannot bind to car service after max retry");
                mMainThreadEventHandler.post(mConnectionRetryFailedRunnable);
            } else {
                mEventHandler.postDelayed(mConnectionRetryRunnable,
                        CAR_SERVICE_BIND_RETRY_INTERVAL_MS);
            }
        } else {
            mEventHandler.removeCallbacks(mConnectionRetryRunnable);
            mMainThreadEventHandler.removeCallbacks(mConnectionRetryFailedRunnable);
            mConnectionRetryCount = 0;
            mServiceBound = true;
        }
    }
}

这里有个判断mConnectionRetryCount > CAR_SERVICE_BIND_MAX_RETRY,就是当启动CarService失败是会重试的,重试多了还不行,则会调用mConnectionRetryFailedRunnable。这里定义是20次。

private static final long CAR_SERVICE_BIND_MAX_RETRY = 20;

mConnectionRetryFailedRunnable的定义是会回调onServiceDisconnected

private final Runnable mConnectionRetryFailedRunnable = new Runnable() {    @Override    public void run() {        mServiceConnectionListener.onServiceDisconnected(new ComponentName(CAR_SERVICE_PACKAGE,                CAR_SERVICE_CLASS));    }};

Car::connect

connect方法实际就是调用startCarService方法,因为connect是公开的,startCarService是私有的。

connect已经弃用了,我们知道startCarService就是和CarService建立连接,因为在CreatCar里已经保证获取到CarService,所以connect不在必要。

@Deprecated
public void connect() throws IllegalStateException {
    synchronized (mLock) {
        if (mConnectionState != STATE_DISCONNECTED) {
            throw new IllegalStateException("already connected or connecting");
        }
        mConnectionState = STATE_CONNECTING;
        startCarService();
    }
}

Car::createCar - Overload

createCar还有个重载方法,这个方法可以自己定义和CarService连接的超时时间,并且会收到和CarService连接状态的变化通知。

public static Car createCar(@NonNull Context context,
        @Nullable Handler handler, long waitTimeoutMs,
        @NonNull CarServiceLifecycleListener statusChangeListener) {
    assertNonNullContext(context);
    Objects.requireNonNull(statusChangeListener);
    Car car = null;
    IBinder service = null;
    boolean started = false;
    int retryCount = 0;
    long maxRetryCount = 0;
    if (waitTimeoutMs > 0) {
        maxRetryCount = waitTimeoutMs / CAR_SERVICE_BINDER_POLLING_INTERVAL_MS;
        // at least wait once if it is positive value.
        if (maxRetryCount == 0) {
            maxRetryCount = 1;
        }
    }
    boolean isMainThread = Looper.myLooper() == Looper.getMainLooper();
    while (true) {
        service = ServiceManager.getService(CAR_SERVICE_BINDER_SERVICE_NAME);
        if (car == null) {
            // service can be still null. The constructor is safe for null service.
            car = new Car(context, ICar.Stub.asInterface(service), null, statusChangeListener,
                    handler);
        }
        if (service != null) {
            if (!started) {  // specialization for most common case : car service already ready
                car.dispatchCarReadyToMainThread(isMainThread);
                // Needs this for CarServiceLifecycleListener. Note that ServiceConnection
                // will skip the callback as valid mService is set already.
                car.startCarService();
                return car;
            }
            // service available after starting.
            break;
        }
        if (!started) {
            car.startCarService();
            started = true;
        }
        retryCount++;
        if (waitTimeoutMs < 0 && retryCount >= CAR_SERVICE_BINDER_POLLING_MAX_RETRY
                && retryCount % CAR_SERVICE_BINDER_POLLING_MAX_RETRY == 0) {
            // Log warning if car service is not alive even for waiting forever case.
            Log.w(TAG_CAR, "car_service not ready, waited for car service (ms):"
                            + retryCount * CAR_SERVICE_BINDER_POLLING_INTERVAL_MS,
                    new RuntimeException());
        } else if (waitTimeoutMs >= 0 && retryCount > maxRetryCount) {
            if (waitTimeoutMs > 0) {
                Log.w(TAG_CAR, "car_service not ready, waited for car service (ms):"
                                + waitTimeoutMs,
                        new RuntimeException());
            }
            return car;
        }

        try {
            Thread.sleep(CAR_SERVICE_BINDER_POLLING_INTERVAL_MS);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            Log.w(TAG_CAR, "interrupted", new RuntimeException());
            return car;
        }
    }
    // Can be accessed from mServiceConnectionListener in main thread.
    synchronized (car.mLock) {
        Log.w(TAG_CAR,
                "waited for car_service (ms):"
                        + retryCount * CAR_SERVICE_BINDER_POLLING_INTERVAL_MS,
                new RuntimeException());
        // ServiceConnection has handled everything.
        if (car.mService != null) {
            return car;
        }
        // mService check in ServiceConnection prevents calling
        // onLifecycleChanged. So onLifecycleChanged should be called explicitly
        // but do it outside lock.
        car.mService = ICar.Stub.asInterface(service);
        car.mConnectionState = STATE_CONNECTED;
    }
    car.dispatchCarReadyToMainThread(isMainThread);
    return car;
}
  1. boolean isMainThread = Looper.myLooper() == Looper.getMainLooper()判断当前线程是否时主线程

  2. car.dispatchCarReadyToMainThread(isMainThread);保证回调的内容在主线程。

    private void dispatchCarReadyToMainThread(boolean isMainThread) {
        if (isMainThread) {
            mStatusChangeCallback.onLifecycleChanged(this, true);
        } else {
            // should dispatch to main thread.
            mMainThreadEventHandler.post(
                    () -> mStatusChangeCallback.onLifecycleChanged(this, true));
        }
    }
    

Car::getCarManager

getCarManager通过一个字符串(比如public static final String PROPERTY_SERVICE = "property";)获取子Manager对象。

public Object getCarManager(String serviceName) {
    CarManagerBase manager;
    synchronized (mLock) {
        if (mService == null) {
            Log.w(TAG_CAR, "getCarManager not working while car service not ready");
            return null;
        }
        manager = mServiceMap.get(serviceName);
        if (manager == null) {
            try {
                IBinder binder = mService.getCarService(serviceName);
                if (binder == null) {
                    Log.w(TAG_CAR, "getCarManager could not get binder for service:"
                            + serviceName);
                    return null;
                }
                manager = createCarManagerLocked(serviceName, binder);
                if (manager == null) {
                    Log.w(TAG_CAR, "getCarManager could not create manager for service:"
                                    + serviceName);
                    return null;
                }
                mServiceMap.put(serviceName, manager);
            } catch (RemoteException e) {
                handleRemoteExceptionFromCarService(e);
            }
        }
    }
    return manager;
}
  1. IBinder binder = mService.getCarService(serviceName);这里是用CarService中拿对应子服务的Binder对象。
  2. 如果拿到了,则会用其创建子Manager实例。
  3. 将子Manager实例存在Map中,为后面使用方便,不重新创建。

CarPropertyManager

CarPropertyManager是提供与车辆特定属性交互的应用程序接口。实际连接的CarService中的CarPropertyService

代码路径:packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

常用方法

  • public boolean registerCallback(@NonNull CarPropertyEventCallback callback, int propertyId, @FloatRange(from = 0.0, to = 100.0) float rate)
  • public void unregisterCallback(@NonNull CarPropertyEventCallback callback)
  • public void unregisterCallback(@NonNull CarPropertyEventCallback callback, int propertyId)
  • public List<CarPropertyConfig> getPropertyList()
  • public List<CarPropertyConfig> getPropertyList(@NonNull ArraySet<Integer> propertyIds)
  • public CarPropertyConfig<?> getCarPropertyConfig(int propId)
  • public int getAreaId(int propId, int area)
  • public String getReadPermission(int propId)
  • public String getWritePermission(int propId)
  • public boolean isPropertyAvailable(int propId, int area)
  • public boolean getBooleanProperty(int prop, int area)
  • public float getFloatProperty(int prop, int area)
  • public int getIntProperty(int prop, int area)
  • public int[] getIntArrayProperty(int prop, int area)
  • public <E> CarPropertyValue<E> getProperty(@NonNull Class<E> clazz, int propId, int areaId)
  • public <E> CarPropertyValue<E> getProperty(int propId, int areaId)
  • public <E> void setProperty(@NonNull Class<E> clazz, int propId, int areaId, @NonNull E val)
  • public void setBooleanProperty(int prop, int areaId, boolean val)
  • public void setFloatProperty(int prop, int areaId, float val)
  • public void setIntProperty(int prop, int areaId, int val)
  • public void onCarDisconnected()

接口

//构造方法
public CarPropertyManager(Car car, @NonNull ICarProperty service);
//返回areaId包含属性的属性区域
public int getAreaId(int propId, int area);
//返回boolen类型的属性值
public boolean getBooleanProperty(int prop, int area);
//根据propertyId获取CarPropertyConfig
public CarPropertyConfig<?> getCarPropertyConfig(int propId);    
//返回float类型的属性值
public float getFloatProperty(int prop, int area);
//返回int[]类型的属性值
public int[] getIntArrayProperty(int prop, int area);
//返回int类型的属性值
public int getIntProperty(int prop, int area);
//返回CarPropertyValue
public <E> CarPropertyValue<E> getProperty(@NonNull Class<E> clazz, int propId, int areaId);
//返回CarPropertyValue
public <E> CarPropertyValue<E> getProperty(int propId, int areaId);
//返回车辆属性列表
public List<CarPropertyConfig> getPropertyList(@NonNull ArraySet<Integer> propertyIds);
//返回给定属性ID的读取权限
public String getReadPermission(int propId);
//返回给定属性ID的写入权限
public String getWritePermission(int propId);
//根据汽车的当前状态检查给定属性是否可用或禁用
public boolean isPropertyAvailable(int propId, int area);
//和车辆服务断开连接
public void onCarDisconnected();
//注册侦听器以获取属性更新。
public boolean registerCallback(@NonNull CarPropertyEventCallback callback, int propertyId, @FloatRange(from = 0.0, to = 100.0) float rate);
//设置boolean类型的值
public void setBooleanProperty(int prop, int areaId, boolean val);
//设置float类型的值
public void setFloatProperty(int prop, int areaId, float val);
//设置int类型的值
public void setIntProperty(int prop, int areaId, int val);
//设置其它类型的值
public <E> void setProperty(@NonNull Class<E> clazz, int propId, int areaId, @NonNull E val);
//取消监听器
public void unregisterCallback(@NonNull CarPropertyEventCallback callback);
//取消某个id的监听器
public void unregisterCallback(@NonNull CarPropertyEventCallback callback, int propertyId);    

CarPropertyManager::CarPropertyManager

public CarPropertyManager(Car car, @NonNull ICarProperty service) {
    super(car);
    mService = service;
    mAppTargetSdk = getContext().getApplicationInfo().targetSdkVersion;
    try {
        List<CarPropertyConfig> configs = mService.getPropertyList();
        for (CarPropertyConfig carPropertyConfig : configs) {
            mConfigMap.put(carPropertyConfig.getPropertyId(), carPropertyConfig);
        }
    } catch (RemoteException e) {
        Log.e(TAG, "getPropertyList exception ", e);
        throw new RuntimeException(e);
    }

    Handler eventHandler = getEventHandler();
    if (eventHandler == null) {
        mHandler = null;
        return;
    }
    mHandler = new SingleMessageHandler<CarPropertyEvent>(eventHandler.getLooper(),
        MSG_GENERIC_EVENT) {
        @Override
        protected void handleEvent(CarPropertyEvent event) {
            CarPropertyListeners listeners;
            synchronized (mActivePropertyListener) {
                listeners = mActivePropertyListener.get(
                    event.getCarPropertyValue().getPropertyId());
            }
            if (listeners != null) {
                switch (event.getEventType()) {
                    case CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE:
                        listeners.onPropertyChanged(event);
                        break;
                    case CarPropertyEvent.PROPERTY_EVENT_ERROR:
                        listeners.onErrorEvent(event);
                        break;
                    default:
                        throw new IllegalArgumentException();
                }
            }
        }
    };
}
  1. 构造函数,mService = service 初始化mService,该变量时类Car传进来的。
  2. List<CarPropertyConfig> configs = mService.getPropertyList(); 获取全部车辆属性的配置
  3. mConfigMap.put(carPropertyConfig.getPropertyId(), carPropertyConfig);将配置存入mConfigMap
  4. Handler eventHandler = getEventHandler() 获取Handler对象,eventHandler是创建Car时,由应用传进来的,如果没有传或者传入时null,会默认使用主线程的Handler
  5. mHandler = new SingleMessageHandler 创建一个回调车辆属性变化事件的Handler,回调会在传入Handler所在的线程。

CarPropertyManager::getProperty

获取一个车辆属性值,这里会直接调用到CarPropertyService

public <E> CarPropertyValue<E> getProperty(int propId, int areaId) {
    checkSupportedProperty(propId);

    try {
        CarPropertyValue<E> propVal = mService.getProperty(propId, areaId);
        return propVal;
    } catch (RemoteException e) {
        return handleRemoteExceptionFromCarService(e, null);
    } catch (ServiceSpecificException e) {
        if (mAppTargetSdk < Build.VERSION_CODES.R) {
            if (e.errorCode == VehicleHalStatusCode.STATUS_TRY_AGAIN) {
                return null;
            } else {
                throw new IllegalStateException(String.format("Failed to get property: 0x%x, "
                        + "areaId: 0x%x", propId, areaId));
            }
        }
        return handleCarServiceSpecificException(e.errorCode, propId, areaId, null);
    }
}

CarPropertyManager::setProperty

设置一个车辆属性值,这里会直接调用到CarPropertyService

public <E> void setProperty(@NonNull Class<E> clazz, int propId, int areaId, @NonNull E val) {
    if (DBG) {
        Log.d(TAG, "setProperty, propId: 0x" + toHexString(propId)
                + ", areaId: 0x" + toHexString(areaId) + ", class: " + clazz + ", val: " + val);
    }
    checkSupportedProperty(propId);
    try {
        if (mCarPropertyEventToService == null) {
            mCarPropertyEventToService = new CarPropertyEventListenerToService(this);
        }
        mService.setProperty(new CarPropertyValue<>(propId, areaId, val),
                mCarPropertyEventToService);
    } catch (RemoteException e) {
        handleRemoteExceptionFromCarService(e);
    } catch (ServiceSpecificException e) {
        if (mAppTargetSdk < Build.VERSION_CODES.R) {
            if (e.errorCode == VehicleHalStatusCode.STATUS_TRY_AGAIN) {
                throw new RuntimeException(String.format("Failed to set property: 0x%x, "
                        + "areaId: 0x%x", propId, areaId));
            } else {
                throw new IllegalStateException(String.format("Failed to set property: 0x%x, "
                        + "areaId: 0x%x", propId, areaId));
            }
        }
        handleCarServiceSpecificException(e.errorCode, propId, areaId, null);
    }
}

CarPropertyManager::registerCallback

订阅一个车辆属性值变化,这里会调用到CarPropertyService

public boolean registerCallback(@NonNull CarPropertyEventCallback callback,
        int propertyId, @FloatRange(from = 0.0, to = 100.0) float rate) {
    synchronized (mActivePropertyListener) {
        if (mCarPropertyEventToService == null) {
            mCarPropertyEventToService = new CarPropertyEventListenerToService(this);
        }
        CarPropertyConfig config = mConfigMap.get(propertyId);
        if (config == null) {
            Log.e(TAG, "registerListener:  propId is not in config list:  " + propertyId);
            return false;
        }
        if (config.getChangeMode() == CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE) {
            rate = SENSOR_RATE_ONCHANGE;
        }
        boolean needsServerUpdate = false;
        CarPropertyListeners listeners;
        listeners = mActivePropertyListener.get(propertyId);
        if (listeners == null) {
            listeners = new CarPropertyListeners(rate);
            mActivePropertyListener.put(propertyId, listeners);
            needsServerUpdate = true;
        }
        if (listeners.addAndUpdateRate(callback, rate)) {
            needsServerUpdate = true;
        }
        if (needsServerUpdate) {
            if (!registerOrUpdatePropertyListener(propertyId, rate)) {
                return false;
            }
        }
    }
    return true;
}

关联Manager

CarCabinManager

弃用,使用CarPropertyManager替代

CarHvacManager

弃用,使用CarPropertyManager替代

CarSensorManager

弃用,使用CarPropertyManager替代

CarVendorExtensionManager

弃用,使用CarPropertyManager替代

CarPropertyEventCallback

应用订阅车辆属性变化的接口

public interface CarPropertyEventCallback {
    /**
     * Called when a property is updated
     * @param value Property that has been updated.
     */
    void onChangeEvent(CarPropertyValue value);

    /**
     * Called when an error is detected when setting a property.
     *
     * @param propId Property ID which is detected an error.
     * @param zone Zone which is detected an error.
     *
     * @see CarPropertyEventCallback#onErrorEvent(int, int, int)
     */
    void onErrorEvent(int propId, int zone);

    /**
     * Called when an error is detected when setting a property.
     *
     * <p>Clients which changed the property value in the areaId most recently will receive
     * this callback. If multiple clients set a property for the same area id simultaneously,
     * which one takes precedence is undefined. Typically, the last set operation
     * (in the order that they are issued to car's ECU) overrides the previous set operations.
     * The delivered error reflects the error happened in the last set operation.
     *
     * @param propId Property ID which is detected an error.
     * @param areaId AreaId which is detected an error.
     * @param errorCode Error code is raised in the car.
     */
    default void onErrorEvent(int propId, int areaId, @CarSetPropertyErrorCode int errorCode) {
        if (DBG) {
            Log.d(TAG, "onErrorEvent propertyId: 0x" + toHexString(propId) + " areaId:0x"
                    + toHexString(areaId) + " ErrorCode: " + errorCode);
        }
        onErrorEvent(propId, areaId);
    }
}
  • void onChangeEvent(CarPropertyValue value);

  • void onErrorEvent(int propId, int zone);

  • default void onErrorEvent(int propId, int areaId, @CarSetPropertyErrorCode int errorCode)

CarPropertyConfig

车辆属性的配置信息封装类。

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;
    private final int mPropertyId;
    private final SparseArray<AreaConfig<T>> mSupportedAreas;
    private final Class<T> mType;

CarPropertyValue

车辆属性数据对象的封装类。里面有property、area、status、mtimestamp、value等信息。

public final class CarPropertyValue<T> implements Parcelable {
    private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;

    private final int mPropertyId;
    private final int mAreaId;
    private final int mStatus;
    private final long mTimestamp;
    private final T mValue;

VehiclePropertyIds

VehiclePropertyIds是复制自android.hardware.automotive.vehicle-V2.0-java_gen_java/gen/android/hardware/automotive/vehicle/V2_0。VHAL的车辆属性变化时,需要更新该文件。

该文件描述了所有支持的车辆属性,并且直接显示了types.hal文件中定义属性的最终值。上层应用可以直接调用API。

代码路径:packages/services/Car/car-lib/src/android/car/VehiclePropertyIds.java

常用方法

  • public int getPropertyId()
  • public int getAreaId()
  • public @PropertyStatus int getStatus()
  • public long getTimestamp()
  • public T getValue()

相关类

还有一些和车辆属性相关的类如下

  • VehiclePropertyIds

  • VehicleAreaType

  • VehicleAreaDoor

  • VehicleAreaMirror

  • VehicleAreaSeat

  • VehicleAreaWheel

  • VehicleAreaWindow

  • VehiclePropertyAccess

  • VehicleUnit

  • CarPropertyValue
    car-lib中定义的一个类,用于car-lib和service通信的数据结构
    代码路径:
    packages/services/Car/car-lib/src/android/car/hardware/CarPropertyValue.java
    packages/services/Car/car-lib/src/android/car/hardware/CarPropertyValue.aidl

  • CarPropertyConfig

    表示有关汽车特性的常规信息,例如汽车区域的数据类型和最小/最大范围(如果适用)。这个类应该是不可变的,可打包的,并且可以传递。
    代码路径:
    packages/services/Car/car-lib/src/android/car/hardware/CarPropertyConfig.java
    packages/services/Car/car-lib/src/android/car/hardware/CarPropertyConfig.aidl

    private final int mAccess; //定义属性是读还是写,或者两者都是
    private final int mAreaType; //p.prop & VehicleArea.MASK
    private final int mChangeMode; //定义属性的更改模式
    private final ArrayList<Integer> mConfigArray; //包含其他配置参数
    private final String mConfigString; //某些属性可能需要传递其他信息
    private final float mMaxSampleRate; //最大采样率
    private final float mMinSampleRate; //最小采样率
    private final int mPropertyId; //属性标识符 p.prop
    
    private final SparseArray<AreaConfig<T>> mSupportedAreas; //包含每个区域的配置 
    private final Class<T> mType; //p.prop & VehiclePropertyType.MASK
    

    VehiclePropConfigCarPropertyConfig

    "CarPropertyConfig{"
        + "mPropertyId=" + mPropertyId /*id int*/
        + ", mAccess=" + mAccess /*读写权限 int*/
        + ", mAreaType=" + mAreaType /*区域类型 int*/
        + ", mChangeMode=" + mChangeMode /*变化模式 int*/
        + ", mConfigArray=" + mConfigArray /*配置参数 ArrayList<Integer>*/
        + ", mConfigString=" + mConfigString /*某些属性可能需要此属性传值 String*/
        + ", mMaxSampleRate=" + mMaxSampleRate /*最大采样率 float*/
        + ", mMinSampleRate=" + mMinSampleRate /*最小采样率 float*/
        + ", mSupportedAreas=" + mSupportedAreas /*属性的区域ID数 SparseArray<AreaConfig<T>>*/
        + ", mType=" + mType /*属性值的数据类型 Class<T>*/
        + '}';
    
    • VehiclePropConfig是HIDL接口中定义的车辆属性配置,用于和HAL层以下通信

    • CarPropertyConfig是AIDL中定义的车辆属性配置,用于和应用层通信

注意:
car-lib的接口实现不同Android版本存在一些差异,调用的时候需要适配一下。

示例程序

Car API的使用可以参考packages/services/Car/tests目录下的EmbeddedKitchenSinkApp工程。

Android Q之前调用Car.createCar之后还需要调用connect接口, 并且Android Q开始,放弃了android.support.car.Car的使用。Android R开始,不再需要调用connect接口并且connect接口被加上了@Deprecated标签, 不再推荐使用.

 类似资料: