1 /* 2 * Copyright (C) 2015 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.hal; 17 18 import static android.hardware.automotive.vehicle.VehicleProperty.AP_POWER_BOOTUP_REASON; 19 import static android.hardware.automotive.vehicle.VehicleProperty.AP_POWER_STATE_REPORT; 20 import static android.hardware.automotive.vehicle.VehicleProperty.AP_POWER_STATE_REQ; 21 import static android.hardware.automotive.vehicle.VehicleProperty.DISPLAY_BRIGHTNESS; 22 import static android.hardware.automotive.vehicle.VehicleProperty.PER_DISPLAY_BRIGHTNESS; 23 import static android.hardware.automotive.vehicle.VehicleProperty.SHUTDOWN_REQUEST; 24 import static android.hardware.automotive.vehicle.VehicleProperty.VEHICLE_IN_USE; 25 26 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; 27 28 import android.annotation.IntDef; 29 import android.annotation.Nullable; 30 import android.car.builtin.util.Slogf; 31 import android.car.builtin.view.DisplayHelper; 32 import android.car.feature.FeatureFlags; 33 import android.content.Context; 34 import android.hardware.automotive.vehicle.VehicleApPowerBootupReason; 35 import android.hardware.automotive.vehicle.VehicleApPowerStateConfigFlag; 36 import android.hardware.automotive.vehicle.VehicleApPowerStateReport; 37 import android.hardware.automotive.vehicle.VehicleApPowerStateReq; 38 import android.hardware.automotive.vehicle.VehicleApPowerStateReqIndex; 39 import android.hardware.automotive.vehicle.VehicleApPowerStateShutdownParam; 40 import android.hardware.automotive.vehicle.VehicleProperty; 41 import android.hardware.automotive.vehicle.VehiclePropertyStatus; 42 import android.hardware.display.DisplayManager; 43 import android.os.ServiceSpecificException; 44 import android.util.SparseArray; 45 import android.util.SparseIntArray; 46 import android.view.Display; 47 48 import com.android.car.CarLog; 49 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 50 import com.android.internal.annotations.GuardedBy; 51 import com.android.internal.annotations.VisibleForTesting; 52 import com.android.internal.util.Preconditions; 53 54 import java.io.PrintWriter; 55 import java.lang.annotation.Retention; 56 import java.lang.annotation.RetentionPolicy; 57 import java.util.ArrayList; 58 import java.util.Collection; 59 import java.util.List; 60 import java.util.Objects; 61 62 /** 63 * Translates HAL power events to higher-level semantic information. 64 */ 65 public class PowerHalService extends HalServiceBase { 66 // Set display brightness from 0-100% 67 public static final int MAX_BRIGHTNESS = 100; 68 69 // TODO(b/337307388): replace this with VehicleProperty.PER_DISPLAY_MAX_BRIGHTNESS once we use 70 // property V4. 71 private static final int PER_DISPLAY_MAX_BRIGHTNESS = 0x11410F4E; 72 PropertyInfo(boolean needSubscription)73 private record PropertyInfo(boolean needSubscription) {} 74 getSupportedProperties()75 private static SparseArray<PropertyInfo> getSupportedProperties() { 76 SparseArray<PropertyInfo> propertyInfo = new SparseArray<>(); 77 propertyInfo.put(AP_POWER_STATE_REQ, new PropertyInfo(/*needSubscription=*/ true)); 78 // This is issued from PowerHalService so we do not need to subscribe to it. 79 propertyInfo.put(AP_POWER_STATE_REPORT, new PropertyInfo(/*needSubscription=*/ false)); 80 propertyInfo.put(DISPLAY_BRIGHTNESS, new PropertyInfo(/*needSubscription=*/ true)); 81 propertyInfo.put(PER_DISPLAY_BRIGHTNESS, new PropertyInfo(/*needSubscription=*/ true)); 82 propertyInfo.put(VEHICLE_IN_USE, new PropertyInfo(/*needSubscription=*/ false)); 83 propertyInfo.put(AP_POWER_BOOTUP_REASON, new PropertyInfo(/*needSubscription=*/ false)); 84 propertyInfo.put(PER_DISPLAY_MAX_BRIGHTNESS, new PropertyInfo(/*needSubscription=*/ false)); 85 return propertyInfo; 86 } 87 88 private static final SparseArray<PropertyInfo> SUPPORTED_PROPERTIES = getSupportedProperties(); 89 90 /** 91 * Unknown bootup reason. 92 */ 93 public static final int BOOTUP_REASON_UNKNOWN = -1; 94 95 /** 96 * Power on due to user's pressing of power key or rotating of ignition switch. 97 */ 98 public static final int BOOTUP_REASON_USER_POWER_ON = 0; 99 100 /** 101 * Automatic power on triggered by door unlock or any other kind of automatic user detection. 102 */ 103 public static final int BOOTUP_REASON_SYSTEM_USER_DETECTION = 1; 104 105 /** 106 * Automatic power on to execute a remote task. This is triggered by receiving a wakeup message 107 * from an external system in the vehicle. 108 */ 109 public static final int BOOTUP_REASON_SYSTEM_REMOTE_ACCESS = 2; 110 111 /** 112 * Automatic power on to enter garage mode. This is triggered by receiving a wakeup message from 113 * an external system in the vehicle. 114 */ 115 public static final int BOOTUP_REASON_SYSTEM_ENTER_GARAGE_MODE = 3; 116 117 /** @hide */ 118 @IntDef(prefix = {"BOOTUP_REASON_"}, value = { 119 BOOTUP_REASON_UNKNOWN, 120 BOOTUP_REASON_USER_POWER_ON, 121 BOOTUP_REASON_SYSTEM_USER_DETECTION, 122 BOOTUP_REASON_SYSTEM_REMOTE_ACCESS, 123 BOOTUP_REASON_SYSTEM_ENTER_GARAGE_MODE, 124 }) 125 @Retention(RetentionPolicy.SOURCE) 126 public @interface BootupReason {} 127 128 @VisibleForTesting 129 public static final int SET_WAIT_FOR_VHAL = VehicleApPowerStateReport.WAIT_FOR_VHAL; 130 @VisibleForTesting 131 public static final int SET_DEEP_SLEEP_ENTRY = VehicleApPowerStateReport.DEEP_SLEEP_ENTRY; 132 @VisibleForTesting 133 public static final int SET_DEEP_SLEEP_EXIT = VehicleApPowerStateReport.DEEP_SLEEP_EXIT; 134 @VisibleForTesting 135 public static final int SET_SHUTDOWN_POSTPONE = VehicleApPowerStateReport.SHUTDOWN_POSTPONE; 136 @VisibleForTesting 137 public static final int SET_SHUTDOWN_START = VehicleApPowerStateReport.SHUTDOWN_START; 138 @VisibleForTesting 139 public static final int SET_ON = VehicleApPowerStateReport.ON; 140 @VisibleForTesting 141 public static final int SET_SHUTDOWN_PREPARE = VehicleApPowerStateReport.SHUTDOWN_PREPARE; 142 @VisibleForTesting 143 public static final int SET_SHUTDOWN_CANCELLED = VehicleApPowerStateReport.SHUTDOWN_CANCELLED; 144 145 @VisibleForTesting 146 public static final int SHUTDOWN_CAN_SLEEP = VehicleApPowerStateShutdownParam.CAN_SLEEP; 147 @VisibleForTesting 148 public static final int SHUTDOWN_IMMEDIATELY = 149 VehicleApPowerStateShutdownParam.SHUTDOWN_IMMEDIATELY; 150 @VisibleForTesting 151 public static final int SHUTDOWN_ONLY = VehicleApPowerStateShutdownParam.SHUTDOWN_ONLY; 152 @VisibleForTesting 153 public static final int SET_HIBERNATION_ENTRY = VehicleApPowerStateReport.HIBERNATION_ENTRY; 154 @VisibleForTesting 155 public static final int SET_HIBERNATION_EXIT = VehicleApPowerStateReport.HIBERNATION_EXIT; 156 157 private final Object mLock = new Object(); 158 powerStateReportName(int state)159 private static String powerStateReportName(int state) { 160 String baseName; 161 switch(state) { 162 case SET_WAIT_FOR_VHAL: baseName = "WAIT_FOR_VHAL"; break; 163 case SET_DEEP_SLEEP_ENTRY: baseName = "DEEP_SLEEP_ENTRY"; break; 164 case SET_DEEP_SLEEP_EXIT: baseName = "DEEP_SLEEP_EXIT"; break; 165 case SET_SHUTDOWN_POSTPONE: baseName = "SHUTDOWN_POSTPONE"; break; 166 case SET_SHUTDOWN_START: baseName = "SHUTDOWN_START"; break; 167 case SET_ON: baseName = "ON"; break; 168 case SET_SHUTDOWN_PREPARE: baseName = "SHUTDOWN_PREPARE"; break; 169 case SET_SHUTDOWN_CANCELLED: baseName = "SHUTDOWN_CANCELLED"; break; 170 case SET_HIBERNATION_ENTRY: baseName = "HIBERNATION_ENTRY"; break; 171 case SET_HIBERNATION_EXIT: baseName = "HIBERNATION_EXIT"; break; 172 default: baseName = "<unknown>"; break; 173 } 174 return baseName + "(" + state + ")"; 175 } 176 powerStateReqName(int state)177 private static String powerStateReqName(int state) { 178 String baseName; 179 switch(state) { 180 case VehicleApPowerStateReq.ON: baseName = "ON"; break; 181 case VehicleApPowerStateReq.SHUTDOWN_PREPARE: baseName = "SHUTDOWN_PREPARE"; break; 182 case VehicleApPowerStateReq.CANCEL_SHUTDOWN: baseName = "CANCEL_SHUTDOWN"; break; 183 case VehicleApPowerStateReq.FINISHED: baseName = "FINISHED"; break; 184 default: baseName = "<unknown>"; break; 185 } 186 return baseName + "(" + state + ")"; 187 } 188 189 /** 190 * Interface to be implemented by any object that wants to be notified by any Vehicle's power 191 * change. 192 */ 193 public interface PowerEventListener { 194 /** 195 * Received power state change event. 196 * @param state One of STATE_* 197 */ onApPowerStateChange(PowerState state)198 void onApPowerStateChange(PowerState state); 199 200 /** 201 * Received display brightness change event. 202 * @param brightness in percentile. 100% full. 203 */ onDisplayBrightnessChange(int brightness)204 void onDisplayBrightnessChange(int brightness); 205 206 /** 207 * Received display brightness change event. 208 * @param displayId the display id. 209 * @param brightness in percentile. 100% full. 210 */ onDisplayBrightnessChange(int displayId, int brightness)211 void onDisplayBrightnessChange(int displayId, int brightness); 212 } 213 214 /** 215 * Contains information about the Vehicle's power state. 216 */ 217 public static final class PowerState { 218 219 @IntDef({SHUTDOWN_TYPE_UNDEFINED, SHUTDOWN_TYPE_POWER_OFF, SHUTDOWN_TYPE_DEEP_SLEEP, 220 SHUTDOWN_TYPE_HIBERNATION, SHUTDOWN_TYPE_EMERGENCY}) 221 @Retention(RetentionPolicy.SOURCE) 222 public @interface ShutdownType {} 223 224 public static final int SHUTDOWN_TYPE_UNDEFINED = 0; 225 public static final int SHUTDOWN_TYPE_POWER_OFF = 1; 226 public static final int SHUTDOWN_TYPE_DEEP_SLEEP = 2; 227 public static final int SHUTDOWN_TYPE_HIBERNATION = 3; 228 public static final int SHUTDOWN_TYPE_EMERGENCY = 4; 229 /** 230 * One of STATE_* 231 */ 232 public final int mState; 233 public final int mParam; 234 PowerState(int state, int param)235 public PowerState(int state, int param) { 236 this.mState = state; 237 this.mParam = param; 238 } 239 240 /** 241 * Whether the current PowerState allows postponing or not. Calling this for 242 * power state other than STATE_SHUTDOWN_PREPARE will trigger exception. 243 * @return 244 * @throws IllegalStateException 245 */ canPostponeShutdown()246 public boolean canPostponeShutdown() { 247 if (mState != VehicleApPowerStateReq.SHUTDOWN_PREPARE) { 248 throw new IllegalStateException("wrong state"); 249 } 250 return (mParam != VehicleApPowerStateShutdownParam.SHUTDOWN_IMMEDIATELY 251 && mParam != VehicleApPowerStateShutdownParam.SLEEP_IMMEDIATELY 252 && mParam != VehicleApPowerStateShutdownParam.HIBERNATE_IMMEDIATELY 253 && mParam != VehicleApPowerStateShutdownParam.EMERGENCY_SHUTDOWN); 254 } 255 256 /** 257 * Gets whether the current PowerState allows suspend or not. 258 * 259 * @throws IllegalStateException if called in state other than {@code 260 * STATE_SHUTDOWN_PREPARE} 261 */ canSuspend()262 public boolean canSuspend() { 263 Preconditions.checkArgument(mState == VehicleApPowerStateReq.SHUTDOWN_PREPARE, 264 "canSuspend was called in the wrong state! State = %d", mState); 265 266 return (mParam == VehicleApPowerStateShutdownParam.CAN_HIBERNATE 267 || mParam == VehicleApPowerStateShutdownParam.HIBERNATE_IMMEDIATELY 268 || mParam == VehicleApPowerStateShutdownParam.CAN_SLEEP 269 || mParam == VehicleApPowerStateShutdownParam.SLEEP_IMMEDIATELY); 270 } 271 272 /** 273 * Gets shutdown type 274 * 275 * @return {@code ShutdownType} - type of shutdown 276 * @throws IllegalStateException if called in state other than {@code 277 * STATE_SHUTDOWN_PREPARE} 278 */ 279 @ShutdownType getShutdownType()280 public int getShutdownType() { 281 Preconditions.checkArgument(mState == VehicleApPowerStateReq.SHUTDOWN_PREPARE, 282 "getShutdownType was called in the wrong state! State = %d", mState); 283 284 int result = SHUTDOWN_TYPE_POWER_OFF; 285 if (mParam == VehicleApPowerStateShutdownParam.CAN_SLEEP 286 || mParam == VehicleApPowerStateShutdownParam.SLEEP_IMMEDIATELY) { 287 result = SHUTDOWN_TYPE_DEEP_SLEEP; 288 } else if (mParam == VehicleApPowerStateShutdownParam.CAN_HIBERNATE 289 || mParam == VehicleApPowerStateShutdownParam.HIBERNATE_IMMEDIATELY) { 290 result = SHUTDOWN_TYPE_HIBERNATION; 291 } else if (mParam == VehicleApPowerStateShutdownParam.EMERGENCY_SHUTDOWN) { 292 result = SHUTDOWN_TYPE_EMERGENCY; 293 } 294 295 return result; 296 } 297 298 @Override equals(Object o)299 public boolean equals(Object o) { 300 if (this == o) { 301 return true; 302 } 303 if (!(o instanceof PowerState)) { 304 return false; 305 } 306 PowerState that = (PowerState) o; 307 return this.mState == that.mState && this.mParam == that.mParam; 308 } 309 310 @Override hashCode()311 public int hashCode() { 312 return Objects.hash(mState, mParam); 313 } 314 315 @Override toString()316 public String toString() { 317 return "PowerState state:" + mState + ", param:" + mParam; 318 } 319 } 320 321 @GuardedBy("mLock") 322 private final SparseArray<HalPropConfig> mProperties = new SparseArray<>(); 323 private final Context mContext; 324 private final VehicleHal mHal; 325 private final FeatureFlags mFeatureFlags; 326 @Nullable 327 @GuardedBy("mLock") 328 private ArrayList<HalPropValue> mQueuedEvents; 329 @GuardedBy("mLock") 330 private PowerEventListener mListener; 331 @GuardedBy("mLock") 332 private int mMaxDisplayBrightness; 333 @GuardedBy("mLock") 334 private SparseIntArray mMaxPerDisplayBrightness = new SparseIntArray(); 335 @GuardedBy("mLock") 336 private boolean mPerDisplayBrightnessSupported; 337 PowerHalService(Context context, FeatureFlags featureFlags, VehicleHal hal)338 public PowerHalService(Context context, FeatureFlags featureFlags, VehicleHal hal) { 339 mContext = context; 340 mFeatureFlags = featureFlags; 341 mHal = hal; 342 } 343 344 /** 345 * Sets the event listener to receive Vehicle's power events. 346 */ setListener(PowerEventListener listener)347 public void setListener(PowerEventListener listener) { 348 ArrayList<HalPropValue> eventsToDispatch = null; 349 synchronized (mLock) { 350 mListener = listener; 351 if (mQueuedEvents != null && !mQueuedEvents.isEmpty()) { 352 eventsToDispatch = mQueuedEvents; 353 } 354 mQueuedEvents = null; 355 } 356 // do this outside lock 357 if (eventsToDispatch != null) { 358 dispatchEvents(eventsToDispatch, listener); 359 } 360 } 361 362 /** 363 * Send WaitForVhal message to VHAL 364 */ sendWaitForVhal()365 public void sendWaitForVhal() { 366 Slogf.i(CarLog.TAG_POWER, "send wait for vhal"); 367 setPowerState(VehicleApPowerStateReport.WAIT_FOR_VHAL, 0); 368 } 369 370 /** 371 * Send SleepEntry message to VHAL 372 * @param wakeupTimeSec Notify VHAL when system wants to be woken from sleep. 373 */ sendSleepEntry(int wakeupTimeSec)374 public void sendSleepEntry(int wakeupTimeSec) { 375 Slogf.i(CarLog.TAG_POWER, "send sleep entry"); 376 setPowerState(VehicleApPowerStateReport.DEEP_SLEEP_ENTRY, wakeupTimeSec); 377 } 378 379 /** 380 * Send SleepExit message to VHAL 381 * Notifies VHAL when SOC has woken. 382 */ sendSleepExit()383 public void sendSleepExit() { 384 Slogf.i(CarLog.TAG_POWER, "send sleep exit"); 385 setPowerState(VehicleApPowerStateReport.DEEP_SLEEP_EXIT, 0); 386 } 387 388 /** 389 * Sends HibernationEntry message to VHAL 390 * 391 * @param wakeupTimeSec Number of seconds from now to be woken from sleep. 392 */ sendHibernationEntry(int wakeupTimeSec)393 public void sendHibernationEntry(int wakeupTimeSec) { 394 Slogf.i(CarLog.TAG_POWER, "send hibernation entry - wakeupTimeSec = %d", 395 wakeupTimeSec); 396 setPowerState(VehicleApPowerStateReport.HIBERNATION_ENTRY, wakeupTimeSec); 397 } 398 399 /** 400 * Sends HibernationExit message to VHAL 401 * 402 * Notifies VHAL after SOC woke up from hibernation. 403 */ sendHibernationExit()404 public void sendHibernationExit() { 405 Slogf.i(CarLog.TAG_POWER, "send hibernation exit"); 406 setPowerState(VehicleApPowerStateReport.HIBERNATION_EXIT, 0); 407 } 408 409 /** 410 * Send Shutdown Postpone message to VHAL 411 */ sendShutdownPostpone(int postponeTimeMs)412 public void sendShutdownPostpone(int postponeTimeMs) { 413 Slogf.i(CarLog.TAG_POWER, "send shutdown postpone, time:" + postponeTimeMs); 414 setPowerState(VehicleApPowerStateReport.SHUTDOWN_POSTPONE, postponeTimeMs); 415 } 416 417 /** 418 * Send Shutdown Start message to VHAL 419 */ sendShutdownStart(int wakeupTimeSec)420 public void sendShutdownStart(int wakeupTimeSec) { 421 Slogf.i(CarLog.TAG_POWER, "send shutdown start"); 422 setPowerState(VehicleApPowerStateReport.SHUTDOWN_START, wakeupTimeSec); 423 } 424 425 /** 426 * Send On message to VHAL 427 */ sendOn()428 public void sendOn() { 429 Slogf.i(CarLog.TAG_POWER, "send on"); 430 setPowerState(VehicleApPowerStateReport.ON, 0); 431 } 432 433 /** 434 * Send Shutdown Prepare message to VHAL 435 */ sendShutdownPrepare()436 public void sendShutdownPrepare() { 437 Slogf.i(CarLog.TAG_POWER, "send shutdown prepare"); 438 setPowerState(VehicleApPowerStateReport.SHUTDOWN_PREPARE, 0); 439 } 440 441 /** 442 * Send Shutdown Cancel message to VHAL 443 */ sendShutdownCancel()444 public void sendShutdownCancel() { 445 Slogf.i(CarLog.TAG_POWER, "send shutdown cancel"); 446 setPowerState(VehicleApPowerStateReport.SHUTDOWN_CANCELLED, 0); 447 } 448 449 /** 450 * Sets the display brightness for the vehicle. 451 * @param brightness value from 0 to 100. 452 */ sendDisplayBrightness(int brightness)453 public void sendDisplayBrightness(int brightness) { 454 int brightnessToSet = adjustBrightness(brightness, /* minBrightness= */ 0, 455 /* maxBrightness= */ 100); 456 457 synchronized (mLock) { 458 if (mProperties.get(DISPLAY_BRIGHTNESS) == null) { 459 return; 460 } 461 if (mPerDisplayBrightnessSupported) { 462 Slogf.w(CarLog.TAG_POWER, "PER_DISPLAY_BRIGHTNESS is supported and " 463 + "sendDisplayBrightness(int displayId, int brightness) should be used " 464 + "instead of DISPLAY_BRIGHTNESS"); 465 return; 466 } 467 } 468 try { 469 mHal.set(VehicleProperty.DISPLAY_BRIGHTNESS, 0).to(brightnessToSet); 470 Slogf.i(CarLog.TAG_POWER, "send display brightness = " + brightnessToSet); 471 } catch (ServiceSpecificException | IllegalArgumentException e) { 472 Slogf.e(CarLog.TAG_POWER, "cannot set DISPLAY_BRIGHTNESS", e); 473 } 474 } 475 476 /** 477 * Received display brightness change event. 478 * @param displayId the display id. 479 * @param brightness in percentile. 100% full. 480 */ sendDisplayBrightness(int displayId, int brightness)481 public void sendDisplayBrightness(int displayId, int brightness) { 482 int brightnessToSet = adjustBrightness(brightness, /* minBrightness= */ 0, 483 /* maxBrightness= */ 100); 484 485 synchronized (mLock) { 486 if (!mPerDisplayBrightnessSupported) { 487 Slogf.w(CarLog.TAG_POWER, "PER_DISPLAY_BRIGHTNESS is not supported"); 488 return; 489 } 490 } 491 int displayPort = getDisplayPort(displayId); 492 if (displayPort == DisplayHelper.INVALID_PORT) { 493 return; 494 } 495 try { 496 HalPropValue value = mHal.getHalPropValueBuilder() 497 .build(PER_DISPLAY_BRIGHTNESS, /* areaId= */ 0, 498 new int[]{displayPort, brightnessToSet}); 499 mHal.set(value); 500 Slogf.i(CarLog.TAG_POWER, "send display brightness = %d, port = %d", 501 brightnessToSet, displayPort); 502 } catch (ServiceSpecificException e) { 503 Slogf.e(CarLog.TAG_POWER, e, "cannot set PER_DISPLAY_BRIGHTNESS port = %d", 504 displayPort); 505 } 506 } 507 508 /** 509 * Sends {@code SHUTDOWN_REQUEST} to the VHAL. 510 */ requestShutdownAp(@owerState.ShutdownType int powerState, boolean runGarageMode)511 public void requestShutdownAp(@PowerState.ShutdownType int powerState, boolean runGarageMode) { 512 int shutdownParam = VehicleApPowerStateShutdownParam.SHUTDOWN_IMMEDIATELY; 513 switch (powerState) { 514 case PowerState.SHUTDOWN_TYPE_POWER_OFF: 515 shutdownParam = runGarageMode ? VehicleApPowerStateShutdownParam.SHUTDOWN_ONLY 516 : VehicleApPowerStateShutdownParam.SHUTDOWN_IMMEDIATELY; 517 break; 518 case PowerState.SHUTDOWN_TYPE_DEEP_SLEEP: 519 shutdownParam = runGarageMode ? VehicleApPowerStateShutdownParam.CAN_SLEEP 520 : VehicleApPowerStateShutdownParam.SLEEP_IMMEDIATELY; 521 break; 522 case PowerState.SHUTDOWN_TYPE_HIBERNATION: 523 shutdownParam = runGarageMode ? VehicleApPowerStateShutdownParam.CAN_HIBERNATE 524 : VehicleApPowerStateShutdownParam.HIBERNATE_IMMEDIATELY; 525 break; 526 case PowerState.SHUTDOWN_TYPE_UNDEFINED: 527 default: 528 Slogf.w(CarLog.TAG_POWER, "Unknown power state(%d) for requestShutdownAp", 529 powerState); 530 return; 531 } 532 533 try { 534 mHal.set(SHUTDOWN_REQUEST, /* areaId= */ 0).to(shutdownParam); 535 } catch (ServiceSpecificException | IllegalArgumentException e) { 536 Slogf.e(CarLog.TAG_POWER, "cannot send SHUTDOWN_REQUEST to VHAL", e); 537 } 538 } 539 setPowerState(int state, int additionalParam)540 private void setPowerState(int state, int additionalParam) { 541 if (isPowerStateSupported()) { 542 int[] values = { state, additionalParam }; 543 try { 544 mHal.set(VehicleProperty.AP_POWER_STATE_REPORT, 0).to(values); 545 Slogf.i(CarLog.TAG_POWER, "setPowerState=" + powerStateReportName(state) 546 + " param=" + additionalParam); 547 } catch (ServiceSpecificException e) { 548 Slogf.e(CarLog.TAG_POWER, "cannot set to AP_POWER_STATE_REPORT", e); 549 } 550 } 551 } 552 553 /** 554 * Returns a {@link PowerState} representing the current power state for the vehicle. 555 */ 556 @Nullable getCurrentPowerState()557 public PowerState getCurrentPowerState() { 558 HalPropValue value; 559 try { 560 value = mHal.get(VehicleProperty.AP_POWER_STATE_REQ); 561 } catch (ServiceSpecificException e) { 562 Slogf.e(CarLog.TAG_POWER, "Cannot get AP_POWER_STATE_REQ", e); 563 return null; 564 } 565 return new PowerState(value.getInt32Value(VehicleApPowerStateReqIndex.STATE), 566 value.getInt32Value(VehicleApPowerStateReqIndex.ADDITIONAL)); 567 } 568 569 /** 570 * Determines if the current properties describe a valid power state 571 * @return true if both the power state request and power state report are valid 572 */ isPowerStateSupported()573 public boolean isPowerStateSupported() { 574 synchronized (mLock) { 575 return (mProperties.get(VehicleProperty.AP_POWER_STATE_REQ) != null) 576 && (mProperties.get(VehicleProperty.AP_POWER_STATE_REPORT) != null); 577 } 578 } 579 580 /** 581 * Returns if the vehicle is currently in use. 582 * 583 * In use means a human user is present in the vehicle and is currently using the vehicle or 584 * will use the vehicle soon. 585 */ isVehicleInUse()586 public boolean isVehicleInUse() { 587 try { 588 HalPropValue value = mHal.get(VEHICLE_IN_USE); 589 return (value.getStatus() == VehiclePropertyStatus.AVAILABLE 590 && value.getInt32ValuesSize() >= 1 && value.getInt32Value(0) != 0); 591 } catch (ServiceSpecificException | IllegalArgumentException e) { 592 Slogf.w(CarLog.TAG_POWER, 593 "Failed to get VEHICLE_IN_USE value, assume vehicle is in use", e); 594 return true; 595 } 596 } 597 598 /** 599 * Returns whether {@code VEHICLE_IN_USE} is supported and getting it returns a valid value. 600 */ isVehicleInUseSupported()601 public boolean isVehicleInUseSupported() { 602 try { 603 HalPropValue value = mHal.get(VEHICLE_IN_USE); 604 if (value.getStatus() != VehiclePropertyStatus.AVAILABLE) { 605 Slogf.w(CarLog.TAG_POWER, 606 "VEHICLE_IN_USE is supported in config but getting it returns a property " 607 + "value: " + value + " which does not contain AVAILABLE status"); 608 return false; 609 } 610 return true; 611 } catch (ServiceSpecificException | IllegalArgumentException e) { 612 Slogf.w(CarLog.TAG_POWER, "VEHICLE_IN_USE is not supported", e); 613 return false; 614 } 615 } 616 617 /** 618 * Returns whether {@code SHUTDOWN_REQUEST} is supported 619 */ isShutdownRequestSupported()620 public boolean isShutdownRequestSupported() { 621 return mHal.getPropConfig(SHUTDOWN_REQUEST) != null; 622 } 623 624 /** 625 * Gets the head unit's bootup reason. 626 * 627 * This reason is only set once during bootup and will not change if, say user enters the 628 * vehicle after the vehicle was booted up for remote access. 629 */ getVehicleApBootupReason()630 public @BootupReason int getVehicleApBootupReason() { 631 try { 632 HalPropValue value = mHal.get(AP_POWER_BOOTUP_REASON); 633 if (value.getStatus() != VehiclePropertyStatus.AVAILABLE) { 634 Slogf.w(CarLog.TAG_POWER, "AP_POWER_BOOTUP_REASON is not available"); 635 return BOOTUP_REASON_UNKNOWN; 636 } 637 if (value.getInt32ValuesSize() < 1) { 638 Slogf.w(CarLog.TAG_POWER, "Invalid AP_POWER_BOOTUP_REASON, no value"); 639 return BOOTUP_REASON_UNKNOWN; 640 } 641 switch (value.getInt32Value(0)) { 642 case VehicleApPowerBootupReason.USER_POWER_ON: 643 return BOOTUP_REASON_USER_POWER_ON; 644 case VehicleApPowerBootupReason.SYSTEM_USER_DETECTION: 645 return BOOTUP_REASON_SYSTEM_USER_DETECTION; 646 case VehicleApPowerBootupReason.SYSTEM_REMOTE_ACCESS: 647 return BOOTUP_REASON_SYSTEM_REMOTE_ACCESS; 648 case VehicleApPowerBootupReason.SYSTEM_ENTER_GARAGE_MODE: 649 return BOOTUP_REASON_SYSTEM_ENTER_GARAGE_MODE; 650 default: 651 return BOOTUP_REASON_UNKNOWN; 652 } 653 } catch (ServiceSpecificException | IllegalArgumentException e) { 654 Slogf.w(CarLog.TAG_POWER, "Failed to get AP_POWER_BOOTUP_REASON value", e); 655 } 656 return BOOTUP_REASON_UNKNOWN; 657 } 658 isConfigFlagSet(int flag)659 private boolean isConfigFlagSet(int flag) { 660 HalPropConfig config; 661 synchronized (mLock) { 662 config = mProperties.get(VehicleProperty.AP_POWER_STATE_REQ); 663 } 664 if (config == null) { 665 return false; 666 } 667 int[] configArray = config.getConfigArray(); 668 if (configArray.length < 1) { 669 return false; 670 } 671 return (configArray[0] & flag) != 0; 672 } 673 isDeepSleepAllowed()674 public boolean isDeepSleepAllowed() { 675 return isConfigFlagSet(VehicleApPowerStateConfigFlag.ENABLE_DEEP_SLEEP_FLAG); 676 } 677 isHibernationAllowed()678 public boolean isHibernationAllowed() { 679 return isConfigFlagSet(VehicleApPowerStateConfigFlag.ENABLE_HIBERNATION_FLAG); 680 } 681 isTimedWakeupAllowed()682 public boolean isTimedWakeupAllowed() { 683 return isConfigFlagSet(VehicleApPowerStateConfigFlag.CONFIG_SUPPORT_TIMER_POWER_ON_FLAG); 684 } 685 686 @Override init()687 public void init() { 688 synchronized (mLock) { 689 for (int i = 0; i < mProperties.size(); i++) { 690 int propId = mProperties.valueAt(i).getPropId(); 691 if (mProperties.contains(propId) 692 && SUPPORTED_PROPERTIES.get(propId).needSubscription) { 693 mHal.subscribeProperty(this, propId); 694 } 695 } 696 HalPropConfig brightnessProperty = mProperties.get(PER_DISPLAY_BRIGHTNESS); 697 mPerDisplayBrightnessSupported = brightnessProperty != null; 698 if (brightnessProperty == null) { 699 brightnessProperty = mProperties.get(DISPLAY_BRIGHTNESS); 700 } 701 if (brightnessProperty != null) { 702 HalAreaConfig[] areaConfigs = brightnessProperty.getAreaConfigs(); 703 mMaxDisplayBrightness = areaConfigs.length > 0 704 ? areaConfigs[0].getMaxInt32Value() : 0; 705 if (mMaxDisplayBrightness <= 0) { 706 Slogf.w(CarLog.TAG_POWER, "Max display brightness from vehicle HAL is invalid:" 707 + mMaxDisplayBrightness); 708 mMaxDisplayBrightness = 1; 709 } 710 711 if (mFeatureFlags.perDisplayMaxBrightness()) { 712 getMaxPerDisplayBrightnessFromVhalLocked(); 713 } 714 } 715 } 716 } 717 718 @GuardedBy("mLock") getMaxPerDisplayBrightnessFromVhalLocked()719 private void getMaxPerDisplayBrightnessFromVhalLocked() { 720 if (!mPerDisplayBrightnessSupported 721 || !mProperties.contains(PER_DISPLAY_MAX_BRIGHTNESS)) { 722 return; 723 } 724 725 try { 726 HalPropValue value = mHal.get(PER_DISPLAY_MAX_BRIGHTNESS); 727 for (int i = 0; i + 1 < value.getInt32ValuesSize(); i += 2) { 728 int displayPort = value.getInt32Value(i); 729 int maxDisplayBrightness = value.getInt32Value(i + 1); 730 if (maxDisplayBrightness <= 0) { 731 Slogf.w(CarLog.TAG_POWER, 732 "Max display brightness from vehicle HAL for display port: %d is " 733 + "invalid: %d", displayPort, maxDisplayBrightness); 734 maxDisplayBrightness = 1; 735 } 736 mMaxPerDisplayBrightness.put(displayPort, maxDisplayBrightness); 737 } 738 } catch (ServiceSpecificException e) { 739 Slogf.e(CarLog.TAG_POWER, "Cannot get PER_DISPLAY_MAX_BRIGHTNESS", e); 740 } 741 742 } 743 744 @Override release()745 public void release() { 746 synchronized (mLock) { 747 for (int i = 0; i < mProperties.size(); i++) { 748 int propId = mProperties.valueAt(i).getPropId(); 749 if (SUPPORTED_PROPERTIES.get(propId).needSubscription) { 750 mHal.unsubscribePropertySafe(this, propId); 751 } 752 } 753 mProperties.clear(); 754 } 755 } 756 757 @Override getAllSupportedProperties()758 public int[] getAllSupportedProperties() { 759 int[] propertyIds = new int[SUPPORTED_PROPERTIES.size()]; 760 for (int i = 0; i < SUPPORTED_PROPERTIES.size(); i++) { 761 propertyIds[i] = SUPPORTED_PROPERTIES.keyAt(i); 762 } 763 return propertyIds; 764 } 765 766 @Override takeProperties(Collection<HalPropConfig> properties)767 public void takeProperties(Collection<HalPropConfig> properties) { 768 if (properties.isEmpty()) { 769 return; 770 } 771 synchronized (mLock) { 772 for (HalPropConfig config : properties) { 773 mProperties.put(config.getPropId(), config); 774 } 775 } 776 } 777 778 @Override onHalEvents(List<HalPropValue> values)779 public void onHalEvents(List<HalPropValue> values) { 780 PowerEventListener listener; 781 synchronized (mLock) { 782 if (mListener == null) { 783 if (mQueuedEvents == null) { 784 mQueuedEvents = new ArrayList<>(values.size()); 785 } 786 mQueuedEvents.addAll(values); 787 return; 788 } 789 listener = mListener; 790 } 791 dispatchEvents(values, listener); 792 } 793 dispatchEvents(List<HalPropValue> values, PowerEventListener listener)794 private void dispatchEvents(List<HalPropValue> values, PowerEventListener listener) { 795 for (int i = 0; i < values.size(); i++) { 796 HalPropValue v = values.get(i); 797 switch (v.getPropId()) { 798 case AP_POWER_STATE_REPORT: 799 // Ignore this property event. It was generated inside of CarService. 800 break; 801 case AP_POWER_STATE_REQ: 802 int state; 803 int param; 804 try { 805 state = v.getInt32Value(VehicleApPowerStateReqIndex.STATE); 806 param = v.getInt32Value(VehicleApPowerStateReqIndex.ADDITIONAL); 807 } catch (IndexOutOfBoundsException e) { 808 Slogf.e(CarLog.TAG_POWER, "Received invalid event, ignore, int32Values: " 809 + v.dumpInt32Values(), e); 810 break; 811 } 812 Slogf.i(CarLog.TAG_POWER, "Received AP_POWER_STATE_REQ=" 813 + powerStateReqName(state) + " param=" + param); 814 listener.onApPowerStateChange(new PowerState(state, param)); 815 break; 816 case DISPLAY_BRIGHTNESS: 817 { 818 int maxBrightness; 819 synchronized (mLock) { 820 if (mPerDisplayBrightnessSupported) { 821 Slogf.w(CarLog.TAG_POWER, "Received DISPLAY_BRIGHTNESS " 822 + "while PER_DISPLAY_BRIGHTNESS is supported, ignore"); 823 return; 824 } 825 maxBrightness = mMaxDisplayBrightness; 826 } 827 int brightness; 828 try { 829 brightness = v.getInt32Value(0) * MAX_BRIGHTNESS / maxBrightness; 830 } catch (IndexOutOfBoundsException e) { 831 Slogf.e(CarLog.TAG_POWER, "Received invalid event, ignore, int32Values: " 832 + v.dumpInt32Values(), e); 833 break; 834 } 835 if (brightness < 0) { 836 Slogf.e(CarLog.TAG_POWER, "invalid brightness: " + brightness 837 + ", set to 0"); 838 brightness = 0; 839 } else if (brightness > MAX_BRIGHTNESS) { 840 Slogf.e(CarLog.TAG_POWER, "invalid brightness: " + brightness + ", set to " 841 + MAX_BRIGHTNESS); 842 brightness = MAX_BRIGHTNESS; 843 } 844 Slogf.i(CarLog.TAG_POWER, "Received DISPLAY_BRIGHTNESS=" + brightness); 845 listener.onDisplayBrightnessChange(brightness); 846 break; 847 } 848 case PER_DISPLAY_BRIGHTNESS: 849 { 850 int displayPort; 851 int brightness; 852 try { 853 displayPort = v.getInt32Value(0); 854 brightness = v.getInt32Value(1); 855 } catch (IndexOutOfBoundsException e) { 856 Slogf.e(CarLog.TAG_POWER, "Received invalid event, ignore, int32Values: " 857 + v.dumpInt32Values(), e); 858 break; 859 } 860 int maxBrightness; 861 synchronized (mLock) { 862 if (!mFeatureFlags.perDisplayMaxBrightness() 863 || mMaxPerDisplayBrightness.size() == 0) { 864 maxBrightness = mMaxDisplayBrightness; 865 } else { 866 maxBrightness = mMaxPerDisplayBrightness.get(displayPort, 867 /* valueIfKeyNotFound= */ 1); 868 } 869 } 870 brightness = brightness * MAX_BRIGHTNESS / maxBrightness; 871 brightness = adjustBrightness(brightness, /* minBrightness= */ 0, 872 MAX_BRIGHTNESS); 873 Slogf.i(CarLog.TAG_POWER, "Received PER_DISPLAY_BRIGHTNESS=" + brightness 874 + ", displayPort=" + displayPort); 875 int displayId = getDisplayId(displayPort); 876 listener.onDisplayBrightnessChange(displayId, brightness); 877 break; 878 } 879 default: 880 Slogf.w(CarLog.TAG_POWER, "Received event with invalid property id: %d", 881 v.getPropId()); 882 break; 883 } 884 } 885 } 886 getDisplayId(int displayPort)887 private int getDisplayId(int displayPort) { 888 DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); 889 int displayId = Display.DEFAULT_DISPLAY; 890 for (Display display : displayManager.getDisplays()) { 891 if (displayPort == DisplayHelper.getPhysicalPort(display)) { 892 displayId = display.getDisplayId(); 893 break; 894 } 895 } 896 return displayId; 897 } 898 getDisplayPort(int displayId)899 private int getDisplayPort(int displayId) { 900 DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); 901 Display display = displayManager.getDisplay(displayId); 902 if (display != null) { 903 int displayPort = DisplayHelper.getPhysicalPort(display); 904 if (displayPort != DisplayHelper.INVALID_PORT) { 905 return displayPort; 906 } 907 } 908 Slogf.w(CarLog.TAG_POWER, "cannot get display port from displayId = %d", 909 displayId); 910 return DisplayHelper.INVALID_PORT; 911 } 912 adjustBrightness(int brightness, int minBrightness, int maxBrightness)913 private int adjustBrightness(int brightness, int minBrightness, int maxBrightness) { 914 if (brightness < minBrightness) { 915 Slogf.w(CarLog.TAG_POWER, "invalid brightness: %d, brightness is set to %d", brightness, 916 minBrightness); 917 brightness = minBrightness; 918 } else if (brightness > maxBrightness) { 919 Slogf.w(CarLog.TAG_POWER, "invalid brightness: %d, brightness is set to %d", brightness, 920 maxBrightness); 921 brightness = maxBrightness; 922 } 923 return brightness; 924 } 925 926 @Override 927 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dump(PrintWriter writer)928 public void dump(PrintWriter writer) { 929 writer.println("*Power HAL*"); 930 writer.printf("isPowerStateSupported:%b, isDeepSleepAllowed:%b, isHibernationAllowed:%b\n", 931 isPowerStateSupported(), isDeepSleepAllowed(), isHibernationAllowed()); 932 933 } 934 } 935