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