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