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