Home | History | Annotate | Download | only in car
      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 com.android.car;
     18 
     19 import android.annotation.Nullable;
     20 import android.car.VehicleAreaType;
     21 import android.car.drivingstate.CarDrivingStateEvent;
     22 import android.car.drivingstate.CarDrivingStateEvent.CarDrivingState;
     23 import android.car.drivingstate.ICarDrivingState;
     24 import android.car.drivingstate.ICarDrivingStateChangeListener;
     25 import android.car.hardware.CarPropertyConfig;
     26 import android.car.hardware.CarPropertyValue;
     27 import android.car.hardware.property.CarPropertyEvent;
     28 import android.car.hardware.property.ICarPropertyEventListener;
     29 import android.content.Context;
     30 import android.hardware.automotive.vehicle.V2_0.VehicleGear;
     31 import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
     32 import android.os.IBinder;
     33 import android.os.RemoteException;
     34 import android.os.SystemClock;
     35 import android.util.Log;
     36 
     37 import java.io.PrintWriter;
     38 import java.util.ArrayList;
     39 import java.util.LinkedList;
     40 import java.util.List;
     41 
     42 /**
     43  * A service that infers the current driving state of the vehicle.  It computes the driving state
     44  * from listening to relevant properties from {@link CarPropertyService}
     45  */
     46 public class CarDrivingStateService extends ICarDrivingState.Stub implements CarServiceBase {
     47     private static final String TAG = "CarDrivingState";
     48     private static final boolean DBG = false;
     49     private static final int MAX_TRANSITION_LOG_SIZE = 20;
     50     private static final int PROPERTY_UPDATE_RATE = 5; // Update rate in Hz
     51     private static final int NOT_RECEIVED = -1;
     52     private final Context mContext;
     53     private CarPropertyService mPropertyService;
     54     // List of clients listening to driving state events.
     55     private final List<DrivingStateClient> mDrivingStateClients = new ArrayList<>();
     56     // Array of properties that the service needs to listen to from CarPropertyService for deriving
     57     // the driving state.
     58     private static final int[] REQUIRED_PROPERTIES = {
     59             VehicleProperty.PERF_VEHICLE_SPEED,
     60             VehicleProperty.GEAR_SELECTION,
     61             VehicleProperty.PARKING_BRAKE_ON};
     62     private CarDrivingStateEvent mCurrentDrivingState;
     63     // For dumpsys logging
     64     private final LinkedList<Utils.TransitionLog> mTransitionLogs = new LinkedList<>();
     65     private int mLastGear;
     66     private long mLastGearTimestamp = NOT_RECEIVED;
     67     private float mLastSpeed;
     68     private long mLastSpeedTimestamp = NOT_RECEIVED;
     69     private boolean mLastParkingBrakeState;
     70     private long mLastParkingBrakeTimestamp = NOT_RECEIVED;
     71     private List<Integer> mSupportedGears;
     72 
     73     public CarDrivingStateService(Context context, CarPropertyService propertyService) {
     74         mContext = context;
     75         mPropertyService = propertyService;
     76         mCurrentDrivingState = createDrivingStateEvent(CarDrivingStateEvent.DRIVING_STATE_UNKNOWN);
     77     }
     78 
     79     @Override
     80     public synchronized void init() {
     81         if (!checkPropertySupport()) {
     82             Log.e(TAG, "init failure.  Driving state will always be fully restrictive");
     83             return;
     84         }
     85         subscribeToProperties();
     86         mCurrentDrivingState = createDrivingStateEvent(inferDrivingStateLocked());
     87         addTransitionLog(TAG + " Boot", CarDrivingStateEvent.DRIVING_STATE_UNKNOWN,
     88                 mCurrentDrivingState.eventValue, mCurrentDrivingState.timeStamp);
     89     }
     90 
     91     @Override
     92     public synchronized void release() {
     93         for (int property : REQUIRED_PROPERTIES) {
     94             mPropertyService.unregisterListener(property, mICarPropertyEventListener);
     95         }
     96         for (DrivingStateClient client : mDrivingStateClients) {
     97             client.listenerBinder.unlinkToDeath(client, 0);
     98         }
     99         mDrivingStateClients.clear();
    100         mCurrentDrivingState = createDrivingStateEvent(CarDrivingStateEvent.DRIVING_STATE_UNKNOWN);
    101     }
    102 
    103     /**
    104      * Checks if the {@link CarPropertyService} supports the required properties.
    105      *
    106      * @return {@code true} if supported, {@code false} if not
    107      */
    108     private synchronized boolean checkPropertySupport() {
    109         List<CarPropertyConfig> configs = mPropertyService.getPropertyList();
    110         for (int propertyId : REQUIRED_PROPERTIES) {
    111             boolean found = false;
    112             for (CarPropertyConfig config : configs) {
    113                 if (config.getPropertyId() == propertyId) {
    114                     found = true;
    115                     break;
    116                 }
    117             }
    118             if (!found) {
    119                 Log.e(TAG, "Required property not supported: " + propertyId);
    120                 return false;
    121             }
    122         }
    123         return true;
    124     }
    125 
    126     /**
    127      * Subscribe to the {@link CarPropertyService} for required sensors.
    128      */
    129     private synchronized void subscribeToProperties() {
    130         for (int propertyId : REQUIRED_PROPERTIES) {
    131             mPropertyService.registerListener(propertyId, PROPERTY_UPDATE_RATE,
    132                     mICarPropertyEventListener);
    133         }
    134 
    135     }
    136 
    137     // Binder methods
    138 
    139     /**
    140      * Register a {@link ICarDrivingStateChangeListener} to be notified for changes to the driving
    141      * state.
    142      *
    143      * @param listener {@link ICarDrivingStateChangeListener}
    144      */
    145     @Override
    146     public synchronized void registerDrivingStateChangeListener(
    147             ICarDrivingStateChangeListener listener) {
    148         if (listener == null) {
    149             if (DBG) {
    150                 Log.e(TAG, "registerDrivingStateChangeListener(): listener null");
    151             }
    152             throw new IllegalArgumentException("Listener is null");
    153         }
    154         // If a new client is registering, create a new DrivingStateClient and add it to the list
    155         // of listening clients.
    156         DrivingStateClient client = findDrivingStateClientLocked(listener);
    157         if (client == null) {
    158             client = new DrivingStateClient(listener);
    159             try {
    160                 listener.asBinder().linkToDeath(client, 0);
    161             } catch (RemoteException e) {
    162                 Log.e(TAG, "Cannot link death recipient to binder " + e);
    163                 return;
    164             }
    165             mDrivingStateClients.add(client);
    166         }
    167     }
    168 
    169     /**
    170      * Iterates through the list of registered Driving State Change clients -
    171      * {@link DrivingStateClient} and finds if the given client is already registered.
    172      *
    173      * @param listener Listener to look for.
    174      * @return the {@link DrivingStateClient} if found, null if not
    175      */
    176     @Nullable
    177     private DrivingStateClient findDrivingStateClientLocked(
    178             ICarDrivingStateChangeListener listener) {
    179         IBinder binder = listener.asBinder();
    180         // Find the listener by comparing the binder object they host.
    181         for (DrivingStateClient client : mDrivingStateClients) {
    182             if (client.isHoldingBinder(binder)) {
    183                 return client;
    184             }
    185         }
    186         return null;
    187     }
    188 
    189     /**
    190      * Unregister the given Driving State Change listener
    191      *
    192      * @param listener client to unregister
    193      */
    194     @Override
    195     public synchronized void unregisterDrivingStateChangeListener(
    196             ICarDrivingStateChangeListener listener) {
    197         if (listener == null) {
    198             Log.e(TAG, "unregisterDrivingStateChangeListener(): listener null");
    199             throw new IllegalArgumentException("Listener is null");
    200         }
    201 
    202         DrivingStateClient client = findDrivingStateClientLocked(listener);
    203         if (client == null) {
    204             Log.e(TAG, "unregisterDrivingStateChangeListener(): listener was not previously "
    205                     + "registered");
    206             return;
    207         }
    208         listener.asBinder().unlinkToDeath(client, 0);
    209         mDrivingStateClients.remove(client);
    210     }
    211 
    212     /**
    213      * Gets the current driving state
    214      *
    215      * @return {@link CarDrivingStateEvent} for the given event type
    216      */
    217     @Override
    218     @Nullable
    219     public synchronized CarDrivingStateEvent getCurrentDrivingState() {
    220         return mCurrentDrivingState;
    221     }
    222 
    223     /**
    224      * Class that holds onto client related information - listener interface, process that hosts the
    225      * binder object etc.
    226      * <p>
    227      * It also registers for death notifications of the host.
    228      */
    229     private class DrivingStateClient implements IBinder.DeathRecipient {
    230         private final IBinder listenerBinder;
    231         private final ICarDrivingStateChangeListener listener;
    232 
    233         public DrivingStateClient(ICarDrivingStateChangeListener l) {
    234             listener = l;
    235             listenerBinder = l.asBinder();
    236         }
    237 
    238         @Override
    239         public void binderDied() {
    240             if (DBG) {
    241                 Log.d(TAG, "Binder died " + listenerBinder);
    242             }
    243             listenerBinder.unlinkToDeath(this, 0);
    244             synchronized (CarDrivingStateService.this) {
    245                 mDrivingStateClients.remove(this);
    246             }
    247         }
    248 
    249         /**
    250          * Returns if the given binder object matches to what this client info holds.
    251          * Used to check if the listener asking to be registered is already registered.
    252          *
    253          * @return true if matches, false if not
    254          */
    255         public boolean isHoldingBinder(IBinder binder) {
    256             return listenerBinder == binder;
    257         }
    258 
    259         /**
    260          * Dispatch the events to the listener
    261          *
    262          * @param event {@link CarDrivingStateEvent}.
    263          */
    264         public void dispatchEventToClients(CarDrivingStateEvent event) {
    265             if (event == null) {
    266                 return;
    267             }
    268             try {
    269                 listener.onDrivingStateChanged(event);
    270             } catch (RemoteException e) {
    271                 if (DBG) {
    272                     Log.d(TAG, "Dispatch to listener failed");
    273                 }
    274             }
    275         }
    276     }
    277 
    278     @Override
    279     public void dump(PrintWriter writer) {
    280         writer.println("Driving state change log:");
    281         for (Utils.TransitionLog tLog : mTransitionLogs) {
    282             writer.println(tLog);
    283         }
    284         writer.println("Current Driving State: " + mCurrentDrivingState.eventValue);
    285         if (mSupportedGears != null) {
    286             writer.println("Supported gears:");
    287             for (Integer gear : mSupportedGears) {
    288                 writer.print("Gear:" + gear);
    289             }
    290         }
    291     }
    292 
    293     /**
    294      * {@link CarPropertyEvent} listener registered with the {@link CarPropertyService} for getting
    295      * property change notifications.
    296      */
    297     private final ICarPropertyEventListener mICarPropertyEventListener =
    298             new ICarPropertyEventListener.Stub() {
    299                 @Override
    300                 public void onEvent(List<CarPropertyEvent> events) throws RemoteException {
    301                     for (CarPropertyEvent event : events) {
    302                         handlePropertyEvent(event);
    303                     }
    304                 }
    305             };
    306 
    307     /**
    308      * Handle events coming from {@link CarPropertyService}.  Compute the driving state, map it to
    309      * the corresponding UX Restrictions and dispatch the events to the registered clients.
    310      */
    311     private synchronized void handlePropertyEvent(CarPropertyEvent event) {
    312         switch (event.getEventType()) {
    313             case CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE:
    314                 CarPropertyValue value = event.getCarPropertyValue();
    315                 int propId = value.getPropertyId();
    316                 long curTimestamp = value.getTimestamp();
    317                 Log.d(TAG, "Property Changed: propId=" + propId);
    318                 switch (propId) {
    319                     case VehicleProperty.PERF_VEHICLE_SPEED:
    320                         float curSpeed = (Float) value.getValue();
    321                         if (DBG) {
    322                             Log.d(TAG, "Speed: " + curSpeed + "@" + curTimestamp);
    323                         }
    324                         if (curTimestamp > mLastSpeedTimestamp) {
    325                             mLastSpeedTimestamp = curTimestamp;
    326                             mLastSpeed = curSpeed;
    327                         } else if (DBG) {
    328                             Log.d(TAG, "Ignoring speed with older timestamp:" + curTimestamp);
    329                         }
    330                         break;
    331                     case VehicleProperty.GEAR_SELECTION:
    332                         if (mSupportedGears == null) {
    333                             mSupportedGears = getSupportedGears();
    334                         }
    335                         int curGear = (Integer) value.getValue();
    336                         if (DBG) {
    337                             Log.d(TAG, "Gear: " + curGear + "@" + curTimestamp);
    338                         }
    339                         if (curTimestamp > mLastGearTimestamp) {
    340                             mLastGearTimestamp = curTimestamp;
    341                             mLastGear = (Integer) value.getValue();
    342                         } else if (DBG) {
    343                             Log.d(TAG, "Ignoring Gear with older timestamp:" + curTimestamp);
    344                         }
    345                         break;
    346                     case VehicleProperty.PARKING_BRAKE_ON:
    347                         boolean curParkingBrake = (boolean) value.getValue();
    348                         if (DBG) {
    349                             Log.d(TAG, "Parking Brake: " + curParkingBrake + "@" + curTimestamp);
    350                         }
    351                         if (curTimestamp > mLastParkingBrakeTimestamp) {
    352                             mLastParkingBrakeTimestamp = curTimestamp;
    353                             mLastParkingBrakeState = curParkingBrake;
    354                         } else if (DBG) {
    355                             Log.d(TAG, "Ignoring Parking Brake status with an older timestamp:"
    356                                     + curTimestamp);
    357                         }
    358                         break;
    359                     default:
    360                         Log.e(TAG, "Received property event for unhandled propId=" + propId);
    361                         break;
    362                 }
    363 
    364                 int drivingState = inferDrivingStateLocked();
    365                 // Check if the driving state has changed.  If it has, update our records and
    366                 // dispatch the new events to the listeners.
    367                 if (DBG) {
    368                     Log.d(TAG, "Driving state new->old " + drivingState + "->"
    369                             + mCurrentDrivingState.eventValue);
    370                 }
    371                 if (drivingState != mCurrentDrivingState.eventValue) {
    372                     addTransitionLog(TAG, mCurrentDrivingState.eventValue, drivingState,
    373                             System.currentTimeMillis());
    374                     // Update if there is a change in state.
    375                     mCurrentDrivingState = createDrivingStateEvent(drivingState);
    376                     if (DBG) {
    377                         Log.d(TAG, "dispatching to " + mDrivingStateClients.size() + " clients");
    378                     }
    379                     for (DrivingStateClient client : mDrivingStateClients) {
    380                         client.dispatchEventToClients(mCurrentDrivingState);
    381                     }
    382                 }
    383                 break;
    384             default:
    385                 // Unhandled event
    386                 break;
    387         }
    388     }
    389 
    390     private List<Integer> getSupportedGears() {
    391         List<CarPropertyConfig> properyList = mPropertyService.getPropertyList();
    392         for (CarPropertyConfig p : properyList) {
    393             if (p.getPropertyId() == VehicleProperty.GEAR_SELECTION) {
    394                 return p.getConfigArray();
    395             }
    396         }
    397         return null;
    398     }
    399 
    400     private void addTransitionLog(String name, int from, int to, long timestamp) {
    401         if (mTransitionLogs.size() >= MAX_TRANSITION_LOG_SIZE) {
    402             mTransitionLogs.remove();
    403         }
    404 
    405         Utils.TransitionLog tLog = new Utils.TransitionLog(name, from, to, timestamp);
    406         mTransitionLogs.add(tLog);
    407     }
    408 
    409     /**
    410      * Infers the current driving state of the car from the other Car Sensor properties like
    411      * Current Gear, Speed etc.
    412      *
    413      * @return Current driving state
    414      */
    415     @CarDrivingState
    416     private int inferDrivingStateLocked() {
    417         updateVehiclePropertiesIfNeeded();
    418         if (DBG) {
    419             Log.d(TAG, "Last known Gear:" + mLastGear + " Last known speed:" + mLastSpeed);
    420         }
    421 
    422         /*
    423             Logic to start off deriving driving state:
    424             1. If gear == parked, then Driving State is parked.
    425             2. If gear != parked,
    426                     2a. if parking brake is applied, then Driving state is parked.
    427                     2b. if parking brake is not applied or unknown/unavailable, then driving state
    428                     is still unknown.
    429             3. If driving state is unknown at the end of step 2,
    430                 3a. if speed == 0, then driving state is idling
    431                 3b. if speed != 0, then driving state is moving
    432                 3c. if speed unavailable, then driving state is unknown
    433          */
    434 
    435         if (isVehicleKnownToBeParked()) {
    436             return CarDrivingStateEvent.DRIVING_STATE_PARKED;
    437         }
    438 
    439         // We don't know if the vehicle is parked, let's look at the speed.
    440         if (mLastSpeedTimestamp == NOT_RECEIVED || mLastSpeed < 0) {
    441             return CarDrivingStateEvent.DRIVING_STATE_UNKNOWN;
    442         } else if (mLastSpeed == 0f) {
    443             return CarDrivingStateEvent.DRIVING_STATE_IDLING;
    444         } else {
    445             return CarDrivingStateEvent.DRIVING_STATE_MOVING;
    446         }
    447     }
    448 
    449     /**
    450      * Find if we have signals to know if the vehicle is parked
    451      *
    452      * @return true if we have enough information to say the vehicle is parked.
    453      * false, if the vehicle is either not parked or if we don't have any information.
    454      */
    455     private boolean isVehicleKnownToBeParked() {
    456         // If we know the gear is in park, return true
    457         if (mLastGearTimestamp != NOT_RECEIVED && mLastGear == VehicleGear.GEAR_PARK) {
    458             return true;
    459         } else if (mLastParkingBrakeTimestamp != NOT_RECEIVED) {
    460             // if gear is not in park or unknown, look for status of parking brake if transmission
    461             // type is manual.
    462             if (isCarManualTransmissionType()) {
    463                 return mLastParkingBrakeState;
    464             }
    465         }
    466         // if neither information is available, return false to indicate we can't determine
    467         // if the vehicle is parked.
    468         return false;
    469     }
    470 
    471     /**
    472      * If Supported gears information is available and GEAR_PARK is not one of the supported gears,
    473      * transmission type is considered to be Manual.  Automatic transmission is assumed otherwise.
    474      */
    475     private boolean isCarManualTransmissionType() {
    476         if (mSupportedGears != null
    477                 && !mSupportedGears.isEmpty()
    478                 && !mSupportedGears.contains(VehicleGear.GEAR_PARK)) {
    479             return true;
    480         }
    481         return false;
    482     }
    483 
    484     /**
    485      * Try querying the gear selection and parking brake if we haven't received the event yet.
    486      * This could happen if the gear change occurred before car service booted up like in the
    487      * case of a HU restart in the middle of a drive.  Since gear and parking brake are
    488      * on-change only properties, we could be in this situation where we will have to query
    489      * VHAL.
    490      */
    491     private void updateVehiclePropertiesIfNeeded() {
    492         if (mLastGearTimestamp == NOT_RECEIVED) {
    493             CarPropertyValue propertyValue = mPropertyService.getProperty(
    494                     VehicleProperty.GEAR_SELECTION,
    495                     VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL);
    496             if (propertyValue != null) {
    497                 mLastGear = (Integer) propertyValue.getValue();
    498                 mLastGearTimestamp = propertyValue.getTimestamp();
    499                 if (DBG) {
    500                     Log.d(TAG, "updateVehiclePropertiesIfNeeded: gear:" + mLastGear);
    501                 }
    502             }
    503         }
    504 
    505         if (mLastParkingBrakeTimestamp == NOT_RECEIVED) {
    506             CarPropertyValue propertyValue = mPropertyService.getProperty(
    507                     VehicleProperty.PARKING_BRAKE_ON,
    508                     VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL);
    509             if (propertyValue != null) {
    510                 mLastParkingBrakeState = (boolean) propertyValue.getValue();
    511                 mLastParkingBrakeTimestamp = propertyValue.getTimestamp();
    512                 if (DBG) {
    513                     Log.d(TAG, "updateVehiclePropertiesIfNeeded: brake:" + mLastParkingBrakeState);
    514                 }
    515             }
    516         }
    517 
    518         if (mLastSpeedTimestamp == NOT_RECEIVED) {
    519             CarPropertyValue propertyValue = mPropertyService.getProperty(
    520                     VehicleProperty.PERF_VEHICLE_SPEED,
    521                     VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL);
    522             if (propertyValue != null) {
    523                 mLastSpeed = (float) propertyValue.getValue();
    524                 mLastSpeedTimestamp = propertyValue.getTimestamp();
    525                 if (DBG) {
    526                     Log.d(TAG, "updateVehiclePropertiesIfNeeded: speed:" + mLastSpeed);
    527                 }
    528             }
    529         }
    530     }
    531 
    532     private static CarDrivingStateEvent createDrivingStateEvent(int eventValue) {
    533         return new CarDrivingStateEvent(eventValue, SystemClock.elapsedRealtimeNanos());
    534     }
    535 
    536 }
    537