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 onPassengerTemperatureChange(float temp)60 public void onPassengerTemperatureChange(float temp) { 61 } 62 onDriverTemperatureChange(float temp)63 public void onDriverTemperatureChange(float temp) { 64 } 65 onFanSpeedChange(int position)66 public void onFanSpeedChange(int position) { 67 } 68 onAcStateChange(boolean isOn)69 public void onAcStateChange(boolean isOn) { 70 } 71 onFrontDefrosterChange(boolean isOn)72 public void onFrontDefrosterChange(boolean isOn) { 73 } 74 onRearDefrosterChange(boolean isOn)75 public void onRearDefrosterChange(boolean isOn) { 76 } 77 onPassengerSeatWarmerChange(int level)78 public void onPassengerSeatWarmerChange(int level) { 79 } 80 onDriverSeatWarmerChange(int level)81 public void onDriverSeatWarmerChange(int level) { 82 } 83 onFanDirectionChange(int direction)84 public void onFanDirectionChange(int direction) { 85 } 86 onAirCirculationChange(boolean isOn)87 public void onAirCirculationChange(boolean isOn) { 88 } 89 onAutoModeChange(boolean isOn)90 public void onAutoModeChange(boolean isOn) { 91 } 92 onHvacPowerChange(boolean isOn)93 public void onHvacPowerChange(boolean isOn){ 94 } 95 } 96 97 public class LocalBinder extends Binder { getService()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 onCreate()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 onDestroy()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 onStartCommand(Intent intent, int flags, int startId)141 public int onStartCommand(Intent intent, int flags, int startId) { 142 return START_STICKY; 143 } 144 145 @Override onBind(Intent intent)146 public IBinder onBind(Intent intent) { 147 return mBinder; 148 } 149 registerCallback(Callback callback)150 public void registerCallback(Callback callback) { 151 synchronized (mCallbacks) { 152 mCallbacks.add(callback); 153 } 154 } 155 unregisterCallback(Callback callback)156 public void unregisterCallback(Callback callback) { 157 synchronized (mCallbacks) { 158 mCallbacks.remove(callback); 159 } 160 } 161 initHvacManager(CarHvacManager carHvacManager)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") getValue(CarPropertyValue propertyValue)241 public static <E> E getValue(CarPropertyValue propertyValue) { 242 return (E) propertyValue.getValue(); 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 == VehicleZone.ZONE_ROW_1_LEFT) { 269 mCallbacks.get(i).onDriverSeatWarmerChange(level); 270 } else { 271 mCallbacks.get(i).onPassengerSeatWarmerChange(level); 272 } 273 } 274 } 275 } 276 } 277 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(int zone, float temp)355 private void handleTempUpdate(int zone, float temp) { 356 boolean shouldPropagate = mDataStore.shouldPropagateTempUpdate(zone, temp); 357 if (Log.isLoggable(TAG, Log.DEBUG)) { 358 Log.d(TAG, "Temp Update, zone: " + zone + " temp: " + temp + 359 " should propagate: " + shouldPropagate); 360 } 361 if (shouldPropagate) { 362 int userTemperature = mPolicy.hardwareToUserTemp(temp); 363 synchronized (mCallbacks) { 364 for (int i = 0; i < mCallbacks.size(); i++) { 365 if (zone == VehicleZone.ZONE_ROW_1_LEFT) { 366 mCallbacks.get(i) 367 .onDriverTemperatureChange(userTemperature); 368 } else { 369 mCallbacks.get(i) 370 .onPassengerTemperatureChange(userTemperature); 371 } 372 } 373 } 374 } 375 } 376 handleDefrosterUpdate(int zone, boolean defrosterState)377 private void handleDefrosterUpdate(int zone, boolean defrosterState) { 378 boolean shouldPropagate = mDataStore.shouldPropagateDefrosterUpdate(zone, defrosterState); 379 if (Log.isLoggable(TAG, Log.DEBUG)) { 380 Log.d(TAG, "Defroster Update, zone: " + zone + " state: " + defrosterState + 381 " should propagate: " + shouldPropagate); 382 } 383 if (shouldPropagate) { 384 synchronized (mCallbacks) { 385 for (int i = 0; i < mCallbacks.size(); i++) { 386 if (zone == VehicleWindow.WINDOW_FRONT_WINDSHIELD) { 387 mCallbacks.get(i).onFrontDefrosterChange(defrosterState); 388 } else if (zone == VehicleWindow.WINDOW_REAR_WINDSHIELD) { 389 mCallbacks.get(i).onRearDefrosterChange(defrosterState); 390 } 391 } 392 } 393 } 394 } 395 requestRefresh(final Runnable r, final Handler h)396 public void requestRefresh(final Runnable r, final Handler h) { 397 final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() { 398 @Override 399 protected Void doInBackground(Void... unused) { 400 synchronized (mHvacManagerReady) { 401 while (mHvacManager == null) { 402 try { 403 mHvacManagerReady.wait(); 404 } catch (InterruptedException e) { 405 // We got interrupted so we might be shutting down. 406 return null; 407 } 408 } 409 } 410 fetchTemperature(DRIVER_ZONE_ID); 411 fetchTemperature(PASSENGER_ZONE_ID); 412 fetchFanSpeed(); 413 fetchDefrosterState(VehicleWindow.WINDOW_FRONT_WINDSHIELD); 414 fetchDefrosterState(VehicleWindow.WINDOW_REAR_WINDSHIELD); 415 fetchAirflow(DRIVER_ZONE_ID); 416 fetchAirflow(PASSENGER_ZONE_ID); 417 fetchAcState(); 418 fetchAirCirculation(); 419 fetchHvacPowerState(); 420 return null; 421 } 422 423 @Override 424 protected void onPostExecute(Void unused) { 425 h.post(r); 426 } 427 }; 428 task.execute(); 429 } 430 getPolicy()431 public HvacPolicy getPolicy() { 432 return mPolicy; 433 } 434 fetchTemperature(int zone)435 private void fetchTemperature(int zone) { 436 if (mHvacManager != null) { 437 try { 438 mDataStore.setTemperature(zone, mHvacManager.getFloatProperty( 439 CarHvacManager.ID_ZONED_TEMP_SETPOINT, zone)); 440 } catch (android.car.CarNotConnectedException e) { 441 Log.e(TAG, "Car not connected in fetchTemperature"); 442 } 443 } 444 } 445 getDriverTemperature()446 public int getDriverTemperature() { 447 return mPolicy.hardwareToUserTemp(mDataStore.getTemperature(DRIVER_ZONE_ID)); 448 } 449 getPassengerTemperature()450 public int getPassengerTemperature() { 451 return mPolicy.hardwareToUserTemp(mDataStore.getTemperature(PASSENGER_ZONE_ID)); 452 } 453 setDriverTemperature(int temperature)454 public void setDriverTemperature(int temperature) { 455 setTemperature(DRIVER_ZONE_ID, mPolicy.userToHardwareTemp(temperature)); 456 } 457 setPassengerTemperature(int temperature)458 public void setPassengerTemperature(int temperature) { 459 setTemperature(PASSENGER_ZONE_ID, mPolicy.userToHardwareTemp(temperature)); 460 } 461 setTemperature(final int zone, final float temperature)462 public void setTemperature(final int zone, final float temperature) { 463 mDataStore.setTemperature(zone, temperature); 464 final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() { 465 protected Void doInBackground(Void... unused) { 466 if (mHvacManager != null) { 467 try { 468 mHvacManager.setFloatProperty( 469 CarHvacManager.ID_ZONED_TEMP_SETPOINT, zone, temperature); 470 } catch (android.car.CarNotConnectedException e) { 471 Log.e(TAG, "Car not connected in setTemperature"); 472 } 473 } 474 return null; 475 } 476 }; 477 task.execute(); 478 } 479 setDriverSeatWarmerLevel(int level)480 public void setDriverSeatWarmerLevel(int level) { 481 setSeatWarmerLevel(DRIVER_ZONE_ID, level); 482 } 483 setPassengerSeatWarmerLevel(int level)484 public void setPassengerSeatWarmerLevel(int level) { 485 setSeatWarmerLevel(PASSENGER_ZONE_ID, level); 486 } 487 setSeatWarmerLevel(final int zone, final int level)488 public void setSeatWarmerLevel(final int zone, final int level) { 489 mDataStore.setSeatWarmerLevel(zone, level); 490 final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() { 491 protected Void doInBackground(Void... unused) { 492 if (mHvacManager != null) { 493 try { 494 mHvacManager.setIntProperty( 495 CarHvacManager.ID_ZONED_SEAT_TEMP, zone, level); 496 } catch (android.car.CarNotConnectedException e) { 497 Log.e(TAG, "Car not connected in setSeatWarmerLevel"); 498 } 499 } 500 return null; 501 } 502 }; 503 task.execute(); 504 } 505 fetchFanSpeed()506 private void fetchFanSpeed() { 507 if (mHvacManager != null) { 508 int zone = VehicleZone.ZONE_ROW_1_ALL; // Car specific workaround. 509 try { 510 mDataStore.setFanSpeed(mHvacManager.getIntProperty( 511 CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, zone)); 512 } catch (android.car.CarNotConnectedException e) { 513 Log.e(TAG, "Car not connected in fetchFanSpeed"); 514 } 515 } 516 } 517 getFanSpeed()518 public int getFanSpeed() { 519 return mDataStore.getFanSpeed(); 520 } 521 setFanSpeed(final int fanSpeed)522 public void setFanSpeed(final int fanSpeed) { 523 mDataStore.setFanSpeed(fanSpeed); 524 525 final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() { 526 int newFanSpeed; 527 528 protected Void doInBackground(Void... unused) { 529 if (mHvacManager != null) { 530 int zone = VehicleZone.ZONE_ROW_1_ALL; // Car specific workaround. 531 try { 532 if (Log.isLoggable(TAG, Log.DEBUG)) { 533 Log.d(TAG, "Setting fanspeed to: " + fanSpeed); 534 } 535 mHvacManager.setIntProperty( 536 CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, zone, fanSpeed); 537 538 newFanSpeed = mHvacManager.getIntProperty( 539 CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, zone); 540 } catch (android.car.CarNotConnectedException e) { 541 Log.e(TAG, "Car not connected in setFanSpeed"); 542 } 543 } 544 return null; 545 } 546 547 @Override 548 protected void onPostExecute(final Void result) { 549 Log.e(TAG, "postExecute new fanSpeed: " + newFanSpeed); 550 } 551 }; 552 task.execute(); 553 } 554 fetchDefrosterState(int zone)555 private void fetchDefrosterState(int zone) { 556 if (mHvacManager != null) { 557 try { 558 mDataStore.setDefrosterState(zone, mHvacManager.getBooleanProperty( 559 CarHvacManager.ID_WINDOW_DEFROSTER_ON, zone)); 560 } catch (android.car.CarNotConnectedException e) { 561 Log.e(TAG, "Car not connected in fetchDefrosterState"); 562 } 563 } 564 } 565 getFrontDefrosterState()566 public boolean getFrontDefrosterState() { 567 return mDataStore.getDefrosterState(VehicleWindow.WINDOW_FRONT_WINDSHIELD); 568 } 569 getRearDefrosterState()570 public boolean getRearDefrosterState() { 571 return mDataStore.getDefrosterState(VehicleWindow.WINDOW_REAR_WINDSHIELD); 572 } 573 setFrontDefrosterState(boolean state)574 public void setFrontDefrosterState(boolean state) { 575 setDefrosterState(VehicleWindow.WINDOW_FRONT_WINDSHIELD, state); 576 } 577 setRearDefrosterState(boolean state)578 public void setRearDefrosterState(boolean state) { 579 setDefrosterState(VehicleWindow.WINDOW_REAR_WINDSHIELD, state); 580 } 581 setDefrosterState(final int zone, final boolean state)582 public void setDefrosterState(final int zone, final boolean state) { 583 mDataStore.setDefrosterState(zone, state); 584 final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() { 585 protected Void doInBackground(Void... unused) { 586 if (mHvacManager != null) { 587 try { 588 mHvacManager.setBooleanProperty( 589 CarHvacManager.ID_WINDOW_DEFROSTER_ON, zone, state); 590 } catch (android.car.CarNotConnectedException e) { 591 Log.e(TAG, "Car not connected in setDeforsterState"); 592 } 593 } 594 return null; 595 } 596 }; 597 task.execute(); 598 } 599 fetchAcState()600 private void fetchAcState() { 601 if (mHvacManager != null) { 602 try { 603 mDataStore.setAcState(mHvacManager.getBooleanProperty(CarHvacManager.ID_ZONED_AC_ON, 604 VehicleZone.ZONE_ROW_1_ALL)); 605 } catch (android.car.CarNotConnectedException e) { 606 Log.e(TAG, "Car not connected in fetchAcState"); 607 } 608 } 609 } 610 getAcState()611 public boolean getAcState() { 612 return mDataStore.getAcState(); 613 } 614 setAcState(final boolean state)615 public void setAcState(final boolean state) { 616 mDataStore.setAcState(state); 617 final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() { 618 protected Void doInBackground(Void... unused) { 619 if (mHvacManager != null) { 620 try { 621 mHvacManager.setBooleanProperty(CarHvacManager.ID_ZONED_AC_ON, 622 VehicleZone.ZONE_ROW_1_ALL, state); 623 } catch (android.car.CarNotConnectedException e) { 624 Log.e(TAG, "Car not connected in setAcState"); 625 } 626 } 627 return null; 628 } 629 }; 630 task.execute(); 631 } 632 fanPositionToAirflowIndex(int fanPosition)633 private int fanPositionToAirflowIndex(int fanPosition) { 634 for (int i = 0; i < AIRFLOW_STATES.length; i++) { 635 if (fanPosition == AIRFLOW_STATES[i]) { 636 return i; 637 } 638 } 639 Log.e(TAG, "Unknown fan position " + fanPosition + ". Returning default."); 640 return AIRFLOW_STATES[0]; 641 } 642 fetchAirflow(int zone)643 private void fetchAirflow(int zone) { 644 if (mHvacManager != null) { 645 zone = VehicleZone.ZONE_ROW_1_ALL; // Car specific workaround. 646 try { 647 int val = mHvacManager.getIntProperty(CarHvacManager.ID_ZONED_FAN_POSITION, zone); 648 mDataStore.setAirflow(zone, fanPositionToAirflowIndex(val)); 649 } catch (android.car.CarNotConnectedException e) { 650 Log.e(TAG, "Car not connected in fetchAirFlow"); 651 } 652 } 653 } 654 getAirflowIndex(int zone)655 public int getAirflowIndex(int zone) { 656 return mDataStore.getAirflow(zone); 657 } 658 setAirflowIndex(final int zone, final int index)659 public void setAirflowIndex(final int zone, final int index) { 660 mDataStore.setAirflow(zone, index); 661 int override = VehicleZone.ZONE_ROW_1_ALL; // Car specific workaround. 662 int val = AIRFLOW_STATES[index]; 663 setFanDirection(override, val); 664 } 665 setFanDirection(final int direction)666 public void setFanDirection(final int direction) { 667 mDataStore.setAirflow(VehicleZone.ZONE_ROW_1_ALL, direction); 668 setFanDirection(VehicleZone.ZONE_ROW_1_ALL, direction); 669 } 670 setFanDirection(final int zone, final int direction)671 private void setFanDirection(final int zone, final int direction) { 672 final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() { 673 protected Void doInBackground(Void... unused) { 674 if (mHvacManager != null) { 675 try { 676 mHvacManager.setIntProperty( 677 CarHvacManager.ID_ZONED_FAN_POSITION, zone, direction); 678 } catch (android.car.CarNotConnectedException e) { 679 Log.e(TAG, "Car not connected in setAirflowIndex"); 680 } 681 } 682 return null; 683 } 684 }; 685 task.execute(); 686 } 687 688 fetchAirCirculation()689 private void fetchAirCirculation() { 690 if (mHvacManager != null) { 691 try { 692 mDataStore.setAirCirculationState(mHvacManager 693 .getBooleanProperty(CarHvacManager.ID_ZONED_AIR_RECIRCULATION_ON, 694 VehicleZone.ZONE_ROW_1_ALL)); 695 } catch (android.car.CarNotConnectedException e) { 696 Log.e(TAG, "Car not connected in fetchAirCirculationState"); 697 } 698 } 699 } 700 getAirCirculationState()701 public boolean getAirCirculationState() { 702 return mDataStore.getAirCirculationState(); 703 } 704 setAirCirculation(final boolean state)705 public void setAirCirculation(final boolean state) { 706 mDataStore.setAirCirculationState(state); 707 final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() { 708 protected Void doInBackground(Void... unused) { 709 if (mHvacManager != null) { 710 try { 711 mHvacManager.setBooleanProperty( 712 CarHvacManager.ID_ZONED_AIR_RECIRCULATION_ON, 713 VehicleZone.ZONE_ROW_1_ALL, state); 714 } catch (android.car.CarNotConnectedException e) { 715 Log.e(TAG, "Car not connected in setAcState"); 716 } 717 } 718 return null; 719 } 720 }; 721 task.execute(); 722 } 723 getAutoModeState()724 public boolean getAutoModeState() { 725 return mDataStore.getAutoModeState(); 726 } 727 setAutoMode(final boolean state)728 public void setAutoMode(final boolean state) { 729 mDataStore.setAutoModeState(state); 730 final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() { 731 protected Void doInBackground(Void... unused) { 732 if (mHvacManager != null) { 733 try { 734 mHvacManager.setBooleanProperty(CarHvacManager.ID_ZONED_AUTOMATIC_MODE_ON, 735 VehicleZone.ZONE_ROW_1_ALL, state); 736 } catch (android.car.CarNotConnectedException e) { 737 Log.e(TAG, "Car not connected in setAutoModeState"); 738 } 739 } 740 return null; 741 } 742 }; 743 task.execute(); 744 } 745 getHvacPowerState()746 public boolean getHvacPowerState() { 747 return mDataStore.getHvacPowerState(); 748 } 749 fetchHvacPowerState()750 private void fetchHvacPowerState() { 751 if (mHvacManager != null) { 752 try { 753 mDataStore.setHvacPowerState(mHvacManager.getBooleanProperty( 754 CarHvacManager.ID_ZONED_HVAC_POWER_ON, VehicleZone.ZONE_ROW_1_ALL)); 755 } catch (android.car.CarNotConnectedException e) { 756 Log.e(TAG, "Car not connected in fetchHvacPowerState"); 757 } 758 } 759 } 760 } 761