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