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 java.lang.annotation.Retention; 20 import java.lang.annotation.RetentionPolicy; 21 import java.lang.ref.WeakReference; 22 import java.util.concurrent.Executor; 23 24 import com.android.internal.annotations.GuardedBy; 25 26 import android.annotation.IntDef; 27 import android.annotation.Nullable; 28 import android.annotation.SystemApi; 29 import android.car.Car; 30 import android.car.CarManagerBase; 31 import android.car.CarNotConnectedException; 32 import android.content.Context; 33 import android.os.Handler; 34 import android.os.IBinder; 35 import android.os.Message; 36 import android.os.RemoteException; 37 import android.util.Log; 38 39 /** 40 * API for receiving power state change notifications. 41 * @hide 42 */ 43 @SystemApi 44 public class CarPowerManager implements CarManagerBase { 45 private final static boolean DBG = false; 46 private final static String TAG = "CarPowerManager"; 47 private CarPowerStateListener mListener; 48 private final ICarPower mService; 49 private Executor mExecutor; 50 51 @GuardedBy("mLock") 52 private ICarPowerStateListener mListenerToService; 53 54 private final Object mLock = new Object(); 55 56 /** 57 * Power boot up reasons, returned by {@link getBootReason} 58 */ 59 /** 60 * User powered on the vehicle. These definitions must match the ones located in the native 61 * CarPowerManager: packages/services/Car/car-lib/native/CarPowerManager/CarPowerManager.h 62 * 63 */ 64 public static final int BOOT_REASON_USER_POWER_ON = 1; 65 /** 66 * Door unlock caused device to boot 67 */ 68 public static final int BOOT_REASON_DOOR_UNLOCK = 2; 69 /** 70 * Timer expired and vehicle woke up the AP 71 */ 72 public static final int BOOT_REASON_TIMER = 3; 73 /** 74 * Door open caused device to boot 75 */ 76 public static final int BOOT_REASON_DOOR_OPEN = 4; 77 /** 78 * User activated remote start 79 */ 80 public static final int BOOT_REASON_REMOTE_START = 5; 81 82 /** @hide */ 83 @IntDef({ 84 BOOT_REASON_USER_POWER_ON, 85 BOOT_REASON_DOOR_UNLOCK, 86 BOOT_REASON_TIMER, 87 BOOT_REASON_DOOR_OPEN, 88 BOOT_REASON_REMOTE_START, 89 }) 90 @Retention(RetentionPolicy.SOURCE) 91 public @interface BootReason{} 92 93 /** 94 * Applications set a {@link CarPowerStateListener} for power state event updates. 95 */ 96 public interface CarPowerStateListener { 97 /** 98 * onStateChanged() states. These definitions must match the ones located in the native 99 * CarPowerManager: packages/services/Car/car-lib/native/CarPowerManager/CarPowerManager.h 100 * 101 */ 102 /** 103 * Shutdown is cancelled, return to normal state. 104 */ 105 public static final int SHUTDOWN_CANCELLED = 0; 106 /** 107 * Enter shutdown state. Application is expected to cleanup and be ready to shutdown. 108 */ 109 public static final int SHUTDOWN_ENTER = 1; 110 /** 111 * Enter suspend state. Application is expected to cleanup and be ready to suspend. 112 */ 113 public static final int SUSPEND_ENTER = 2; 114 /** 115 * Wake up from suspend, or resume from a cancelled suspend. Application transitions to 116 * normal state. 117 */ 118 public static final int SUSPEND_EXIT = 3; 119 120 /** 121 * Called when power state changes 122 * @param state New power state of device. 123 * @param token Opaque identifier to keep track of listener events. 124 */ 125 void onStateChanged(int state); 126 } 127 128 /** 129 * Get an instance of the CarPowerManager. 130 * 131 * Should not be obtained directly by clients, use {@link Car#getCarManager(String)} instead. 132 * @param service 133 * @param context 134 * @param handler 135 * @hide 136 */ 137 public CarPowerManager(IBinder service, Context context, Handler handler) { 138 mService = ICarPower.Stub.asInterface(service); 139 } 140 141 /** 142 * Returns the current {@link BootReason}. This value does not change until the device goes 143 * through a suspend/resume cycle. 144 * @return int 145 * @throws CarNotConnectedException 146 * @hide 147 */ 148 public int getBootReason() throws CarNotConnectedException { 149 try { 150 return mService.getBootReason(); 151 } catch (RemoteException e) { 152 Log.e(TAG, "Exception in getBootReason", e); 153 throw new CarNotConnectedException(e); 154 } 155 } 156 157 /** 158 * Request power manager to shutdown in lieu of suspend at the next opportunity. 159 * @throws CarNotConnectedException 160 * @hide 161 */ 162 public void requestShutdownOnNextSuspend() throws CarNotConnectedException { 163 try { 164 mService.requestShutdownOnNextSuspend(); 165 } catch (RemoteException e) { 166 Log.e(TAG, "Exception in requestShutdownOnNextSuspend", e); 167 throw new CarNotConnectedException(e); 168 } 169 } 170 171 /** 172 * Sets a listener to receive power state changes. Only one listener may be set at a time. 173 * Caller may add an Executor to allow the callback to run in a seperate thread of execution 174 * if the {@link onStateChanged} method will take some time. If no Executor is passed in, 175 * the listener will run in the Binder thread and should finish quickly. After 176 * {@link onStateChanged} is called, the {@link finished} method will automatically be called 177 * to notify {@link CarPowerManagementService} that the application has handled the 178 * {@link #SHUTDOWN_ENTER} or {@link #SUSPEND_ENTER} state transitions. Only these two states 179 * require a confirmation from the application. 180 * 181 * @param executor 182 * @param listener 183 * @throws CarNotConnectedException, IllegalStateException 184 * @hide 185 */ 186 public void setListener(CarPowerStateListener listener, Executor executor) throws 187 CarNotConnectedException, IllegalStateException { 188 synchronized(mLock) { 189 if (mListenerToService == null) { 190 ICarPowerStateListener listenerToService = new ICarPowerStateListener.Stub() { 191 @Override 192 public void onStateChanged(int state, int token) throws RemoteException { 193 handleEvent(state, token); 194 } 195 }; 196 try { 197 mService.registerListener(listenerToService); 198 mListenerToService = listenerToService; 199 } catch (RemoteException ex) { 200 Log.e(TAG, "Could not connect: ", ex); 201 throw new CarNotConnectedException(ex); 202 } catch (IllegalStateException ex) { 203 Car.checkCarNotConnectedExceptionFromCarService(ex); 204 } 205 } 206 if ((mExecutor == null) && (mListener == null)) { 207 // Update listener and executor 208 mExecutor = executor; 209 mListener = listener; 210 } else { 211 throw new IllegalStateException("Listener must be cleared first"); 212 } 213 } 214 } 215 216 /** 217 * Removes the listener from {@link CarPowerManagementService} 218 * @hide 219 */ 220 public void clearListener() { 221 ICarPowerStateListener listenerToService; 222 synchronized (mLock) { 223 listenerToService = mListenerToService; 224 mListenerToService = null; 225 mListener = null; 226 mExecutor = null; 227 } 228 229 if (listenerToService == null) { 230 Log.w(TAG, "unregisterListener: listener was not registered"); 231 return; 232 } 233 234 try { 235 mService.unregisterListener(listenerToService); 236 } catch (RemoteException ex) { 237 Log.e(TAG, "Failed to unregister listener", ex); 238 //ignore 239 } catch (IllegalStateException ex) { 240 Car.hideCarNotConnectedExceptionFromCarService(ex); 241 } 242 } 243 244 private void handleEvent(int state, int token) { 245 Executor executor; 246 synchronized (mLock) { 247 executor = mExecutor; 248 } 249 if (executor != null) { 250 executor.execute(() -> { 251 handleEventInternal(state, token); 252 }); 253 } else { 254 // If no executor provided, run in binder thread. This should only be done for 255 // trivial listener logic. 256 handleEventInternal(state, token); 257 } 258 } 259 260 private void handleEventInternal(int state, int token) { 261 mListener.onStateChanged(state); 262 if ((state == CarPowerStateListener.SHUTDOWN_ENTER) || 263 (state == CarPowerStateListener.SUSPEND_ENTER)) { 264 // Notify service that state change is complete for SHUTDOWN_ENTER and SUSPEND_ENTER 265 // states only. 266 try { 267 mService.finished(mListenerToService, token); 268 } catch (RemoteException e) { 269 Log.e(TAG, "Exception in finished", e); 270 } 271 } 272 } 273 274 /** @hide */ 275 @Override 276 public void onCarDisconnected() { 277 ICarPowerStateListener listenerToService; 278 synchronized (mLock) { 279 listenerToService = mListenerToService; 280 } 281 282 if (listenerToService != null) { 283 clearListener(); 284 } 285 } 286 } 287