Home | History | Annotate | Download | only in power
      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