current position:Home>Systemui vehicle HVAC development

Systemui vehicle HVAC development

2022-01-27 03:44:10 Yiling Xiaozu

Architecture Overview

remarks : The following analysis is based on Android 9.0

SystemUI meaning System interface , According to the original responsibilities , On the car SystemUI More is to provide general functions ( And mobile phones ) Display of the state of , Such as the display of time and electricity in the status bar , The navigation bar provides jump entry for each function page , That's it , As for others, such as HVAC And other hardware related control , They are all assigned to the corresponding module .

HVAC yes Heating, Ventilation and Air Conditioning English abbreviations , Heating, ventilation and air conditioning , and On board pair HVAC The control of related functions is put into an independent system apk - CarHvacApp Inside to do , By means of SystemUI Function entry is provided in the navigation bar of to realize jump

as follows , Most of the buttons in the native navigation bar are jump entries for other function modules , among HVAC The jump page of the button is a HVAC Management panel , The panel can be set such as seat temperature , Switch of front and rear windshield demister , The temperature of the driver and passenger, etc

hvac_panel.png

However, in many cases, manufacturers need to directly provide access to some hardware related functions in the navigation bar , For example, operate the seat heating function directly in the navigation bar . and Google has also provided support for this potential expansion when developing , That is, by encapsulating the operation of hardware related modules into general purpose modules lib library , Allow the required application to directly reference the package

and these lib The library encapsulates the underlying HAL modular ( Hardware abstraction layer module ) Call details for , And at the bottom of the HAL The module contains the specific implementation details of the corresponding hardware , Let's take a look at its architecture

sysui_car_arch.png

The general hardware development process is like this :

  • 1 . stay The kernel layer develops hardware drivers
  • 2 . stay Hardware abstraction layer (HAL) Develop the corresponding hardware abstraction layer module
    • 2.1 explain : The hardware abstraction layer manages various hardware access interfaces in the form of modules , Each hardware module has a dynamic link library file , every last hal Each module has a driver in the kernel ,hal Modules access hardware devices through these drivers , They communicate by reading and writing device files
  • 3 . stay framework Layer development hardware access services
  • 4 . The upper layer visitors of the hardware operate the hardware indirectly by accessing the hardware access service

Interested in learning about HAL You can see Luo Shengyang's 《Android System source code scenario analysis 》 This book , meanwhile Android 8.0 Then Google provided HIDL Language to facilitate HAL Module development , You can see this Android HIDL HAL Detailed explanation of interface definition language

Corresponding to the above hardware development process , Let's briefly describe the architecture diagram , among :

    1. For the driver layer , The corresponding driver file has not been found yet , Skip first
    1. hardware/interfaces/automotive/vehicle/2.0/IVehicle.hal Corresponding to the hardware abstraction layer module , The corresponding implementation is in hardware/interfaces/automotive/vehicle/2.0/default/impl/vhal_v2_0 Next , Its core implementation is to call the driver interface function open / read / write To open the device file / read / Write So as to realize the interaction with hardware devices .
    1. car-lib Corresponding to hardware access service , Its interior encapsulates a pair of HAL Layer call , For example, for HVAC Hardware interface ,car-lib Class is provided externally CarHvacManager, The upper visitors can query directly through this kind of object / Set up HVAC Related properties , The following is an introduction
    1. CarHvacApp And SystemUI Such applications correspond to Hardware upper layer visitor , The interaction with hardware is realized by accessing hardware access services

After understanding the architecture , We can know ,CarHvacApp / SystemUI And other applications actually correspond to the hardware upper layer visitors in the architecture , Our development work is to find the hardware access service corresponding to the hardware , When the user operates, our interface is to feed back the operation to the hardware access service , The underlying implementation may not care

Here are the introduction :

  • 1.CarHvacApp Middle and bottom Hvac Interaction principle of hardware
  • 2.SystemUI Middle and bottom Hvac Hardware interaction principle and extended implementation

CarHvacApp Interacting with hardware

