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.V2_0.VehicleProperty.AP_POWER_STATE;
19 import static android.hardware.automotive.vehicle.V2_0.VehicleProperty.DISPLAY_BRIGHTNESS;
20 
21 import android.annotation.Nullable;
22 import android.hardware.automotive.vehicle.V2_0.VehicleApPowerSetState;
23 import android.hardware.automotive.vehicle.V2_0.VehicleApPowerState;
24 import android.hardware.automotive.vehicle.V2_0.VehicleApPowerStateConfigFlag;
25 import android.hardware.automotive.vehicle.V2_0.VehicleApPowerStateIndex;
26 import android.hardware.automotive.vehicle.V2_0.VehicleApPowerStateShutdownParam;
27 import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig;
28 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
29 import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
30 import android.util.Log;
31 
32 import com.android.car.CarLog;
33 import com.android.internal.annotations.VisibleForTesting;
34 
35 import java.io.PrintWriter;
36 import java.util.Collection;
37 import java.util.HashMap;
38 import java.util.LinkedList;
39 import java.util.List;
40 
41 public class PowerHalService extends HalServiceBase {
42 
43     public static final int STATE_OFF = VehicleApPowerState.OFF;
44     public static final int STATE_DEEP_SLEEP = VehicleApPowerState.DEEP_SLEEP;
45     public static final int STATE_ON_DISP_OFF = VehicleApPowerState.ON_DISP_OFF;
46     public static final int STATE_ON_FULL = VehicleApPowerState.ON_FULL;
47     public static final int STATE_SHUTDOWN_PREPARE = VehicleApPowerState.SHUTDOWN_PREPARE;
48 
49     @VisibleForTesting
50     public static final int SET_BOOT_COMPLETE = VehicleApPowerSetState.BOOT_COMPLETE;
51     @VisibleForTesting
52     public static final int SET_DEEP_SLEEP_ENTRY = VehicleApPowerSetState.DEEP_SLEEP_ENTRY;
53     @VisibleForTesting
54     public static final int SET_DEEP_SLEEP_EXIT = VehicleApPowerSetState.DEEP_SLEEP_EXIT;
55     @VisibleForTesting
56     public static final int SET_SHUTDOWN_POSTPONE = VehicleApPowerSetState.SHUTDOWN_POSTPONE;
57     @VisibleForTesting
58     public static final int SET_SHUTDOWN_START = VehicleApPowerSetState.SHUTDOWN_START;
59     @VisibleForTesting
60     public static final int SET_DISPLAY_ON = VehicleApPowerSetState.DISPLAY_ON;
61     @VisibleForTesting
62     public static final int SET_DISPLAY_OFF =
63             VehicleApPowerSetState.DISPLAY_OFF;
64 
65     @VisibleForTesting
66     public static final int FLAG_SHUTDOWN_PARAM_CAN_SLEEP =
67             VehicleApPowerStateShutdownParam.CAN_SLEEP;
68     @VisibleForTesting
69     public static final int FLAG_SHUTDOWN_IMMEDIATELY =
70             VehicleApPowerStateShutdownParam.SHUTDOWN_IMMEDIATELY;
71 
72     public interface PowerEventListener {
73         /**
74          * Received power state change event.
75          * @param state One of STATE_*
76          */
onApPowerStateChange(PowerState state)77         void onApPowerStateChange(PowerState state);
78         /**
79          * Received display brightness change event.
80          * @param brightness in percentile. 100% full.
81          */
onDisplayBrightnessChange(int brightness)82         void onDisplayBrightnessChange(int brightness);
83     }
84 
85     public static final class PowerState {
86         /**
87          * One of STATE_*
88          */
89         public final int mState;
90         public final int mParam;
91 
PowerState(int state, int param)92         public PowerState(int state, int param) {
93             this.mState = state;
94             this.mParam = param;
95         }
96 
97         /**
98          * Whether the current PowerState allows deep sleep or not. Calling this for
99          * power state other than STATE_SHUTDOWN_PREPARE will trigger exception.
100          * @return
101          * @throws IllegalStateException
102          */
canEnterDeepSleep()103         public boolean canEnterDeepSleep() {
104             if (mState != STATE_SHUTDOWN_PREPARE) {
105                 throw new IllegalStateException("wrong state");
106             }
107             return (mParam & VehicleApPowerStateShutdownParam.CAN_SLEEP) != 0;
108         }
109 
110         /**
111          * Whether the current PowerState allows postponing or not. Calling this for
112          * power state other than STATE_SHUTDOWN_PREPARE will trigger exception.
113          * @return
114          * @throws IllegalStateException
115          */
canPostponeShutdown()116         public boolean canPostponeShutdown() {
117             if (mState != STATE_SHUTDOWN_PREPARE) {
118                 throw new IllegalStateException("wrong state");
119             }
120             return (mParam & VehicleApPowerStateShutdownParam.SHUTDOWN_IMMEDIATELY) == 0;
121         }
122 
123         @Override
equals(Object o)124         public boolean equals(Object o) {
125             if (this == o) {
126                 return true;
127             }
128             if (!(o instanceof PowerState)) {
129                 return false;
130             }
131             PowerState that = (PowerState) o;
132             return this.mState == that.mState && this.mParam == that.mParam;
133         }
134 
135         @Override
toString()136         public String toString() {
137             return "PowerState state:" + mState + ", param:" + mParam;
138         }
139     }
140 
141     private final HashMap<Integer, VehiclePropConfig> mProperties = new HashMap<>();
142     private final VehicleHal mHal;
143     private LinkedList<VehiclePropValue> mQueuedEvents;
144     private PowerEventListener mListener;
145     private int mMaxDisplayBrightness;
146 
PowerHalService(VehicleHal hal)147     public PowerHalService(VehicleHal hal) {
148         mHal = hal;
149     }
150 
setListener(PowerEventListener listener)151     public void setListener(PowerEventListener listener) {
152         LinkedList<VehiclePropValue> eventsToDispatch = null;
153         synchronized (this) {
154             mListener = listener;
155             if (mQueuedEvents != null && mQueuedEvents.size() > 0) {
156                 eventsToDispatch = mQueuedEvents;
157             }
158             mQueuedEvents = null;
159         }
160         // do this outside lock
161         if (eventsToDispatch != null) {
162             dispatchEvents(eventsToDispatch, listener);
163         }
164     }
165 
sendBootComplete()166     public void sendBootComplete() {
167         Log.i(CarLog.TAG_POWER, "send boot complete");
168         setPowerState(VehicleApPowerSetState.BOOT_COMPLETE, 0);
169     }
170 
sendSleepEntry()171     public void sendSleepEntry() {
172         Log.i(CarLog.TAG_POWER, "send sleep entry");
173         setPowerState(VehicleApPowerSetState.DEEP_SLEEP_ENTRY, 0);
174     }
175 
sendSleepExit()176     public void sendSleepExit() {
177         Log.i(CarLog.TAG_POWER, "send sleep exit");
178         setPowerState(VehicleApPowerSetState.DEEP_SLEEP_EXIT, 0);
179     }
180 
sendShutdownPostpone(int postponeTimeMs)181     public void sendShutdownPostpone(int postponeTimeMs) {
182         Log.i(CarLog.TAG_POWER, "send shutdown postpone, time:" + postponeTimeMs);
183         setPowerState(VehicleApPowerSetState.SHUTDOWN_POSTPONE,
184                 postponeTimeMs);
185     }
186 
sendShutdownStart(int wakeupTimeSec)187     public void sendShutdownStart(int wakeupTimeSec) {
188         Log.i(CarLog.TAG_POWER, "send shutdown start");
189         setPowerState(VehicleApPowerSetState.SHUTDOWN_START, 0);
190     }
191 
sendDisplayOn()192     public void sendDisplayOn() {
193         Log.i(CarLog.TAG_POWER, "send display on");
194         setPowerState(VehicleApPowerSetState.DISPLAY_ON, 0);
195     }
196 
sendDisplayOff()197     public void sendDisplayOff() {
198         Log.i(CarLog.TAG_POWER, "send display off");
199         setPowerState(VehicleApPowerSetState.DISPLAY_OFF, 0);
200     }
201 
setPowerState(int state, int additionalParam)202     private void setPowerState(int state, int additionalParam) {
203         int[] values = { state, additionalParam };
204         try {
205             mHal.set(VehicleProperty.AP_POWER_STATE).to(values);
206         } catch (PropertyTimeoutException e) {
207             Log.e(CarLog.TAG_POWER, "cannot set to AP_POWER_STATE", e);
208         }
209     }
210 
211     @Nullable
getCurrentPowerState()212     public PowerState getCurrentPowerState() {
213         int[] state;
214         try {
215             state = mHal.get(int[].class, VehicleProperty.AP_POWER_STATE);
216         } catch (PropertyTimeoutException e) {
217             Log.e(CarLog.TAG_POWER, "Cannot get AP_POWER_STATE", e);
218             return null;
219         }
220         return new PowerState(state[VehicleApPowerStateIndex.STATE],
221                 state[VehicleApPowerStateIndex.ADDITIONAL]);
222     }
223 
isPowerStateSupported()224     public synchronized boolean isPowerStateSupported() {
225         VehiclePropConfig config = mProperties.get(VehicleProperty.AP_POWER_STATE);
226         return config != null;
227     }
228 
isDeepSleepAllowed()229     public synchronized boolean isDeepSleepAllowed() {
230         VehiclePropConfig config = mProperties.get(VehicleProperty.AP_POWER_STATE);
231         if (config == null) {
232             return false;
233         }
234         return (config.configArray.get(0)
235                 & VehicleApPowerStateConfigFlag.ENABLE_DEEP_SLEEP_FLAG) != 0;
236     }
237 
isTimedWakeupAllowed()238     public synchronized boolean isTimedWakeupAllowed() {
239         VehiclePropConfig config = mProperties.get(
240                 AP_POWER_STATE);
241         if (config == null) {
242             return false;
243         }
244         return (config.configArray.get(0)
245                 & VehicleApPowerStateConfigFlag.CONFIG_SUPPORT_TIMER_POWER_ON_FLAG) != 0;
246     }
247 
248     @Override
init()249     public synchronized void init() {
250         for (VehiclePropConfig config : mProperties.values()) {
251             if (VehicleHal.isPropertySubscribable(config)) {
252                 mHal.subscribeProperty(this, config.prop);
253             }
254         }
255         VehiclePropConfig brightnessProperty = mProperties.get(DISPLAY_BRIGHTNESS);
256         if (brightnessProperty != null) {
257             mMaxDisplayBrightness = brightnessProperty.areaConfigs.size() > 0
258                     ? brightnessProperty.areaConfigs.get(0).maxInt32Value : 0;
259             if (mMaxDisplayBrightness <= 0) {
260                 Log.w(CarLog.TAG_POWER, "Max display brightness from vehicle HAL is invalid:" +
261                         mMaxDisplayBrightness);
262                 mMaxDisplayBrightness = 1;
263             }
264         }
265     }
266 
267     @Override
release()268     public synchronized void release() {
269         mProperties.clear();
270     }
271 
272     @Override
takeSupportedProperties( Collection<VehiclePropConfig> allProperties)273     public synchronized Collection<VehiclePropConfig> takeSupportedProperties(
274             Collection<VehiclePropConfig> allProperties) {
275         for (VehiclePropConfig config : allProperties) {
276             switch (config.prop) {
277                 case AP_POWER_STATE:
278                 case DISPLAY_BRIGHTNESS:
279                     mProperties.put(config.prop, config);
280                     break;
281             }
282         }
283         return new LinkedList<>(mProperties.values());
284     }
285 
286     @Override
handleHalEvents(List<VehiclePropValue> values)287     public void handleHalEvents(List<VehiclePropValue> values) {
288         PowerEventListener listener;
289         synchronized (this) {
290             if (mListener == null) {
291                 if (mQueuedEvents == null) {
292                     mQueuedEvents = new LinkedList<>();
293                 }
294                 mQueuedEvents.addAll(values);
295                 return;
296             }
297             listener = mListener;
298         }
299         dispatchEvents(values, listener);
300     }
301 
dispatchEvents(List<VehiclePropValue> values, PowerEventListener listener)302     private void dispatchEvents(List<VehiclePropValue> values, PowerEventListener listener) {
303         for (VehiclePropValue v : values) {
304             switch (v.prop) {
305                 case AP_POWER_STATE:
306                     int state = v.value.int32Values.get(VehicleApPowerStateIndex.STATE);
307                     int param = v.value.int32Values.get(VehicleApPowerStateIndex.ADDITIONAL);
308                     listener.onApPowerStateChange(new PowerState(state, param));
309                     break;
310                 case DISPLAY_BRIGHTNESS:
311                     int maxBrightness;
312                     synchronized (this) {
313                         maxBrightness = mMaxDisplayBrightness;
314                     }
315                     listener.onDisplayBrightnessChange(
316                             (v.value.int32Values.get(0) * 100) / maxBrightness);
317                     break;
318             }
319         }
320     }
321 
322     @Override
dump(PrintWriter writer)323     public void dump(PrintWriter writer) {
324         writer.println("*Power HAL*");
325         writer.println("isPowerStateSupported:" + isPowerStateSupported() +
326                 ",isDeepSleepAllowed:" + isDeepSleepAllowed());
327     }
328 }
329