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 19 import static android.hardware.automotive.vehicle.V2_0.VehicleProperty.AP_POWER_STATE_REPORT; 20 import static android.hardware.automotive.vehicle.V2_0.VehicleProperty.AP_POWER_STATE_REQ; 21 import static android.hardware.automotive.vehicle.V2_0.VehicleProperty.DISPLAY_BRIGHTNESS; 22 23 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; 24 25 import android.annotation.Nullable; 26 import android.hardware.automotive.vehicle.V2_0.VehicleApPowerStateConfigFlag; 27 import android.hardware.automotive.vehicle.V2_0.VehicleApPowerStateReport; 28 import android.hardware.automotive.vehicle.V2_0.VehicleApPowerStateReq; 29 import android.hardware.automotive.vehicle.V2_0.VehicleApPowerStateReqIndex; 30 import android.hardware.automotive.vehicle.V2_0.VehicleApPowerStateShutdownParam; 31 import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig; 32 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue; 33 import android.hardware.automotive.vehicle.V2_0.VehicleProperty; 34 import android.os.ServiceSpecificException; 35 import android.util.Slog; 36 37 import com.android.car.CarLog; 38 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 39 import com.android.internal.annotations.GuardedBy; 40 import com.android.internal.annotations.VisibleForTesting; 41 42 import java.io.PrintWriter; 43 import java.util.Collection; 44 import java.util.HashMap; 45 import java.util.LinkedList; 46 import java.util.List; 47 48 /** 49 * Translates HAL power events to higher-level semantic information. 50 */ 51 public class PowerHalService extends HalServiceBase { 52 // Set display brightness from 0-100% 53 public static final int MAX_BRIGHTNESS = 100; 54 55 private static final int[] SUPPORTED_PROPERTIES = new int[]{ 56 AP_POWER_STATE_REQ, 57 AP_POWER_STATE_REPORT, 58 DISPLAY_BRIGHTNESS 59 }; 60 61 @VisibleForTesting 62 public static final int SET_WAIT_FOR_VHAL = VehicleApPowerStateReport.WAIT_FOR_VHAL; 63 @VisibleForTesting 64 public static final int SET_DEEP_SLEEP_ENTRY = VehicleApPowerStateReport.DEEP_SLEEP_ENTRY; 65 @VisibleForTesting 66 public static final int SET_DEEP_SLEEP_EXIT = VehicleApPowerStateReport.DEEP_SLEEP_EXIT; 67 @VisibleForTesting 68 public static final int SET_SHUTDOWN_POSTPONE = VehicleApPowerStateReport.SHUTDOWN_POSTPONE; 69 @VisibleForTesting 70 public static final int SET_SHUTDOWN_START = VehicleApPowerStateReport.SHUTDOWN_START; 71 @VisibleForTesting 72 public static final int SET_ON = VehicleApPowerStateReport.ON; 73 @VisibleForTesting 74 public static final int SET_SHUTDOWN_PREPARE = VehicleApPowerStateReport.SHUTDOWN_PREPARE; 75 @VisibleForTesting 76 public static final int SET_SHUTDOWN_CANCELLED = VehicleApPowerStateReport.SHUTDOWN_CANCELLED; 77 78 @VisibleForTesting 79 public static final int SHUTDOWN_CAN_SLEEP = VehicleApPowerStateShutdownParam.CAN_SLEEP; 80 @VisibleForTesting 81 public static final int SHUTDOWN_IMMEDIATELY = 82 VehicleApPowerStateShutdownParam.SHUTDOWN_IMMEDIATELY; 83 @VisibleForTesting 84 public static final int SHUTDOWN_ONLY = VehicleApPowerStateShutdownParam.SHUTDOWN_ONLY; 85 86 private final Object mLock = new Object(); 87 powerStateReportName(int state)88 private static String powerStateReportName(int state) { 89 String baseName; 90 switch(state) { 91 case SET_WAIT_FOR_VHAL: baseName = "WAIT_FOR_VHAL"; break; 92 case SET_DEEP_SLEEP_ENTRY: baseName = "DEEP_SLEEP_ENTRY"; break; 93 case SET_DEEP_SLEEP_EXIT: baseName = "DEEP_SLEEP_EXIT"; break; 94 case SET_SHUTDOWN_POSTPONE: baseName = "SHUTDOWN_POSTPONE"; break; 95 case SET_SHUTDOWN_START: baseName = "SHUTDOWN_START"; break; 96 case SET_ON: baseName = "ON"; break; 97 case SET_SHUTDOWN_PREPARE: baseName = "SHUTDOWN_PREPARE"; break; 98 case SET_SHUTDOWN_CANCELLED: baseName = "SHUTDOWN_CANCELLED"; break; 99 default: baseName = "<unknown>"; break; 100 } 101 return baseName + "(" + state + ")"; 102 } 103 powerStateReqName(int state)104 private static String powerStateReqName(int state) { 105 String baseName; 106 switch(state) { 107 case VehicleApPowerStateReq.ON: baseName = "ON"; break; 108 case VehicleApPowerStateReq.SHUTDOWN_PREPARE: baseName = "SHUTDOWN_PREPARE"; break; 109 case VehicleApPowerStateReq.CANCEL_SHUTDOWN: baseName = "CANCEL_SHUTDOWN"; break; 110 case VehicleApPowerStateReq.FINISHED: baseName = "FINISHED"; break; 111 default: baseName = "<unknown>"; break; 112 } 113 return baseName + "(" + state + ")"; 114 } 115 116 /** 117 * Interface to be implemented by any object that wants to be notified by any Vehicle's power 118 * change. 119 */ 120 public interface PowerEventListener { 121 /** 122 * Received power state change event. 123 * @param state One of STATE_* 124 */ onApPowerStateChange(PowerState state)125 void onApPowerStateChange(PowerState state); 126 127 /** 128 * Received display brightness change event. 129 * @param brightness in percentile. 100% full. 130 */ onDisplayBrightnessChange(int brightness)131 void onDisplayBrightnessChange(int brightness); 132 } 133 134 /** 135 * Contains information about the Vehicle's power state. 136 */ 137 public static final class PowerState { 138 /** 139 * One of STATE_* 140 */ 141 public final int mState; 142 public final int mParam; 143 PowerState(int state, int param)144 public PowerState(int state, int param) { 145 this.mState = state; 146 this.mParam = param; 147 } 148 149 /** 150 * Whether the current PowerState allows deep sleep or not. Calling this for 151 * power state other than STATE_SHUTDOWN_PREPARE will trigger exception. 152 * @return 153 * @throws IllegalStateException 154 */ canEnterDeepSleep()155 public boolean canEnterDeepSleep() { 156 if (mState != VehicleApPowerStateReq.SHUTDOWN_PREPARE) { 157 throw new IllegalStateException("wrong state"); 158 } 159 return (mParam == VehicleApPowerStateShutdownParam.CAN_SLEEP 160 || mParam == VehicleApPowerStateShutdownParam.SLEEP_IMMEDIATELY); 161 } 162 163 /** 164 * Whether the current PowerState allows postponing or not. Calling this for 165 * power state other than STATE_SHUTDOWN_PREPARE will trigger exception. 166 * @return 167 * @throws IllegalStateException 168 */ canPostponeShutdown()169 public boolean canPostponeShutdown() { 170 if (mState != VehicleApPowerStateReq.SHUTDOWN_PREPARE) { 171 throw new IllegalStateException("wrong state"); 172 } 173 return (mParam != VehicleApPowerStateShutdownParam.SHUTDOWN_IMMEDIATELY 174 && mParam != VehicleApPowerStateShutdownParam.SLEEP_IMMEDIATELY); 175 } 176 177 @Override equals(Object o)178 public boolean equals(Object o) { 179 if (this == o) { 180 return true; 181 } 182 if (!(o instanceof PowerState)) { 183 return false; 184 } 185 PowerState that = (PowerState) o; 186 return this.mState == that.mState && this.mParam == that.mParam; 187 } 188 189 @Override toString()190 public String toString() { 191 return "PowerState state:" + mState + ", param:" + mParam; 192 } 193 } 194 195 @GuardedBy("mLock") 196 private final HashMap<Integer, VehiclePropConfig> mProperties = new HashMap<>(); 197 private final VehicleHal mHal; 198 @GuardedBy("mLock") 199 private LinkedList<VehiclePropValue> mQueuedEvents; 200 @GuardedBy("mLock") 201 private PowerEventListener mListener; 202 @GuardedBy("mLock") 203 private int mMaxDisplayBrightness; 204 PowerHalService(VehicleHal hal)205 public PowerHalService(VehicleHal hal) { 206 mHal = hal; 207 } 208 209 /** 210 * Sets the event listener to receive Vehicle's power events. 211 */ setListener(PowerEventListener listener)212 public void setListener(PowerEventListener listener) { 213 LinkedList<VehiclePropValue> eventsToDispatch = null; 214 synchronized (mLock) { 215 mListener = listener; 216 if (mQueuedEvents != null && mQueuedEvents.size() > 0) { 217 eventsToDispatch = mQueuedEvents; 218 } 219 mQueuedEvents = null; 220 } 221 // do this outside lock 222 if (eventsToDispatch != null) { 223 dispatchEvents(eventsToDispatch, listener); 224 } 225 } 226 227 /** 228 * Send WaitForVhal message to VHAL 229 */ sendWaitForVhal()230 public void sendWaitForVhal() { 231 Slog.i(CarLog.TAG_POWER, "send wait for vhal"); 232 setPowerState(VehicleApPowerStateReport.WAIT_FOR_VHAL, 0); 233 } 234 235 /** 236 * Send SleepEntry message to VHAL 237 * @param wakeupTimeSec Notify VHAL when system wants to be woken from sleep. 238 */ sendSleepEntry(int wakeupTimeSec)239 public void sendSleepEntry(int wakeupTimeSec) { 240 Slog.i(CarLog.TAG_POWER, "send sleep entry"); 241 setPowerState(VehicleApPowerStateReport.DEEP_SLEEP_ENTRY, wakeupTimeSec); 242 } 243 244 /** 245 * Send SleepExit message to VHAL 246 * Notifies VHAL when SOC has woken. 247 */ sendSleepExit()248 public void sendSleepExit() { 249 Slog.i(CarLog.TAG_POWER, "send sleep exit"); 250 setPowerState(VehicleApPowerStateReport.DEEP_SLEEP_EXIT, 0); 251 } 252 253 /** 254 * Send Shutdown Postpone message to VHAL 255 */ sendShutdownPostpone(int postponeTimeMs)256 public void sendShutdownPostpone(int postponeTimeMs) { 257 Slog.i(CarLog.TAG_POWER, "send shutdown postpone, time:" + postponeTimeMs); 258 setPowerState(VehicleApPowerStateReport.SHUTDOWN_POSTPONE, postponeTimeMs); 259 } 260 261 /** 262 * Send Shutdown Start message to VHAL 263 */ sendShutdownStart(int wakeupTimeSec)264 public void sendShutdownStart(int wakeupTimeSec) { 265 Slog.i(CarLog.TAG_POWER, "send shutdown start"); 266 setPowerState(VehicleApPowerStateReport.SHUTDOWN_START, wakeupTimeSec); 267 } 268 269 /** 270 * Send On message to VHAL 271 */ sendOn()272 public void sendOn() { 273 Slog.i(CarLog.TAG_POWER, "send on"); 274 setPowerState(VehicleApPowerStateReport.ON, 0); 275 } 276 277 /** 278 * Send Shutdown Prepare message to VHAL 279 */ sendShutdownPrepare()280 public void sendShutdownPrepare() { 281 Slog.i(CarLog.TAG_POWER, "send shutdown prepare"); 282 setPowerState(VehicleApPowerStateReport.SHUTDOWN_PREPARE, 0); 283 } 284 285 /** 286 * Send Shutdown Cancel message to VHAL 287 */ sendShutdownCancel()288 public void sendShutdownCancel() { 289 Slog.i(CarLog.TAG_POWER, "send shutdown cancel"); 290 setPowerState(VehicleApPowerStateReport.SHUTDOWN_CANCELLED, 0); 291 } 292 293 /** 294 * Sets the display brightness for the vehicle. 295 * @param brightness value from 0 to 100. 296 */ sendDisplayBrightness(int brightness)297 public void sendDisplayBrightness(int brightness) { 298 if (brightness < 0) { 299 brightness = 0; 300 } else if (brightness > 100) { 301 brightness = 100; 302 } 303 VehiclePropConfig prop = mProperties.get(DISPLAY_BRIGHTNESS); 304 if (prop == null) { 305 return; 306 } 307 try { 308 mHal.set(VehicleProperty.DISPLAY_BRIGHTNESS, 0).to(brightness); 309 Slog.i(CarLog.TAG_POWER, "send display brightness = " + brightness); 310 } catch (ServiceSpecificException | IllegalArgumentException e) { 311 Slog.e(CarLog.TAG_POWER, "cannot set DISPLAY_BRIGHTNESS", e); 312 } 313 } 314 setPowerState(int state, int additionalParam)315 private void setPowerState(int state, int additionalParam) { 316 if (isPowerStateSupported()) { 317 int[] values = { state, additionalParam }; 318 try { 319 mHal.set(VehicleProperty.AP_POWER_STATE_REPORT, 0).to(values); 320 Slog.i(CarLog.TAG_POWER, "setPowerState=" + powerStateReportName(state) 321 + " param=" + additionalParam); 322 } catch (ServiceSpecificException e) { 323 Slog.e(CarLog.TAG_POWER, "cannot set to AP_POWER_STATE_REPORT", e); 324 } 325 } 326 } 327 328 /** 329 * Returns a {@link PowerState} representing the current power state for the vehicle. 330 */ 331 @Nullable getCurrentPowerState()332 public PowerState getCurrentPowerState() { 333 int[] state; 334 try { 335 state = mHal.get(int[].class, VehicleProperty.AP_POWER_STATE_REQ); 336 } catch (ServiceSpecificException e) { 337 Slog.e(CarLog.TAG_POWER, "Cannot get AP_POWER_STATE_REQ", e); 338 return null; 339 } 340 return new PowerState(state[VehicleApPowerStateReqIndex.STATE], 341 state[VehicleApPowerStateReqIndex.ADDITIONAL]); 342 } 343 344 /** 345 * Determines if the current properties describe a valid power state 346 * @return true if both the power state request and power state report are valid 347 */ isPowerStateSupported()348 public boolean isPowerStateSupported() { 349 synchronized (mLock) { 350 return (mProperties.get(VehicleProperty.AP_POWER_STATE_REQ) != null) 351 && (mProperties.get(VehicleProperty.AP_POWER_STATE_REPORT) != null); 352 } 353 } 354 isConfigFlagSet(int flag)355 private boolean isConfigFlagSet(int flag) { 356 VehiclePropConfig config; 357 synchronized (mLock) { 358 config = mProperties.get(VehicleProperty.AP_POWER_STATE_REQ); 359 } 360 if (config == null || config.configArray.size() < 1) { 361 return false; 362 } 363 return (config.configArray.get(0) & flag) != 0; 364 } 365 isDeepSleepAllowed()366 public boolean isDeepSleepAllowed() { 367 return isConfigFlagSet(VehicleApPowerStateConfigFlag.ENABLE_DEEP_SLEEP_FLAG); 368 } 369 isTimedWakeupAllowed()370 public boolean isTimedWakeupAllowed() { 371 return isConfigFlagSet(VehicleApPowerStateConfigFlag.CONFIG_SUPPORT_TIMER_POWER_ON_FLAG); 372 } 373 374 @Override init()375 public void init() { 376 synchronized (mLock) { 377 for (VehiclePropConfig config : mProperties.values()) { 378 if (VehicleHal.isPropertySubscribable(config)) { 379 mHal.subscribeProperty(this, config.prop); 380 } 381 } 382 VehiclePropConfig brightnessProperty = mProperties.get(DISPLAY_BRIGHTNESS); 383 if (brightnessProperty != null) { 384 mMaxDisplayBrightness = brightnessProperty.areaConfigs.size() > 0 385 ? brightnessProperty.areaConfigs.get(0).maxInt32Value : 0; 386 if (mMaxDisplayBrightness <= 0) { 387 Slog.w(CarLog.TAG_POWER, "Max display brightness from vehicle HAL is invalid:" 388 + mMaxDisplayBrightness); 389 mMaxDisplayBrightness = 1; 390 } 391 } 392 } 393 } 394 395 @Override release()396 public void release() { 397 synchronized (mLock) { 398 mProperties.clear(); 399 } 400 } 401 402 @Override getAllSupportedProperties()403 public int[] getAllSupportedProperties() { 404 return SUPPORTED_PROPERTIES; 405 } 406 407 @Override takeProperties(Collection<VehiclePropConfig> properties)408 public void takeProperties(Collection<VehiclePropConfig> properties) { 409 if (properties.isEmpty()) { 410 return; 411 } 412 synchronized (mLock) { 413 for (VehiclePropConfig config : properties) { 414 mProperties.put(config.prop, config); 415 } 416 } 417 } 418 419 @Override onHalEvents(List<VehiclePropValue> values)420 public void onHalEvents(List<VehiclePropValue> values) { 421 PowerEventListener listener; 422 synchronized (mLock) { 423 if (mListener == null) { 424 if (mQueuedEvents == null) { 425 mQueuedEvents = new LinkedList<>(); 426 } 427 mQueuedEvents.addAll(values); 428 return; 429 } 430 listener = mListener; 431 } 432 dispatchEvents(values, listener); 433 } 434 dispatchEvents(List<VehiclePropValue> values, PowerEventListener listener)435 private void dispatchEvents(List<VehiclePropValue> values, PowerEventListener listener) { 436 for (VehiclePropValue v : values) { 437 switch (v.prop) { 438 case AP_POWER_STATE_REPORT: 439 // Ignore this property event. It was generated inside of CarService. 440 break; 441 case AP_POWER_STATE_REQ: 442 int state = v.value.int32Values.get(VehicleApPowerStateReqIndex.STATE); 443 int param = v.value.int32Values.get(VehicleApPowerStateReqIndex.ADDITIONAL); 444 Slog.i(CarLog.TAG_POWER, "Received AP_POWER_STATE_REQ=" 445 + powerStateReqName(state) + " param=" + param); 446 listener.onApPowerStateChange(new PowerState(state, param)); 447 break; 448 case DISPLAY_BRIGHTNESS: 449 { 450 int maxBrightness; 451 synchronized (mLock) { 452 maxBrightness = mMaxDisplayBrightness; 453 } 454 int brightness = v.value.int32Values.get(0) * MAX_BRIGHTNESS / maxBrightness; 455 if (brightness < 0) { 456 Slog.e(CarLog.TAG_POWER, "invalid brightness: " + brightness 457 + ", set to 0"); 458 brightness = 0; 459 } else if (brightness > MAX_BRIGHTNESS) { 460 Slog.e(CarLog.TAG_POWER, "invalid brightness: " + brightness + ", set to " 461 + MAX_BRIGHTNESS); 462 brightness = MAX_BRIGHTNESS; 463 } 464 Slog.i(CarLog.TAG_POWER, "Received DISPLAY_BRIGHTNESS=" + brightness); 465 listener.onDisplayBrightnessChange(brightness); 466 } 467 break; 468 } 469 } 470 } 471 472 @Override 473 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dump(PrintWriter writer)474 public void dump(PrintWriter writer) { 475 writer.println("*Power HAL*"); 476 writer.printf("isPowerStateSupported:%b, isDeepSleepAllowed:%b\n", 477 isPowerStateSupported(), isDeepSleepAllowed()); 478 } 479 } 480