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