• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
CarDrivingStateService(Context context, CarPropertyService propertyService)73     public CarDrivingStateService(Context context, CarPropertyService propertyService) {
74         mContext = context;
75         mPropertyService = propertyService;
76         mCurrentDrivingState = createDrivingStateEvent(CarDrivingStateEvent.DRIVING_STATE_UNKNOWN);
77     }
78 
79     @Override
init()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
release()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      */
checkPropertySupport()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      */
subscribeToProperties()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
registerDrivingStateChangeListener( ICarDrivingStateChangeListener listener)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
findDrivingStateClientLocked( ICarDrivingStateChangeListener listener)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
unregisterDrivingStateChangeListener( ICarDrivingStateChangeListener listener)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
getCurrentDrivingState()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 
DrivingStateClient(ICarDrivingStateChangeListener l)233         public DrivingStateClient(ICarDrivingStateChangeListener l) {
234             listener = l;
235             listenerBinder = l.asBinder();
236         }
237 
238         @Override
binderDied()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          */
isHoldingBinder(IBinder binder)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          */
dispatchEventToClients(CarDrivingStateEvent event)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
dump(PrintWriter writer)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      */
handlePropertyEvent(CarPropertyEvent event)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 
getSupportedGears()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 
addTransitionLog(String name, int from, int to, long timestamp)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
inferDrivingStateLocked()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      */
isVehicleKnownToBeParked()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      */
isCarManualTransmissionType()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      */
updateVehiclePropertiesIfNeeded()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 
createDrivingStateEvent(int eventValue)532     private static CarDrivingStateEvent createDrivingStateEvent(int eventValue) {
533         return new CarDrivingStateEvent(eventValue, SystemClock.elapsedRealtimeNanos());
534     }
535 
536 }
537