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