Home | History | Annotate | Download | only in hvac
      1 /*
      2  * Copyright (c) 2016, The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *     http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.car.hvac;
     17 
     18 import android.app.Service;
     19 import android.car.VehicleSeat;
     20 import android.car.VehicleWindow;
     21 import android.car.VehicleZone;
     22 import android.car.hardware.CarPropertyConfig;
     23 import android.car.hardware.CarPropertyValue;
     24 import android.car.hardware.hvac.CarHvacManager;
     25 import android.content.Intent;
     26 import android.content.pm.PackageManager;
     27 import android.os.AsyncTask;
     28 import android.os.Binder;
     29 import android.os.Handler;
     30 import android.os.IBinder;
     31 import android.os.SystemProperties;
     32 import android.support.car.Car;
     33 import android.support.car.CarNotConnectedException;
     34 import android.support.car.CarConnectionCallback;
     35 import android.util.Log;
     36 
     37 import java.util.ArrayList;
     38 import java.util.List;
     39 
     40 import javax.annotation.concurrent.GuardedBy;
     41 
     42 public class HvacController extends Service {
     43     private static final String DEMO_MODE_PROPERTY = "android.car.hvac.demo";
     44     private static final String TAG = "HvacController";
     45     private static final int DRIVER_ZONE_ID = VehicleSeat.SEAT_ROW_1_LEFT;
     46     private static final int PASSENGER_ZONE_ID = VehicleSeat.SEAT_ROW_1_RIGHT;
     47 
     48     public static final int[] AIRFLOW_STATES = new int[]{
     49             CarHvacManager.FAN_POSITION_FACE,
     50             CarHvacManager.FAN_POSITION_FLOOR,
     51             CarHvacManager.FAN_POSITION_FACE_AND_FLOOR
     52     };
     53 
     54     /**
     55      * Callback for receiving updates from the hvac manager. A Callback can be
     56      * registered using {@link #registerCallback}.
     57      */
     58     public static abstract class Callback {
     59 
     60         public void onPassengerTemperatureChange(float temp) {
     61         }
     62 
     63         public void onDriverTemperatureChange(float temp) {
     64         }
     65 
     66         public void onFanSpeedChange(int position) {
     67         }
     68 
     69         public void onAcStateChange(boolean isOn) {
     70         }
     71 
     72         public void onFrontDefrosterChange(boolean isOn) {
     73         }
     74 
     75         public void onRearDefrosterChange(boolean isOn) {
     76         }
     77 
     78         public void onPassengerSeatWarmerChange(int level) {
     79         }
     80 
     81         public void onDriverSeatWarmerChange(int level) {
     82         }
     83 
     84         public void onFanDirectionChange(int direction) {
     85         }
     86 
     87         public void onAirCirculationChange(boolean isOn) {
     88         }
     89 
     90         public void onAutoModeChange(boolean isOn) {
     91         }
     92 
     93         public void onHvacPowerChange(boolean isOn){
     94         }
     95     }
     96 
     97     public class LocalBinder extends Binder {
     98         HvacController getService() {
     99             return HvacController.this;
    100         }
    101     }
    102 
    103     private final Binder mBinder = new LocalBinder();
    104 
    105     private Car mCarApiClient;
    106     private CarHvacManager mHvacManager;
    107     private Object mHvacManagerReady = new Object();
    108 
    109     private HvacPolicy mPolicy;
    110     @GuardedBy("mCallbacks")
    111     private List<Callback> mCallbacks = new ArrayList<>();
    112     private DataStore mDataStore = new DataStore();
    113 
    114     @Override
    115     public void onCreate() {
    116         super.onCreate();
    117         if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
    118             if (SystemProperties.getBoolean(DEMO_MODE_PROPERTY, false)) {
    119                 IBinder binder = (new LocalHvacPropertyService()).getCarPropertyService();
    120                 initHvacManager(new CarHvacManager(binder, this, new Handler()));
    121                 return;
    122             }
    123 
    124             mCarApiClient = Car.createCar(this, mCarConnectionCallback);
    125             mCarApiClient.connect();
    126         }
    127     }
    128 
    129     @Override
    130     public void onDestroy() {
    131         super.onDestroy();
    132         if (mHvacManager != null) {
    133             mHvacManager.unregisterCallback(mHardwareCallback);
    134         }
    135         if (mCarApiClient != null) {
    136             mCarApiClient.disconnect();
    137         }
    138     }
    139 
    140     @Override
    141     public int onStartCommand(Intent intent, int flags, int startId) {
    142         return START_STICKY;
    143     }
    144 
    145     @Override
    146     public IBinder onBind(Intent intent) {
    147         return mBinder;
    148     }
    149 
    150     public void registerCallback(Callback callback) {
    151         synchronized (mCallbacks) {
    152             mCallbacks.add(callback);
    153         }
    154     }
    155 
    156     public void unregisterCallback(Callback callback) {
    157         synchronized (mCallbacks) {
    158             mCallbacks.remove(callback);
    159         }
    160     }
    161 
    162     private void initHvacManager(CarHvacManager carHvacManager) {
    163         mHvacManager = carHvacManager;
    164         List<CarPropertyConfig> properties = null;
    165         try {
    166             properties = mHvacManager.getPropertyList();
    167             mPolicy = new HvacPolicy(HvacController.this, properties);
    168             mHvacManager.registerCallback(mHardwareCallback);
    169         } catch (android.car.CarNotConnectedException e) {
    170             Log.e(TAG, "Car not connected in HVAC");
    171         }
    172 
    173     }
    174 
    175     private final CarConnectionCallback mCarConnectionCallback =
    176             new CarConnectionCallback() {
    177                 @Override
    178                 public void onConnected(Car car) {
    179                     synchronized (mHvacManagerReady) {
    180                         try {
    181                             initHvacManager((CarHvacManager) mCarApiClient.getCarManager(
    182                                     android.car.Car.HVAC_SERVICE));
    183                             mHvacManagerReady.notifyAll();
    184                         } catch (CarNotConnectedException e) {
    185                             Log.e(TAG, "Car not connected in onServiceConnected");
    186                         }
    187                     }
    188                 }
    189 
    190                 @Override
    191                 public void onDisconnected(Car car) {
    192                 }
    193             };
    194 
    195     private final CarHvacManager.CarHvacEventCallback mHardwareCallback =
    196             new CarHvacManager.CarHvacEventCallback() {
    197                 @Override
    198                 public void onChangeEvent(final CarPropertyValue val) {
    199                     int areaId = val.getAreaId();
    200                     switch (val.getPropertyId()) {
    201                         case CarHvacManager.ID_ZONED_AC_ON:
    202                             handleAcStateUpdate(getValue(val));
    203                             break;
    204                         case CarHvacManager.ID_ZONED_FAN_POSITION:
    205                             handleFanPositionUpdate(areaId, getValue(val));
    206                             break;
    207                         case CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT:
    208                             handleFanSpeedUpdate(areaId, getValue(val));
    209                             break;
    210                         case CarHvacManager.ID_ZONED_TEMP_SETPOINT:
    211                             handleTempUpdate(areaId, getValue(val));
    212                             break;
    213                         case CarHvacManager.ID_WINDOW_DEFROSTER_ON:
    214                             handleDefrosterUpdate(areaId, getValue(val));
    215                             break;
    216                         case CarHvacManager.ID_ZONED_AIR_RECIRCULATION_ON:
    217                             handleAirCirculationUpdate(getValue(val));
    218                             break;
    219                         case CarHvacManager.ID_ZONED_SEAT_TEMP:
    220                             handleSeatWarmerUpdate(areaId, getValue(val));
    221                             break;
    222                         case CarHvacManager.ID_ZONED_AUTOMATIC_MODE_ON:
    223                             handleAutoModeUpdate(getValue(val));
    224                             break;
    225                         case CarHvacManager.ID_ZONED_HVAC_POWER_ON:
    226                             handleHvacPowerOn(getValue(val));
    227                             break;
    228                         default:
    229                             if (Log.isLoggable(TAG, Log.DEBUG)) {
    230                                 Log.d(TAG, "Unhandled HVAC event, id: " + val.getPropertyId());
    231                             }
    232                     }
    233                 }
    234 
    235                 @Override
    236                 public void onErrorEvent(final int propertyId, final int zone) {
    237                 }
    238             };
    239 
    240     @SuppressWarnings("unchecked")
    241     public static <E> E getValue(CarPropertyValue propertyValue) {
    242         return (E) propertyValue.getValue();
    243     }
    244 
    245     void handleHvacPowerOn(boolean isOn) {
    246         boolean shouldPropagate = mDataStore.shouldPropagateHvacPowerUpdate(isOn);
    247         if (Log.isLoggable(TAG, Log.DEBUG)) {
    248             Log.d(TAG, "Hvac Power On: " + isOn + " should propagate: " + shouldPropagate);
    249         }
    250         if (shouldPropagate) {
    251             synchronized (mCallbacks) {
    252                 for (int i = 0; i < mCallbacks.size(); i++) {
    253                     mCallbacks.get(i).onHvacPowerChange(isOn);
    254                 }
    255             }
    256         }
    257     }
    258 
    259     void handleSeatWarmerUpdate(int zone, int level) {
    260         boolean shouldPropagate = mDataStore.shouldPropagateSeatWarmerLevelUpdate(zone, level);
    261         if (Log.isLoggable(TAG, Log.DEBUG)) {
    262             Log.d(TAG, "Seat Warmer Update, zone: " + zone + " level: " + level +
    263                     " should propagate: " + shouldPropagate);
    264         }
    265         if (shouldPropagate) {
    266             synchronized (mCallbacks) {
    267                 for (int i = 0; i < mCallbacks.size(); i++) {
    268                     if (zone == VehicleZone.ZONE_ROW_1_LEFT) {
    269                         mCallbacks.get(i).onDriverSeatWarmerChange(level);
    270                     } else {
    271                         mCallbacks.get(i).onPassengerSeatWarmerChange(level);
    272                     }
    273                 }
    274             }
    275         }
    276     }
    277 
    278     private void handleAirCirculationUpdate(boolean airCirculationState) {
    279         boolean shouldPropagate
    280                 = mDataStore.shouldPropagateAirCirculationUpdate(airCirculationState);
    281         if (Log.isLoggable(TAG, Log.DEBUG)) {
    282             Log.d(TAG, "Air Circulation Update: " + airCirculationState +
    283                     " should propagate: " + shouldPropagate);
    284         }
    285         if (shouldPropagate) {
    286             synchronized (mCallbacks) {
    287                 for (int i = 0; i < mCallbacks.size(); i++) {
    288                     mCallbacks.get(i).onAirCirculationChange(airCirculationState);
    289                 }
    290             }
    291         }
    292     }
    293 
    294     private void handleAutoModeUpdate(boolean autoModeState) {
    295         boolean shouldPropagate = mDataStore.shouldPropagateAutoModeUpdate(autoModeState);
    296         if (Log.isLoggable(TAG, Log.DEBUG)) {
    297             Log.d(TAG, "AutoMode Update, id: " + autoModeState +
    298                     " should propagate: " + shouldPropagate);
    299         }
    300         if (shouldPropagate) {
    301             synchronized (mCallbacks) {
    302                 for (int i = 0; i < mCallbacks.size(); i++) {
    303                     mCallbacks.get(i).onAutoModeChange(autoModeState);
    304                 }
    305             }
    306         }
    307     }
    308 
    309     private void handleAcStateUpdate(boolean acState) {
    310         boolean shouldPropagate = mDataStore.shouldPropagateAcUpdate(acState);
    311         if (Log.isLoggable(TAG, Log.DEBUG)) {
    312             Log.d(TAG, "AC State Update, id: " + acState +
    313                     " should propagate: " + shouldPropagate);
    314         }
    315         if (shouldPropagate) {
    316             synchronized (mCallbacks) {
    317                 for (int i = 0; i < mCallbacks.size(); i++) {
    318                     mCallbacks.get(i).onAcStateChange(acState);
    319                 }
    320             }
    321         }
    322     }
    323 
    324     private void handleFanPositionUpdate(int zone, int position) {
    325         int index = fanPositionToAirflowIndex(position);
    326         boolean shouldPropagate = mDataStore.shouldPropagateFanPositionUpdate(zone, index);
    327         if (Log.isLoggable(TAG, Log.DEBUG)) {
    328             Log.d(TAG, "Fan Position Update, zone: " + zone + " position: " + position +
    329                     " should propagate: " + shouldPropagate);
    330         }
    331         if (shouldPropagate) {
    332             synchronized (mCallbacks) {
    333                 for (int i = 0; i < mCallbacks.size(); i++) {
    334                     mCallbacks.get(i).onFanDirectionChange(position);
    335                 }
    336             }
    337         }
    338     }
    339 
    340     private void handleFanSpeedUpdate(int zone, int speed) {
    341         boolean shouldPropagate = mDataStore.shouldPropagateFanSpeedUpdate(zone, speed);
    342         if (Log.isLoggable(TAG, Log.DEBUG)) {
    343             Log.d(TAG, "Fan Speed Update, zone: " + zone + " speed: " + speed +
    344                     " should propagate: " + shouldPropagate);
    345         }
    346         if (shouldPropagate) {
    347             synchronized (mCallbacks) {
    348                 for (int i = 0; i < mCallbacks.size(); i++) {
    349                     mCallbacks.get(i).onFanSpeedChange(speed);
    350                 }
    351             }
    352         }
    353     }
    354 
    355     private void handleTempUpdate(int zone, float temp) {
    356         boolean shouldPropagate = mDataStore.shouldPropagateTempUpdate(zone, temp);
    357         if (Log.isLoggable(TAG, Log.DEBUG)) {
    358             Log.d(TAG, "Temp Update, zone: " + zone + " temp: " + temp +
    359                     " should propagate: " + shouldPropagate);
    360         }
    361         if (shouldPropagate) {
    362             int userTemperature =  mPolicy.hardwareToUserTemp(temp);
    363             synchronized (mCallbacks) {
    364                 for (int i = 0; i < mCallbacks.size(); i++) {
    365                     if (zone == VehicleZone.ZONE_ROW_1_LEFT) {
    366                         mCallbacks.get(i)
    367                                 .onDriverTemperatureChange(userTemperature);
    368                     } else {
    369                         mCallbacks.get(i)
    370                                 .onPassengerTemperatureChange(userTemperature);
    371                     }
    372                 }
    373             }
    374         }
    375     }
    376 
    377     private void handleDefrosterUpdate(int zone, boolean defrosterState) {
    378         boolean shouldPropagate = mDataStore.shouldPropagateDefrosterUpdate(zone, defrosterState);
    379         if (Log.isLoggable(TAG, Log.DEBUG)) {
    380             Log.d(TAG, "Defroster Update, zone: " + zone + " state: " + defrosterState +
    381                     " should propagate: " + shouldPropagate);
    382         }
    383         if (shouldPropagate) {
    384             synchronized (mCallbacks) {
    385                 for (int i = 0; i < mCallbacks.size(); i++) {
    386                     if (zone == VehicleWindow.WINDOW_FRONT_WINDSHIELD) {
    387                         mCallbacks.get(i).onFrontDefrosterChange(defrosterState);
    388                     } else if (zone == VehicleWindow.WINDOW_REAR_WINDSHIELD) {
    389                         mCallbacks.get(i).onRearDefrosterChange(defrosterState);
    390                     }
    391                 }
    392             }
    393         }
    394     }
    395 
    396     public void requestRefresh(final Runnable r, final Handler h) {
    397         final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
    398             @Override
    399             protected Void doInBackground(Void... unused) {
    400                 synchronized (mHvacManagerReady) {
    401                     while (mHvacManager == null) {
    402                         try {
    403                             mHvacManagerReady.wait();
    404                         } catch (InterruptedException e) {
    405                             // We got interrupted so we might be shutting down.
    406                             return null;
    407                         }
    408                     }
    409                 }
    410                 fetchTemperature(DRIVER_ZONE_ID);
    411                 fetchTemperature(PASSENGER_ZONE_ID);
    412                 fetchFanSpeed();
    413                 fetchDefrosterState(VehicleWindow.WINDOW_FRONT_WINDSHIELD);
    414                 fetchDefrosterState(VehicleWindow.WINDOW_REAR_WINDSHIELD);
    415                 fetchAirflow(DRIVER_ZONE_ID);
    416                 fetchAirflow(PASSENGER_ZONE_ID);
    417                 fetchAcState();
    418                 fetchAirCirculation();
    419                 fetchHvacPowerState();
    420                 return null;
    421             }
    422 
    423             @Override
    424             protected void onPostExecute(Void unused) {
    425                 h.post(r);
    426             }
    427         };
    428         task.execute();
    429     }
    430 
    431     public HvacPolicy getPolicy() {
    432         return mPolicy;
    433     }
    434 
    435     private void fetchTemperature(int zone) {
    436         if (mHvacManager != null) {
    437             try {
    438                 mDataStore.setTemperature(zone, mHvacManager.getFloatProperty(
    439                         CarHvacManager.ID_ZONED_TEMP_SETPOINT, zone));
    440             } catch (android.car.CarNotConnectedException e) {
    441                 Log.e(TAG, "Car not connected in fetchTemperature");
    442             }
    443         }
    444     }
    445 
    446     public int getDriverTemperature() {
    447         return mPolicy.hardwareToUserTemp(mDataStore.getTemperature(DRIVER_ZONE_ID));
    448     }
    449 
    450     public int getPassengerTemperature() {
    451         return mPolicy.hardwareToUserTemp(mDataStore.getTemperature(PASSENGER_ZONE_ID));
    452     }
    453 
    454     public void setDriverTemperature(int temperature) {
    455         setTemperature(DRIVER_ZONE_ID, mPolicy.userToHardwareTemp(temperature));
    456     }
    457 
    458     public void setPassengerTemperature(int temperature) {
    459         setTemperature(PASSENGER_ZONE_ID, mPolicy.userToHardwareTemp(temperature));
    460     }
    461 
    462     public void setTemperature(final int zone, final float temperature) {
    463         mDataStore.setTemperature(zone, temperature);
    464         final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
    465             protected Void doInBackground(Void... unused) {
    466                 if (mHvacManager != null) {
    467                     try {
    468                         mHvacManager.setFloatProperty(
    469                                 CarHvacManager.ID_ZONED_TEMP_SETPOINT, zone, temperature);
    470                     } catch (android.car.CarNotConnectedException e) {
    471                         Log.e(TAG, "Car not connected in setTemperature");
    472                     }
    473                 }
    474                 return null;
    475             }
    476         };
    477         task.execute();
    478     }
    479 
    480     public void setDriverSeatWarmerLevel(int level) {
    481         setSeatWarmerLevel(DRIVER_ZONE_ID, level);
    482     }
    483 
    484     public void setPassengerSeatWarmerLevel(int level) {
    485         setSeatWarmerLevel(PASSENGER_ZONE_ID, level);
    486     }
    487 
    488     public void setSeatWarmerLevel(final int zone, final int level) {
    489         mDataStore.setSeatWarmerLevel(zone, level);
    490         final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
    491             protected Void doInBackground(Void... unused) {
    492                 if (mHvacManager != null) {
    493                     try {
    494                         mHvacManager.setIntProperty(
    495                                 CarHvacManager.ID_ZONED_SEAT_TEMP, zone, level);
    496                     } catch (android.car.CarNotConnectedException e) {
    497                         Log.e(TAG, "Car not connected in setSeatWarmerLevel");
    498                     }
    499                 }
    500                 return null;
    501             }
    502         };
    503         task.execute();
    504     }
    505 
    506     private void fetchFanSpeed() {
    507         if (mHvacManager != null) {
    508             int zone = VehicleZone.ZONE_ROW_1_ALL; // Car specific workaround.
    509             try {
    510                 mDataStore.setFanSpeed(mHvacManager.getIntProperty(
    511                         CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, zone));
    512             } catch (android.car.CarNotConnectedException e) {
    513                 Log.e(TAG, "Car not connected in fetchFanSpeed");
    514             }
    515         }
    516     }
    517 
    518     public int getFanSpeed() {
    519         return mDataStore.getFanSpeed();
    520     }
    521 
    522     public void setFanSpeed(final int fanSpeed) {
    523         mDataStore.setFanSpeed(fanSpeed);
    524 
    525         final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
    526             int newFanSpeed;
    527 
    528             protected Void doInBackground(Void... unused) {
    529                 if (mHvacManager != null) {
    530                     int zone = VehicleZone.ZONE_ROW_1_ALL; // Car specific workaround.
    531                     try {
    532                         if (Log.isLoggable(TAG, Log.DEBUG)) {
    533                             Log.d(TAG, "Setting fanspeed to: " + fanSpeed);
    534                         }
    535                         mHvacManager.setIntProperty(
    536                                 CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, zone, fanSpeed);
    537 
    538                         newFanSpeed = mHvacManager.getIntProperty(
    539                                 CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, zone);
    540                     } catch (android.car.CarNotConnectedException e) {
    541                         Log.e(TAG, "Car not connected in setFanSpeed");
    542                     }
    543                 }
    544                 return null;
    545             }
    546 
    547             @Override
    548             protected void onPostExecute(final Void result) {
    549                 Log.e(TAG, "postExecute new fanSpeed: " + newFanSpeed);
    550             }
    551         };
    552         task.execute();
    553     }
    554 
    555     private void fetchDefrosterState(int zone) {
    556         if (mHvacManager != null) {
    557             try {
    558                 mDataStore.setDefrosterState(zone, mHvacManager.getBooleanProperty(
    559                         CarHvacManager.ID_WINDOW_DEFROSTER_ON, zone));
    560             } catch (android.car.CarNotConnectedException e) {
    561                 Log.e(TAG, "Car not connected in fetchDefrosterState");
    562             }
    563         }
    564     }
    565 
    566     public boolean getFrontDefrosterState() {
    567         return mDataStore.getDefrosterState(VehicleWindow.WINDOW_FRONT_WINDSHIELD);
    568     }
    569 
    570     public boolean getRearDefrosterState() {
    571         return mDataStore.getDefrosterState(VehicleWindow.WINDOW_REAR_WINDSHIELD);
    572     }
    573 
    574     public void setFrontDefrosterState(boolean state) {
    575         setDefrosterState(VehicleWindow.WINDOW_FRONT_WINDSHIELD, state);
    576     }
    577 
    578     public void setRearDefrosterState(boolean state) {
    579         setDefrosterState(VehicleWindow.WINDOW_REAR_WINDSHIELD, state);
    580     }
    581 
    582     public void setDefrosterState(final int zone, final boolean state) {
    583         mDataStore.setDefrosterState(zone, state);
    584         final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
    585             protected Void doInBackground(Void... unused) {
    586                 if (mHvacManager != null) {
    587                     try {
    588                         mHvacManager.setBooleanProperty(
    589                                 CarHvacManager.ID_WINDOW_DEFROSTER_ON, zone, state);
    590                     } catch (android.car.CarNotConnectedException e) {
    591                         Log.e(TAG, "Car not connected in setDeforsterState");
    592                     }
    593                 }
    594                 return null;
    595             }
    596         };
    597         task.execute();
    598     }
    599 
    600     private void fetchAcState() {
    601         if (mHvacManager != null) {
    602             try {
    603                 mDataStore.setAcState(mHvacManager.getBooleanProperty(CarHvacManager.ID_ZONED_AC_ON,
    604                         VehicleZone.ZONE_ROW_1_ALL));
    605             } catch (android.car.CarNotConnectedException e) {
    606                 Log.e(TAG, "Car not connected in fetchAcState");
    607             }
    608         }
    609     }
    610 
    611     public boolean getAcState() {
    612         return mDataStore.getAcState();
    613     }
    614 
    615     public void setAcState(final boolean state) {
    616         mDataStore.setAcState(state);
    617         final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
    618             protected Void doInBackground(Void... unused) {
    619                 if (mHvacManager != null) {
    620                     try {
    621                         mHvacManager.setBooleanProperty(CarHvacManager.ID_ZONED_AC_ON,
    622                                 VehicleZone.ZONE_ROW_1_ALL, state);
    623                     } catch (android.car.CarNotConnectedException e) {
    624                         Log.e(TAG, "Car not connected in setAcState");
    625                     }
    626                 }
    627                 return null;
    628             }
    629         };
    630         task.execute();
    631     }
    632 
    633     private int fanPositionToAirflowIndex(int fanPosition) {
    634         for (int i = 0; i < AIRFLOW_STATES.length; i++) {
    635             if (fanPosition == AIRFLOW_STATES[i]) {
    636                 return i;
    637             }
    638         }
    639         Log.e(TAG, "Unknown fan position " + fanPosition + ". Returning default.");
    640         return AIRFLOW_STATES[0];
    641     }
    642 
    643     private void fetchAirflow(int zone) {
    644         if (mHvacManager != null) {
    645             zone = VehicleZone.ZONE_ROW_1_ALL; // Car specific workaround.
    646             try {
    647                 int val = mHvacManager.getIntProperty(CarHvacManager.ID_ZONED_FAN_POSITION, zone);
    648                 mDataStore.setAirflow(zone, fanPositionToAirflowIndex(val));
    649             } catch (android.car.CarNotConnectedException e) {
    650                 Log.e(TAG, "Car not connected in fetchAirFlow");
    651             }
    652         }
    653     }
    654 
    655     public int getAirflowIndex(int zone) {
    656         return mDataStore.getAirflow(zone);
    657     }
    658 
    659     public void setAirflowIndex(final int zone, final int index) {
    660         mDataStore.setAirflow(zone, index);
    661         int override = VehicleZone.ZONE_ROW_1_ALL; // Car specific workaround.
    662         int val = AIRFLOW_STATES[index];
    663         setFanDirection(override, val);
    664     }
    665 
    666     public void setFanDirection(final int direction) {
    667         mDataStore.setAirflow(VehicleZone.ZONE_ROW_1_ALL, direction);
    668         setFanDirection(VehicleZone.ZONE_ROW_1_ALL, direction);
    669     }
    670 
    671     private void setFanDirection(final int zone, final int direction) {
    672         final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
    673             protected Void doInBackground(Void... unused) {
    674                 if (mHvacManager != null) {
    675                     try {
    676                         mHvacManager.setIntProperty(
    677                                 CarHvacManager.ID_ZONED_FAN_POSITION, zone, direction);
    678                     } catch (android.car.CarNotConnectedException e) {
    679                         Log.e(TAG, "Car not connected in setAirflowIndex");
    680                     }
    681                 }
    682                 return null;
    683             }
    684         };
    685         task.execute();
    686     }
    687 
    688 
    689     private void fetchAirCirculation() {
    690         if (mHvacManager != null) {
    691             try {
    692                 mDataStore.setAirCirculationState(mHvacManager
    693                         .getBooleanProperty(CarHvacManager.ID_ZONED_AIR_RECIRCULATION_ON,
    694                                 VehicleZone.ZONE_ROW_1_ALL));
    695             } catch (android.car.CarNotConnectedException e) {
    696                 Log.e(TAG, "Car not connected in fetchAirCirculationState");
    697             }
    698         }
    699     }
    700 
    701     public boolean getAirCirculationState() {
    702         return mDataStore.getAirCirculationState();
    703     }
    704 
    705     public void setAirCirculation(final boolean state) {
    706         mDataStore.setAirCirculationState(state);
    707         final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
    708             protected Void doInBackground(Void... unused) {
    709                 if (mHvacManager != null) {
    710                     try {
    711                         mHvacManager.setBooleanProperty(
    712                                 CarHvacManager.ID_ZONED_AIR_RECIRCULATION_ON,
    713                                 VehicleZone.ZONE_ROW_1_ALL, state);
    714                     } catch (android.car.CarNotConnectedException e) {
    715                         Log.e(TAG, "Car not connected in setAcState");
    716                     }
    717                 }
    718                 return null;
    719             }
    720         };
    721         task.execute();
    722     }
    723 
    724     public boolean getAutoModeState() {
    725         return mDataStore.getAutoModeState();
    726     }
    727 
    728     public void setAutoMode(final boolean state) {
    729         mDataStore.setAutoModeState(state);
    730         final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
    731             protected Void doInBackground(Void... unused) {
    732                 if (mHvacManager != null) {
    733                     try {
    734                         mHvacManager.setBooleanProperty(CarHvacManager.ID_ZONED_AUTOMATIC_MODE_ON,
    735                                 VehicleZone.ZONE_ROW_1_ALL, state);
    736                     } catch (android.car.CarNotConnectedException e) {
    737                         Log.e(TAG, "Car not connected in setAutoModeState");
    738                     }
    739                 }
    740                 return null;
    741             }
    742         };
    743         task.execute();
    744     }
    745 
    746     public boolean getHvacPowerState() {
    747         return mDataStore.getHvacPowerState();
    748     }
    749 
    750     private void fetchHvacPowerState() {
    751         if (mHvacManager != null) {
    752             try {
    753                 mDataStore.setHvacPowerState(mHvacManager.getBooleanProperty(
    754                         CarHvacManager.ID_ZONED_HVAC_POWER_ON, VehicleZone.ZONE_ROW_1_ALL));
    755             } catch (android.car.CarNotConnectedException e) {
    756                 Log.e(TAG, "Car not connected in fetchHvacPowerState");
    757             }
    758         }
    759     }
    760 }
    761