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