Let's take a look CarHvacApp How does this application initialize HVAC panel as well as In user operation HVAC Panel time , How to feed back the user operation results to the underlying hardware

We said earlier , Google will HVAC The control of related functions is put into a separate system apk - CarHvacApp in , Code is located packags/apps/Car/Havc , The composition of the subject is very simple , look down manifest file

[packages/apps/Car/Hvac/AndroidManifest.xml]

    <application android:label="@string/hvac_label"
        android:icon="@drawable/ic_launcher_hvac"
        android:persistent="true">
        <service android:name=".HvacController"
            android:singleUser="true"
            android:exported="false" />

        <service android:name=".HvacUiService"
            android:singleUser="true"
            android:exported="false"/>

        <receiver android:name=".BootCompleteReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>
    </application>
 Copy code 

It consists of only two services and a boot broadcast , The startup broadcast is responsible for starting the service when starting up HvacUiService

HvacUiService

[packages/apps/Car/Hvac/src/com/android/car/hvac/HvacUiService.java]
public class HvacUiService extends Service {}
 Copy code 

The service mainly does the following things :

  • 1. stay layoutHvacUi() Method HVAC Construction of panel , The page consists of five windows , All window types are WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY, Will cover sysui above , Namely
    • 1.1 Seat heating in the middle of the interface , Window composed of front and rear windshield demist switches and other elements , Corresponding layout hvac_panel
    • 1.2 The left driver's temperature control bar window , Corresponding layout hvac_temperature_bar_overlay, The same below
    • 1.3 The driver's temperature control strip on the left is stowed (Collapsed) window , Through configuration in the code config_showCollapsedBars Field to control whether the window is displayed , The default is false, In fact, this window is to provide a non fully retracted temperature control bar when the control panel is retracted, so as to adjust the control panel
    • 1.4 The front passenger's temperature control bar window on the right
    • 1.5 The front passenger's temperature control strip on the right is stowed (Collapsed) window , ditto

hvac_panel.png

  • 2. Register to listen to the broadcast android.car.intent.action.TOGGLE_HVAC_CONTROLS, follow-up sysui This broadcast will be used to inform the service of the deployment / Retract HVAC panel
    @Override
    public void onCreate() { 
        ......
        IntentFilter filter = new IntentFilter();
        filter.addAction(CAR_INTENT_ACTION_TOGGLE_HVAC_CONTROLS);
        // Register receiver such that any user with climate control permission can call it.
        registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter,
                Car.PERMISSION_CONTROL_CAR_CLIMATE, null);
    }
 Copy code 
  • 3. establish HvacPanelController object , The five windows constructed above correspond to View Pass in
    private void layoutHvacUi() {
        ......
        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.");
        }
    }
 Copy code 
  • 4. Binding services HvacController, And when the binding is successful, the service object HvacController Give to the HvacPanelController, Here you can see , be relative to sysui,HvacUiService Server side , As opposed to HvacUiService,HvacController It's the server

HvacPanelController

[packages/apps/Car/Hvac/src/com/android/car/hvac/controllers/HvacPanelController.java]
public class HvacPanelController {}
 Copy code 

The controller is a HVAC UI Layout element state machine , Responsible for the whole HVAC panel UI Element initialization , Respond to the events of each layout element ( The user clicks & Changes in internal hardware properties of the system ) And perform state switching ( If there is a vanishing animation )

This is about HvacUiService When we saw ,HvacUiService After creating each window , Put the of each window View The object came in , and HvacPanelController Then further study these View Object , Let's take a look at the main responsibilities of this class :

  • 1. Take out each sub element in the construction method (findViewById), And set its Icon resources
  • 2. It was said that , When HvacUiService Successfully bind the service HvacController after , Will HvacController The service object is passed into HvacPanelController, here HvacPanelController Can construct various sub elements View The corresponding controller , Go to HvacController Get the initial state of each child element and set it to each child element , And set the click event of each sub element
