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         if (mDataStore.shouldPropagateHvacPowerUpdate(isOn)) {
    247             synchronized (mCallbacks) {
    248                 for (int i = 0; i < mCallbacks.size(); i++) {
    249                     mCallbacks.get(i).onHvacPowerChange(isOn);
    250                 }
    251             }
    252         }
    253     }
    254 
    255     void handleSeatWarmerUpdate(int zone, int level) {
    256         if (mDataStore.shouldPropagateSeatWarmerLevelUpdate(zone, level)) {
    257             synchronized (mCallbacks) {
    258                 for (int i = 0; i < mCallbacks.size(); i++) {
    259                     if (zone == VehicleZone.ZONE_ROW_1_LEFT) {
    260                         mCallbacks.get(i).onDriverSeatWarmerChange(level);
    261                     } else {
    262                         mCallbacks.get(i).onPassengerSeatWarmerChange(level);
    263                     }
    264                 }
    265             }
    266         }
    267     }
    268 
    269     private void handleAirCirculationUpdate(boolean airCirculationState) {
    270         if (mDataStore.shouldPropagateAirCirculationUpdate(airCirculationState)) {
    271             synchronized (mCallbacks) {
    272                 for (int i = 0; i < mCallbacks.size(); i++) {
    273                     mCallbacks.get(i).onAirCirculationChange(airCirculationState);
    274                 }
    275             }
    276         }
    277     }
    278 
    279     private void handleAutoModeUpdate(boolean autoModeState) {
    280         if (mDataStore.shouldPropagateAutoModeUpdate(autoModeState)) {
    281             synchronized (mCallbacks) {
    282                 for (int i = 0; i < mCallbacks.size(); i++) {
    283                     mCallbacks.get(i).onAutoModeChange(autoModeState);
    284                 }
    285             }
    286         }
    287     }
    288 
    289     private void handleAcStateUpdate(boolean acState) {
    290         if (mDataStore.shouldPropagateAcUpdate(acState)) {
    291             synchronized (mCallbacks) {
    292                 for (int i = 0; i < mCallbacks.size(); i++) {
    293                     mCallbacks.get(i).onAcStateChange(acState);
    294                 }
    295             }
    296         }
    297     }
    298 
    299     private void handleFanPositionUpdate(int zone, int position) {
    300         int index = fanPositionToAirflowIndex(position);
    301         if (mDataStore.shouldPropagateFanPositionUpdate(zone, index)) {
    302             synchronized (mCallbacks) {
    303                 for (int i = 0; i < mCallbacks.size(); i++) {
    304                     mCallbacks.get(i).onFanDirectionChange(position);
    305                 }
    306             }
    307         }
    308     }
    309 
    310     private void handleFanSpeedUpdate(int zone, int speed) {
    311         if (mDataStore.shouldPropagateFanSpeedUpdate(zone, speed)) {
    312             synchronized (mCallbacks) {
    313                 for (int i = 0; i < mCallbacks.size(); i++) {
    314                     mCallbacks.get(i).onFanSpeedChange(speed);
    315                 }
    316             }
    317         }
    318     }
    319 
    320     private void handleTempUpdate(int zone, float temp) {
    321         if (mDataStore.shouldPropagateTempUpdate(zone, temp)) {
    322             int userTemperature =  mPolicy.hardwareToUserTemp(temp);
    323             synchronized (mCallbacks) {
    324                 for (int i = 0; i < mCallbacks.size(); i++) {
    325                     if (zone == VehicleZone.ZONE_ROW_1_LEFT) {
    326                         mCallbacks.get(i)
    327                                 .onDriverTemperatureChange(userTemperature);
    328                     } else {
    329                         mCallbacks.get(i)
    330                                 .onPassengerTemperatureChange(userTemperature);
    331                     }
    332                 }
    333             }
    334         }
    335     }
    336 
    337     private void handleDefrosterUpdate(int zone, boolean defrosterState) {
    338         if (mDataStore.shouldPropagateDefrosterUpdate(zone, defrosterState)) {
    339             synchronized (mCallbacks) {
    340                 for (int i = 0; i < mCallbacks.size(); i++) {
    341                     if (zone == VehicleWindow.WINDOW_FRONT_WINDSHIELD) {
    342                         mCallbacks.get(i).onFrontDefrosterChange(defrosterState);
    343                     } else if (zone == VehicleWindow.WINDOW_REAR_WINDSHIELD) {
    344                         mCallbacks.get(i).onRearDefrosterChange(defrosterState);
    345                     }
    346                 }
    347             }
    348 
    349         }
    350     }
    351 
    352     public void requestRefresh(final Runnable r, final Handler h) {
    353         final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
    354             @Override
    355             protected Void doInBackground(Void... unused) {
    356                 synchronized (mHvacManagerReady) {
    357                     while (mHvacManager == null) {
    358                         try {
    359                             mHvacManagerReady.wait();
    360                         } catch (InterruptedException e) {
    361                             // We got interrupted so we might be shutting down.
    362                             return null;
    363                         }
    364                     }
    365                 }
    366                 fetchTemperature(DRIVER_ZONE_ID);
    367                 fetchTemperature(PASSENGER_ZONE_ID);
    368                 fetchFanSpeed();
    369                 fetchDefrosterState(VehicleWindow.WINDOW_FRONT_WINDSHIELD);
    370                 fetchDefrosterState(VehicleWindow.WINDOW_REAR_WINDSHIELD);
    371                 fetchAirflow(DRIVER_ZONE_ID);
    372                 fetchAirflow(PASSENGER_ZONE_ID);
    373                 fetchAcState();
    374                 fetchAirCirculation();
    375                 fetchHvacPowerState();
    376                 return null;
    377             }
    378 
    379             @Override
    380             protected void onPostExecute(Void unused) {
    381                 h.post(r);
    382             }
    383         };
    384         task.execute();
    385     }
    386 
    387     public HvacPolicy getPolicy() {
    388         return mPolicy;
    389     }
    390 
    391     private void fetchTemperature(int zone) {
    392         if (mHvacManager != null) {
    393             try {
    394                 mDataStore.setTemperature(zone, mHvacManager.getFloatProperty(
    395                         CarHvacManager.ID_ZONED_TEMP_SETPOINT, zone));
    396             } catch (android.car.CarNotConnectedException e) {
    397                 Log.e(TAG, "Car not connected in fetchTemperature");
    398             }
    399         }
    400     }
    401 
    402     public int getDriverTemperature() {
    403         return mPolicy.hardwareToUserTemp(mDataStore.getTemperature(DRIVER_ZONE_ID));
    404     }
    405 
    406     public int getPassengerTemperature() {
    407         return mPolicy.hardwareToUserTemp(mDataStore.getTemperature(PASSENGER_ZONE_ID));
    408     }
    409 
    410     public void setDriverTemperature(int temperature) {
    411         setTemperature(DRIVER_ZONE_ID, mPolicy.userToHardwareTemp(temperature));
    412     }
    413 
    414     public void setPassengerTemperature(int temperature) {
    415         setTemperature(PASSENGER_ZONE_ID, mPolicy.userToHardwareTemp(temperature));
    416     }
    417 
    418     public void setTemperature(final int zone, final float temperature) {
    419         mDataStore.setTemperature(zone, temperature);
    420         final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
    421             protected Void doInBackground(Void... unused) {
    422                 if (mHvacManager != null) {
    423                     try {
    424                         mHvacManager.setFloatProperty(
    425                                 CarHvacManager.ID_ZONED_TEMP_SETPOINT, zone, temperature);
    426                     } catch (android.car.CarNotConnectedException e) {
    427                         Log.e(TAG, "Car not connected in setTemperature");
    428                     }
    429                 }
    430                 return null;
    431             }
    432         };
    433         task.execute();
    434     }
    435 
    436     public void setDriverSeatWarmerLevel(int level) {
    437         setSeatWarmerLevel(DRIVER_ZONE_ID, level);
    438     }
    439 
    440     public void setPassengerSeatWarmerLevel(int level) {
    441         setSeatWarmerLevel(PASSENGER_ZONE_ID, level);
    442     }
    443 
    444     public void setSeatWarmerLevel(final int zone, final int level) {
    445         mDataStore.setSeatWarmerLevel(zone, level);
    446         final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
    447             protected Void doInBackground(Void... unused) {
    448                 if (mHvacManager != null) {
    449                     try {
    450                         mHvacManager.setIntProperty(
    451                                 CarHvacManager.ID_ZONED_SEAT_TEMP, zone, level);
    452                     } catch (android.car.CarNotConnectedException e) {
    453                         Log.e(TAG, "Car not connected in setSeatWarmerLevel");
    454                     }
    455                 }
    456                 return null;
    457             }
    458         };
    459         task.execute();
    460     }
    461 
    462     private void fetchFanSpeed() {
    463         if (mHvacManager != null) {
    464             int zone = VehicleZone.ZONE_ROW_1_ALL; // Car specific workaround.
    465             try {
    466                 mDataStore.setFanSpeed(mHvacManager.getIntProperty(
    467                         CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, zone));
    468             } catch (android.car.CarNotConnectedException e) {
    469                 Log.e(TAG, "Car not connected in fetchFanSpeed");
    470             }
    471         }
    472     }
    473 
    474     public int getFanSpeed() {
    475         return mDataStore.getFanSpeed();
    476     }
    477 
    478     public void setFanSpeed(final int fanSpeed) {
    479         mDataStore.setFanSpeed(fanSpeed);
    480 
    481         final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
    482             int newFanSpeed;
    483 
    484             protected Void doInBackground(Void... unused) {
    485                 if (mHvacManager != null) {
    486                     int zone = VehicleZone.ZONE_ROW_1_ALL; // Car specific workaround.
    487                     try {
    488                         if (Log.isLoggable(TAG, Log.DEBUG)) {
    489                             Log.d(TAG, "Setting fanspeed to: " + fanSpeed);
    490                         }
    491                         mHvacManager.setIntProperty(
    492                                 CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, zone, fanSpeed);
    493 
    494                         newFanSpeed = mHvacManager.getIntProperty(
    495                                 CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, zone);
    496                     } catch (android.car.CarNotConnectedException e) {
    497                         Log.e(TAG, "Car not connected in setFanSpeed");
    498                     }
    499                 }
    500                 return null;
    501             }
    502 
    503             @Override
    504             protected void onPostExecute(final Void result) {
    505                 Log.e(TAG, "postExecute new fanSpeed: " + newFanSpeed);
    506             }
    507         };
    508         task.execute();
    509     }
    510 
    511     private void fetchDefrosterState(int zone) {
    512         if (mHvacManager != null) {
    513             try {
    514                 mDataStore.setDefrosterState(zone, mHvacManager.getBooleanProperty(
    515                         CarHvacManager.ID_WINDOW_DEFROSTER_ON, zone));
    516             } catch (android.car.CarNotConnectedException e) {
    517                 Log.e(TAG, "Car not connected in fetchDefrosterState");
    518             }
    519         }
    520     }
    521 
    522     public boolean getFrontDefrosterState() {
    523         return mDataStore.getDefrosterState(VehicleWindow.WINDOW_FRONT_WINDSHIELD);
    524     }
    525 
    526     public boolean getRearDefrosterState() {
    527         return mDataStore.getDefrosterState(VehicleWindow.WINDOW_REAR_WINDSHIELD);
    528     }
    529 
    530     public void setFrontDefrosterState(boolean state) {
    531         setDefrosterState(VehicleWindow.WINDOW_FRONT_WINDSHIELD, state);
    532     }
    533 
    534     public void setRearDefrosterState(boolean state) {
    535         setDefrosterState(VehicleWindow.WINDOW_REAR_WINDSHIELD, state);
    536     }
    537 
    538     public void setDefrosterState(final int zone, final boolean state) {
    539         mDataStore.setDefrosterState(zone, state);
    540         final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
    541             protected Void doInBackground(Void... unused) {
    542                 if (mHvacManager != null) {
    543                     try {
    544                         mHvacManager.setBooleanProperty(
    545                                 CarHvacManager.ID_WINDOW_DEFROSTER_ON, zone, state);
    546                     } catch (android.car.CarNotConnectedException e) {
    547                         Log.e(TAG, "Car not connected in setDeforsterState");
    548                     }
    549                 }
    550                 return null;
    551             }
    552         };
    553         task.execute();
    554     }
    555 
    556     private void fetchAcState() {
    557         if (mHvacManager != null) {
    558             try {
    559                 mDataStore.setAcState(mHvacManager.getBooleanProperty(CarHvacManager.ID_ZONED_AC_ON,
    560                         VehicleZone.ZONE_ROW_1_ALL));
    561             } catch (android.car.CarNotConnectedException e) {
    562                 Log.e(TAG, "Car not connected in fetchAcState");
    563             }
    564         }
    565     }
    566 
    567     public boolean getAcState() {
    568         return mDataStore.getAcState();
    569     }
    570 
    571     public void setAcState(final boolean state) {
    572         mDataStore.setAcState(state);
    573         final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
    574             protected Void doInBackground(Void... unused) {
    575                 if (mHvacManager != null) {
    576                     try {
    577                         mHvacManager.setBooleanProperty(CarHvacManager.ID_ZONED_AC_ON,
    578                                 VehicleZone.ZONE_ROW_1_ALL, state);
    579                     } catch (android.car.CarNotConnectedException e) {
    580                         Log.e(TAG, "Car not connected in setAcState");
    581                     }
    582                 }
    583                 return null;
    584             }
    585         };
    586         task.execute();
    587     }
    588 
    589     private int fanPositionToAirflowIndex(int fanPosition) {
    590         for (int i = 0; i < AIRFLOW_STATES.length; i++) {
    591             if (fanPosition == AIRFLOW_STATES[i]) {
    592                 return i;
    593             }
    594         }
    595         Log.e(TAG, "Unknown fan position " + fanPosition + ". Returning default.");
    596         return AIRFLOW_STATES[0];
    597     }
    598 
    599     private void fetchAirflow(int zone) {
    600         if (mHvacManager != null) {
    601             zone = VehicleZone.ZONE_ROW_1_ALL; // Car specific workaround.
    602             try {
    603                 int val = mHvacManager.getIntProperty(CarHvacManager.ID_ZONED_FAN_POSITION, zone);
    604                 mDataStore.setAirflow(zone, fanPositionToAirflowIndex(val));
    605             } catch (android.car.CarNotConnectedException e) {
    606                 Log.e(TAG, "Car not connected in fetchAirFlow");
    607             }
    608         }
    609     }
    610 
    611     public int getAirflowIndex(int zone) {
    612         return mDataStore.getAirflow(zone);
    613     }
    614 
    615     public void setAirflowIndex(final int zone, final int index) {
    616         mDataStore.setAirflow(zone, index);
    617         int override = VehicleZone.ZONE_ROW_1_ALL; // Car specific workaround.
    618         int val = AIRFLOW_STATES[index];
    619         setFanDirection(override, val);
    620     }
    621 
    622     public void setFanDirection(final int direction) {
    623         mDataStore.setAirflow(VehicleZone.ZONE_ROW_1_ALL, direction);
    624         setFanDirection(VehicleZone.ZONE_ROW_1_ALL, direction);
    625     }
    626 
    627     private void setFanDirection(final int zone, final int direction) {
    628         final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
    629             protected Void doInBackground(Void... unused) {
    630                 if (mHvacManager != null) {
    631                     try {
    632                         mHvacManager.setIntProperty(
    633                                 CarHvacManager.ID_ZONED_FAN_POSITION, zone, direction);
    634                     } catch (android.car.CarNotConnectedException e) {
    635                         Log.e(TAG, "Car not connected in setAirflowIndex");
    636                     }
    637                 }
    638                 return null;
    639             }
    640         };
    641         task.execute();
    642     }
    643 
    644 
    645     private void fetchAirCirculation() {
    646         if (mHvacManager != null) {
    647             try {
    648                 mDataStore.setAirCirculationState(mHvacManager
    649                         .getBooleanProperty(CarHvacManager.ID_ZONED_AIR_RECIRCULATION_ON,
    650                                 VehicleZone.ZONE_ROW_1_ALL));
    651             } catch (android.car.CarNotConnectedException e) {
    652                 Log.e(TAG, "Car not connected in fetchAirCirculationState");
    653             }
    654         }
    655     }
    656 
    657     public boolean getAirCirculationState() {
    658         return mDataStore.getAirCirculationState();
    659     }
    660 
    661     public void setAirCirculation(final boolean state) {
    662         mDataStore.setAirCirculationState(state);
    663         final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
    664             protected Void doInBackground(Void... unused) {
    665                 if (mHvacManager != null) {
    666                     try {
    667                         mHvacManager.setBooleanProperty(
    668                                 CarHvacManager.ID_ZONED_AIR_RECIRCULATION_ON,
    669                                 VehicleZone.ZONE_ROW_1_ALL, state);
    670                     } catch (android.car.CarNotConnectedException e) {
    671                         Log.e(TAG, "Car not connected in setAcState");
    672                     }
    673                 }
    674                 return null;
    675             }
    676         };
    677         task.execute();
    678     }
    679 
    680     public boolean getAutoModeState() {
    681         return mDataStore.getAutoModeState();
    682     }
    683 
    684     public void setAutoMode(final boolean state) {
    685         mDataStore.setAutoModeState(state);
    686         final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
    687             protected Void doInBackground(Void... unused) {
    688                 if (mHvacManager != null) {
    689                     try {
    690                         mHvacManager.setBooleanProperty(CarHvacManager.ID_ZONED_AUTOMATIC_MODE_ON,
    691                                 VehicleZone.ZONE_ROW_1_ALL, state);
    692                     } catch (android.car.CarNotConnectedException e) {
    693                         Log.e(TAG, "Car not connected in setAutoModeState");
    694                     }
    695                 }
    696                 return null;
    697             }
    698         };
    699         task.execute();
    700     }
    701 
    702     public boolean getHvacPowerState() {
    703         return mDataStore.getHvacPowerState();
    704     }
    705 
    706     private void fetchHvacPowerState() {
    707         if (mHvacManager != null) {
    708             try {
    709                 mDataStore.setHvacPowerState(mHvacManager.getBooleanProperty(
    710                         CarHvacManager.ID_ZONED_HVAC_POWER_ON, VehicleZone.ZONE_ROW_1_ALL));
    711             } catch (android.car.CarNotConnectedException e) {
    712                 Log.e(TAG, "Car not connected in fetchHvacPowerState");
    713             }
    714         }
    715     }
    716 }
    717