Android Automotive

作者: 云中的醉 | 来源:发表于2019-06-12 18:11 被阅读0次

    1.Hvac

    Hvac:供暖通风与空气调节(Heating Ventilation and Air Conditioning)

    在Android Automotive中,Hvac是作为控制汽车供暖通风与空气调节的系统应用,如下为Hvac源码目录结构及相关说明:

    │  BootCompleteReceiver.java  用于开机启动HvacUiService
    │  DataStore.java  存储管理Hvac属性数据
    │  HvacController.java  Hvac属性控制类
    │  HvacPolicy.java  Hvac属性规则类
    │  HvacUiService.java  Hvac界面服务
    │  LocalHvacPropertyService.java Demo模式使用的模拟服务
    ├─controllers
    │      FanDirectionButtonsController.java 风扇方向控制
    │      FanSpeedBarController.java 风扇速度控制
    │      HvacPanelController.java  Hvac界面布局及相关逻辑
    │      SeatWarmerController.java  座椅温度控制
    │      TemperatureController.java 温度控制
    └─ui
            FanDirectionButtons.java
            FanSpeedBar.java
            FanSpeedBarSegment.java
            HvacPanelRow.java
            PressAndHoldTouchListener.java
            SeatWarmerButton.java
            TemperatureBarOverlay.java
            ToggleButton.java
    

    2.Hvac Manifest文件

    在Hvac的Manifest文件中,一共注册了三个组件
    服务HvacController:用于设置及获取Hvac相关属性
    服务HvacUiService:用于显示Hvac的界面
    广播接收器BootCompleteReceiver:接收开机启动广播并启动HvacUiService服务

    3.Hvac启动

    应用在Manifest文件中注册了开机广播,所以在开机完成后会收到开机完成的广播,然后再广播接收器里面会启动HvacUiService。

    BootCompleteReceiver.java
    
    @Override
    public void onReceive(Context context, Intent intent) {
        Intent hvacUiService = new Intent(context, HvacUiService.class);
        context.startService(hvacUiService);
    }
    

    在HvacUiService启动的时候实例化HvacPanelController并绑定HvacController服务。
    HvacUiService服务通过WindowManager将View添加Window中,并且给HvacPanelController传入了布局信息,然后在HvacPanelController中进行view的layout操作。并且在HvacPanelController包含了controller包下的FanSpeedBarController,SeatWarmerController等几个类,使得HvacPanelController可以作为Hvac的界面统一控制类。

    HvacUiService.java
    
    mHvacPanelController = new HvacPanelController(this /* context */, mContainer,
            mWindowManager, mDriverTemperatureBar, mPassengerTemperatureBar,
            mDriverTemperatureBarCollapsed, mPassengerTemperatureBarCollapsed
    );
    
    Intent bindIntent = new Intent(this /* context */, HvacController.class);
    if (!bindService(bindIntent, mServiceConnection, Context.BIND_AUTO_CREATE)) {
        Log.e(TAG, "Failed to connect to HvacController.");
    }
    

    当HvacUiService成功绑定HvacController时,HvacPanelController会通过HvacController获取Hvac属性进行界面更新,并且HvacController也会从系统中读取各属性值,并存取在DateStore中。

    HvacUiService.java
    
    final Runnable r = () -> {
        // Once the hvac controller has refreshed its values from the vehicle,
        // bind all the values.
        mHvacPanelController.updateHvacController(mHvacController);// 更新界面
    };
    
    if (mHvacController != null) {
        mHvacController.requestRefresh(r, new Handler(context.getMainLooper()));// 存储数据
    }
    

    4.Hvac属性存储

    DataStore用于存储HvacController从系统中获取的属性值。

    我们可以从两个位置进行更新Hvac相关属性,即用户界面和硬件按钮。这两种不同的更新方式都是从不同的线程更新到当前状态。此外,在某些情况下,暖通空调系统可能会发送虚假的更新,因此这个类将所有内容更新管理合并,从而确保在用户看来应用程序的界面是正常的。

    如风扇速度,DateStore会在收到系统事件是通过shouldPropagateFanSpeedUpdate方法判断是否需要更新,判断是否要更新的规则也很简单,即最近两秒内是否更新过该属性值,如果更新过,那么从系统回调的属性值将不会被Hvac应用处理。

    DataStore.java
    
    public int getFanSpeed() {
        synchronized (mFanSpeed) {
            return mFanSpeed;
        }
    }
    public void setFanSpeed(int speed) {
        synchronized (mFanSpeed) {
            mFanSpeed = speed;
            mLastFanSpeedSet = SystemClock.uptimeMillis();
        }
    }
    
    public boolean shouldPropagateFanSpeedUpdate(int zone, int speed) {
        // TODO: We ignore fan speed zones for now because we dont have a multi zone car.
        synchronized (mFanSpeed) {
            if (SystemClock.uptimeMillis() - mLastFanSpeedSet < COALESCE_TIME_MS) {
                return false;
            }
            mFanSpeed = speed;
        }
        return true;
    }
    

    比如用户通过界面设置风扇速度
    此时记录了最近设置风扇速度的时间mLastFanSpeedSet,而如果Hvac在两秒内收到系统的回调,可能是系统的错误信息也可能是通过风扇实体按钮改变的回调信息,那么这样的信息是不会显示在界面上的(这点有待验证)。

    HvacController.java
    
    public void setFanSpeed(final int fanSpeed) {
        mDataStore.setFanSpeed(fanSpeed);
        final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
            int newFanSpeed;
            protected Void doInBackground(Void... unused) {
                if (mHvacManager != null) {
                    int zone = SEAT_ALL; // Car specific workaround.
                    try {
                        if (Log.isLoggable(TAG, Log.DEBUG)) {
                            Log.d(TAG, "Setting fanspeed to: " + fanSpeed);
                        }
                        mHvacManager.setIntProperty(
                                CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, zone, fanSpeed);
    
                        newFanSpeed = mHvacManager.getIntProperty(
                                CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, zone);
                    } catch (android.car.CarNotConnectedException e) {
                        Log.e(TAG, "Car not connected in setFanSpeed");
                    }
                }
                return null;
            }
            @Override
            protected void onPostExecute(final Void result) {
                Log.e(TAG, "postExecute new fanSpeed: " + newFanSpeed);
            }
        };
        task.execute();
    }
    

    5.HvacController

    HvacController作为Hvac应用与系统的信息传输控制器。
    在Hvac中的设置及获取操作都是通过HvacController进行的,在HvacController启动时会获取一个Car实例,并通过connect方法连接CarService。

    HvacController.java
    
    mCarApiClient = Car.createCar(this, mCarConnectionCallback);
    mCarApiClient.connect();
    

    当成功连接CarService时,会进行初始化CarHvacManager并通过CarHvacManager获取车辆支持的属性列表,然后通知HvacPanelController已经成连接CarService并更新相关属性存储到DataStore中。

    HvacController.java
    
    @Override
    public void onConnected(Car car) {
        synchronized (mHvacManagerReady) {
            try {
                initHvacManager((CarHvacManager) mCarApiClient.getCarManager(
                        android.car.Car.HVAC_SERVICE));
                mHvacManagerReady.notifyAll();
            } catch (CarNotConnectedException e) {
                Log.e(TAG, "Car not connected in onServiceConnected");
            }
        }
    }
    
    private void initHvacManager(CarHvacManager carHvacManager) {
        mHvacManager = carHvacManager;
        List<CarPropertyConfig> properties = null;
        try {
            properties = mHvacManager.getPropertyList();
            mPolicy = new HvacPolicy(HvacController.this, properties);
            mHvacManager.registerCallback(mHardwareCallback);
        } catch (android.car.CarNotConnectedException e) {
            Log.e(TAG, "Car not connected in HVAC");
        }
    }
    

    在HvacController中有两个重要的对象,Car和CarHvacManager,HvacController正是通过这两个对象与CarService进行通信。

    (1)Car

    Car作为汽车平台最高等级的API为外界提供汽车所有服务和数据的访问。

    通过createCar方法可以新建一个Car实例,通过connect方法连接CarService。当成功连接时可以通过getCarManager方法获取一个一个相关的manager,比如Hvac通过getCarManager方法获取了一个CarHvacManager,当获取到manager后就可以进行相关操作了。

    (2)CarHvacManager

    CarHvacManager作为控制汽车Hvac系统的API为外界提供数据访问。

    CarHvacManager实现了CarManagerBase接口,并且只要是作为Car*Manager, 都需要实现CarManagerBase接口,如CarCabinManager,CarSensorManager等都实现了该接口。
    CarHvacManager的控制操作是通过CarPropertyManager来完成的,CarPropertyManager统一控制汽车属性相关的操作。CarHvacManager只是控制与Hvac相关的操作,在汽车中还有很多属性控制的Manager,如传感器,座舱等属性的控制,他们都是通过CarPropertyManager进行属性操作,通过在操作时传入的属性ID,属性区域以及属性值,在CarPropertyManager中会将这些参数转化为一个CarPropertyValue对象继续往下层传递。

    CarHvacManager控制的属性有:

    // 全局属性,只有一个
    ID_MIRROR_DEFROSTER_ON  //视镜除雾
    ID_STEERING_WHEEL_HEAT  //方向盘温度
    ID_OUTSIDE_AIR_TEMP  //室外温度
    ID_TEMPERATURE_DISPLAY_UNITS  //在使用的温度
    // 区域属性,可在不同区域设置
    ID_ZONED_TEMP_SETPOINT  //用户设置的温度
    ID_ZONED_TEMP_ACTUAL  //区域实际温度
    ID_ZONED_HVAC_POWER_ON  //HVAC系统电源开关
    ID_ZONED_FAN_SPEED_SETPOINT  //风扇设置的速度
    ID_ZONED_FAN_SPEED_RPM  //风扇实际的速度
    ID_ZONED_FAN_DIRECTION_AVAILABLE  //风扇可设置的方向
    ID_ZONED_FAN_DIRECTION  //现在风扇设置的方向
    ID_ZONED_SEAT_TEMP  //座椅温度
    ID_ZONED_AC_ON  //空调开关
    ID_ZONED_AUTOMATIC_MODE_ON  //HVAC自动模式开关
    ID_ZONED_AIR_RECIRCULATION_ON  //空气循环开关
    ID_ZONED_MAX_AC_ON  //空调最大速度开关
    ID_ZONED_DUAL_ZONE_ON  //双区模式开关
    ID_ZONED_MAX_DEFROST_ON  //最大除雾开关
    ID_ZONED_HVAC_AUTO_RECIRC_ON  //自动循环模式开关
    ID_WINDOW_DEFROSTER_ON  //除雾模式开关
    

    6.一次设置过程

    现在我们跟踪一次风扇速度的设置过程。

    点击Hvac界面中的风扇速度最大按钮时,通过在HvacPanelController中实例化时传入的mHvacController对象来设置最大速度。

    FanSpeedBarController.java
    
    private static final int MAX_FAN_SPEED = 6;
    private FanSpeedBar.FanSpeedButtonClickListener mClickListener
            = new FanSpeedBar.FanSpeedButtonClickListener() {
        @Override
        public void onMaxButtonClicked() {
            mHvacController.setFanSpeed(MAX_FAN_SPEED);
        }
        ……
    };
    

    HvacController收到传入的为值为MAX_FAN_SPEED = 6;
    首先会将这个值存储到DataStore中,然后通过CarHvacManager的setInProperty方法设置
    传入属性ID为ID_ZONED_FAN_SPEED_SETPOINT,属性区域为SEAT_ALL,属性值为要设置的风扇速度。

    HvacController.java
    
    // Hardware specific value for the front seats
    public static final int SEAT_ALL = VehicleAreaSeat.SEAT_ROW_1_LEFT |
            VehicleAreaSeat.SEAT_ROW_1_RIGHT | VehicleAreaSeat.SEAT_ROW_2_LEFT |
            VehicleAreaSeat.SEAT_ROW_2_CENTER | VehicleAreaSeat.SEAT_ROW_2_RIGHT;
    public void setFanSpeed(final int fanSpeed) {
        mDataStore.setFanSpeed(fanSpeed);
        final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
            int newFanSpeed;
    
            protected Void doInBackground(Void... unused) {
                if (mHvacManager != null) {
                    int zone = SEAT_ALL; // Car specific workaround.
                    try {
                        if (Log.isLoggable(TAG, Log.DEBUG)) {
                            Log.d(TAG, "Setting fanspeed to: " + fanSpeed);
                        }
                        mHvacManager.setIntProperty(
                                CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, zone, fanSpeed);
                        newFanSpeed = mHvacManager.getIntProperty(
                                CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, zone);
                    } catch (android.car.CarNotConnectedException e) {
                        Log.e(TAG, "Car not connected in setFanSpeed");
                    }
                }
                return null;
            }
            @Override
            protected void onPostExecute(final Void result) {
                Log.e(TAG, "postExecute new fanSpeed: " + newFanSpeed);
            }
        };
        task.execute();
    }
    

    在CarHvacManager中进行了属性ID合法性的检查,判断这个属性是否为Hvac相关的属性。然后通过CarPropertyManager进行设置。

    CarHvacManager.java
    
    public void setIntProperty(@PropertyId int propertyId, int area, int val)
            throws CarNotConnectedException {
        if (mHvacPropertyIds.contains(propertyId)) {
            mCarPropertyMgr.setIntProperty(propertyId, area, val);
        }
    }
    

    在CarPropertyManager中继续进行属性设置,最后是通过CarPropertyService进行操作的,
    而CarPropertyService是Car调用getCarManager时通过AIDL绑定的Service,所以可能会出现RemoteException,而出现这个异常时,意味着CarService不可用,需要抛出去让用户知道。

    CarPropertyManager.java
    
    /** Set int value of property*/
    public void setIntProperty(int prop, int area, int val) throws CarNotConnectedException {
        setProperty(Integer.class, prop, area, val);
    }
    
    /** Set CarPropertyValue */
    public <E> void setProperty(Class<E> clazz, int propId, int area, E val)
            throws CarNotConnectedException {
        if (mDbg) {
            Log.d(mTag, "setProperty, propId: 0x" + toHexString(propId)
                    + ", area: 0x" + toHexString(area) + ", class: " + clazz + ", val: " + val);
        }
        try {
            mService.setProperty(new CarPropertyValue<>(propId, area, val));
        } catch (RemoteException e) {
            Log.e(mTag, "setProperty failed with " + e.toString(), e);
            throw new CarNotConnectedException(e);
        }
    }
    

    在CarPropertyService中,会再次对属性ID合法性进行判断,并判断要修改该属性的应用是否有权限,最后通过PropertyHalService进行设置。

    CarPropertyService.java
    
    @Override
    public void setProperty(CarPropertyValue prop) {
        int propId = prop.getPropertyId();
        if (mConfigs.get(propId) == null) {
            // Do not attempt to register an invalid propId
            Log.e(TAG, "setProperty:  propId is not in config list:0x" + toHexString(propId));
            return;
        }
        ICarImpl.assertPermission(mContext, mHal.getWritePermission(propId));
        mHal.setProperty(prop);
    }
    

    在PropertyHalService中,首先将Car属性值对象转换为Vehicle的Hal属性值对象,并判断此车机是否支持该属性,然后通过VehicleHal进行设置,VehiclePropValue是在Hal层定义的一个数据类型。

    PropertyHalService.java
    
    public void setProperty(CarPropertyValue prop) {
        int halPropId = managerToHalPropId(prop.getPropertyId());
        if (halPropId == NOT_SUPPORTED_PROPERTY) {
            throw new IllegalArgumentException("Invalid property Id : 0x"
                    + toHexString(prop.getPropertyId()));
        }
        VehiclePropValue halProp = toVehiclePropValue(prop, halPropId);
        try {
            mVehicleHal.set(halProp);
        } catch (PropertyTimeoutException e) {
            Log.e(CarLog.TAG_PROPERTY, "set, property not ready 0x" + toHexString(halPropId), e);
            throw new RuntimeException(e);
        }
    }
    
    types.hal
    
    /**
     * Encapsulates the property name and the associated value. It
     * is used across various API calls to set values, get values or to register for
     * events.
     */
    struct VehiclePropValue {
        /** Time is elapsed nanoseconds since boot */
        int64_t timestamp;
    
        /**
         * Area type(s) for non-global property it must be one of the value from
         * VehicleArea* enums or 0 for global properties.
         */
        int32_t areaId;
    
        /** Property identifier */
        int32_t prop;
    
        /** Status of the property */
        VehiclePropertyStatus status;
    
        /**
         * Contains value for a single property. Depending on property data type of
         * this property (VehiclePropetyType) one field of this structure must be filled in.
         */
        struct RawValue {
            /**
             * This is used for properties of types VehiclePropertyType#INT
             * and VehiclePropertyType#INT_VEC
             */
            vec<int32_t> int32Values;
    
            /**
             * This is used for properties of types VehiclePropertyType#FLOAT
             * and VehiclePropertyType#FLOAT_VEC
             */
            vec<float> floatValues;
    
            /** This is used for properties of type VehiclePropertyType#INT64 */
            vec<int64_t> int64Values;
    
            /** This is used for properties of type VehiclePropertyType#BYTES */
            vec<uint8_t> bytes;
    
            /** This is used for properties of type VehiclePropertyType#STRING */
            string stringValue;
        };
    
        RawValue value;
    };
    

    VehicleHal为车辆HAL的抽象。此类处理与本机HAL的接口信息,并对接收到的数据进行基本分析(类型检查)。
    在这里的set方法中,使用的是HalClient的setValue方法。

    VehicleHal.java
    
    void set(VehiclePropValue propValue) throws PropertyTimeoutException {
        mHalClient.setValue(propValue);
    }
    

    HalClient为直接与车辆HAL接口交互的类。
    到此时,已经是与Hal层通过HIDL进行交互了,在HalClient实例化时,会传入一个Ivehicle对象,这个对象是在CarService创建时通过HIDL获取的对象,与Hal层进行数据传输。

    HalClient.java
    
    public void setValue(VehiclePropValue propValue) throws PropertyTimeoutException {
        int status = invokeRetriable(() -> {
            try {
                return mVehicle.set(propValue);
            } catch (RemoteException e) {
                Log.e(CarLog.TAG_HAL, "Failed to set value", e);
                return StatusCode.TRY_AGAIN;
            }
        }, WAIT_CAP_FOR_RETRIABLE_RESULT_MS, SLEEP_BETWEEN_RETRIABLE_INVOKES_MS);
    
        ……
    }
    

    如下方法为CarService通过HIDL的方式获取Hal层服务对象,获取的为Hal层VehicleService注册的服务。

    CarService.java
    
    @Nullable
    private static IVehicle getVehicle() {
        try {
            return android.hardware.automotive.vehicle.V2_0.IVehicle.getService();
        } catch (RemoteException e) {
            Log.e(CarLog.TAG_SERVICE, "Failed to get IVehicle service", e);
        } catch (NoSuchElementException e) {
            Log.e(CarLog.TAG_SERVICE, "IVehicle service not registered yet");
        }
        return null;
    }
    
    VehicleService.cpp
    
    ALOGI("Registering as service...");
    status_t status = service->registerAsService();
    

    在HalClient中调用了IVehicle的set方法,这个set方法的真正实现在EmulatedVehicleHal.cpp中

    IVehicle.hal
    
    /**
     * Set a vehicle property value.
     *
     * Timestamp of data must be ignored for set operation.
     *
     * Setting some properties require having initial state available. If initial
     * data is not available yet this call must return StatusCode::TRY_AGAIN.
     * For a property with separate power control this call must return
     * StatusCode::NOT_AVAILABLE error if property is not powered on.
     */
    set(VehiclePropValue propValue) generates (StatusCode status);
    
    EmulatedVehicleHal.cpp
    
    StatusCode EmulatedVehicleHal::set(const VehiclePropValue& propValue) {
        static constexpr bool shouldUpdateStatus = false;
    
        if (propValue.prop == kGenerateFakeDataControllingProperty) {
            StatusCode status = handleGenerateFakeDataRequest(propValue);
            if (status != StatusCode::OK) {
                return status;
            }
        } else if (mHvacPowerProps.count(propValue.prop)) {
            auto hvacPowerOn = mPropStore->readValueOrNull(
                toInt(VehicleProperty::HVAC_POWER_ON),
                (VehicleAreaSeat::ROW_1_LEFT | VehicleAreaSeat::ROW_1_RIGHT |
                 VehicleAreaSeat::ROW_2_LEFT | VehicleAreaSeat::ROW_2_CENTER |
                 VehicleAreaSeat::ROW_2_RIGHT));
    
            if (hvacPowerOn && hvacPowerOn->value.int32Values.size() == 1
                    && hvacPowerOn->value.int32Values[0] == 0) {
                return StatusCode::NOT_AVAILABLE;
            }
        } else {
            // Handle property specific code
            switch (propValue.prop) {
                case OBD2_FREEZE_FRAME_CLEAR:
                    return clearObd2FreezeFrames(propValue);
                case VEHICLE_MAP_SERVICE:
                    // Placeholder for future implementation of VMS property in the default hal. For
                    // now, just returns OK; otherwise, hal clients crash with property not supported.
                    return StatusCode::OK;
                case AP_POWER_STATE_REPORT:
                    // This property has different behavior between get/set.  When it is set, the value
                    //  goes to the vehicle but is NOT updated in the property store back to Android.
                    // Commented out for now, because it may mess up automated testing that use the
                    //  emulator interface.
                    // getEmulatorOrDie()->doSetValueFromClient(propValue);
                    return StatusCode::OK;
            }
        }
    
        if (propValue.status != VehiclePropertyStatus::AVAILABLE) {
            // Android side cannot set property status - this value is the
            // purview of the HAL implementation to reflect the state of
            // its underlying hardware
            return StatusCode::INVALID_ARG;
        }
        auto currentPropValue = mPropStore->readValueOrNull(propValue);
    
        if (currentPropValue == nullptr) {
            return StatusCode::INVALID_ARG;
        }
        if (currentPropValue->status != VehiclePropertyStatus::AVAILABLE) {
            // do not allow Android side to set() a disabled/error property
            return StatusCode::NOT_AVAILABLE;
        }
    
        if (!mPropStore->writeValue(propValue, shouldUpdateStatus)) {
            return StatusCode::INVALID_ARG;
        }
    
        getEmulatorOrDie()->doSetValueFromClient(propValue);
    
        return StatusCode::OK;
    }
    

    7.Hvac注册及监听属性信息

    在hardware的vehicle目录下,总共定义了3个hal文件:Ivehicle.hal, IvehicleCallback.hal和types.hal文件。在上面的一次设置过程中,我们可以看到在最后与hal层进行交互时调用的正是Ivehicle.hal中的set方法,而我们仅仅是通过CarService中以HIDL方式获取的Ivehicle服务来调用他,其中复杂的调用过程都由HIDL来完成,最终会调用到服务端实现该接口的方法中去,即EmulatedVehicleHal.cpp中的set方法。

    这三个文件对应的功能为:

    IVehicle.hal: 定义了服务端给客户端调用的的接口
    IVehicleCallback.hal: 定义了服务端回调客户端的接口
    types.hal: 定义了需要使用的数据结构
    

    在Hvac应用中,通过HvacController注册了CarHvacManager.CarHvacEventCallback,我们用这个回调函数来接收Hvac相关的属性改变。
    在CarHvacManager的registerCallback中,首先会通过传入的属性ID获取当前支持的属性列表(如果不支持,那么就在返回列表中就不会存在该属性),传输的ID列表mHvacPropertyIds为Hvac相关的属性ID,然后通过一个for循环,将可支持的属性ID都进行注册监听,并通过CarPropertyEventListenerToBase来接收属性变化。

    CarHvacManager.java
    
    public synchronized void registerCallback(CarHvacEventCallback callback)
            throws CarNotConnectedException {
        if (mCallbacks.isEmpty()) {
            mListenerToBase = new CarPropertyEventListenerToBase(this);
        }
        List<CarPropertyConfig> configs = getPropertyList();
        for (CarPropertyConfig c : configs) {
            // Register each individual propertyId
            mCarPropertyMgr.registerListener(mListenerToBase, c.getPropertyId(), 0);
        }
        mCallbacks.add(callback);
    }
    
    public List<CarPropertyConfig> getPropertyList() throws CarNotConnectedException {
        return mCarPropertyMgr.getPropertyList(mHvacPropertyIds);
    }
    

    在CarPropertyManager中进行简单的判断后调用CarPropertyService中的registerListener方法;

    CarPropertyManager.java
    
    public boolean registerListener(CarPropertyEventListener listener, int propertyId, float rate)
            throws CarNotConnectedException {
        synchronized (mActivePropertyListener) {
            if (mCarPropertyEventToService == null) {
                mCarPropertyEventToService = new CarPropertyEventListenerToService(this);
            }
            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(listener, rate)) {
                needsServerUpdate = true;
            }
            if (needsServerUpdate) {
                if (!registerOrUpdatePropertyListener(propertyId, rate)) {
                    return false;
                }
            }
        }
        return true;
    }
    
    private boolean registerOrUpdatePropertyListener(int propertyId, float rate)
            throws CarNotConnectedException {
        try {
            mService.registerListener(propertyId, rate, mCarPropertyEventToService);
        } catch (IllegalStateException e) {
            CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
        } catch (RemoteException e) {
            throw new CarNotConnectedException(e);
        }
        return true;
    }
    

    在CarPropertyService的registerListener方法中,会判断当前要监听的属性是否已经监听,如果没有监听则会调用PropertyHalService中的subscribeProperty方法来监听该属性的变化;

    CarPropertyService.java
    
    @Override
    public void registerListener(int propId, float rate, ICarPropertyEventListener listener) {
        ……
        IBinder listenerBinder = listener.asBinder();
        synchronized (mLock) {
            // Get the client for this listener
            Client client = mClientMap.get(listenerBinder);
            if (client == null) {
                client = new Client(listener);
            }
            client.addProperty(propId, rate);
            // Insert the client into the propId --> clients map
            List<Client> clients = mPropIdClientMap.get(propId);
            if (clients == null) {
                clients = new CopyOnWriteArrayList<Client>();
                mPropIdClientMap.put(propId, clients);
            }
            if (!clients.contains(client)) {
                clients.add(client);
            }
            // Set the HAL listener if necessary
            if (!mListenerIsSet) {
                mHal.setListener(this);
            }
            // Set the new rate
            if (rate > mHal.getSampleRate(propId)) {
                mHal.subscribeProperty(propId, rate);
            }
        }
        ……
    }
    

    在PropertyHalService中,进行简单处理进入到VehicleHal的subscribePropert的方法;

    PropertyHalService.java
    
    public void subscribeProperty(int propId, float rate) {
        ……
        synchronized (mSubscribedPropIds) {
            mSubscribedPropIds.add(halPropId);
        }
        mVehicleHal.subscribeProperty(this, halPropId, rate);
    }
    

    在VehicleHal的subscribeProperty方法中,判断当前要注册监听的属性是否是可注册的(即该属性是否可读或者是可更改的),然后进入HalClient的subscribe方法进行注册;

    VehicleHal.java
    
    public void subscribeProperty(HalServiceBase service, int property,
            float samplingRateHz, int flags) throws IllegalArgumentException {
        ……
        if (config == null) {
            throw new IllegalArgumentException("subscribe error: config is null for property 0x" +
                    toHexString(property));
        } else if (isPropertySubscribable(config)) {
            SubscribeOptions opts = new SubscribeOptions();
            opts.propId = property;
            opts.sampleRate = samplingRateHz;
            opts.flags = flags;
            synchronized (this) {
                assertServiceOwnerLocked(service, property);
                mSubscribedProperties.put(property, opts);
            }
            try {
                mHalClient.subscribe(opts);
            } catch (RemoteException e) {
                Log.e(CarLog.TAG_HAL, "Failed to subscribe to property: 0x" + property, e);
            }
        } else {
            Log.e(CarLog.TAG_HAL, "Cannot subscribe to property: " + property);
        }
    }
    

    在HalClient中,属性合法性问题都在之前进行了处理,这里直接通过Ivehicle获得的服务端方法进行注册,之后会通过HIDL调用到EmulatedVehicleHal的subscribe方法,在该方法中除了传入需要监听的属性之外,还传入了一个回调对象VehicleCallback。VehicleCallback继承自IVehicleCallback.Stub并实现了该类中的相关方法,当hal服务端属性信息变化时,会通过该回调把信息传回来,客户端会做出相应的处理。

    HalClient.java
    
    public void subscribe(SubscribeOptions... options) throws RemoteException {
        mVehicle.subscribe(mInternalCallback, new ArrayList<>(Arrays.asList(options)));
    }
    

    至此,Car的属性注册及监听过程结束。

    8.Car获取CarHvacManager

    Car获取Car相关的manager都是通过传入要获取manager的名称,类似于getSystemService的方法,然后会返回相关的manager
    获取manager是通过CarServiceLoaderEmbedded中的getCarManager方法

    Car.java
    
    public Object getCarManager(String serviceName)
            throws CarNotConnectedException {
        Object manager = null;
        synchronized (mCarManagerLock) {
            manager = mServiceMap.get(serviceName);
            if (manager == null) {
                manager = mCarServiceLoader.getCarManager(serviceName);
            }
            // do not store if it is not CarManagerBase. This can happen when system version
            // is retrieved from this call.
            if (manager != null && manager instanceof CarManagerBase) {
                mServiceMap.put(serviceName, (CarManagerBase) manager);
            }
        }
        return manager;
    }
    

    这里的mEmbeddedCar为包android.car下面的Car,而我们之前使用的Car为android.support.car下面的Car;

    CarServiceLoaderEmbedded.java
    
    public Object getCarManager(String serviceName) throws CarNotConnectedException {
        Object manager;
        try {
            manager = mEmbeddedCar.getCarManager(serviceName);
        } catch (android.car.CarNotConnectedException e) {
            throw new CarNotConnectedException(e);
        }
    ……
            default:
                return manager;
        }
    }
    

    通过传入的manager名称来获取服务,然后为该服务创建一个manager并返回给请求者
    其中ICar的getCarService的实现在ICarImpl中;

    Car.java
    
    public Object getCarManager(String serviceName) throws CarNotConnectedException {
        CarManagerBase manager;
        ICar service = getICarOrThrow();
        synchronized (mCarManagerLock) {
            manager = mServiceMap.get(serviceName);
            if (manager == null) {
                try {
                    IBinder binder = service.getCarService(serviceName);
                    ……
                    manager = createCarManager(serviceName, binder);
                    ……
                    mServiceMap.put(serviceName, manager);
                } catch (RemoteException e) {
                    handleRemoteException(e);
                }
            }
        }
        return manager;
    }
    

    通过获取的服务新建manager实例,然后返回;

    Car.java
    
    private CarManagerBase createCarManager(String serviceName, IBinder binder)
            throws CarNotConnectedException {
        CarManagerBase manager = null;
        switch (serviceName) {
            ……
            case HVAC_SERVICE:
                manager = new CarHvacManager(binder, mContext, mEventHandler);
                break;
            ……
            default:
                break;
        }
        return manager;
    }
    

    可以看到,在请求舱室,暖通空调,信息,属性,传感器以及三方拓展服务是返回的都是CarPropertyService服务

    IcarImpl.java
    
    @Override
    public IBinder getCarService(String serviceName) {
        switch (serviceName) {
            case Car.AUDIO_SERVICE:
                return mCarAudioService;
            ……
            case Car.CABIN_SERVICE:
            case Car.HVAC_SERVICE:
            case Car.INFO_SERVICE:
            case Car.PROPERTY_SERVICE:
            case Car.SENSOR_SERVICE:
            case Car.VENDOR_EXTENSION_SERVICE:
                return mCarPropertyService;
            ……
            default:
                Log.w(CarLog.TAG_SERVICE, "getCarService for unknown service:" + serviceName);
                return null;
        }
    }
    

    至此,获取manager的过程结束。

    9.Car获取CarServie

    这里的Car为android.car包下的Car
    在CarService中定义了action

    Car.java
    
    public static final String CAR_SERVICE_INTERFACE_NAME = "android.car.ICar";
    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);
        ……
    }
    
    AndroidManifest.xml
    
    <service android:name=".CarService"  
        android:singleUser="true">
        <intent-filter>
            <action android:name="android.car.ICar" />
        </intent-filter>
    </service>
    

    10.与Hal交互的数据类型

    在Hal层定义了一系列的属性值,都放在了types.hal文件中。
    如座椅属性与Hvac中传入的VehicleAreaSeat.java中的座椅属性,两者的值是相同的

    types.hal
    
    /**
        * Various Seats in the car.
        */
    enum VehicleAreaSeat : int32_t {
        ROW_1_LEFT   = 0x0001,
        ROW_1_CENTER = 0x0002,
        ROW_1_RIGHT  = 0x0004,
        ROW_2_LEFT   = 0x0010,
        ROW_2_CENTER = 0x0020,
        ROW_2_RIGHT  = 0x0040,
        ROW_3_LEFT   = 0x0100,
        ROW_3_CENTER = 0x0200,
        ROW_3_RIGHT  = 0x0400
    };
    
    
    VehicleAreaSeat.java
    
    public static final int SEAT_ROW_1_LEFT = 0x0001;
    public static final int SEAT_ROW_1_CENTER = 0x0002;
    public static final int SEAT_ROW_1_RIGHT = 0x0004;
    public static final int SEAT_ROW_2_LEFT = 0x0010;
    public static final int SEAT_ROW_2_CENTER = 0x0020;
    public static final int SEAT_ROW_2_RIGHT = 0x0040;
    public static final int SEAT_ROW_3_LEFT = 0x0100;
    public static final int SEAT_ROW_3_CENTER = 0x0200;
    public static final int SEAT_ROW_3_RIGHT = 0x0400;
    

    11.车机属性与权限

    在设置Hvac时,用到了暖通空调相关的属性。

    在车机中,与PropertyHalService有关的属性可分为4种舱室属性(车门,车镜,座椅等),HVAC属性(空调,风扇等),信息属性(制造商,型号,ID等)以及传感器属性(车速,油量等),与修改这些属性需要的相关权限都被定义在PropertyHalServiceIds文件中。

    PropertyHalServiceIds.java
    
    // Index (key is propertyId, and the value is readPermission, writePermission
    private final SparseArray<Pair<String, String>> mProps;
    // HVAC properties
    mProps.put(VehicleProperty.HVAC_FAN_SPEED, new Pair<>(
                Car.PERMISSION_CONTROL_CAR_CLIMATE,
                Car.PERMISSION_CONTROL_CAR_CLIMATE));
    mProps.put(VehicleProperty.HVAC_FAN_DIRECTION, new Pair<>(
                Car.PERMISSION_CONTROL_CAR_CLIMATE,
                Car.PERMISSION_CONTROL_CAR_CLIMATE));
    mProps.put(VehicleProperty.HVAC_TEMPERATURE_CURRENT, new Pair<>(
                Car.PERMISSION_CONTROL_CAR_CLIMATE,
                Car.PERMISSION_CONTROL_CAR_CLIMATE));
    mProps.put(VehicleProperty.HVAC_TEMPERATURE_SET, new Pair<>(
                Car.PERMISSION_CONTROL_CAR_CLIMATE,
                Car.PERMISSION_CONTROL_CAR_CLIMATE));
    mProps.put(VehicleProperty.HVAC_DEFROSTER, new Pair<>(
                Car.PERMISSION_CONTROL_CAR_CLIMATE,
                Car.PERMISSION_CONTROL_CAR_CLIMATE));
    

    12.CarInfoManager

    用于获取车辆固定属性信息的工具,这些信息都是不可变的。
    获取这些信息都是通过CarPropertyService获得的
    信息有:

    BASIC_INFO_KEY_MANUFACTURER //制造商
    BASIC_INFO_KEY_MODEL //型号
    BASIC_INFO_KEY_MODEL_YEAR //
    BASIC_INFO_KEY_VEHICLE_ID //车机ID
    INFO_KEY_PRODUCT_CONFIGURATION //配置相关
    BASIC_INFO_FUEL_CAPACITY //油箱容量
    BASIC_INFO_FUEL_TYPES //燃油类型
    BASIC_INFO_EV_BATTERY_CAPACITY //电源容量
    BASIC_INFO_EV_CONNECTOR_TYPES //电源连接类型
    

    13.CarPropertyService

    CarPropertyService 实现了IcarProperty.aidl接口,控制各manager对车机属性的注册,监听以及读写。当属性发生变化时,回调给注册监听了该属性的manager,然后继续向上传递,使得各属性manager方便的处理车机相关属性。该类对车机属性的处理是通过PropertyHalService进行的,而在PropertyHalService中的操作为VehicleHal。

    14.PropertyHalService

    PropertyHalService是汽车HAL用来传输车辆属性的服务。PropertyHalService是继承自HalServiceBase,继承HalServiceBase的还有InputHalService, SensorHalService,PowerHalService以及VmsHalService。
    PropertyHalService为CarPropertyService提供对各属性的操作。

    15.HIDL

    一次使用HIDL实现简单的HAL通信过程

    16.HAL

    HAL简介

    HAL开发步骤

    17.Treble & HIDL

    讲解Treble & HIDL源代码及相关实现过程

    相关文章

      网友评论

        本文标题:Android Automotive

        本文链接:https://www.haomeiwen.com/subject/ixlrfctx.html