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