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 static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
20 
21 import android.annotation.NonNull;
22 import android.car.Car;
23 import android.car.VehicleAreaType;
24 import android.car.builtin.os.BinderHelper;
25 import android.car.builtin.util.Slogf;
26 import android.car.drivingstate.CarDrivingStateEvent;
27 import android.car.drivingstate.CarDrivingStateEvent.CarDrivingState;
28 import android.car.drivingstate.ICarDrivingState;
29 import android.car.drivingstate.ICarDrivingStateChangeListener;
30 import android.car.hardware.CarPropertyConfig;
31 import android.car.hardware.CarPropertyValue;
32 import android.car.hardware.property.CarPropertyEvent;
33 import android.car.hardware.property.ICarPropertyEventListener;
34 import android.content.Context;
35 import android.hardware.automotive.vehicle.VehicleGear;
36 import android.hardware.automotive.vehicle.VehicleIgnitionState;
37 import android.hardware.automotive.vehicle.VehicleProperty;
38 import android.os.Handler;
39 import android.os.HandlerThread;
40 import android.os.RemoteCallbackList;
41 import android.os.RemoteException;
42 import android.os.SystemClock;
43 import android.util.Log;
44 import android.util.SparseBooleanArray;
45 import android.util.proto.ProtoOutputStream;
46 
47 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
48 import com.android.car.internal.util.IndentingPrintWriter;
49 import com.android.car.util.TransitionLog;
50 import com.android.internal.annotations.GuardedBy;
51 import com.android.internal.annotations.VisibleForTesting;
52 
53 import java.util.Collections;
54 import java.util.LinkedList;
55 import java.util.List;
56 
57 /**
58  * A service that infers the current driving state of the vehicle.  It computes the driving state
59  * from listening to relevant properties from {@link CarPropertyService}
60  */
61 public class CarDrivingStateService extends ICarDrivingState.Stub implements CarServiceBase {
62     private static final String TAG = CarLog.tagFor(CarDrivingStateService.class);
63     private static final boolean DBG = Slogf.isLoggable(TAG, Log.DEBUG);
64     private static final int MAX_TRANSITION_LOG_SIZE = 20;
65     private static final int PROPERTY_UPDATE_RATE = 5; // Update rate in Hz
66     private final Context mContext;
67     private final CarPropertyService mPropertyService;
68     // List of clients listening to driving state events.
69     private final RemoteCallbackList<ICarDrivingStateChangeListener> mDrivingStateClients =
70             new RemoteCallbackList<>();
71     // Array of properties that the service needs to listen to from CarPropertyService for deriving
72     // the driving state.
73     private static final int[] REQUIRED_PROPERTIES = {
74             VehicleProperty.PERF_VEHICLE_SPEED,
75             VehicleProperty.GEAR_SELECTION,
76             VehicleProperty.PARKING_BRAKE_ON,
77     };
78     // Array of optional properties that the service needs to listen to from CarPropertyService
79     // for deriving the driving state.
80     private static final int[] OPTIONAL_PROPERTIES = {
81             VehicleProperty.IGNITION_STATE
82     };
83     private final HandlerThread mClientDispatchThread = CarServiceUtils.getHandlerThread(
84             getClass().getSimpleName());
85     private final Handler mClientDispatchHandler = new Handler(mClientDispatchThread.getLooper());
86     private final Object mLock = new Object();
87 
88     // For dumpsys logging
89     @GuardedBy("mLock")
90     private final LinkedList<TransitionLog> mTransitionLogs = new LinkedList<>();
91 
92     /**
93      * A set of optional {@link VehicleProperty} that are supported.
94      */
95     @GuardedBy("mLock")
96     private SparseBooleanArray mOptionalPropertiesSupported;
97     @GuardedBy("mLock")
98     private CarPropertyValue mLastParkingBrake;
99     @GuardedBy("mLock")
100     private CarPropertyValue mLastGear;
101     @GuardedBy("mLock")
102     private CarPropertyValue mLastSpeed;
103     @GuardedBy("mLock")
104     private CarPropertyValue mLastIgnitionState;
105 
106     @GuardedBy("mLock")
107     private List<Integer> mSupportedGears;
108 
109     @GuardedBy("mLock")
110     private CarDrivingStateEvent mCurrentDrivingState;
111 
CarDrivingStateService(Context context, CarPropertyService propertyService)112     public CarDrivingStateService(Context context, CarPropertyService propertyService) {
113         mContext = context;
114         mPropertyService = propertyService;
115         mCurrentDrivingState = createDrivingStateEvent(CarDrivingStateEvent.DRIVING_STATE_UNKNOWN);
116     }
117 
118     @Override
init()119     public void init() {
120         if (!checkPropertySupport()) {
121             Slogf.e(TAG, "init failure.  Driving state will always be fully restrictive");
122             return;
123         }
124         // Gets the boot state first, before getting any events from car.
125         synchronized (mLock) {
126             mCurrentDrivingState = createDrivingStateEvent(inferDrivingStateLocked());
127             addTransitionLogLocked(TAG + " Boot", CarDrivingStateEvent.DRIVING_STATE_UNKNOWN,
128                     mCurrentDrivingState.eventValue, mCurrentDrivingState.timeStamp);
129         }
130         subscribeToProperties();
131     }
132 
133     @Override
release()134     public void release() {
135         for (int property : REQUIRED_PROPERTIES) {
136             mPropertyService.unregisterListenerSafe(property, mICarPropertyEventListener);
137         }
138         for (int property : OPTIONAL_PROPERTIES) {
139             synchronized (mLock) {
140                 if (isOptionalPropertySupportedLocked(property)) {
141                     mPropertyService.unregisterListenerSafe(property, mICarPropertyEventListener);
142                 }
143             }
144         }
145         while (mDrivingStateClients.getRegisteredCallbackCount() > 0) {
146             for (int i = mDrivingStateClients.getRegisteredCallbackCount() - 1; i >= 0; i--) {
147                 ICarDrivingStateChangeListener client =
148                         mDrivingStateClients.getRegisteredCallbackItem(i);
149                 if (client == null) {
150                     continue;
151                 }
152                 mDrivingStateClients.unregister(client);
153             }
154         }
155         synchronized (mLock) {
156             mCurrentDrivingState = createDrivingStateEvent(
157                     CarDrivingStateEvent.DRIVING_STATE_UNKNOWN);
158             if (mOptionalPropertiesSupported != null) {
159                 mOptionalPropertiesSupported.clear();
160             }
161         }
162     }
163 
164     /**
165      * Checks if the {@link CarPropertyService} supports the required properties.
166      *
167      * @return {@code true} if supported, {@code false} if not
168      */
checkPropertySupport()169     private boolean checkPropertySupport() {
170         List<CarPropertyConfig> configs = mPropertyService
171                 .getPropertyConfigList(REQUIRED_PROPERTIES).carPropertyConfigList.getConfigs();
172         for (int propertyId : REQUIRED_PROPERTIES) {
173             boolean found = false;
174             for (CarPropertyConfig config : configs) {
175                 if (config.getPropertyId() == propertyId) {
176                     found = true;
177                     break;
178                 }
179             }
180             if (!found) {
181                 Slogf.e(TAG, "Required property not supported: " + propertyId);
182                 return false;
183             }
184         }
185         configs = mPropertyService
186                 .getPropertyConfigList(OPTIONAL_PROPERTIES).carPropertyConfigList.getConfigs();
187         if (configs != null) {
188             SparseBooleanArray optionalPropertiesSupported = new SparseBooleanArray();
189             for (CarPropertyConfig config : configs) {
190                 optionalPropertiesSupported.put(config.getPropertyId(), true);
191             }
192             synchronized (mLock) {
193                 mOptionalPropertiesSupported = optionalPropertiesSupported;
194             }
195         }
196         return true;
197     }
198 
199     /**
200      * Check if the optional property is supported.
201      */
202     @GuardedBy("mLock")
isOptionalPropertySupportedLocked(int propertyId)203     private boolean isOptionalPropertySupportedLocked(int propertyId) {
204         if (mOptionalPropertiesSupported != null) {
205             return mOptionalPropertiesSupported.get(propertyId);
206         }
207         return false;
208     }
209 
210     /**
211      * Subscribe to the {@link CarPropertyService} for required sensors.
212      */
subscribeToProperties()213     private void subscribeToProperties() {
214         for (int propertyId : REQUIRED_PROPERTIES) {
215             mPropertyService.registerListenerSafe(propertyId, PROPERTY_UPDATE_RATE,
216                     mICarPropertyEventListener);
217         }
218         for (int propertyId : OPTIONAL_PROPERTIES) {
219             synchronized (mLock) {
220                 if (isOptionalPropertySupportedLocked(propertyId)) {
221                     mPropertyService.registerListenerSafe(propertyId, PROPERTY_UPDATE_RATE,
222                             mICarPropertyEventListener);
223                 }
224             }
225         }
226     }
227 
228     // Binder methods
229 
230     /**
231      * Register a {@link ICarDrivingStateChangeListener} to be notified for changes to the driving
232      * state.
233      *
234      * @param listener {@link ICarDrivingStateChangeListener}
235      */
236     @Override
registerDrivingStateChangeListener(ICarDrivingStateChangeListener listener)237     public void registerDrivingStateChangeListener(ICarDrivingStateChangeListener listener) {
238         if (listener == null) {
239             if (DBG) {
240                 Slogf.e(TAG, "registerDrivingStateChangeListener(): listener null");
241             }
242             throw new IllegalArgumentException("Listener is null");
243         }
244         mDrivingStateClients.register(listener);
245     }
246 
247     /**
248      * Unregister the given Driving State Change listener
249      *
250      * @param listener client to unregister
251      */
252     @Override
unregisterDrivingStateChangeListener(ICarDrivingStateChangeListener listener)253     public void unregisterDrivingStateChangeListener(ICarDrivingStateChangeListener listener) {
254         if (listener == null) {
255             Slogf.e(TAG, "unregisterDrivingStateChangeListener(): listener null");
256             throw new IllegalArgumentException("Listener is null");
257         }
258 
259         mDrivingStateClients.unregister(listener);
260     }
261 
262     /**
263      * Gets the current driving state
264      *
265      * @return {@link CarDrivingStateEvent} for the given event type
266      */
267     @Override
268     @NonNull
getCurrentDrivingState()269     public CarDrivingStateEvent getCurrentDrivingState() {
270         synchronized (mLock) {
271             return mCurrentDrivingState;
272         }
273     }
274 
275     @Override
injectDrivingState(CarDrivingStateEvent event)276     public void injectDrivingState(CarDrivingStateEvent event) {
277         CarServiceUtils.assertPermission(mContext, Car.PERMISSION_CONTROL_APP_BLOCKING);
278 
279         dispatchEventToClients(event);
280     }
281 
dispatchEventToClients(CarDrivingStateEvent event)282     private void dispatchEventToClients(CarDrivingStateEvent event) {
283         boolean success = mClientDispatchHandler.post(() -> {
284             int numClients = mDrivingStateClients.beginBroadcast();
285             for (int i = 0; i < numClients; i++) {
286                 ICarDrivingStateChangeListener callback = mDrivingStateClients.getBroadcastItem(i);
287                 try {
288                     callback.onDrivingStateChanged(event);
289                 } catch (RemoteException e) {
290                     Slogf.e(TAG, "Dispatch to listener %s failed for event (%s)", callback, event);
291                 }
292             }
293             mDrivingStateClients.finishBroadcast();
294         });
295 
296         if (!success) {
297             Slogf.e(TAG, "Unable to post (" + event + ") event to dispatch handler");
298         }
299     }
300 
301     @Override
302     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dump(IndentingPrintWriter writer)303     public void dump(IndentingPrintWriter writer) {
304         writer.println("*CarDrivingStateService*");
305 
306         writer.println("Driving State Clients:");
307         writer.increaseIndent();
308         BinderHelper.dumpRemoteCallbackList(mDrivingStateClients, writer);
309         writer.decreaseIndent();
310 
311         writer.println("Driving state change log:");
312         synchronized (mLock) {
313             for (TransitionLog tLog : mTransitionLogs) {
314                 writer.println(tLog);
315             }
316             writer.println("Current Driving State: " + mCurrentDrivingState.eventValue);
317             if (mSupportedGears != null) {
318                 writer.print("Supported gears:");
319                 for (Integer gear : mSupportedGears) {
320                     writer.print(' ');
321                     writer.print(gear);
322                 }
323                 writer.println();
324             }
325         }
326     }
327 
328     @Override
329     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dumpProto(ProtoOutputStream proto)330     public void dumpProto(ProtoOutputStream proto) {}
331 
332     /**
333      * {@link CarPropertyEvent} listener registered with the {@link CarPropertyService} for getting
334      * property change notifications.
335      */
336     private final ICarPropertyEventListener mICarPropertyEventListener =
337             new ICarPropertyEventListener.Stub() {
338                 @Override
339                 public void onEvent(List<CarPropertyEvent> events) throws RemoteException {
340                     synchronized (mLock) {
341                         for (CarPropertyEvent event : events) {
342                             handlePropertyEventLocked(event);
343                         }
344                     }
345                 }
346             };
347 
348     /**
349      * Handle events coming from {@link CarPropertyService}.  Compute the driving state, map it to
350      * the corresponding UX Restrictions and dispatch the events to the registered clients.
351      */
352     @GuardedBy("mLock")
353     @VisibleForTesting
handlePropertyEventLocked(CarPropertyEvent event)354     void handlePropertyEventLocked(CarPropertyEvent event) {
355         if (event.getEventType() != CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE) {
356             return;
357         }
358         CarPropertyValue value = event.getCarPropertyValue();
359         if (DBG) {
360             Slogf.d(TAG, "Property Changed: " + value);
361         }
362         switch (value.getPropertyId()) {
363             case VehicleProperty.PERF_VEHICLE_SPEED:
364                 mLastSpeed = value;
365                 if (DBG) {
366                     Slogf.d(TAG, "mLastSpeed: " + mLastSpeed);
367                 }
368                 break;
369             case VehicleProperty.GEAR_SELECTION:
370                 if (mSupportedGears == null) {
371                     mSupportedGears = getSupportedGears();
372                 }
373                 mLastGear = value;
374                 if (DBG) {
375                     Slogf.d(TAG, "mLastGear: " + mLastGear);
376                 }
377                 break;
378             case VehicleProperty.PARKING_BRAKE_ON:
379                 mLastParkingBrake = value;
380                 if (DBG) {
381                     Slogf.d(TAG, "mLastParkingBrake: " + mLastParkingBrake);
382                 }
383                 break;
384             case VehicleProperty.IGNITION_STATE:
385                 mLastIgnitionState = value;
386                 if (DBG) {
387                     Slogf.d(TAG, "mLastIgnitionState: " + mLastIgnitionState);
388                 }
389                 break;
390             default:
391                 Slogf.e(TAG,
392                         "Received property event for unhandled propId=" + value.getPropertyId());
393                 break;
394         }
395 
396         int drivingState = inferDrivingStateLocked();
397         // Check if the driving state has changed.  If it has, update our records and
398         // dispatch the new events to the listeners.
399         if (DBG) {
400             Slogf.d(TAG, "Driving state new->old " + drivingState + "->"
401                     + mCurrentDrivingState.eventValue);
402         }
403         if (drivingState != mCurrentDrivingState.eventValue) {
404             addTransitionLogLocked(TAG, mCurrentDrivingState.eventValue, drivingState,
405                     System.currentTimeMillis());
406             // Update if there is a change in state.
407             mCurrentDrivingState = createDrivingStateEvent(drivingState);
408             if (DBG) {
409                 Slogf.d(TAG, "dispatching to " + mDrivingStateClients.getRegisteredCallbackCount()
410                         + " clients");
411             }
412             // Dispatch to clients on a separate thread to prevent a deadlock
413             final CarDrivingStateEvent currentDrivingStateEvent = mCurrentDrivingState;
414             dispatchEventToClients(currentDrivingStateEvent);
415         }
416     }
417 
getSupportedGears()418     private List<Integer> getSupportedGears() {
419         List<CarPropertyConfig> propertyList = mPropertyService
420                 .getPropertyConfigList(REQUIRED_PROPERTIES).carPropertyConfigList.getConfigs();
421         for (CarPropertyConfig p : propertyList) {
422             if (p.getPropertyId() == VehicleProperty.GEAR_SELECTION) {
423                 return p.getConfigArray();
424             }
425         }
426         return Collections.emptyList();
427     }
428 
429     @GuardedBy("mLock")
addTransitionLogLocked(String name, int from, int to, long timestamp)430     private void addTransitionLogLocked(String name, int from, int to, long timestamp) {
431         if (mTransitionLogs.size() >= MAX_TRANSITION_LOG_SIZE) {
432             mTransitionLogs.remove();
433         }
434 
435         TransitionLog tLog = new TransitionLog(name, from, to, timestamp);
436         mTransitionLogs.add(tLog);
437     }
438 
isPropertyStatusAvailable(CarPropertyValue<?> carPropertyValue)439     private static boolean isPropertyStatusAvailable(CarPropertyValue<?> carPropertyValue) {
440         return carPropertyValue != null
441                 && carPropertyValue.getStatus() == CarPropertyValue.STATUS_AVAILABLE;
442     }
443 
444     /**
445      * Infers the current driving state of the car from the other Car Sensor properties like
446      * Current Gear, Speed etc.
447      *
448      * @return Current driving state
449      */
450     @GuardedBy("mLock")
451     @CarDrivingState
inferDrivingStateLocked()452     private int inferDrivingStateLocked() {
453         updateVehiclePropertiesIfNeededLocked();
454         if (DBG) {
455             Slogf.d(TAG,
456                     "inferDrivingStateLocked mLastGear: " + mLastGear + "mLastSpeed: " + mLastSpeed
457                             + "mLastIgnitionState: " + mLastIgnitionState);
458         }
459 
460         /*
461             Logic to start off deriving driving state:
462             1. If gear == parked, then Driving State is parked.
463             2. If gear != parked,
464                     2a. if parking brake is applied, then Driving state is parked.
465                     2b. if parking brake is not applied or unknown/unavailable, then driving state
466                     is still unknown.
467             3. If driving state is unknown at the end of step 2,
468                 3a. if speed == 0, then driving state is idling
469                 3b. if speed != 0, then driving state is moving
470                 3c. if speed is unavailable,
471                     3ca. If ignition state is supported and ignition state == lock or acc or
472                     off, then driving state is parked
473                     3cb. else driving state is unknown
474          */
475 
476         if (isVehicleKnownToBeParkedLocked()) {
477             return CarDrivingStateEvent.DRIVING_STATE_PARKED;
478         }
479 
480         // We don't know if the vehicle is parked, let's look at the speed.
481         if (!isPropertyStatusAvailable(mLastSpeed)) {
482             if (isOptionalPropertySupportedLocked(VehicleProperty.IGNITION_STATE)
483                     && isVehicleKnownToBeInLockOrAccOrOffIgnitionStateLocked()) {
484                 return CarDrivingStateEvent.DRIVING_STATE_PARKED;
485             }
486             return CarDrivingStateEvent.DRIVING_STATE_UNKNOWN;
487         } else if (mLastSpeed.getValue().equals(0f)) {
488             return CarDrivingStateEvent.DRIVING_STATE_IDLING;
489         } else {
490             return CarDrivingStateEvent.DRIVING_STATE_MOVING;
491         }
492     }
493 
494     /**
495      * Find if we have signals to know if the vehicle is in ignition state lock or acc or off.
496      *
497      * @return true if we have enough information to say the vehicle is in ignition state acc or
498      * off, false if the vehicle is either not in ignition state acc or off or if we don't have any
499      * information.
500      */
501     @GuardedBy("mLock")
isVehicleKnownToBeInLockOrAccOrOffIgnitionStateLocked()502     private boolean isVehicleKnownToBeInLockOrAccOrOffIgnitionStateLocked() {
503         return isPropertyStatusAvailable(mLastIgnitionState)
504                 && (mLastIgnitionState.getValue().equals(VehicleIgnitionState.ACC)
505                 || mLastIgnitionState.getValue().equals(VehicleIgnitionState.OFF)
506                 || mLastIgnitionState.getValue().equals(VehicleIgnitionState.LOCK));
507     }
508 
509     /**
510      * Find if we have signals to know if the vehicle is parked
511      *
512      * @return true if we have enough information to say the vehicle is parked.
513      * false, if the vehicle is either not parked or if we don't have any information.
514      */
515     @GuardedBy("mLock")
isVehicleKnownToBeParkedLocked()516     private boolean isVehicleKnownToBeParkedLocked() {
517         // If we know the gear is in park, return true
518         if (isPropertyStatusAvailable(mLastGear) && mLastGear.getValue()
519                 .equals(VehicleGear.GEAR_PARK)) {
520             return true;
521         } else if (isPropertyStatusAvailable(mLastParkingBrake)) {
522             // if gear is not in park or unknown, look for status of parking brake if transmission
523             // type is manual.
524             if (isCarManualTransmissionTypeLocked()) {
525                 return (boolean) mLastParkingBrake.getValue();
526             }
527         }
528         // if neither information is available, return false to indicate we can't determine
529         // if the vehicle is parked.
530         return false;
531     }
532 
533     /**
534      * If Supported gears information is available and GEAR_PARK is not one of the supported gears,
535      * transmission type is considered to be Manual.  Automatic transmission is assumed otherwise.
536      */
537     @GuardedBy("mLock")
isCarManualTransmissionTypeLocked()538     private boolean isCarManualTransmissionTypeLocked() {
539         return mSupportedGears != null
540                 && !mSupportedGears.isEmpty()
541                 && !mSupportedGears.contains(VehicleGear.GEAR_PARK);
542     }
543 
544     /**
545      * Try querying the gear selection and parking brake if we haven't received the event yet.
546      * This could happen if the gear change occurred before car service booted up like in the
547      * case of a HU restart in the middle of a drive.  Since gear and parking brake are
548      * on-change only properties, we could be in this situation where we will have to query
549      * VHAL.
550      */
551     @GuardedBy("mLock")
updateVehiclePropertiesIfNeededLocked()552     private void updateVehiclePropertiesIfNeededLocked() {
553         if (mLastGear == null) {
554             CarPropertyValue propertyValue = mPropertyService.getPropertySafe(
555                     VehicleProperty.GEAR_SELECTION,
556                     VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL);
557             if (propertyValue != null) {
558                 mLastGear = propertyValue;
559                 if (DBG) {
560                     Slogf.d(TAG, "updateVehiclePropertiesIfNeeded: gear:" + mLastGear);
561                 }
562             }
563         }
564 
565         if (mLastParkingBrake == null) {
566             CarPropertyValue propertyValue = mPropertyService.getPropertySafe(
567                     VehicleProperty.PARKING_BRAKE_ON,
568                     VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL);
569             if (propertyValue != null) {
570                 mLastParkingBrake = propertyValue;
571                 if (DBG) {
572                     Slogf.d(TAG, "updateVehiclePropertiesIfNeeded: brake:" + mLastParkingBrake);
573                 }
574             }
575         }
576 
577         if (mLastSpeed == null) {
578             CarPropertyValue propertyValue = mPropertyService.getPropertySafe(
579                     VehicleProperty.PERF_VEHICLE_SPEED,
580                     VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL);
581             if (propertyValue != null) {
582                 mLastSpeed = propertyValue;
583                 if (DBG) {
584                     Slogf.d(TAG, "updateVehiclePropertiesIfNeeded: speed:" + mLastSpeed);
585                 }
586             }
587         }
588 
589         if (mLastIgnitionState == null) {
590             CarPropertyValue propertyValue = mPropertyService.getPropertySafe(
591                     VehicleProperty.IGNITION_STATE,
592                     VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL);
593             if (propertyValue != null) {
594                 mLastIgnitionState = propertyValue;
595                 if (DBG) {
596                     Slogf.d(TAG, "updateVehiclePropertiesIfNeeded: ignition state:"
597                             + mLastIgnitionState);
598                 }
599             }
600         }
601     }
602 
createDrivingStateEvent(int eventValue)603     private static CarDrivingStateEvent createDrivingStateEvent(int eventValue) {
604         return new CarDrivingStateEvent(eventValue, SystemClock.elapsedRealtimeNanos());
605     }
606 }
607