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