public void updateHvacController(HvacController controller) {
        //  Construct various sub elements View The corresponding controller 
        mFanSpeedBarController = new FanSpeedBarController(mFanSpeedBar, mHvacController);
        mSeatWarmerController = new SeatWarmerController(mPassengerSeatWarmer,
                mDriverSeatWarmer, mHvacController);
        ......
        //  Initialization button status , And set the click event 
        mAcButton.setIsOn(mHvacController.getAcState());
        mAcButton.setToggleListener(new ToggleButton.ToggleListener() {
            @Override
            public void onToggled(boolean isOn) {
                mHvacController.setAcState(isOn);
            }
        });
        //  Register callback 
        mHvacController.registerCallback(mToggleButtonCallbacks);
    }
 Copy code 
  • 3. be responsible for View Of show/hide as well as Execution of animation (transition), Described above HvacUiService It's time 2 Point we said ,HvacUiService Will listen to a broadcast to start / Retract HVAC panel , The logic is as follows , among transitionState Is to finally unfold / Where the panel is stowed
[packages/apps/Car/Hvac/src/com/android/car/hvac/HvacUiService.java]
    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if(action.equals(CAR_INTENT_ACTION_TOGGLE_HVAC_CONTROLS)){
                mHvacPanelController.toggleHvacUi();
            }
        }
    };

[packages/apps/Car/Hvac/src/com/android/car/hvac/controllers/HvacPanelController.java]
    public void toggleHvacUi() {
        if(mCurrentState != STATE_COLLAPSED) {
            mCollapseHvac.onClick(null);
        } else {
            mExpandHvac.onClick(null);
        }
    }

    public View.OnClickListener mExpandHvac = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
             if (mInAnimation) {
                return;
            }
            if (mCurrentState != STATE_FULL_EXPANDED) {
                transitionState(mCurrentState, STATE_FULL_EXPANDED);
            }
        }
    };
 Copy code 

car-lib library

When we introduced the architecture earlier, we said ,car-lib Corresponding to hardware access service , Its interior encapsulates a pair of HAL Layer call , For example, for HVAC Hardware interface ,car-lib Class is provided externally CarHvacManager, The upper visitors can query directly through this kind of object / Set up HVAC Related properties

Introduce HvacController Let's have a look at car-lib Two key classes under the library

[packages/services/Car/car-lib/src/android/car/Car.java]
//  Source code description of this class : in the light of Android The top of vehicle development car API
public final class Car {
    // 1. Access to external hardware access services 
    public static Car createCar(Context context, ServiceConnection serviceConnectionListener,@Nullable Handler handler) {
          return new Car(context, serviceConnectionListener, handler);
    }
    // 2. Cached  mServiceConnectionListenerClient  We'll use that later 
    private Car(Context context, ServiceConnection serviceConnectionListener,@Nullable Handler handler) {
        mServiceConnectionListenerClient = serviceConnectionListener;
    }
    // 3. When this function is called, it will trigger  startCarService  To create  CarService service 
    public void connect() throws IllegalStateException {
            startCarService();
    }

    private void startCarService() {
        // 4.intent  Yes CarService service , The real implementation of this service is ICarImpl in , Not here 
        boolean bound = mContext.bindServiceAsUser(intent, mServiceConnectionListener,Context.BIND_AUTO_CREATE, UserHandle.CURRENT_OR_SELF);
    }

    private final ServiceConnection mServiceConnectionListener = new ServiceConnection () {
        public void onServiceConnected(ComponentName name, IBinder service) {
            synchronized (Car.this) {
                mService = ICar.Stub.asInterface(service);
                mConnectionState = STATE_CONNECTED;
            }
            // 5. Notify when the connection is successful  mServiceConnectionListenerClient,  Namely front createCar The service connection listener passed in when ,
           //  In turn, upper level visitors can begin to get information such as  CarHvacManager  Objects such as 
            mServiceConnectionListenerClient.onServiceConnected(name, service);
        }
    };
}
 Copy code 

The above first 5 Point to point connection CarService When successful, the caller will be notified , It's meant to tell the caller that you can get something like CarHvacManager Wait for the object , Because such as CarHvacManager The acquisition of such objects depends on CarService.

