• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 
17 package android.car.hardware.power;
18 
19 import java.lang.annotation.Retention;
20 import java.lang.annotation.RetentionPolicy;
21 import java.lang.ref.WeakReference;
22 import java.util.concurrent.Executor;
23 
24 import com.android.internal.annotations.GuardedBy;
25 
26 import android.annotation.IntDef;
27 import android.annotation.Nullable;
28 import android.annotation.SystemApi;
29 import android.car.Car;
30 import android.car.CarManagerBase;
31 import android.car.CarNotConnectedException;
32 import android.content.Context;
33 import android.os.Handler;
34 import android.os.IBinder;
35 import android.os.Message;
36 import android.os.RemoteException;
37 import android.util.Log;
38 
39 /**
40  * API for receiving power state change notifications.
41  * @hide
42  */
43 @SystemApi
44 public class CarPowerManager implements CarManagerBase {
45     private final static boolean DBG = false;
46     private final static String TAG = "CarPowerManager";
47     private CarPowerStateListener mListener;
48     private final ICarPower mService;
49     private Executor mExecutor;
50 
51     @GuardedBy("mLock")
52     private ICarPowerStateListener mListenerToService;
53 
54     private final Object mLock = new Object();
55 
56     /**
57      * Power boot up reasons, returned by {@link getBootReason}
58      */
59     /**
60      * User powered on the vehicle.  These definitions must match the ones located in the native
61      * CarPowerManager:  packages/services/Car/car-lib/native/CarPowerManager/CarPowerManager.h
62      *
63      */
64     public static final int BOOT_REASON_USER_POWER_ON = 1;
65     /**
66      * Door unlock caused device to boot
67      */
68     public static final int BOOT_REASON_DOOR_UNLOCK = 2;
69     /**
70      * Timer expired and vehicle woke up the AP
71      */
72     public static final int BOOT_REASON_TIMER = 3;
73     /**
74      * Door open caused device to boot
75      */
76     public static final int BOOT_REASON_DOOR_OPEN = 4;
77     /**
78      * User activated remote start
79      */
80     public static final int BOOT_REASON_REMOTE_START = 5;
81 
82     /** @hide */
83     @IntDef({
84         BOOT_REASON_USER_POWER_ON,
85         BOOT_REASON_DOOR_UNLOCK,
86         BOOT_REASON_TIMER,
87         BOOT_REASON_DOOR_OPEN,
88         BOOT_REASON_REMOTE_START,
89     })
90     @Retention(RetentionPolicy.SOURCE)
91     public @interface BootReason{}
92 
93     /**
94      *  Applications set a {@link CarPowerStateListener} for power state event updates.
95      */
96     public interface CarPowerStateListener {
97         /**
98          * onStateChanged() states.  These definitions must match the ones located in the native
99          * CarPowerManager:  packages/services/Car/car-lib/native/CarPowerManager/CarPowerManager.h
100          *
101          */
102         /**
103          * Shutdown is cancelled, return to normal state.
104          */
105         public static final int SHUTDOWN_CANCELLED = 0;
106         /**
107          * Enter shutdown state.  Application is expected to cleanup and be ready to shutdown.
108          */
109         public static final int SHUTDOWN_ENTER = 1;
110         /**
111          * Enter suspend state.  Application is expected to cleanup and be ready to suspend.
112          */
113         public static final int SUSPEND_ENTER = 2;
114         /**
115          * Wake up from suspend, or resume from a cancelled suspend.  Application transitions to
116          * normal state.
117          */
118         public static final int SUSPEND_EXIT = 3;
119 
120         /**
121          *  Called when power state changes
122          *  @param state New power state of device.
123          *  @param token Opaque identifier to keep track of listener events.
124          */
onStateChanged(int state)125         void onStateChanged(int state);
126     }
127 
128     /**
129      * Get an instance of the CarPowerManager.
130      *
131      * Should not be obtained directly by clients, use {@link Car#getCarManager(String)} instead.
132      * @param service
133      * @param context
134      * @param handler
135      * @hide
136      */
CarPowerManager(IBinder service, Context context, Handler handler)137     public CarPowerManager(IBinder service, Context context, Handler handler) {
138         mService = ICarPower.Stub.asInterface(service);
139     }
140 
141     /**
142      * Returns the current {@link BootReason}.  This value does not change until the device goes
143      * through a suspend/resume cycle.
144      * @return int
145      * @throws CarNotConnectedException
146      * @hide
147      */
getBootReason()148     public int getBootReason() throws CarNotConnectedException {
149         try {
150             return mService.getBootReason();
151         } catch (RemoteException e) {
152             Log.e(TAG, "Exception in getBootReason", e);
153             throw new CarNotConnectedException(e);
154         }
155     }
156 
157     /**
158      * Request power manager to shutdown in lieu of suspend at the next opportunity.
159      * @throws CarNotConnectedException
160      * @hide
161      */
requestShutdownOnNextSuspend()162     public void requestShutdownOnNextSuspend() throws CarNotConnectedException {
163         try {
164             mService.requestShutdownOnNextSuspend();
165         } catch (RemoteException e) {
166             Log.e(TAG, "Exception in requestShutdownOnNextSuspend", e);
167             throw new CarNotConnectedException(e);
168         }
169     }
170 
171     /**
172      * Sets a listener to receive power state changes.  Only one listener may be set at a time.
173      * Caller may add an Executor to allow the callback to run in a seperate thread of execution
174      * if the {@link onStateChanged} method will take some time.  If no Executor is passed in,
175      * the listener will run in the Binder thread and should finish quickly.  After
176      * {@link onStateChanged} is called, the {@link finished} method will automatically be called
177      * to notify {@link CarPowerManagementService} that the application has handled the
178      * {@link #SHUTDOWN_ENTER} or {@link #SUSPEND_ENTER} state transitions.  Only these two states
179      * require a confirmation from the application.
180      *
181      * @param executor
182      * @param listener
183      * @throws CarNotConnectedException, IllegalStateException
184      * @hide
185      */
setListener(CarPowerStateListener listener, Executor executor)186     public void setListener(CarPowerStateListener listener, Executor executor) throws
187             CarNotConnectedException, IllegalStateException {
188         synchronized(mLock) {
189             if (mListenerToService == null) {
190                 ICarPowerStateListener listenerToService = new ICarPowerStateListener.Stub() {
191                     @Override
192                     public void onStateChanged(int state, int token) throws RemoteException {
193                         handleEvent(state, token);
194                     }
195                 };
196                 try {
197                     mService.registerListener(listenerToService);
198                     mListenerToService = listenerToService;
199                 } catch (RemoteException ex) {
200                     Log.e(TAG, "Could not connect: ", ex);
201                     throw new CarNotConnectedException(ex);
202                 } catch (IllegalStateException ex) {
203                     Car.checkCarNotConnectedExceptionFromCarService(ex);
204                 }
205             }
206             if ((mExecutor == null) && (mListener == null)) {
207                 // Update listener and executor
208                 mExecutor = executor;
209                 mListener = listener;
210             } else {
211                 throw new IllegalStateException("Listener must be cleared first");
212             }
213         }
214     }
215 
216     /**
217      * Removes the listener from {@link CarPowerManagementService}
218      * @hide
219      */
clearListener()220     public void clearListener() {
221         ICarPowerStateListener listenerToService;
222         synchronized (mLock) {
223             listenerToService = mListenerToService;
224             mListenerToService = null;
225             mListener = null;
226             mExecutor = null;
227         }
228 
229         if (listenerToService == null) {
230             Log.w(TAG, "unregisterListener: listener was not registered");
231             return;
232         }
233 
234         try {
235             mService.unregisterListener(listenerToService);
236         } catch (RemoteException ex) {
237             Log.e(TAG, "Failed to unregister listener", ex);
238             //ignore
239         } catch (IllegalStateException ex) {
240             Car.hideCarNotConnectedExceptionFromCarService(ex);
241         }
242     }
243 
handleEvent(int state, int token)244     private void handleEvent(int state, int token) {
245         Executor executor;
246         synchronized (mLock) {
247             executor = mExecutor;
248         }
249         if (executor != null) {
250             executor.execute(() -> {
251                 handleEventInternal(state, token);
252             });
253         } else {
254             // If no executor provided, run in binder thread.  This should only be done for
255             //  trivial listener logic.
256             handleEventInternal(state, token);
257         }
258     }
259 
handleEventInternal(int state, int token)260     private void handleEventInternal(int state, int token) {
261         mListener.onStateChanged(state);
262         if ((state == CarPowerStateListener.SHUTDOWN_ENTER) ||
263             (state == CarPowerStateListener.SUSPEND_ENTER)) {
264             // Notify service that state change is complete for SHUTDOWN_ENTER and SUSPEND_ENTER
265             //  states only.
266             try {
267                 mService.finished(mListenerToService, token);
268             } catch (RemoteException e) {
269                 Log.e(TAG, "Exception in finished", e);
270             }
271         }
272     }
273 
274     /** @hide */
275     @Override
onCarDisconnected()276     public void onCarDisconnected() {
277         ICarPowerStateListener listenerToService;
278         synchronized (mLock) {
279             listenerToService = mListenerToService;
280         }
281 
282         if (listenerToService != null) {
283             clearListener();
284         }
285     }
286 }
287