• 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 android.annotation.RequiresPermission;
20 import android.annotation.SystemApi;
21 import android.car.Car;
22 import android.car.CarManagerBase;
23 import android.os.IBinder;
24 import android.os.RemoteException;
25 import android.util.Log;
26 
27 import com.android.internal.annotations.GuardedBy;
28 
29 import java.util.concurrent.CancellationException;
30 import java.util.concurrent.CompletableFuture;
31 
32 /**
33  * API for receiving power state change notifications.
34  * @hide
35  */
36 @SystemApi
37 public class CarPowerManager extends CarManagerBase {
38     private static final boolean DBG = false;
39     private static final String TAG = CarPowerManager.class.getSimpleName();
40 
41     private final Object mLock = new Object();
42     private final ICarPower mService;
43 
44     @GuardedBy("mLock")
45     private CarPowerStateListener mListener;
46     @GuardedBy("mLock")
47     private CarPowerStateListenerWithCompletion mListenerWithCompletion;
48     @GuardedBy("mLock")
49     private CompletableFuture<Void> mFuture;
50     @GuardedBy("mLock")
51     private ICarPowerStateListener mListenerToService;
52 
53 
54     /**
55      *  Applications set a {@link CarPowerStateListener} for power state event updates.
56      */
57     public interface CarPowerStateListener {
58         /**
59          * onStateChanged() states.  These definitions must match the ones located in the native
60          * CarPowerManager:  packages/services/Car/car-lib/native/include/CarPowerManager.h
61          */
62 
63         /**
64          * The current power state is unavailable, unknown, or invalid
65          * @hide
66          */
67         int INVALID = 0;
68         /**
69          * Android is up, but vendor is controlling the audio / display
70          * @hide
71          */
72         int WAIT_FOR_VHAL = 1;
73         /**
74          * Enter suspend state.  CPMS is switching to WAIT_FOR_FINISHED state.
75          * @hide
76          */
77         int SUSPEND_ENTER = 2;
78         /**
79          * Wake up from suspend.
80          * @hide
81          */
82         int SUSPEND_EXIT = 3;
83         /**
84          * Enter shutdown state.  CPMS is switching to WAIT_FOR_FINISHED state.
85          * @hide
86          */
87         int SHUTDOWN_ENTER = 5;
88         /**
89          * On state
90          * @hide
91          */
92         int ON = 6;
93         /**
94          * State where system is getting ready for shutdown or suspend.  Application is expected to
95          * cleanup and be ready to suspend
96          * @hide
97          */
98         int SHUTDOWN_PREPARE = 7;
99         /**
100          * Shutdown is cancelled, return to normal state.
101          * @hide
102          */
103         int SHUTDOWN_CANCELLED = 8;
104 
105         /**
106          * Called when power state changes. This callback is available to
107          * any listener, even if it is not running in the system process.
108          * @param state New power state of device.
109          * @hide
110          */
onStateChanged(int state)111         void onStateChanged(int state);
112     }
113 
114     /**
115      * Applications set a {@link CarPowerStateListenerWithCompletion} for power state
116      * event updates where a CompletableFuture is used.
117      * @hide
118      */
119     public interface CarPowerStateListenerWithCompletion {
120         /**
121          * Called when power state changes. This callback is only for listeners
122          * that are running in the system process.
123          * @param state New power state of device.
124          * @param future CompletableFuture used by Car modules to notify CPMS that they
125          *               are ready to continue shutting down. CPMS will wait until this
126          *               future is completed.
127          * @hide
128          */
onStateChanged(int state, CompletableFuture<Void> future)129         void onStateChanged(int state, CompletableFuture<Void> future);
130     }
131 
132     /**
133      * Get an instance of the CarPowerManager.
134      *
135      * Should not be obtained directly by clients, use {@link Car#getCarManager(String)} instead.
136      * @param service
137      * @param context
138      * @param handler
139      * @hide
140      */
CarPowerManager(Car car, IBinder service)141     public CarPowerManager(Car car, IBinder service) {
142         super(car);
143         mService = ICarPower.Stub.asInterface(service);
144     }
145 
146     /**
147      * Request power manager to shutdown in lieu of suspend at the next opportunity.
148      * @hide
149      */
requestShutdownOnNextSuspend()150     public void requestShutdownOnNextSuspend() {
151         try {
152             mService.requestShutdownOnNextSuspend();
153         } catch (RemoteException e) {
154             handleRemoteExceptionFromCarService(e);
155         }
156     }
157 
158     /**
159      * Schedule next wake up time in CarPowerManagementSystem
160      * @hide
161      */
scheduleNextWakeupTime(int seconds)162     public void scheduleNextWakeupTime(int seconds) {
163         try {
164             mService.scheduleNextWakeupTime(seconds);
165         } catch (RemoteException e) {
166             handleRemoteExceptionFromCarService(e);
167         }
168     }
169 
170     /**
171      * Returns the current power state
172      * @return One of the values defined in {@link CarPowerStateListener}
173      * @hide
174      */
175     @RequiresPermission(Car.PERMISSION_CAR_POWER)
getPowerState()176     public int getPowerState() {
177         try {
178             return mService.getPowerState();
179         } catch (RemoteException e) {
180             return handleRemoteExceptionFromCarService(e, CarPowerStateListener.INVALID);
181         }
182     }
183 
184     /**
185      * Sets a listener to receive power state changes. Only one listener may be set at a
186      * time for an instance of CarPowerManager.
187      * The listener is assumed to completely handle the 'onStateChanged' before returning.
188      *
189      * @param listener
190      * @throws IllegalStateException
191      * @hide
192      */
setListener(CarPowerStateListener listener)193     public void setListener(CarPowerStateListener listener) {
194         synchronized (mLock) {
195             if (mListener != null || mListenerWithCompletion != null) {
196                 throw new IllegalStateException("Listener must be cleared first");
197             }
198             // Update listener
199             mListener = listener;
200             setServiceForListenerLocked(false);
201         }
202     }
203 
204     /**
205      * Sets a listener to receive power state changes. Only one listener may be set at a
206      * time for an instance of CarPowerManager.
207      * For calls that require completion before continue, we attach a {@link CompletableFuture}
208      * which is being used as a signal that caller is finished and ready to proceed.
209      * Once future is completed, the {@link finished} method will automatically be called to notify
210      * {@link CarPowerManagementService} that the application has handled the
211      * {@link #SHUTDOWN_PREPARE} state transition.
212      *
213      * @param listener
214      * @throws IllegalStateException
215      * @hide
216      */
setListenerWithCompletion(CarPowerStateListenerWithCompletion listener)217     public void setListenerWithCompletion(CarPowerStateListenerWithCompletion listener) {
218         synchronized (mLock) {
219             if (mListener != null || mListenerWithCompletion != null) {
220                 throw new IllegalStateException("Listener must be cleared first");
221             }
222             // Update listener
223             mListenerWithCompletion = listener;
224             setServiceForListenerLocked(true);
225         }
226     }
227 
setServiceForListenerLocked(boolean useCompletion)228     private void setServiceForListenerLocked(boolean useCompletion) {
229         if (mListenerToService == null) {
230             ICarPowerStateListener listenerToService = new ICarPowerStateListener.Stub() {
231                 @Override
232                 public void onStateChanged(int state) throws RemoteException {
233                     if (useCompletion) {
234                         CarPowerStateListenerWithCompletion listenerWithCompletion;
235                         CompletableFuture<Void> future;
236                         synchronized (mLock) {
237                             // Update CompletableFuture. This will recreate it or just clean it up.
238                             updateFutureLocked(state);
239                             listenerWithCompletion = mListenerWithCompletion;
240                             future = mFuture;
241                         }
242                         // Notify user that the state has changed and supply a future
243                         if (listenerWithCompletion != null) {
244                             listenerWithCompletion.onStateChanged(state, future);
245                         }
246                     } else {
247                         CarPowerStateListener listener;
248                         synchronized (mLock) {
249                             listener = mListener;
250                         }
251                         // Notify the user without supplying a future
252                         if (listener != null) {
253                             listener.onStateChanged(state);
254                         }
255                     }
256                 }
257             };
258             try {
259                 if (useCompletion) {
260                     mService.registerListenerWithCompletion(listenerToService);
261                 } else {
262                     mService.registerListener(listenerToService);
263                 }
264                 mListenerToService = listenerToService;
265             } catch (RemoteException e) {
266                 handleRemoteExceptionFromCarService(e);
267             }
268         }
269     }
270 
271     /**
272      * Removes the listener from {@link CarPowerManagementService}
273      * @hide
274      */
clearListener()275     public void clearListener() {
276         ICarPowerStateListener listenerToService;
277         synchronized (mLock) {
278             listenerToService = mListenerToService;
279             mListenerToService = null;
280             mListener = null;
281             mListenerWithCompletion = null;
282             cleanupFutureLocked();
283         }
284 
285         if (listenerToService == null) {
286             Log.w(TAG, "unregisterListener: listener was not registered");
287             return;
288         }
289 
290         try {
291             mService.unregisterListener(listenerToService);
292         } catch (RemoteException e) {
293             handleRemoteExceptionFromCarService(e);
294         }
295     }
296 
updateFutureLocked(int state)297     private void updateFutureLocked(int state) {
298         cleanupFutureLocked();
299         if (state == CarPowerStateListener.SHUTDOWN_PREPARE) {
300             // Create a CompletableFuture and pass it to the listener.
301             // When the listener completes the future, tell
302             // CarPowerManagementService that this action is finished.
303             mFuture = new CompletableFuture<>();
304             mFuture.whenComplete((result, exception) -> {
305                 if (exception != null && !(exception instanceof CancellationException)) {
306                     Log.e(TAG, "Exception occurred while waiting for future", exception);
307                 }
308                 ICarPowerStateListener listenerToService;
309                 synchronized (mLock) {
310                     listenerToService = mListenerToService;
311                 }
312                 try {
313                     mService.finished(listenerToService);
314                 } catch (RemoteException e) {
315                     handleRemoteExceptionFromCarService(e);
316                 }
317             });
318         }
319     }
320 
cleanupFutureLocked()321     private void cleanupFutureLocked() {
322         if (mFuture != null) {
323             if (!mFuture.isDone()) {
324                 mFuture.cancel(false);
325             }
326             mFuture = null;
327         }
328     }
329 
330     /** @hide */
331     @Override
onCarDisconnected()332     public void onCarDisconnected() {
333         synchronized (mLock) {
334             mListener = null;
335             mListenerWithCompletion = null;
336         }
337     }
338 }
339