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