1 /* 2 * Copyright (C) 2015 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 static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertNotNull; 22 import static org.junit.Assert.assertTrue; 23 24 import android.car.Car; 25 import android.car.hardware.CarSensorEvent; 26 import android.car.hardware.CarSensorManager; 27 import android.hardware.automotive.vehicle.V2_0.VehicleGear; 28 import android.hardware.automotive.vehicle.V2_0.VehicleIgnitionState; 29 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue; 30 import android.hardware.automotive.vehicle.V2_0.VehicleProperty; 31 import android.os.SystemClock; 32 import android.util.Log; 33 34 import androidx.test.ext.junit.runners.AndroidJUnit4; 35 import androidx.test.filters.MediumTest; 36 37 import com.android.car.vehiclehal.VehiclePropValueBuilder; 38 39 import org.junit.Test; 40 import org.junit.runner.RunWith; 41 42 /** 43 * Test the public entry points for the CarSensorManager 44 */ 45 @RunWith(AndroidJUnit4.class) 46 @MediumTest 47 public class CarSensorManagerTest extends MockedCarTestBase { 48 private static final String TAG = CarSensorManagerTest.class.getSimpleName(); 49 50 private CarSensorManager mCarSensorManager; 51 52 @Override configureMockedHal()53 protected synchronized void configureMockedHal() { 54 addProperty(VehicleProperty.NIGHT_MODE, 55 VehiclePropValueBuilder.newBuilder(VehicleProperty.NIGHT_MODE) 56 .addIntValue(0) 57 .build()); 58 addProperty(VehicleProperty.PERF_VEHICLE_SPEED, 59 VehiclePropValueBuilder.newBuilder(VehicleProperty.PERF_VEHICLE_SPEED) 60 .addFloatValue(0f) 61 .build()); 62 addProperty(VehicleProperty.FUEL_LEVEL, 63 VehiclePropValueBuilder.newBuilder(VehicleProperty.FUEL_LEVEL) 64 .addFloatValue(20000) // ml 65 .build()); 66 addProperty(VehicleProperty.PARKING_BRAKE_ON, 67 VehiclePropValueBuilder.newBuilder(VehicleProperty.PARKING_BRAKE_ON) 68 .setBooleanValue(true) 69 .build()); 70 addProperty(VehicleProperty.CURRENT_GEAR, 71 VehiclePropValueBuilder.newBuilder(VehicleProperty.CURRENT_GEAR) 72 .addIntValue(0) 73 .build()); 74 addProperty(VehicleProperty.GEAR_SELECTION, 75 VehiclePropValueBuilder.newBuilder(VehicleProperty.GEAR_SELECTION) 76 .addIntValue(0) 77 .build()); 78 addProperty(VehicleProperty.IGNITION_STATE, 79 VehiclePropValueBuilder.newBuilder(VehicleProperty.IGNITION_STATE) 80 .addIntValue(CarSensorEvent.IGNITION_STATE_ACC) 81 .build()); 82 } 83 84 @Override setUp()85 public void setUp() throws Exception { 86 super.setUp(); 87 // Start the HAL layer and set up the sensor manager service 88 mCarSensorManager = (CarSensorManager) getCar().getCarManager(Car.SENSOR_SERVICE); 89 } 90 91 /** 92 * Test single sensor availability entry point 93 * @throws Exception 94 */ 95 @Test testSensorAvailability()96 public void testSensorAvailability() throws Exception { 97 // NOTE: Update this test if/when the reserved values put into use. For now, we 98 // expect them to never be supported. 99 assertFalse(mCarSensorManager.isSensorSupported(CarSensorManager.SENSOR_TYPE_RESERVED1)); 100 assertFalse(mCarSensorManager.isSensorSupported(CarSensorManager.SENSOR_TYPE_RESERVED13)); 101 assertFalse(mCarSensorManager.isSensorSupported(CarSensorManager.SENSOR_TYPE_RESERVED21)); 102 103 // We expect these sensors to always be available 104 assertTrue(mCarSensorManager.isSensorSupported(CarSensorManager.SENSOR_TYPE_CAR_SPEED)); 105 assertTrue(mCarSensorManager.isSensorSupported(CarSensorManager.SENSOR_TYPE_FUEL_LEVEL)); 106 assertTrue(mCarSensorManager.isSensorSupported(CarSensorManager.SENSOR_TYPE_PARKING_BRAKE)); 107 assertTrue(mCarSensorManager.isSensorSupported(CarSensorManager.SENSOR_TYPE_GEAR)); 108 assertTrue(mCarSensorManager.isSensorSupported(CarSensorManager.SENSOR_TYPE_NIGHT)); 109 assertTrue(mCarSensorManager.isSensorSupported( 110 CarSensorManager.SENSOR_TYPE_IGNITION_STATE)); 111 } 112 113 /** 114 * Test sensor enumeration entry point 115 * @throws Exception 116 */ 117 @Test testSensorEnumeration()118 public void testSensorEnumeration() throws Exception { 119 int[] supportedSensors = mCarSensorManager.getSupportedSensors(); 120 assertNotNull(supportedSensors); 121 122 Log.i(TAG, "Found " + supportedSensors.length + " supported sensors."); 123 124 // Unfortunately, we don't have a definitive range for legal sensor values, 125 // so we have set a "reasonable" range here. The ending value, in particular, 126 // will need to be updated if/when new sensor types are allowed. 127 // Here we are ensuring that all the enumerated sensors also return supported. 128 for (int candidate = 0; candidate <= CarSensorManager.SENSOR_TYPE_RESERVED21; ++candidate) { 129 boolean supported = mCarSensorManager.isSensorSupported(candidate); 130 boolean found = false; 131 for (int sensor : supportedSensors) { 132 if (candidate == sensor) { 133 found = true; 134 Log.i(TAG, "Sensor type " + sensor + " is supported."); 135 break; 136 } 137 } 138 139 // Make sure the individual query on a sensor type is consistent 140 assertEquals(found, supported); 141 } 142 } 143 144 /** 145 * Test sensor notification registration, delivery, and unregistration 146 * @throws Exception 147 */ 148 @Test testEvents()149 public void testEvents() throws Exception { 150 // Set up our listener callback 151 SensorListener listener = new SensorListener(); 152 mCarSensorManager.registerListener(listener, 153 CarSensorManager.SENSOR_TYPE_PARKING_BRAKE, 154 CarSensorManager.SENSOR_RATE_FASTEST); 155 156 VehiclePropValue value; 157 CarSensorEvent event; 158 CarSensorEvent.ParkingBrakeData data = null; 159 160 // Clear event generated by registerCallback() 161 listener.waitForSensorChange(); 162 listener.reset(); 163 164 // Set the value TRUE and wait for the event to arrive 165 getMockedVehicleHal().injectEvent( 166 VehiclePropValueBuilder.newBuilder(VehicleProperty.PARKING_BRAKE_ON) 167 .setBooleanValue(true) 168 .setTimestamp(51L) 169 .build(), true); 170 assertTrue(listener.waitForSensorChange(51L)); 171 172 // Ensure we got the expected event 173 assertEquals(listener.getLastEvent().sensorType, 174 CarSensorManager.SENSOR_TYPE_PARKING_BRAKE); 175 176 // Ensure we got the expected value in our callback 177 data = listener.getLastEvent().getParkingBrakeData(data); 178 Log.d(TAG, "Parking: " + data.isEngaged + " at " + data.timestamp); 179 assertTrue(data.isEngaged); 180 181 // Ensure we have the expected value in the sensor manager's cache 182 event = mCarSensorManager.getLatestSensorEvent(CarSensorManager.SENSOR_TYPE_PARKING_BRAKE); 183 assertNotNull(event); 184 data = event.getParkingBrakeData(data); 185 assertEquals("Unexpected event timestamp", data.timestamp, 51); 186 assertTrue("Unexpected value", data.isEngaged); 187 188 listener.reset(); 189 // Set the value FALSE 190 getMockedVehicleHal().injectEvent( 191 VehiclePropValueBuilder.newBuilder(VehicleProperty.PARKING_BRAKE_ON) 192 .setTimestamp(1001) 193 .setBooleanValue(false) 194 .build(), true); 195 assertTrue(listener.waitForSensorChange(1001)); 196 197 // Ensure we got the expected event 198 assertEquals(listener.getLastEvent().sensorType, 199 CarSensorManager.SENSOR_TYPE_PARKING_BRAKE); 200 201 // Ensure we got the expected value in our callback 202 data = listener.getLastEvent().getParkingBrakeData(data); 203 assertEquals("Unexpected event timestamp", 1001, data.timestamp); 204 assertFalse("Unexpected value", data.isEngaged); 205 206 // Ensure we have the expected value in the sensor manager's cache 207 event = mCarSensorManager.getLatestSensorEvent(CarSensorManager.SENSOR_TYPE_PARKING_BRAKE); 208 assertNotNull(event); 209 data = event.getParkingBrakeData(data); 210 assertFalse(data.isEngaged); 211 212 // Unregister our handler (from all sensor types) 213 mCarSensorManager.unregisterListener(listener); 214 215 listener.reset(); 216 // Set the value TRUE again 217 value = VehiclePropValueBuilder.newBuilder(VehicleProperty.PARKING_BRAKE_ON) 218 .setTimestamp(2001) 219 .setBooleanValue(true) 220 .build(); 221 getMockedVehicleHal().injectEvent(value, true); 222 223 // Ensure we did not get a callback (should timeout) 224 Log.i(TAG, "waiting for unexpected callback -- should timeout."); 225 assertFalse(listener.waitForSensorChange(2001)); 226 227 // Despite us not having a callback registered, the Sensor Manager should see the update 228 event = mCarSensorManager.getLatestSensorEvent(CarSensorManager.SENSOR_TYPE_PARKING_BRAKE); 229 assertNotNull(event); 230 data = event.getParkingBrakeData(data); 231 assertEquals("Unexpected event timestamp", data.timestamp, 2001); 232 assertTrue("Unexpected value", data.isEngaged); 233 } 234 235 @Test testIgnitionState()236 public void testIgnitionState() { 237 CarSensorEvent event = mCarSensorManager.getLatestSensorEvent( 238 CarSensorManager.SENSOR_TYPE_IGNITION_STATE); 239 assertNotNull(event); 240 assertEquals(CarSensorEvent.IGNITION_STATE_ACC, event.intValues[0]); 241 } 242 243 @Test testIgnitionEvents()244 public void testIgnitionEvents() throws Exception { 245 SensorListener listener = new SensorListener(); 246 mCarSensorManager.registerListener(listener, CarSensorManager.SENSOR_TYPE_IGNITION_STATE, 247 CarSensorManager.SENSOR_RATE_NORMAL); 248 // Clear event generated by registerCallback() 249 listener.waitForSensorChange(); 250 251 // Mapping of HAL -> Manager ignition states. 252 int[] ignitionStates = new int[] { 253 VehicleIgnitionState.UNDEFINED, CarSensorEvent.IGNITION_STATE_UNDEFINED, 254 VehicleIgnitionState.LOCK, CarSensorEvent.IGNITION_STATE_LOCK, 255 VehicleIgnitionState.OFF, CarSensorEvent.IGNITION_STATE_OFF, 256 VehicleIgnitionState.ACC, CarSensorEvent.IGNITION_STATE_ACC, 257 VehicleIgnitionState.ON, CarSensorEvent.IGNITION_STATE_ON, 258 VehicleIgnitionState.START, CarSensorEvent.IGNITION_STATE_START, 259 VehicleIgnitionState.ON, CarSensorEvent.IGNITION_STATE_ON, 260 VehicleIgnitionState.LOCK, CarSensorEvent.IGNITION_STATE_LOCK, 261 }; 262 263 for (int i = 0; i < ignitionStates.length; i += 2) { 264 injectIgnitionStateAndAssert(listener, ignitionStates[i], ignitionStates[i + 1]); 265 } 266 } 267 injectIgnitionStateAndAssert(SensorListener listener, int halIgnitionState, int mgrIgnitionState)268 private void injectIgnitionStateAndAssert(SensorListener listener, int halIgnitionState, 269 int mgrIgnitionState) throws Exception{ 270 listener.reset(); 271 long time = SystemClock.elapsedRealtimeNanos(); 272 getMockedVehicleHal().injectEvent( 273 VehiclePropValueBuilder.newBuilder(VehicleProperty.IGNITION_STATE) 274 .addIntValue(halIgnitionState) 275 .setTimestamp(time) 276 .build(), true); 277 assertTrue(listener.waitForSensorChange(time)); 278 279 CarSensorEvent eventReceived = listener.getLastEvent(); 280 assertEquals(CarSensorManager.SENSOR_TYPE_IGNITION_STATE, eventReceived.sensorType); 281 assertEquals(mgrIgnitionState, eventReceived.intValues[0]); 282 } 283 284 @Test testGear()285 public void testGear() throws Exception { 286 SensorListener listener = new SensorListener(); 287 288 mCarSensorManager.registerListener(listener, CarSensorManager.SENSOR_TYPE_GEAR, 289 CarSensorManager.SENSOR_RATE_NORMAL); 290 291 // Clear event generated by registerCallback() 292 listener.waitForSensorChange(); 293 294 // Mapping of HAL -> Manager gear selection states. 295 int[] gears = new int[] { 296 VehicleGear.GEAR_PARK, CarSensorEvent.GEAR_PARK, 297 VehicleGear.GEAR_DRIVE, CarSensorEvent.GEAR_DRIVE, 298 VehicleGear.GEAR_NEUTRAL, CarSensorEvent.GEAR_NEUTRAL, 299 VehicleGear.GEAR_REVERSE, CarSensorEvent.GEAR_REVERSE, 300 VehicleGear.GEAR_1, CarSensorEvent.GEAR_FIRST, 301 VehicleGear.GEAR_2, CarSensorEvent.GEAR_SECOND, 302 VehicleGear.GEAR_3, CarSensorEvent.GEAR_THIRD, 303 VehicleGear.GEAR_4, CarSensorEvent.GEAR_FOURTH, 304 VehicleGear.GEAR_5, CarSensorEvent.GEAR_FIFTH, 305 VehicleGear.GEAR_6, CarSensorEvent.GEAR_SIXTH, 306 VehicleGear.GEAR_7, CarSensorEvent.GEAR_SEVENTH, 307 VehicleGear.GEAR_8, CarSensorEvent.GEAR_EIGHTH, 308 VehicleGear.GEAR_9, CarSensorEvent.GEAR_NINTH, 309 }; 310 311 for (int i = 0; i < gears.length; i += 2) { 312 injectGearEventAndAssert(listener, gears[i], gears[i + 1]); 313 } 314 } 315 injectGearEventAndAssert(SensorListener listener, int halValue, int carSensorValue)316 private void injectGearEventAndAssert(SensorListener listener, int halValue, 317 int carSensorValue) throws Exception { 318 listener.reset(); 319 long time = SystemClock.elapsedRealtimeNanos(); 320 getMockedVehicleHal().injectEvent( 321 VehiclePropValueBuilder.newBuilder(VehicleProperty.GEAR_SELECTION) 322 .addIntValue(halValue) 323 .setTimestamp(time) 324 .build(), true); 325 assertTrue(listener.waitForSensorChange(time)); 326 CarSensorEvent event = mCarSensorManager.getLatestSensorEvent( 327 CarSensorManager.SENSOR_TYPE_GEAR); 328 assertNotNull(event); 329 assertEquals(carSensorValue, event.intValues[0]); 330 } 331 332 /** 333 * Test sensor multiple liseners notification registration, delivery and unregistration. 334 * @throws Exception 335 */ 336 @Test testEventsWithMultipleListeners()337 public void testEventsWithMultipleListeners() throws Exception { 338 // Set up our listeners callback 339 SensorListener listener1 = new SensorListener(); 340 SensorListener listener2 = new SensorListener(); 341 SensorListener listener3 = new SensorListener(); 342 343 mCarSensorManager.registerListener(listener1, 344 CarSensorManager.SENSOR_TYPE_PARKING_BRAKE, 345 CarSensorManager.SENSOR_RATE_NORMAL); 346 347 mCarSensorManager.registerListener(listener2, 348 CarSensorManager.SENSOR_TYPE_PARKING_BRAKE, 349 CarSensorManager.SENSOR_RATE_NORMAL); 350 351 mCarSensorManager.registerListener(listener3, 352 CarSensorManager.SENSOR_TYPE_PARKING_BRAKE, 353 CarSensorManager.SENSOR_RATE_FASTEST); 354 355 CarSensorEvent.ParkingBrakeData data = null; 356 VehiclePropValue value; 357 CarSensorEvent event; 358 359 // Clear event generated by registerCallback() 360 listener1.waitForSensorChange(); 361 listener2.waitForSensorChange(); 362 listener3.waitForSensorChange(); 363 listener1.reset(); 364 listener2.reset(); 365 listener3.reset(); 366 367 // Set the value TRUE and wait for the event to arrive 368 value = VehiclePropValueBuilder.newBuilder(VehicleProperty.PARKING_BRAKE_ON) 369 .setTimestamp(1001L) 370 .setBooleanValue(true) 371 .build(); 372 getMockedVehicleHal().injectEvent(value, true); 373 374 assertTrue(listener1.waitForSensorChange(1001L)); 375 assertTrue(listener2.waitForSensorChange(1001L)); 376 assertTrue(listener3.waitForSensorChange(1001L)); 377 378 // Ensure we got the expected event 379 assertEquals(listener1.getLastEvent().sensorType, 380 CarSensorManager.SENSOR_TYPE_PARKING_BRAKE); 381 assertEquals(listener2.getLastEvent().sensorType, 382 CarSensorManager.SENSOR_TYPE_PARKING_BRAKE); 383 assertEquals(listener3.getLastEvent().sensorType, 384 CarSensorManager.SENSOR_TYPE_PARKING_BRAKE); 385 386 // Ensure we got the expected value in our callback 387 data = listener1.getLastEvent().getParkingBrakeData(data); 388 Log.d(TAG, "Parking brake is " + data.isEngaged + " at " + data.timestamp); 389 assertTrue(data.isEngaged); 390 391 data = listener2.getLastEvent().getParkingBrakeData(data); 392 Log.d(TAG, "Parking brake is " + data.isEngaged + " at " + data.timestamp); 393 assertTrue(data.isEngaged); 394 395 data = listener3.getLastEvent().getParkingBrakeData(data); 396 Log.d(TAG, "Parking brake is" + data.isEngaged + " at " + data.timestamp); 397 assertTrue(data.isEngaged); 398 399 // Ensure we have the expected value in the sensor manager's cache 400 event = mCarSensorManager.getLatestSensorEvent(CarSensorManager.SENSOR_TYPE_PARKING_BRAKE); 401 data = event.getParkingBrakeData(data); 402 assertEquals("Unexpected event timestamp", 1001, data.timestamp); 403 assertTrue("Unexpected value", data.isEngaged); 404 405 listener1.reset(); 406 listener2.reset(); 407 listener3.reset(); 408 // Set the value FALSE 409 value = VehiclePropValueBuilder.newBuilder(VehicleProperty.PARKING_BRAKE_ON) 410 .setTimestamp(2001) 411 .setBooleanValue(false) 412 .build(); 413 getMockedVehicleHal().injectEvent(value, true); 414 assertTrue(listener1.waitForSensorChange(2001)); 415 assertTrue(listener2.waitForSensorChange(2001)); 416 assertTrue(listener3.waitForSensorChange(2001)); 417 418 // Ensure we got the expected event 419 assertEquals(listener1.getLastEvent().sensorType, 420 CarSensorManager.SENSOR_TYPE_PARKING_BRAKE); 421 assertEquals(listener2.getLastEvent().sensorType, 422 CarSensorManager.SENSOR_TYPE_PARKING_BRAKE); 423 assertEquals(listener3.getLastEvent().sensorType, 424 CarSensorManager.SENSOR_TYPE_PARKING_BRAKE); 425 426 // Ensure we got the expected value in our callback 427 data = listener1.getLastEvent().getParkingBrakeData(data); 428 assertEquals("Unexpected event timestamp", 2001, data.timestamp); 429 assertFalse("Unexpected value", data.isEngaged); 430 431 data = listener2.getLastEvent().getParkingBrakeData(data); 432 assertEquals("Unexpected event timestamp", 2001, data.timestamp); 433 assertFalse("Unexpected value", data.isEngaged); 434 435 data = listener3.getLastEvent().getParkingBrakeData(data); 436 assertEquals("Unexpected event timestamp", 2001, data.timestamp); 437 assertFalse("Unexpected value", data.isEngaged); 438 439 // Ensure we have the expected value in the sensor manager's cache 440 event = mCarSensorManager.getLatestSensorEvent(CarSensorManager.SENSOR_TYPE_PARKING_BRAKE); 441 data = event.getParkingBrakeData(data); 442 assertFalse(data.isEngaged); 443 444 Log.d(TAG, "Unregistering listener3"); 445 listener1.reset(); 446 listener2.reset(); 447 listener3.reset(); 448 mCarSensorManager.unregisterListener(listener3); 449 Log.d(TAG, "Rate changed - expect sensor restart and change event sent."); 450 value = VehiclePropValueBuilder.newBuilder(VehicleProperty.PARKING_BRAKE_ON) 451 .setTimestamp(3002) 452 .setBooleanValue(false) 453 .build(); 454 getMockedVehicleHal().injectEvent(value, true); 455 assertTrue(listener1.waitForSensorChange()); 456 assertTrue(listener2.waitForSensorChange()); 457 assertFalse(listener3.waitForSensorChange()); 458 listener1.reset(); 459 listener2.reset(); 460 listener3.reset(); 461 // Set the value TRUE again 462 value = VehiclePropValueBuilder.newBuilder(VehicleProperty.PARKING_BRAKE_ON) 463 .setTimestamp() 464 .setBooleanValue(true) 465 .build(); 466 getMockedVehicleHal().injectEvent(value, true); 467 468 assertTrue(listener1.waitForSensorChange()); 469 assertTrue(listener2.waitForSensorChange()); 470 listener1.reset(); 471 listener2.reset(); 472 473 // Ensure we did not get a callback (should timeout) 474 Log.i(TAG, "waiting for unexpected callback -- should timeout."); 475 assertFalse(listener3.waitForSensorChange()); 476 477 Log.d(TAG, "Unregistering listener2"); 478 mCarSensorManager.unregisterListener(listener2); 479 480 Log.d(TAG, "Rate did nor change - dont expect sensor restart and change event sent."); 481 assertFalse(listener1.waitForSensorChange()); 482 assertFalse(listener2.waitForSensorChange()); 483 assertFalse(listener3.waitForSensorChange()); 484 } 485 486 487 /** 488 * Callback function we register for sensor update notifications. 489 * This tracks the number of times it has been called via the mAvailable semaphore, 490 * and keeps a reference to the most recent event delivered. 491 */ 492 class SensorListener implements CarSensorManager.OnSensorChangedListener { 493 private final Object mSync = new Object(); 494 495 private CarSensorEvent mLastEvent = null; 496 getLastEvent()497 CarSensorEvent getLastEvent() { 498 return mLastEvent; 499 } 500 reset()501 void reset() { 502 synchronized (mSync) { 503 mLastEvent = null; 504 } 505 } 506 waitForSensorChange()507 boolean waitForSensorChange() throws InterruptedException { 508 return waitForSensorChange(0); 509 } 510 511 // Returns True to indicate receipt of a sensor event. False indicates a timeout. waitForSensorChange(long eventTimeStamp)512 boolean waitForSensorChange(long eventTimeStamp) throws InterruptedException { 513 long start = SystemClock.elapsedRealtime(); 514 boolean matchTimeStamp = eventTimeStamp != 0; 515 synchronized (mSync) { 516 Log.d(TAG, "waitForSensorChange, mLastEvent: " + mLastEvent); 517 while ((mLastEvent == null 518 || (matchTimeStamp && mLastEvent.timestamp != eventTimeStamp)) 519 && (start + SHORT_WAIT_TIMEOUT_MS > SystemClock.elapsedRealtime())) { 520 mSync.wait(10L); 521 } 522 return mLastEvent != null && 523 (!matchTimeStamp || mLastEvent.timestamp == eventTimeStamp); 524 } 525 } 526 527 @Override onSensorChanged(CarSensorEvent event)528 public void onSensorChanged(CarSensorEvent event) { 529 Log.d(TAG, "onSensorChanged, event: " + event); 530 synchronized (mSync) { 531 // We're going to hold a reference to this object 532 mLastEvent = event; 533 mSync.notify(); 534 } 535 } 536 } 537 538 } 539