1 /*
2  * Copyright (C) 2016 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 #define LOG_TAG "DefaultVehicleHal_v2_0"
17 
18 #include <android-base/macros.h>
19 #include <android-base/properties.h>
20 #include <android/log.h>
21 #include <dirent.h>
22 #include <sys/system_properties.h>
23 #include <fstream>
24 #include <regex>
25 
26 #include "EmulatedVehicleHal.h"
27 #include "JsonFakeValueGenerator.h"
28 #include "LinearFakeValueGenerator.h"
29 #include "Obd2SensorStore.h"
30 
31 namespace android {
32 namespace hardware {
33 namespace automotive {
34 namespace vehicle {
35 namespace V2_0 {
36 
37 namespace impl {
38 
fillDefaultObd2Frame(size_t numVendorIntegerSensors,size_t numVendorFloatSensors)39 static std::unique_ptr<Obd2SensorStore> fillDefaultObd2Frame(size_t numVendorIntegerSensors,
40                                                              size_t numVendorFloatSensors) {
41     std::unique_ptr<Obd2SensorStore> sensorStore(
42         new Obd2SensorStore(numVendorIntegerSensors, numVendorFloatSensors));
43 
44     sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::FUEL_SYSTEM_STATUS,
45                                   toInt(Obd2FuelSystemStatus::CLOSED_LOOP));
46     sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::MALFUNCTION_INDICATOR_LIGHT_ON, 0);
47     sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::IGNITION_MONITORS_SUPPORTED,
48                                   toInt(Obd2IgnitionMonitorKind::SPARK));
49     sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::IGNITION_SPECIFIC_MONITORS,
50                                   Obd2CommonIgnitionMonitors::COMPONENTS_AVAILABLE |
51                                       Obd2CommonIgnitionMonitors::MISFIRE_AVAILABLE |
52                                       Obd2SparkIgnitionMonitors::AC_REFRIGERANT_AVAILABLE |
53                                       Obd2SparkIgnitionMonitors::EVAPORATIVE_SYSTEM_AVAILABLE);
54     sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::INTAKE_AIR_TEMPERATURE, 35);
55     sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::COMMANDED_SECONDARY_AIR_STATUS,
56                                   toInt(Obd2SecondaryAirStatus::FROM_OUTSIDE_OR_OFF));
57     sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::NUM_OXYGEN_SENSORS_PRESENT, 1);
58     sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::RUNTIME_SINCE_ENGINE_START, 500);
59     sensorStore->setIntegerSensor(
60         DiagnosticIntegerSensorIndex::DISTANCE_TRAVELED_WITH_MALFUNCTION_INDICATOR_LIGHT_ON, 0);
61     sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::WARMUPS_SINCE_CODES_CLEARED, 51);
62     sensorStore->setIntegerSensor(
63         DiagnosticIntegerSensorIndex::DISTANCE_TRAVELED_SINCE_CODES_CLEARED, 365);
64     sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::ABSOLUTE_BAROMETRIC_PRESSURE, 30);
65     sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::CONTROL_MODULE_VOLTAGE, 12);
66     sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::AMBIENT_AIR_TEMPERATURE, 18);
67     sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::MAX_FUEL_AIR_EQUIVALENCE_RATIO, 1);
68     sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::FUEL_TYPE,
69                                   toInt(Obd2FuelType::GASOLINE));
70     sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::CALCULATED_ENGINE_LOAD, 0.153);
71     sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::SHORT_TERM_FUEL_TRIM_BANK1, -0.16);
72     sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::LONG_TERM_FUEL_TRIM_BANK1, -0.16);
73     sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::SHORT_TERM_FUEL_TRIM_BANK2, -0.16);
74     sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::LONG_TERM_FUEL_TRIM_BANK2, -0.16);
75     sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::INTAKE_MANIFOLD_ABSOLUTE_PRESSURE, 7.5);
76     sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::ENGINE_RPM, 1250.);
77     sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::VEHICLE_SPEED, 40.);
78     sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::TIMING_ADVANCE, 2.5);
79     sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::THROTTLE_POSITION, 19.75);
80     sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::OXYGEN_SENSOR1_VOLTAGE, 0.265);
81     sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::FUEL_TANK_LEVEL_INPUT, 0.824);
82     sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::EVAPORATION_SYSTEM_VAPOR_PRESSURE,
83                                 -0.373);
84     sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::CATALYST_TEMPERATURE_BANK1_SENSOR1,
85                                 190.);
86     sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::RELATIVE_THROTTLE_POSITION, 3.);
87     sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::ABSOLUTE_THROTTLE_POSITION_B, 0.306);
88     sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::ACCELERATOR_PEDAL_POSITION_D, 0.188);
89     sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::ACCELERATOR_PEDAL_POSITION_E, 0.094);
90     sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::COMMANDED_THROTTLE_ACTUATOR, 0.024);
91 
92     return sensorStore;
93 }
94 
EmulatedVehicleHal(VehiclePropertyStore * propStore,VehicleHalClient * client,EmulatedUserHal * emulatedUserHal)95 EmulatedVehicleHal::EmulatedVehicleHal(VehiclePropertyStore* propStore, VehicleHalClient* client,
96                                        EmulatedUserHal* emulatedUserHal)
97     : mPropStore(propStore),
98       mHvacPowerProps(std::begin(kHvacPowerProperties), std::end(kHvacPowerProperties)),
99       mRecurrentTimer(std::bind(&EmulatedVehicleHal::onContinuousPropertyTimer, this,
100                                 std::placeholders::_1)),
101       mVehicleClient(client),
102       mEmulatedUserHal(emulatedUserHal) {
103     initStaticConfig();
104     for (size_t i = 0; i < arraysize(kVehicleProperties); i++) {
105         mPropStore->registerProperty(kVehicleProperties[i].config);
106     }
107     mVehicleClient->registerPropertyValueCallback(std::bind(&EmulatedVehicleHal::onPropertyValue,
108                                                             this, std::placeholders::_1,
109                                                             std::placeholders::_2));
110 
111     mInitVhalValueOverride =
112             android::base::GetBoolProperty("persist.vendor.vhal_init_value_override", false);
113     if (mInitVhalValueOverride) {
114         getAllPropertiesOverride();
115     }
116 }
117 
getAllPropertiesOverride()118 void EmulatedVehicleHal::getAllPropertiesOverride() {
119     if (auto dir = opendir("/vendor/etc/vhaloverride/")) {
120         std::regex reg_json(".*[.]json", std::regex::icase);
121         while (auto f = readdir(dir)) {
122             if (!regex_match(f->d_name, reg_json)) {
123                 continue;
124             }
125             std::string file = "/vendor/etc/vhaloverride/" + std::string(f->d_name);
126             JsonFakeValueGenerator tmpGenerator(file);
127 
128             std::vector<VehiclePropValue> propvalues = tmpGenerator.getAllEvents();
129             mVehiclePropertiesOverride.insert(std::end(mVehiclePropertiesOverride),
130                                               std::begin(propvalues), std::end(propvalues));
131         }
132         closedir(dir);
133     }
134 }
135 
get(const VehiclePropValue & requestedPropValue,StatusCode * outStatus)136 VehicleHal::VehiclePropValuePtr EmulatedVehicleHal::get(
137         const VehiclePropValue& requestedPropValue, StatusCode* outStatus) {
138     auto propId = requestedPropValue.prop;
139     ALOGV("get(%d)", propId);
140 
141     auto& pool = *getValuePool();
142     VehiclePropValuePtr v = nullptr;
143 
144     switch (propId) {
145         case OBD2_FREEZE_FRAME:
146             v = pool.obtainComplex();
147             *outStatus = fillObd2FreezeFrame(requestedPropValue, v.get());
148             break;
149         case OBD2_FREEZE_FRAME_INFO:
150             v = pool.obtainComplex();
151             *outStatus = fillObd2DtcInfo(v.get());
152             break;
153         default:
154             if (mEmulatedUserHal != nullptr && mEmulatedUserHal->isSupported(propId)) {
155                 ALOGI("get(): getting value for prop %d from User HAL", propId);
156                 const auto& ret = mEmulatedUserHal->onGetProperty(requestedPropValue);
157                 if (!ret.ok()) {
158                     ALOGE("get(): User HAL returned error: %s", ret.error().message().c_str());
159                     *outStatus = StatusCode(ret.error().code());
160                 } else {
161                     auto value = ret.value().get();
162                     if (value != nullptr) {
163                         ALOGI("get(): User HAL returned value: %s", toString(*value).c_str());
164                         v = getValuePool()->obtain(*value);
165                         *outStatus = StatusCode::OK;
166                     } else {
167                         ALOGE("get(): User HAL returned null value");
168                         *outStatus = StatusCode::INTERNAL_ERROR;
169                     }
170                 }
171                 break;
172             }
173 
174             auto internalPropValue = mPropStore->readValueOrNull(requestedPropValue);
175             if (internalPropValue != nullptr) {
176                 v = getValuePool()->obtain(*internalPropValue);
177             }
178 
179             *outStatus = v != nullptr ? StatusCode::OK : StatusCode::INVALID_ARG;
180             break;
181     }
182     if (v.get()) {
183         v->timestamp = elapsedRealtimeNano();
184     }
185     return v;
186 }
187 
dump(const hidl_handle & fd,const hidl_vec<hidl_string> & options)188 bool EmulatedVehicleHal::dump(const hidl_handle& fd, const hidl_vec<hidl_string>& options) {
189     return mVehicleClient->dump(fd, options);
190 }
191 
set(const VehiclePropValue & propValue)192 StatusCode EmulatedVehicleHal::set(const VehiclePropValue& propValue) {
193     constexpr bool updateStatus = false;
194 
195     if (propValue.prop == kGenerateFakeDataControllingProperty) {
196         // Send the generator controlling request to the server.
197         // 'updateStatus' flag is only for the value sent by setProperty (propValue in this case)
198         // instead of the generated values triggered by it. 'propValue' works as a control signal
199         // here, since we never send the control signal back, the value of 'updateStatus' flag
200         // does not matter here.
201         auto status = mVehicleClient->setProperty(propValue, updateStatus);
202         return status;
203     } else if (mHvacPowerProps.count(propValue.prop)) {
204         auto hvacPowerOn = mPropStore->readValueOrNull(
205             toInt(VehicleProperty::HVAC_POWER_ON),
206             (VehicleAreaSeat::ROW_1_LEFT | VehicleAreaSeat::ROW_1_RIGHT |
207              VehicleAreaSeat::ROW_2_LEFT | VehicleAreaSeat::ROW_2_CENTER |
208              VehicleAreaSeat::ROW_2_RIGHT));
209 
210         if (hvacPowerOn && hvacPowerOn->value.int32Values.size() == 1
211                 && hvacPowerOn->value.int32Values[0] == 0) {
212             return StatusCode::NOT_AVAILABLE;
213         }
214     } else {
215         // Handle property specific code
216         switch (propValue.prop) {
217             case OBD2_FREEZE_FRAME_CLEAR:
218                 return clearObd2FreezeFrames(propValue);
219             case VEHICLE_MAP_SERVICE:
220                 // Placeholder for future implementation of VMS property in the default hal. For
221                 // now, just returns OK; otherwise, hal clients crash with property not supported.
222                 return StatusCode::OK;
223         }
224     }
225 
226     if (propValue.status != VehiclePropertyStatus::AVAILABLE) {
227         // Android side cannot set property status - this value is the
228         // purview of the HAL implementation to reflect the state of
229         // its underlying hardware
230         return StatusCode::INVALID_ARG;
231     }
232     auto currentPropValue = mPropStore->readValueOrNull(propValue);
233 
234     if (currentPropValue == nullptr) {
235         return StatusCode::INVALID_ARG;
236     }
237     if (currentPropValue->status != VehiclePropertyStatus::AVAILABLE) {
238         // do not allow Android side to set() a disabled/error property
239         return StatusCode::NOT_AVAILABLE;
240     }
241 
242     if (mInEmulator && propValue.prop == toInt(VehicleProperty::DISPLAY_BRIGHTNESS)) {
243         // Emulator does not support remote brightness control, b/139959479
244         // do not send it down so that it does not bring unnecessary property change event
245         // return other error code, such NOT_AVAILABLE, causes Emulator to be freezing
246         // TODO: return StatusCode::NOT_AVAILABLE once the above issue is fixed
247         return StatusCode::OK;
248     }
249 
250     /**
251      * After checking all conditions, such as the property is available, a real vhal will
252      * sent the events to Car ECU to take actions.
253      */
254 
255     // Send the value to the vehicle server, the server will talk to the (real or emulated) car
256     auto setValueStatus = mVehicleClient->setProperty(propValue, updateStatus);
257     if (setValueStatus != StatusCode::OK) {
258         return setValueStatus;
259     }
260 
261     return StatusCode::OK;
262 }
263 
isDiagnosticProperty(VehiclePropConfig propConfig)264 static bool isDiagnosticProperty(VehiclePropConfig propConfig) {
265     switch (propConfig.prop) {
266         case OBD2_LIVE_FRAME:
267         case OBD2_FREEZE_FRAME:
268         case OBD2_FREEZE_FRAME_CLEAR:
269         case OBD2_FREEZE_FRAME_INFO:
270             return true;
271     }
272     return false;
273 }
274 
275 // determine if it's running inside Android Emulator
isInEmulator()276 static bool isInEmulator() {
277     char propValue[PROP_VALUE_MAX];
278     bool isEmulator = (__system_property_get("ro.kernel.qemu", propValue) != 0);
279     if (!isEmulator) {
280         isEmulator = (__system_property_get("ro.hardware", propValue) != 0) &&
281                      (!strcmp(propValue, "ranchu") || !strcmp(propValue, "goldfish"));
282     }
283     return isEmulator;
284 }
285 
286 // Parse supported properties list and generate vector of property values to hold current values.
onCreate()287 void EmulatedVehicleHal::onCreate() {
288     static constexpr bool shouldUpdateStatus = true;
289 
290     for (auto& it : kVehicleProperties) {
291         VehiclePropConfig cfg = it.config;
292         int32_t numAreas = cfg.areaConfigs.size();
293 
294         if (isDiagnosticProperty(cfg)) {
295             // do not write an initial empty value for the diagnostic properties
296             // as we will initialize those separately.
297             continue;
298         }
299 
300         // A global property will have only a single area
301         if (isGlobalProp(cfg.prop)) {
302             numAreas = 1;
303         }
304 
305         for (int i = 0; i < numAreas; i++) {
306             int32_t curArea;
307 
308             if (isGlobalProp(cfg.prop)) {
309                 curArea = 0;
310             } else {
311                 curArea = cfg.areaConfigs[i].areaId;
312             }
313 
314             // Create a separate instance for each individual zone
315             VehiclePropValue prop = {
316                     .areaId = curArea,
317                     .prop = cfg.prop,
318             };
319 
320             if (it.initialAreaValues.size() > 0) {
321                 auto valueForAreaIt = it.initialAreaValues.find(curArea);
322                 if (valueForAreaIt != it.initialAreaValues.end()) {
323                     prop.value = valueForAreaIt->second;
324                 } else {
325                     ALOGW("%s failed to get default value for prop 0x%x area 0x%x",
326                             __func__, cfg.prop, curArea);
327                 }
328             } else {
329                 prop.value = it.initialValue;
330                 if (mInitVhalValueOverride) {
331                     for (auto& itOverride : mVehiclePropertiesOverride) {
332                         if (itOverride.prop == cfg.prop) {
333                             prop.value = itOverride.value;
334                         }
335                     }
336                 }
337             }
338             mPropStore->writeValue(prop, shouldUpdateStatus);
339         }
340     }
341     initObd2LiveFrame(*mPropStore->getConfigOrDie(OBD2_LIVE_FRAME));
342     initObd2FreezeFrame(*mPropStore->getConfigOrDie(OBD2_FREEZE_FRAME));
343     mInEmulator = isInEmulator();
344     ALOGD("mInEmulator=%s", mInEmulator ? "true" : "false");
345 }
346 
listProperties()347 std::vector<VehiclePropConfig> EmulatedVehicleHal::listProperties()  {
348     return mPropStore->getAllConfigs();
349 }
350 
onContinuousPropertyTimer(const std::vector<int32_t> & properties)351 void EmulatedVehicleHal::onContinuousPropertyTimer(const std::vector<int32_t>& properties) {
352     VehiclePropValuePtr v;
353 
354     auto& pool = *getValuePool();
355 
356     for (int32_t property : properties) {
357         if (isContinuousProperty(property)) {
358             auto internalPropValue = mPropStore->readValueOrNull(property);
359             if (internalPropValue != nullptr) {
360                 v = pool.obtain(*internalPropValue);
361             }
362         } else {
363             ALOGE("Unexpected onContinuousPropertyTimer for property: 0x%x", property);
364         }
365 
366         if (v.get()) {
367             v->timestamp = elapsedRealtimeNano();
368             doHalEvent(std::move(v));
369         }
370     }
371 }
372 
subscribe(int32_t property,float sampleRate)373 StatusCode EmulatedVehicleHal::subscribe(int32_t property, float sampleRate) {
374     ALOGI("%s propId: 0x%x, sampleRate: %f", __func__, property, sampleRate);
375 
376     if (isContinuousProperty(property)) {
377         mRecurrentTimer.registerRecurrentEvent(hertzToNanoseconds(sampleRate), property);
378     }
379     return StatusCode::OK;
380 }
381 
unsubscribe(int32_t property)382 StatusCode EmulatedVehicleHal::unsubscribe(int32_t property) {
383     ALOGI("%s propId: 0x%x", __func__, property);
384     if (isContinuousProperty(property)) {
385         mRecurrentTimer.unregisterRecurrentEvent(property);
386     }
387     return StatusCode::OK;
388 }
389 
isContinuousProperty(int32_t propId) const390 bool EmulatedVehicleHal::isContinuousProperty(int32_t propId) const {
391     const VehiclePropConfig* config = mPropStore->getConfigOrNull(propId);
392     if (config == nullptr) {
393         ALOGW("Config not found for property: 0x%x", propId);
394         return false;
395     }
396     return config->changeMode == VehiclePropertyChangeMode::CONTINUOUS;
397 }
398 
setPropertyFromVehicle(const VehiclePropValue & propValue)399 bool EmulatedVehicleHal::setPropertyFromVehicle(const VehiclePropValue& propValue) {
400     constexpr bool updateStatus = true;
401     return mVehicleClient->setProperty(propValue, updateStatus) == StatusCode::OK;
402 }
403 
getAllProperties() const404 std::vector<VehiclePropValue> EmulatedVehicleHal::getAllProperties() const  {
405     return mPropStore->readAllValues();
406 }
407 
onPropertyValue(const VehiclePropValue & value,bool updateStatus)408 void EmulatedVehicleHal::onPropertyValue(const VehiclePropValue& value, bool updateStatus) {
409     VehiclePropValuePtr updatedPropValue = getValuePool()->obtain(value);
410 
411     if (mPropStore->writeValue(*updatedPropValue, updateStatus)) {
412         getEmulatorOrDie()->doSetValueFromClient(*updatedPropValue);
413         doHalEvent(std::move(updatedPropValue));
414     }
415 }
416 
initStaticConfig()417 void EmulatedVehicleHal::initStaticConfig() {
418     for (auto&& it = std::begin(kVehicleProperties); it != std::end(kVehicleProperties); ++it) {
419         const auto& cfg = it->config;
420         VehiclePropertyStore::TokenFunction tokenFunction = nullptr;
421 
422         switch (cfg.prop) {
423             case OBD2_FREEZE_FRAME: {
424                 tokenFunction = [](const VehiclePropValue& propValue) {
425                     return propValue.timestamp;
426                 };
427                 break;
428             }
429             default:
430                 break;
431         }
432 
433         mPropStore->registerProperty(cfg, tokenFunction);
434     }
435 }
436 
initObd2LiveFrame(const VehiclePropConfig & propConfig)437 void EmulatedVehicleHal::initObd2LiveFrame(const VehiclePropConfig& propConfig) {
438     static constexpr bool shouldUpdateStatus = true;
439 
440     auto liveObd2Frame = createVehiclePropValue(VehiclePropertyType::MIXED, 0);
441     auto sensorStore = fillDefaultObd2Frame(static_cast<size_t>(propConfig.configArray[0]),
442                                             static_cast<size_t>(propConfig.configArray[1]));
443     sensorStore->fillPropValue("", liveObd2Frame.get());
444     liveObd2Frame->prop = OBD2_LIVE_FRAME;
445 
446     mPropStore->writeValue(*liveObd2Frame, shouldUpdateStatus);
447 }
448 
initObd2FreezeFrame(const VehiclePropConfig & propConfig)449 void EmulatedVehicleHal::initObd2FreezeFrame(const VehiclePropConfig& propConfig) {
450     static constexpr bool shouldUpdateStatus = true;
451 
452     auto sensorStore = fillDefaultObd2Frame(static_cast<size_t>(propConfig.configArray[0]),
453                                             static_cast<size_t>(propConfig.configArray[1]));
454 
455     static std::vector<std::string> sampleDtcs = {"P0070",
456                                                   "P0102"
457                                                   "P0123"};
458     for (auto&& dtc : sampleDtcs) {
459         auto freezeFrame = createVehiclePropValue(VehiclePropertyType::MIXED, 0);
460         sensorStore->fillPropValue(dtc, freezeFrame.get());
461         freezeFrame->prop = OBD2_FREEZE_FRAME;
462 
463         mPropStore->writeValue(*freezeFrame, shouldUpdateStatus);
464     }
465 }
466 
fillObd2FreezeFrame(const VehiclePropValue & requestedPropValue,VehiclePropValue * outValue)467 StatusCode EmulatedVehicleHal::fillObd2FreezeFrame(const VehiclePropValue& requestedPropValue,
468                                                    VehiclePropValue* outValue) {
469     if (requestedPropValue.value.int64Values.size() != 1) {
470         ALOGE("asked for OBD2_FREEZE_FRAME without valid timestamp");
471         return StatusCode::INVALID_ARG;
472     }
473     auto timestamp = requestedPropValue.value.int64Values[0];
474     auto freezeFrame = mPropStore->readValueOrNull(OBD2_FREEZE_FRAME, 0, timestamp);
475     if (freezeFrame == nullptr) {
476         ALOGE("asked for OBD2_FREEZE_FRAME at invalid timestamp");
477         return StatusCode::INVALID_ARG;
478     }
479     outValue->prop = OBD2_FREEZE_FRAME;
480     outValue->value.int32Values = freezeFrame->value.int32Values;
481     outValue->value.floatValues = freezeFrame->value.floatValues;
482     outValue->value.bytes = freezeFrame->value.bytes;
483     outValue->value.stringValue = freezeFrame->value.stringValue;
484     outValue->timestamp = freezeFrame->timestamp;
485     return StatusCode::OK;
486 }
487 
clearObd2FreezeFrames(const VehiclePropValue & propValue)488 StatusCode EmulatedVehicleHal::clearObd2FreezeFrames(const VehiclePropValue& propValue) {
489     if (propValue.value.int64Values.size() == 0) {
490         mPropStore->removeValuesForProperty(OBD2_FREEZE_FRAME);
491         return StatusCode::OK;
492     } else {
493         for (int64_t timestamp : propValue.value.int64Values) {
494             auto freezeFrame = mPropStore->readValueOrNull(OBD2_FREEZE_FRAME, 0, timestamp);
495             if (freezeFrame == nullptr) {
496                 ALOGE("asked for OBD2_FREEZE_FRAME at invalid timestamp");
497                 return StatusCode::INVALID_ARG;
498             }
499             mPropStore->removeValue(*freezeFrame);
500         }
501     }
502     return StatusCode::OK;
503 }
504 
fillObd2DtcInfo(VehiclePropValue * outValue)505 StatusCode EmulatedVehicleHal::fillObd2DtcInfo(VehiclePropValue* outValue) {
506     std::vector<int64_t> timestamps;
507     for (const auto& freezeFrame : mPropStore->readValuesForProperty(OBD2_FREEZE_FRAME)) {
508         timestamps.push_back(freezeFrame.timestamp);
509     }
510     outValue->value.int64Values = timestamps;
511     outValue->prop = OBD2_FREEZE_FRAME_INFO;
512     return StatusCode::OK;
513 }
514 
515 }  // impl
516 
517 }  // namespace V2_0
518 }  // namespace vehicle
519 }  // namespace automotive
520 }  // namespace hardware
521 }  // namespace android
522