meanwhile Car Class can not only get CarHvacManager object , You can also get Android Car Develop various management classes related to , Such as CarAudioManager,CarSensorManager etc. , See it createCarManager function

Whole Car The main function is

  • 1. As an entrance for the outside world to obtain hardware access services , Upper callers can use createCar To get one Car object
  • 2. When the caller calls its connect Function to connect CarService service , This service is a bridge connecting the vehicle bottom module , And notify the caller when the connection is successful ( adopt ServiceConnection)
  • 3. Provide access for developers to get the required Manager, such as HVAC Required in the module CarHvacManager, This is the entry to the related hardware attributes of the caller's subsequent operations

Let's see below. CarHvacManager The function of

CarHvacManager A data structure is used , This is a data structure that can be transmitted across processes , Encapsulates vehicle related attributes 
[packages/services/Car/car-lib/src/android/car/hardware/CarPropertyValue.java]
public class CarPropertyValue<T> implements Parcelable {
    private final int mPropertyId;
    private final int mAreaId;
    private final int mStatus;
    private final long mTimestamp;
    private final T mValue;
}


[packages/services/Car/car-lib/src/android/car/hardware/hvac/CarHvacManager.java]
//  Source code description of this class : Used to control the HVAC Systematic API
public final class CarHvacManager implements CarManagerBase {
    //  step 1 :  Defines a series of constants , Used to correspond to each area of the vehicle (zone) Properties of 
    //  Air conditioning temperature 
    public static final int ID_ZONED_TEMP_SETPOINT = 0x15600503;
    //  Seat temperature , Negative value refrigeration , Positive heating ,
    public static final int ID_ZONED_SEAT_TEMP = 0x1540050b;
    //  Mirror demister , Boolean value 
    public static final int ID_MIRROR_DEFROSTER_ON = 0x1440050c;
    //  Window demister , Boolean value 
    public static final int ID_WINDOW_DEFROSTER_ON = 0x13200504;
    
    //  step 2 :  Callback   You can register this callback to listen to each  CarPropertyValue  The change of 
    public interface CarHvacEventCallback {
        void onChangeEvent(CarPropertyValue value);
    }
    public synchronized void registerCallback(CarHvacEventCallback callback) {}
    public synchronized void unregisterCallback(CarHvacEventCallback callback) {}

    //  step 3 :  Provide support for each  HVAC  Attribute   State query   And   Status setting interface 
    public boolean getBooleanProperty(@PropertyId int propertyId, int area)
            throws CarNotConnectedException {
        return mCarPropertyMgr.getBooleanProperty(propertyId, area);
    }

    public float getFloatProperty(@PropertyId int propertyId, int area)
            throws CarNotConnectedException {
        return mCarPropertyMgr.getFloatProperty(propertyId, area);
    }
}
 Copy code 

First, it encapsulates a serializable class CarPropertyValue, It corresponds to a vehicle attribute , A vehicle attribute is mainly composed of two parts :[propertyId, area]

  • propertyId Defines the vehicle attributes , Such as air conditioning temperature , Seat temperature, etc , The specific value is defined in CarHvacManager Class , See step 1
  • area Defines the area corresponding to the attribute , Because the same attribute will have multiple positions to control , Like seat temperature , The temperature of the driver's seat , Passenger seat temperature , Therefore, this value is used to distinguish different positions of the same attribute .

and CarHvacManager I did the following three things

  • step 1 : Define vehicle attributes
  • step 2 : Provide CarHvacEventCallback Callback and its registration interface , It will monitor the state changes of the underlying hardware , In order to tell the upper callers when the underlying hardware properties change , As one can imagine , This is an interface that we need to pay attention to when developing
  • step 3 : Provide query / Upper layer interface for setting hardware properties , Again This is also an interface we need to pay attention to when developing

Both classes are located in packages/services/Car/car-lib Next , Let's take a look at it mk file

[packages/services/Car/car-lib/Android.mk]

...
LOCAL_MODULE := android.car
...
 Copy code 

That is, the module will be compiled into a shared library android.car, Can be referenced by other applications , for example CarHvacApp This library is referenced in

