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