1 /*
2  * Copyright (C) 2021 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 #define LOG_TAG "DefaultVehicleHalServer"
18 
19 #include <fstream>
20 #include <regex>
21 
22 #include <android-base/format.h>
23 #include <android-base/logging.h>
24 #include <android-base/parsedouble.h>
25 #include <android-base/parseint.h>
26 #include <android-base/properties.h>
27 #include <utils/SystemClock.h>
28 
29 #include "DefaultConfig.h"
30 #include "FakeObd2Frame.h"
31 #include "JsonFakeValueGenerator.h"
32 #include "LinearFakeValueGenerator.h"
33 
34 #include "DefaultVehicleHalServer.h"
35 
36 namespace android {
37 namespace hardware {
38 namespace automotive {
39 namespace vehicle {
40 namespace V2_0 {
41 
42 namespace impl {
43 
44 namespace {
45 const char* VENDOR_OVERRIDE_DIR = "/vendor/etc/vhaloverride/";
46 }  // namespace
47 
storePropInitialValue(const ConfigDeclaration & config)48 void DefaultVehicleHalServer::storePropInitialValue(const ConfigDeclaration& config) {
49     VehiclePropConfig cfg = config.config;
50 
51     // A global property will have only a single area
52     int32_t numAreas = isGlobalProp(cfg.prop) ? 1 : cfg.areaConfigs.size();
53 
54     for (int i = 0; i < numAreas; i++) {
55         int32_t curArea = isGlobalProp(cfg.prop) ? 0 : cfg.areaConfigs[i].areaId;
56 
57         // Create a separate instance for each individual zone
58         VehiclePropValue prop = {
59                 .areaId = curArea,
60                 .prop = cfg.prop,
61         };
62 
63         if (config.initialAreaValues.empty()) {
64             prop.value = config.initialValue;
65         } else if (auto valueForAreaIt = config.initialAreaValues.find(curArea);
66                    valueForAreaIt != config.initialAreaValues.end()) {
67             prop.value = valueForAreaIt->second;
68         } else {
69             LOG(WARNING) << __func__ << " failed to get default value for"
70                          << " prop 0x" << std::hex << cfg.prop << " area 0x" << std::hex << curArea;
71             prop.status = VehiclePropertyStatus::UNAVAILABLE;
72         }
73 
74         mServerSidePropStore.writeValue(prop, true);
75     }
76 }
77 
DefaultVehicleHalServer()78 DefaultVehicleHalServer::DefaultVehicleHalServer() {
79     for (auto& it : kVehicleProperties) {
80         VehiclePropConfig cfg = it.config;
81         mServerSidePropStore.registerProperty(cfg);
82         // Skip diagnostic properties since there is special logic to handle those.
83         if (isDiagnosticProperty(cfg)) {
84             continue;
85         }
86         storePropInitialValue(it);
87     }
88     maybeOverrideProperties(VENDOR_OVERRIDE_DIR);
89 }
90 
sendAllValuesToClient()91 void DefaultVehicleHalServer::sendAllValuesToClient() {
92     constexpr bool update_status = true;
93     auto values = mServerSidePropStore.readAllValues();
94     for (const auto& value : values) {
95         onPropertyValueFromCar(value, update_status);
96     }
97 }
98 
getGeneratorHub()99 GeneratorHub* DefaultVehicleHalServer::getGeneratorHub() {
100     return &mGeneratorHub;
101 }
102 
getValuePool() const103 VehiclePropValuePool* DefaultVehicleHalServer::getValuePool() const {
104     if (!mValuePool) {
105         LOG(WARNING) << __func__ << ": Value pool not set!";
106     }
107     return mValuePool;
108 }
109 
setValuePool(VehiclePropValuePool * valuePool)110 void DefaultVehicleHalServer::setValuePool(VehiclePropValuePool* valuePool) {
111     if (!valuePool) {
112         LOG(WARNING) << __func__ << ": Setting value pool to nullptr!";
113     }
114     mValuePool = valuePool;
115 }
116 
onFakeValueGenerated(const VehiclePropValue & value)117 void DefaultVehicleHalServer::onFakeValueGenerated(const VehiclePropValue& value) {
118     constexpr bool updateStatus = true;
119     LOG(DEBUG) << __func__ << ": " << toString(value);
120     auto updatedPropValue = getValuePool()->obtain(value);
121     if (updatedPropValue) {
122         updatedPropValue->timestamp = value.timestamp;
123         updatedPropValue->status = VehiclePropertyStatus::AVAILABLE;
124         mServerSidePropStore.writeValue(*updatedPropValue, updateStatus);
125         onPropertyValueFromCar(*updatedPropValue, updateStatus);
126     }
127 }
128 
onGetAllPropertyConfig() const129 std::vector<VehiclePropConfig> DefaultVehicleHalServer::onGetAllPropertyConfig() const {
130     return mServerSidePropStore.getAllConfigs();
131 }
132 
createApPowerStateReq(VehicleApPowerStateReq state,int32_t param)133 DefaultVehicleHalServer::VehiclePropValuePtr DefaultVehicleHalServer::createApPowerStateReq(
134         VehicleApPowerStateReq state, int32_t param) {
135     auto req = getValuePool()->obtain(VehiclePropertyType::INT32_VEC, 2);
136     req->prop = toInt(VehicleProperty::AP_POWER_STATE_REQ);
137     req->areaId = 0;
138     req->timestamp = elapsedRealtimeNano();
139     req->status = VehiclePropertyStatus::AVAILABLE;
140     req->value.int32Values[0] = toInt(state);
141     req->value.int32Values[1] = param;
142     return req;
143 }
144 
createHwInputKeyProp(VehicleHwKeyInputAction action,int32_t keyCode,int32_t targetDisplay)145 DefaultVehicleHalServer::VehiclePropValuePtr DefaultVehicleHalServer::createHwInputKeyProp(
146         VehicleHwKeyInputAction action, int32_t keyCode, int32_t targetDisplay) {
147     auto keyEvent = getValuePool()->obtain(VehiclePropertyType::INT32_VEC, 3);
148     keyEvent->prop = toInt(VehicleProperty::HW_KEY_INPUT);
149     keyEvent->areaId = 0;
150     keyEvent->timestamp = elapsedRealtimeNano();
151     keyEvent->status = VehiclePropertyStatus::AVAILABLE;
152     keyEvent->value.int32Values[0] = toInt(action);
153     keyEvent->value.int32Values[1] = keyCode;
154     keyEvent->value.int32Values[2] = targetDisplay;
155     return keyEvent;
156 }
157 
onSetProperty(const VehiclePropValue & value,bool updateStatus)158 StatusCode DefaultVehicleHalServer::onSetProperty(const VehiclePropValue& value,
159                                                   bool updateStatus) {
160     LOG(DEBUG) << "onSetProperty(" << value.prop << ")";
161 
162     // Some properties need to be treated non-trivially
163     switch (value.prop) {
164         case AP_POWER_STATE_REPORT:
165             switch (value.value.int32Values[0]) {
166                 case toInt(VehicleApPowerStateReport::DEEP_SLEEP_EXIT):
167                 case toInt(VehicleApPowerStateReport::SHUTDOWN_CANCELLED):
168                 case toInt(VehicleApPowerStateReport::WAIT_FOR_VHAL):
169                     // CPMS is in WAIT_FOR_VHAL state, simply move to ON
170                     // Send back to HAL
171                     // ALWAYS update status for generated property value
172                     onPropertyValueFromCar(*createApPowerStateReq(VehicleApPowerStateReq::ON, 0),
173                                            true /* updateStatus */);
174                     break;
175                 case toInt(VehicleApPowerStateReport::DEEP_SLEEP_ENTRY):
176                 case toInt(VehicleApPowerStateReport::SHUTDOWN_START):
177                     // CPMS is in WAIT_FOR_FINISH state, send the FINISHED command
178                     // Send back to HAL
179                     // ALWAYS update status for generated property value
180                     onPropertyValueFromCar(
181                             *createApPowerStateReq(VehicleApPowerStateReq::FINISHED, 0),
182                             true /* updateStatus */);
183                     break;
184                 case toInt(VehicleApPowerStateReport::ON):
185                 case toInt(VehicleApPowerStateReport::SHUTDOWN_POSTPONE):
186                 case toInt(VehicleApPowerStateReport::SHUTDOWN_PREPARE):
187                     // Do nothing
188                     break;
189                 default:
190                     // Unknown state
191                     break;
192             }
193             break;
194 
195 #ifdef ENABLE_VENDOR_CLUSTER_PROPERTY_FOR_TESTING
196         case toInt(VehicleProperty::CLUSTER_REPORT_STATE):
197         case toInt(VehicleProperty::CLUSTER_REQUEST_DISPLAY):
198         case toInt(VehicleProperty::CLUSTER_NAVIGATION_STATE):
199         case VENDOR_CLUSTER_SWITCH_UI:
200         case VENDOR_CLUSTER_DISPLAY_STATE: {
201             auto updatedPropValue = createVehiclePropValue(getPropType(value.prop), 0);
202             updatedPropValue->prop = value.prop & ~toInt(VehiclePropertyGroup::MASK);
203             if (isSystemProperty(value.prop)) {
204                 updatedPropValue->prop |= toInt(VehiclePropertyGroup::VENDOR);
205             } else {
206                 updatedPropValue->prop |= toInt(VehiclePropertyGroup::SYSTEM);
207             }
208             updatedPropValue->value = value.value;
209             updatedPropValue->timestamp = elapsedRealtimeNano();
210             updatedPropValue->areaId = value.areaId;
211             onPropertyValueFromCar(*updatedPropValue, updateStatus);
212             return StatusCode::OK;
213         }
214 #endif  // ENABLE_VENDOR_CLUSTER_PROPERTY_FOR_TESTING
215 
216         default:
217             break;
218     }
219 
220     // In the real vhal, the value will be sent to Car ECU.
221     // We just pretend it is done here and send back to HAL
222     auto updatedPropValue = getValuePool()->obtain(value);
223     updatedPropValue->timestamp = elapsedRealtimeNano();
224 
225     mServerSidePropStore.writeValue(*updatedPropValue, updateStatus);
226     onPropertyValueFromCar(*updatedPropValue, updateStatus);
227     return StatusCode::OK;
228 }
229 
onDump(const std::vector<std::string> & options)230 IVehicleServer::DumpResult DefaultVehicleHalServer::onDump(
231         const std::vector<std::string>& options) {
232     DumpResult result;
233     if (options.size() == 0) {
234         // No options, dump all stored properties.
235         result.callerShouldDumpState = true;
236         result.buffer += "Server side properties: \n";
237         auto values = mServerSidePropStore.readAllValues();
238         size_t i = 0;
239         for (const auto& value : values) {
240             result.buffer += fmt::format("[{}]: {}\n", i, toString(value));
241             i++;
242         }
243         return result;
244     }
245     if (options[0] != "--debughal") {
246         // We only expect "debughal" command. This might be some commands that the caller knows
247         // about, so let caller handle it.
248         result.callerShouldDumpState = true;
249         return result;
250     }
251 
252     return debugCommand(options);
253 }
254 
debugCommand(const std::vector<std::string> & options)255 IVehicleServer::DumpResult DefaultVehicleHalServer::debugCommand(
256         const std::vector<std::string>& options) {
257     DumpResult result;
258     // This is a debug command for the HAL, caller should not continue to dump state.
259     result.callerShouldDumpState = false;
260 
261     if (options.size() < 2) {
262         result.buffer += "No command specified\n";
263         result.buffer += getHelpInfo();
264         return result;
265     }
266 
267     std::string command = options[1];
268     if (command == "--help") {
269         result.buffer += getHelpInfo();
270         return result;
271     } else if (command == "--genfakedata") {
272         return genFakeDataCommand(options);
273     } else if (command == "--setint" || command == "--setfloat" || command == "--setbool") {
274         return setValueCommand(options);
275     }
276 
277     result.buffer += "Unknown command: \"" + command + "\"\n";
278     result.buffer += getHelpInfo();
279     return result;
280 }
281 
getHelpInfo()282 std::string DefaultVehicleHalServer::getHelpInfo() {
283     return "Help: \n"
284            "Generate Fake Data: \n"
285            "\tStart a linear generator: \n"
286            "\t--debughal --genfakedata --startlinear [propID(int32)] [middleValue(float)] "
287            "[currentValue(float)] [dispersion(float)] [increment(float)] [interval(int64)]\n"
288            "\tStop a linear generator: \n"
289            "\t--debughal --genfakedata --stoplinear [propID(int32)]\n"
290            "\tStart a json generator: \n"
291            "\t--debughal --genfakedata --startjson [jsonFilePath(string)] "
292            "[repetition(int32)(optional)]\n"
293            "\tStop a json generator: \n"
294            "\t--debughal --genfakedata --stopjson [jsonFilePath(string)]\n"
295            "\tGenerate key press: \n"
296            "\t--debughal --genfakedata --keypress [keyCode(int32)] [display[int32]]\n"
297            "\tSet a int property value: \n"
298            "\t--setint [propID(int32)] [value(int32)] [timestamp(int64)] "
299            "[areaID(int32)(optional)]\n"
300            "\tSet a boolean property value: \n"
301            "\t--setbool [propID(int32)] [value(\"true\"/\"false\")] [timestamp(int64)] "
302            "[areaID(int32)(optional)]\n"
303            "\tSet a float property value: \n"
304            "\t--setfloat [propID(int32)] [value(float)] [timestamp(int64)] "
305            "[areaID(int32)(optional)]\n";
306 }
307 
genFakeDataCommand(const std::vector<std::string> & options)308 IVehicleServer::DumpResult DefaultVehicleHalServer::genFakeDataCommand(
309         const std::vector<std::string>& options) {
310     DumpResult result;
311     // This is a debug command for the HAL, caller should not continue to dump state.
312     result.callerShouldDumpState = false;
313 
314     if (options.size() < 3) {
315         result.buffer += "No subcommand specified for genfakedata\n";
316         result.buffer += getHelpInfo();
317         return result;
318     }
319 
320     std::string command = options[2];
321     if (command == "--startlinear") {
322         LOG(INFO) << __func__ << "FakeDataCommand::StartLinear";
323         // --debughal --genfakedata --startlinear [propID(int32)] [middleValue(float)]
324         // [currentValue(float)] [dispersion(float)] [increment(float)] [interval(int64)]
325         if (options.size() != 9) {
326             result.buffer +=
327                     "incorrect argument count, need 9 arguments for --genfakedata --startlinear\n";
328             result.buffer += getHelpInfo();
329             return result;
330         }
331         int32_t propId;
332         float middleValue;
333         float currentValue;
334         float dispersion;
335         float increment;
336         int64_t interval;
337         if (!android::base::ParseInt(options[3], &propId)) {
338             result.buffer += "failed to parse propdID as int: \"" + options[3] + "\"\n";
339             result.buffer += getHelpInfo();
340             return result;
341         }
342         if (!android::base::ParseFloat(options[4], &middleValue)) {
343             result.buffer += "failed to parse middleValue as float: \"" + options[4] + "\"\n";
344             result.buffer += getHelpInfo();
345             return result;
346         }
347         if (!android::base::ParseFloat(options[5], &currentValue)) {
348             result.buffer += "failed to parse currentValue as float: \"" + options[5] + "\"\n";
349             result.buffer += getHelpInfo();
350             return result;
351         }
352         if (!android::base::ParseFloat(options[6], &dispersion)) {
353             result.buffer += "failed to parse dispersion as float: \"" + options[6] + "\"\n";
354             result.buffer += getHelpInfo();
355             return result;
356         }
357         if (!android::base::ParseFloat(options[7], &increment)) {
358             result.buffer += "failed to parse increment as float: \"" + options[7] + "\"\n";
359             result.buffer += getHelpInfo();
360             return result;
361         }
362         if (!android::base::ParseInt(options[8], &interval)) {
363             result.buffer += "failed to parse interval as int: \"" + options[8] + "\"\n";
364             result.buffer += getHelpInfo();
365             return result;
366         }
367         auto generator = std::make_unique<LinearFakeValueGenerator>(
368                 propId, middleValue, currentValue, dispersion, increment, interval);
369         getGeneratorHub()->registerGenerator(propId, std::move(generator));
370         return result;
371     } else if (command == "--stoplinear") {
372         LOG(INFO) << __func__ << "FakeDataCommand::StopLinear";
373         // --debughal --genfakedata --stoplinear [propID(int32)]
374         if (options.size() != 4) {
375             result.buffer +=
376                     "incorrect argument count, need 4 arguments for --genfakedata --stoplinear\n";
377             result.buffer += getHelpInfo();
378             return result;
379         }
380         int32_t propId;
381         if (!android::base::ParseInt(options[3], &propId)) {
382             result.buffer += "failed to parse propdID as int: \"" + options[3] + "\"\n";
383             result.buffer += getHelpInfo();
384             return result;
385         }
386         getGeneratorHub()->unregisterGenerator(propId);
387         return result;
388     } else if (command == "--startjson") {
389         LOG(INFO) << __func__ << "FakeDataCommand::StartJson";
390         // --debughal --genfakedata --startjson [jsonFilePath(string)] [repetition(int32)(optional)]
391         if (options.size() != 4 && options.size() != 5) {
392             result.buffer +=
393                     "incorrect argument count, need 4 or 5 arguments for --genfakedata "
394                     "--startjson\n";
395             result.buffer += getHelpInfo();
396             return result;
397         }
398         std::string fileName = options[3];
399         int32_t cookie = std::hash<std::string>()(fileName);
400         // Iterate infinitely if repetition number is not provided
401         int32_t repetition = -1;
402         if (options.size() == 5) {
403             if (!android::base::ParseInt(options[4], &repetition)) {
404                 result.buffer += "failed to parse repetition as int: \"" + options[4] + "\"\n";
405                 result.buffer += getHelpInfo();
406                 return result;
407             }
408         }
409         auto generator = std::make_unique<JsonFakeValueGenerator>(fileName, repetition);
410         if (!generator->hasNext()) {
411             result.buffer += "invalid JSON file, no events";
412             return result;
413         }
414         getGeneratorHub()->registerGenerator(cookie, std::move(generator));
415         return result;
416     } else if (command == "--stopjson") {
417         LOG(INFO) << __func__ << "FakeDataCommand::StopJson";
418         // --debughal --genfakedata --stopjson [jsonFilePath(string)]
419         if (options.size() != 4) {
420             result.buffer +=
421                     "incorrect argument count, need 4 arguments for --genfakedata --stopjson\n";
422             result.buffer += getHelpInfo();
423             return result;
424         }
425         std::string fileName = options[3];
426         int32_t cookie = std::hash<std::string>()(fileName);
427         getGeneratorHub()->unregisterGenerator(cookie);
428         return result;
429     } else if (command == "--keypress") {
430         LOG(INFO) << __func__ << "FakeDataCommand::KeyPress";
431         int32_t keyCode;
432         int32_t display;
433         // --debughal --genfakedata --keypress [keyCode(int32)] [display[int32]]
434         if (options.size() != 5) {
435             result.buffer +=
436                     "incorrect argument count, need 5 arguments for --genfakedata --keypress\n";
437             result.buffer += getHelpInfo();
438             return result;
439         }
440         if (!android::base::ParseInt(options[3], &keyCode)) {
441             result.buffer += "failed to parse keyCode as int: \"" + options[3] + "\"\n";
442             result.buffer += getHelpInfo();
443             return result;
444         }
445         if (!android::base::ParseInt(options[4], &display)) {
446             result.buffer += "failed to parse display as int: \"" + options[4] + "\"\n";
447             result.buffer += getHelpInfo();
448             return result;
449         }
450         // Send back to HAL
451         onPropertyValueFromCar(
452                 *createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_DOWN, keyCode, display),
453                 /*updateStatus=*/true);
454         onPropertyValueFromCar(
455                 *createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_UP, keyCode, display),
456                 /*updateStatus=*/true);
457         return result;
458     }
459 
460     result.buffer += "Unknown command: \"" + command + "\"\n";
461     result.buffer += getHelpInfo();
462     return result;
463 }
464 
maybeOverrideProperties(const char * overrideDir)465 void DefaultVehicleHalServer::maybeOverrideProperties(const char* overrideDir) {
466     if (android::base::GetBoolProperty("persist.vendor.vhal_init_value_override", false)) {
467         overrideProperties(overrideDir);
468     }
469 }
470 
overrideProperties(const char * overrideDir)471 void DefaultVehicleHalServer::overrideProperties(const char* overrideDir) {
472     LOG(INFO) << "loading vendor override properties from " << overrideDir;
473     if (auto dir = opendir(overrideDir)) {
474         std::regex reg_json(".*[.]json", std::regex::icase);
475         while (auto f = readdir(dir)) {
476             if (!regex_match(f->d_name, reg_json)) {
477                 continue;
478             }
479             std::string file = overrideDir + std::string(f->d_name);
480             JsonFakeValueGenerator tmpGenerator(file);
481 
482             std::vector<VehiclePropValue> propValues = tmpGenerator.getAllEvents();
483             for (const VehiclePropValue& prop : propValues) {
484                 mServerSidePropStore.writeValue(prop, true);
485             }
486         }
487         closedir(dir);
488     }
489 }
490 
setValueCommand(const std::vector<std::string> & options)491 IVehicleServer::DumpResult DefaultVehicleHalServer::setValueCommand(
492         const std::vector<std::string>& options) {
493     DumpResult result;
494     // This is a debug command for the HAL, caller should not continue to dump state.
495     result.callerShouldDumpState = false;
496     // --debughal --set* [propID(int32)] [value] [timestamp(int64)]
497     // [areaId(int32)(optional)]
498     if (options.size() != 5 && options.size() != 6) {
499         result.buffer +=
500                 "incorrect argument count, need 5 or 6 arguments for --setint or --setfloat or "
501                 "--setbool\n";
502         result.buffer += getHelpInfo();
503         return result;
504     }
505     std::unique_ptr<VehiclePropValue> updatedPropValue;
506     int32_t propId;
507     int32_t intValue;
508     float floatValue;
509     int64_t timestamp;
510     int32_t areaId = 0;
511     if (options[1] == "--setint") {
512         updatedPropValue = std::move(createVehiclePropValue(VehiclePropertyType::INT32, 1));
513         if (!android::base::ParseInt(options[3], &intValue)) {
514             result.buffer += "failed to parse value as int: \"" + options[3] + "\"\n";
515             result.buffer += getHelpInfo();
516             return result;
517         }
518         updatedPropValue->value.int32Values[0] = intValue;
519     } else if (options[1] == "--setbool") {
520         updatedPropValue = std::move(createVehiclePropValue(VehiclePropertyType::BOOLEAN, 1));
521         if (options[3] == "true" || options[3] == "True") {
522             updatedPropValue->value.int32Values[0] = 1;
523         } else if (options[3] == "false" || options[3] == "False") {
524             updatedPropValue->value.int32Values[0] = 0;
525         } else {
526             result.buffer += "failed to parse value as bool, only accepts true/false: \"" +
527                              options[3] + "\"\n";
528             result.buffer += getHelpInfo();
529             return result;
530         }
531     } else {
532         updatedPropValue = std::move(createVehiclePropValue(VehiclePropertyType::FLOAT, 1));
533         if (!android::base::ParseFloat(options[3], &floatValue)) {
534             result.buffer += "failed to parse value as float: \"" + options[3] + "\"\n";
535             result.buffer += getHelpInfo();
536             return result;
537         }
538         updatedPropValue->value.floatValues[0] = floatValue;
539     }
540     if (!android::base::ParseInt(options[2], &propId)) {
541         result.buffer += "failed to parse propID as int: \"" + options[2] + "\"\n";
542         result.buffer += getHelpInfo();
543         return result;
544     }
545     updatedPropValue->prop = propId;
546     if (!android::base::ParseInt(options[4], &timestamp)) {
547         result.buffer += "failed to parse timestamp as int: \"" + options[4] + "\"\n";
548         result.buffer += getHelpInfo();
549         return result;
550     }
551     updatedPropValue->timestamp = timestamp;
552     if (options.size() == 6) {
553         if (!android::base::ParseInt(options[5], &areaId)) {
554             result.buffer += "failed to parse areaID as int: \"" + options[5] + "\"\n";
555             result.buffer += getHelpInfo();
556             return result;
557         }
558     }
559     updatedPropValue->areaId = areaId;
560 
561     onPropertyValueFromCar(*updatedPropValue, /*updateStatus=*/true);
562     return result;
563 }
564 
565 }  // namespace impl
566 
567 }  // namespace V2_0
568 }  // namespace vehicle
569 }  // namespace automotive
570 }  // namespace hardware
571 }  // namespace android
572