[packages/apps/Car/Hvac/Android.mk]

LOCAL_JAVA_LIBRARIES += android.car
 Copy code 

HvacController

Back to the front CarHvacApp Analysis of , Described above HvacPanelController The first 2 Point in point updateHvacController() Method, we see ,HvacController The object will be passed into various sub elements View In the corresponding controller , At the same time, it is also to obtain / Set various sub elements View State of place , Obviously , This is a master controller , At the same time, the status of each switch is maintained

Let's see what the main responsibilities of this class are

  • 1. When the service starts, use car-lib Library to create a Car Object and call it connect() function , And when the connection is successful, obtain a CarHvacManager object
[packages/apps/Car/Hvac/src/com/android/car/hvac/HvacController.java]
public class HvacController extends Service {
    @Override
    public void onCreate() {
        super.onCreate();
        if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
            mCarApiClient = Car.createCar(this, mCarConnectionCallback);
            mCarApiClient.connect();
        }
    }

    private final CarConnectionCallback mCarConnectionCallback = new CarConnectionCallback() {
                @Override
                public void onConnected(Car car) {
                    synchronized (mHvacManagerReady) {
                        try {
                            initHvacManager((CarHvacManager) mCarApiClient.getCarManager(android.car.Car.HVAC_SERVICE));
                            mHvacManagerReady.notifyAll();
                            ......
                    }

    private void initHvacManager(CarHvacManager carHvacManager) {
        mHvacManager = carHvacManager;
        List<CarPropertyConfig> properties = null;
        try {
            properties = mHvacManager.getPropertyList();
            mPolicy = new HvacPolicy(HvacController.this, properties);
            mHvacManager.registerCallback(mHardwareCallback);
        }
    }
}
 Copy code 
    1. Provide interface for query / Set up various HVAC The status value of the switch , Here is the use of CarHvacManager To get the status value and save it to mDataStore Medium , And the use of CarHvacManager Inform the underlying hardware of attribute changes
    public boolean getAirCirculationState() {
        return mDataStore.getAirCirculationState();
    }

    public void setFanDirection(final int direction) {
        mDataStore.setAirflow(SEAT_ALL, direction);
        setFanDirection(SEAT_ALL, direction);
    }
 Copy code 
  • 3. towards CarHvacManager Register a listener to listen for hardware status changes , And inform each child element to do UI Refresh
    private void initHvacManager(CarHvacManager carHvacManager) {
          ......
          mHvacManager.registerCallback(mHardwareCallback);
          ......
    }

    private final CarHvacManager.CarHvacEventCallback mHardwareCallback =
            new CarHvacManager.CarHvacEventCallback() {
                @Override
                public void onChangeEvent(final CarPropertyValue val) {
                    int areaId = val.getAreaId();
                    switch (val.getPropertyId()) {
                        case CarHvacManager.ID_ZONED_AC_ON:
                            handleAcStateUpdate(getValue(val));
                            break;
                        ......
                    }
                }
};
 Copy code 

In summary ,CarHvacApp utilize CarHvacManager Completed the HVAC Hardware status query , Status setting and status monitoring

SystemUI Interacting with hardware

Actually, I understand the above CarHvacApp Interacting with hardware after , The content of this section is very simple , Follow CarHvacApp equally , It's nothing more than using car-lib Library to get Car object , Connection service , obtain CarHvacManager object , Query hardware status , The user action UI Set hardware status when , Monitor internal changes in hardware

Now let's focus on " How to be in SystemUI Implement... In the navigation bar Directly operate the seat heating function " This purpose , Let's take a look at the implementation :

  • 1. Import car-lib library ( Native imported )
[frameworks/base/packages/SystemUI/Android.mk]

LOCAL_JAVA_LIBRARIES := android.car
 Copy code 
  • 2. obtain Car object , Connection service , obtain CarHvacManager object , Simultaneous direction CarHvacManager Register to monitor internal changes in hardware
[frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java]
    public void start() {
            Dependency.get(HvacController.class).connectToCarService();
    }

[frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java]
    public void connectToCarService() {
        //  obtain  Car  object 
        mCar = Car.createCar(mContext, mServiceConnection, mHandler);
        if (mCar != null) {
            //  Connection service 
            mCar.connect();
        }
    }

    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            try {
                //  obtain  CarHvacManager  object 
                mHvacManager = (CarHvacManager) mCar.getCarManager(Car.HVAC_SERVICE);
                //  Register to monitor internal changes in hardware 
                mHvacManager.registerCallback(mHardwareCallback);
            }
        }
    };
 Copy code 

