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