The code is simple , Just look at the notes , among HvacController It is provided natively for processing and Hvac A class of related things , But will be right Hvac Extensions of related functions are put into this class

Original in SystemUI What we do in the is similar to Hvac There are few hardware related operations , There is only one logic to query the air conditioning temperature , The process is simple , It is obtained when the above connection service is successful CarHvacManager Object to query the temperature status , And use to CarHvacManager Registered callbacks monitor temperature changes

    private void initComponent(TemperatureView view) {
        int id = view.getPropertyId();
        int zone = view.getAreaId();
        //  transfer  CarHvacManager  Of  getFloatProperty  To query the temperature at the specified location 
        view.setTemp(mHvacManager.getFloatProperty(id, zone));
    }

    private final CarHvacEventCallback mHardwareCallback = new CarHvacEventCallback() {
        @Override
        public void onChangeEvent(final CarPropertyValue val) {
            try {
                //  When the temperature attribute changes , Refresh UI
                int areaId = val.getAreaId();
                int propertyId = val.getPropertyId();
                List<TemperatureView> temperatureViews = mTempComponents.get(new HvacKey(propertyId, areaId));
                if (temperatureViews != null && !temperatureViews.isEmpty()) {
                    float value = (float) val.getValue();
                    for (TemperatureView tempView : temperatureViews) {
                        tempView.setTemp(value);
                    }
                }
        }
 Copy code 

And we want to achieve " stay SystemUI Implement... In the navigation bar Directly operate the seat heating function ", Just make a simple extension :

  • 1.UI structure
  • 2. stay HvacController Added seat heating status in Inquire about / Set up Interface , Such as
    private static final int DRIVER_ZONE_ID = VehicleAreaSeat.SEAT_ROW_1_LEFT |
            VehicleAreaSeat.SEAT_ROW_2_LEFT | VehicleAreaSeat.SEAT_ROW_2_CENTER;

    public float getDriverZoneTemperature() {
        if (mHvacManager != null) {
            try {
                return mHvacManager.getFloatProperty(CarHvacManager.ID_ZONED_TEMP_SETPOINT, DRIVER_ZONE_ID);
            }
        }
        return -1;
    }

    public void setDriverSeatWarmerLevel(int level) {
        setSeatWarmerLevel(DRIVER_ZONE_ID, level);
    }

    public void setSeatWarmerLevel(final int zone, final int level) {
        final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
            protected Void doInBackground(Void... unused) {
                if (mHvacManager != null) {
                    try {
                        mHvacManager.setIntProperty(CarHvacManager.ID_ZONED_SEAT_TEMP, zone, level);
                    }
                }
                return null;
            }
        };
        task.execute();
    }
 Copy code 
  • 3. In the CarHvacManager Registered listener callback New processing of seat heating status
    private final CarHvacEventCallback mHardwareCallback = new CarHvacEventCallback() {
        @Override
        public void onChangeEvent(final CarPropertyValue val) {
            try {
                int areaId = val.getAreaId();
                int propertyId = val.getPropertyId();
                switch (propertyId) {
                    case CarHvacManager.ID_ZONED_SEAT_TEMP:
                        handleSeatWarmerUpdate(areaId, getValue(val));
                        break;
                }
        }
 Copy code 

thus , We have realized this function , When expanding other functions, just do the same

copyright notice
author[Yiling Xiaozu],Please bring the original link to reprint, thank you.
https://en.cdmana.com/2022/01/202201270343570513.html

Random recommended