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 #define LOG_TAG "DefaultVehicleHal_v2_0"
17
18 #include <android-base/chrono_utils.h>
19 #include <assert.h>
20 #include <stdio.h>
21 #include <utils/Log.h>
22 #include <utils/SystemClock.h>
23 #include <vhal_v2_0/RecurrentTimer.h>
24 #include <unordered_set>
25
26 #include "FakeObd2Frame.h"
27 #include "PropertyUtils.h"
28 #include "VehicleUtils.h"
29
30 #include "DefaultVehicleHal.h"
31
32 namespace android {
33 namespace hardware {
34 namespace automotive {
35 namespace vehicle {
36 namespace V2_0 {
37
38 namespace impl {
39
40 namespace {
41 constexpr std::chrono::nanoseconds kHeartBeatIntervalNs = 3s;
42
getAreaConfig(const VehiclePropValue & propValue,const VehiclePropConfig * config)43 const VehicleAreaConfig* getAreaConfig(const VehiclePropValue& propValue,
44 const VehiclePropConfig* config) {
45 if (isGlobalProp(propValue.prop)) {
46 if (config->areaConfigs.size() == 0) {
47 return nullptr;
48 }
49 return &(config->areaConfigs[0]);
50 } else {
51 for (auto& c : config->areaConfigs) {
52 if (c.areaId == propValue.areaId) {
53 return &c;
54 }
55 }
56 }
57 return nullptr;
58 }
59
addTimestamp(VehicleHal::VehiclePropValuePtr v)60 VehicleHal::VehiclePropValuePtr addTimestamp(VehicleHal::VehiclePropValuePtr v) {
61 if (v.get()) {
62 v->timestamp = elapsedRealtimeNano();
63 }
64 return v;
65 }
66 } // namespace
67
createVhalHeartBeatProp()68 VehicleHal::VehiclePropValuePtr DefaultVehicleHal::createVhalHeartBeatProp() {
69 VehicleHal::VehiclePropValuePtr v = getValuePool()->obtainInt64(uptimeMillis());
70 v->prop = static_cast<int32_t>(VehicleProperty::VHAL_HEARTBEAT);
71 v->areaId = 0;
72 v->status = VehiclePropertyStatus::AVAILABLE;
73 return v;
74 }
75
DefaultVehicleHal(VehiclePropertyStore * propStore,VehicleHalClient * client)76 DefaultVehicleHal::DefaultVehicleHal(VehiclePropertyStore* propStore, VehicleHalClient* client)
77 : mPropStore(propStore), mRecurrentTimer(getTimerAction()), mVehicleClient(client) {
78 initStaticConfig();
79 mVehicleClient->registerPropertyValueCallback(
80 [this](const VehiclePropValue& value, bool updateStatus) {
81 onPropertyValue(value, updateStatus);
82 });
83 }
84
getUserHalProp(const VehiclePropValue & requestedPropValue,StatusCode * outStatus)85 VehicleHal::VehiclePropValuePtr DefaultVehicleHal::getUserHalProp(
86 const VehiclePropValue& requestedPropValue, StatusCode* outStatus) {
87 auto propId = requestedPropValue.prop;
88 ALOGI("get(): getting value for prop %d from User HAL", propId);
89 const auto& ret = mFakeUserHal.onGetProperty(requestedPropValue);
90 VehicleHal::VehiclePropValuePtr v = nullptr;
91 if (!ret.ok()) {
92 ALOGE("get(): User HAL returned error: %s", ret.error().message().c_str());
93 *outStatus = StatusCode(ret.error().code());
94 } else {
95 auto value = ret.value().get();
96 if (value != nullptr) {
97 ALOGI("get(): User HAL returned value: %s", toString(*value).c_str());
98 v = getValuePool()->obtain(*value);
99 *outStatus = StatusCode::OK;
100 } else {
101 ALOGE("get(): User HAL returned null value");
102 *outStatus = StatusCode::INTERNAL_ERROR;
103 }
104 }
105 return addTimestamp(std::move(v));
106 }
107
get(const VehiclePropValue & requestedPropValue,StatusCode * outStatus)108 VehicleHal::VehiclePropValuePtr DefaultVehicleHal::get(const VehiclePropValue& requestedPropValue,
109 StatusCode* outStatus) {
110 auto propId = requestedPropValue.prop;
111 ALOGV("get(%d)", propId);
112
113 if (mFakeUserHal.isSupported(propId)) {
114 return getUserHalProp(requestedPropValue, outStatus);
115 }
116
117 VehiclePropValuePtr v = nullptr;
118 if (propId == OBD2_FREEZE_FRAME) {
119 v = getValuePool()->obtainComplex();
120 *outStatus = fillObd2FreezeFrame(mPropStore, requestedPropValue, v.get());
121 return addTimestamp(std::move(v));
122 }
123
124 if (propId == OBD2_FREEZE_FRAME_INFO) {
125 v = getValuePool()->obtainComplex();
126 *outStatus = fillObd2DtcInfo(mPropStore, v.get());
127 return addTimestamp(std::move(v));
128 }
129
130 auto internalPropValue = mPropStore->readValueOrNull(requestedPropValue);
131 if (internalPropValue != nullptr) {
132 v = getValuePool()->obtain(*internalPropValue);
133 }
134
135 if (!v) {
136 *outStatus = StatusCode::INVALID_ARG;
137 } else if (v->status == VehiclePropertyStatus::AVAILABLE) {
138 *outStatus = StatusCode::OK;
139 } else {
140 *outStatus = StatusCode::TRY_AGAIN;
141 }
142 return addTimestamp(std::move(v));
143 }
144
listProperties()145 std::vector<VehiclePropConfig> DefaultVehicleHal::listProperties() {
146 return mPropStore->getAllConfigs();
147 }
148
dump(const hidl_handle & fd,const hidl_vec<hidl_string> & options)149 bool DefaultVehicleHal::dump(const hidl_handle& fd, const hidl_vec<hidl_string>& options) {
150 int nativeFd = fd->data[0];
151 if (nativeFd < 0) {
152 ALOGW("Invalid fd from HIDL handle: %d", nativeFd);
153 return false;
154 }
155 if (options.size() > 0) {
156 if (options[0] == "--help") {
157 std::string buffer;
158 buffer += "Fake user hal usage:\n";
159 buffer += mFakeUserHal.showDumpHelp();
160 buffer += "\n";
161 buffer += "VHAL server debug usage:\n";
162 buffer += "--debughal: send debug command to VHAL server, see '--debughal --help'\n";
163 buffer += "\n";
164 dprintf(nativeFd, "%s", buffer.c_str());
165 return false;
166 } else if (options[0] == kUserHalDumpOption) {
167 dprintf(nativeFd, "%s", mFakeUserHal.dump("").c_str());
168 return false;
169 }
170 } else {
171 // No options, dump the fake user hal state first and then send command to VHAL server
172 // to dump its state.
173 std::string buffer;
174 buffer += "Fake user hal state:\n";
175 buffer += mFakeUserHal.dump(" ");
176 buffer += "\n";
177 dprintf(nativeFd, "%s", buffer.c_str());
178 }
179
180 return mVehicleClient->dump(fd, options);
181 }
182
checkPropValue(const VehiclePropValue & value,const VehiclePropConfig * config)183 StatusCode DefaultVehicleHal::checkPropValue(const VehiclePropValue& value,
184 const VehiclePropConfig* config) {
185 int32_t property = value.prop;
186 VehiclePropertyType type = getPropType(property);
187 switch (type) {
188 case VehiclePropertyType::BOOLEAN:
189 case VehiclePropertyType::INT32:
190 if (value.value.int32Values.size() != 1) {
191 return StatusCode::INVALID_ARG;
192 }
193 break;
194 case VehiclePropertyType::INT32_VEC:
195 if (value.value.int32Values.size() < 1) {
196 return StatusCode::INVALID_ARG;
197 }
198 break;
199 case VehiclePropertyType::INT64:
200 if (value.value.int64Values.size() != 1) {
201 return StatusCode::INVALID_ARG;
202 }
203 break;
204 case VehiclePropertyType::INT64_VEC:
205 if (value.value.int64Values.size() < 1) {
206 return StatusCode::INVALID_ARG;
207 }
208 break;
209 case VehiclePropertyType::FLOAT:
210 if (value.value.floatValues.size() != 1) {
211 return StatusCode::INVALID_ARG;
212 }
213 break;
214 case VehiclePropertyType::FLOAT_VEC:
215 if (value.value.floatValues.size() < 1) {
216 return StatusCode::INVALID_ARG;
217 }
218 break;
219 case VehiclePropertyType::BYTES:
220 // We allow setting an empty bytes array.
221 break;
222 case VehiclePropertyType::STRING:
223 // We allow setting an empty string.
224 break;
225 case VehiclePropertyType::MIXED:
226 if (getPropGroup(property) == VehiclePropertyGroup::VENDOR) {
227 // We only checks vendor mixed properties.
228 return checkVendorMixedPropValue(value, config);
229 }
230 break;
231 default:
232 ALOGW("Unknown property type: %d", type);
233 return StatusCode::INVALID_ARG;
234 }
235 return StatusCode::OK;
236 }
237
checkVendorMixedPropValue(const VehiclePropValue & value,const VehiclePropConfig * config)238 StatusCode DefaultVehicleHal::checkVendorMixedPropValue(const VehiclePropValue& value,
239 const VehiclePropConfig* config) {
240 auto configArray = config->configArray;
241 // configArray[0], 1 indicates the property has a String value, we allow the string value to
242 // be empty.
243
244 size_t int32Count = 0;
245 // configArray[1], 1 indicates the property has a Boolean value.
246 if (configArray[1] == 1) {
247 int32Count++;
248 }
249 // configArray[2], 1 indicates the property has an Integer value.
250 if (configArray[2] == 1) {
251 int32Count++;
252 }
253 // configArray[3], the number indicates the size of Integer[] in the property.
254 int32Count += static_cast<size_t>(configArray[3]);
255 if (value.value.int32Values.size() != int32Count) {
256 return StatusCode::INVALID_ARG;
257 }
258
259 size_t int64Count = 0;
260 // configArray[4], 1 indicates the property has a Long value.
261 if (configArray[4] == 1) {
262 int64Count++;
263 }
264 // configArray[5], the number indicates the size of Long[] in the property.
265 int64Count += static_cast<size_t>(configArray[5]);
266 if (value.value.int64Values.size() != int64Count) {
267 return StatusCode::INVALID_ARG;
268 }
269
270 size_t floatCount = 0;
271 // configArray[6], 1 indicates the property has a Float value.
272 if (configArray[6] == 1) {
273 floatCount++;
274 }
275 // configArray[7], the number indicates the size of Float[] in the property.
276 floatCount += static_cast<size_t>(configArray[7]);
277 if (value.value.floatValues.size() != floatCount) {
278 return StatusCode::INVALID_ARG;
279 }
280
281 // configArray[8], the number indicates the size of byte[] in the property.
282 if (configArray[8] != 0 && value.value.bytes.size() != static_cast<size_t>(configArray[8])) {
283 return StatusCode::INVALID_ARG;
284 }
285 return StatusCode::OK;
286 }
287
checkValueRange(const VehiclePropValue & value,const VehicleAreaConfig * areaConfig)288 StatusCode DefaultVehicleHal::checkValueRange(const VehiclePropValue& value,
289 const VehicleAreaConfig* areaConfig) {
290 if (areaConfig == nullptr) {
291 return StatusCode::OK;
292 }
293 int32_t property = value.prop;
294 VehiclePropertyType type = getPropType(property);
295 switch (type) {
296 case VehiclePropertyType::INT32:
297 if (areaConfig->minInt32Value == 0 && areaConfig->maxInt32Value == 0) {
298 break;
299 }
300 // We already checked this in checkPropValue.
301 assert(value.value.int32Values.size() > 0);
302 if (value.value.int32Values[0] < areaConfig->minInt32Value ||
303 value.value.int32Values[0] > areaConfig->maxInt32Value) {
304 return StatusCode::INVALID_ARG;
305 }
306 break;
307 case VehiclePropertyType::INT64:
308 if (areaConfig->minInt64Value == 0 && areaConfig->maxInt64Value == 0) {
309 break;
310 }
311 // We already checked this in checkPropValue.
312 assert(value.value.int64Values.size() > 0);
313 if (value.value.int64Values[0] < areaConfig->minInt64Value ||
314 value.value.int64Values[0] > areaConfig->maxInt64Value) {
315 return StatusCode::INVALID_ARG;
316 }
317 break;
318 case VehiclePropertyType::FLOAT:
319 if (areaConfig->minFloatValue == 0 && areaConfig->maxFloatValue == 0) {
320 break;
321 }
322 // We already checked this in checkPropValue.
323 assert(value.value.floatValues.size() > 0);
324 if (value.value.floatValues[0] < areaConfig->minFloatValue ||
325 value.value.floatValues[0] > areaConfig->maxFloatValue) {
326 return StatusCode::INVALID_ARG;
327 }
328 break;
329 default:
330 // We don't check the rest of property types. Additional logic needs to be added if
331 // required for real implementation. E.g., you might want to enforce the range
332 // checks on vector as well or you might want to check the range for mixed property.
333 break;
334 }
335 return StatusCode::OK;
336 }
337
setUserHalProp(const VehiclePropValue & propValue)338 StatusCode DefaultVehicleHal::setUserHalProp(const VehiclePropValue& propValue) {
339 ALOGI("onSetProperty(): property %d will be handled by UserHal", propValue.prop);
340
341 const auto& ret = mFakeUserHal.onSetProperty(propValue);
342 if (!ret.ok()) {
343 ALOGE("onSetProperty(): HAL returned error: %s", ret.error().message().c_str());
344 return StatusCode(ret.error().code());
345 }
346 auto updatedValue = ret.value().get();
347 if (updatedValue != nullptr) {
348 ALOGI("onSetProperty(): updating property returned by HAL: %s",
349 toString(*updatedValue).c_str());
350 onPropertyValue(*updatedValue, true);
351 }
352 return StatusCode::OK;
353 }
354
set(const VehiclePropValue & propValue)355 StatusCode DefaultVehicleHal::set(const VehiclePropValue& propValue) {
356 if (propValue.status != VehiclePropertyStatus::AVAILABLE) {
357 // Android side cannot set property status - this value is the
358 // purview of the HAL implementation to reflect the state of
359 // its underlying hardware
360 return StatusCode::INVALID_ARG;
361 }
362
363 if (mFakeUserHal.isSupported(propValue.prop)) {
364 return setUserHalProp(propValue);
365 }
366
367 std::unordered_set<int32_t> powerProps(std::begin(kHvacPowerProperties),
368 std::end(kHvacPowerProperties));
369 if (powerProps.count(propValue.prop)) {
370 auto hvacPowerOn = mPropStore->readValueOrNull(
371 toInt(VehicleProperty::HVAC_POWER_ON),
372 (VehicleAreaSeat::ROW_1_LEFT | VehicleAreaSeat::ROW_1_RIGHT |
373 VehicleAreaSeat::ROW_2_LEFT | VehicleAreaSeat::ROW_2_CENTER |
374 VehicleAreaSeat::ROW_2_RIGHT));
375
376 if (hvacPowerOn && hvacPowerOn->value.int32Values.size() == 1 &&
377 hvacPowerOn->value.int32Values[0] == 0) {
378 return StatusCode::NOT_AVAILABLE;
379 }
380 }
381
382 if (propValue.prop == OBD2_FREEZE_FRAME_CLEAR) {
383 return clearObd2FreezeFrames(mPropStore, propValue);
384 }
385 if (propValue.prop == VEHICLE_MAP_SERVICE) {
386 // Placeholder for future implementation of VMS property in the default hal. For
387 // now, just returns OK; otherwise, hal clients crash with property not supported.
388 return StatusCode::OK;
389 }
390
391 int32_t property = propValue.prop;
392 const VehiclePropConfig* config = mPropStore->getConfigOrNull(property);
393 if (config == nullptr) {
394 ALOGW("no config for prop 0x%x", property);
395 return StatusCode::INVALID_ARG;
396 }
397 const VehicleAreaConfig* areaConfig = getAreaConfig(propValue, config);
398 if (!isGlobalProp(property) && areaConfig == nullptr) {
399 // Ignore areaId for global property. For non global property, check whether areaId is
400 // allowed. areaId must appear in areaConfig.
401 ALOGW("invalid area ID: 0x%x for prop 0x%x, not listed in config", propValue.areaId,
402 property);
403 return StatusCode::INVALID_ARG;
404 }
405 auto status = checkPropValue(propValue, config);
406 if (status != StatusCode::OK) {
407 ALOGW("invalid property value: %s", toString(propValue).c_str());
408 return status;
409 }
410 status = checkValueRange(propValue, areaConfig);
411 if (status != StatusCode::OK) {
412 ALOGW("property value out of range: %s", toString(propValue).c_str());
413 return status;
414 }
415
416 auto currentPropValue = mPropStore->readValueOrNull(propValue);
417 if (currentPropValue && currentPropValue->status != VehiclePropertyStatus::AVAILABLE) {
418 // do not allow Android side to set() a disabled/error property
419 return StatusCode::NOT_AVAILABLE;
420 }
421
422 // Send the value to the vehicle server, the server will talk to the (real or emulated) car
423 return mVehicleClient->setProperty(propValue, /*updateStatus=*/false);
424 }
425
426 // Parse supported properties list and generate vector of property values to hold current values.
onCreate()427 void DefaultVehicleHal::onCreate() {
428 auto configs = mVehicleClient->getAllPropertyConfig();
429
430 for (const auto& cfg : configs) {
431 if (isDiagnosticProperty(cfg)) {
432 // do not write an initial empty value for the diagnostic properties
433 // as we will initialize those separately.
434 continue;
435 }
436
437 int32_t numAreas = isGlobalProp(cfg.prop) ? 1 : cfg.areaConfigs.size();
438
439 for (int i = 0; i < numAreas; i++) {
440 int32_t curArea = isGlobalProp(cfg.prop) ? 0 : cfg.areaConfigs[i].areaId;
441
442 // Create a separate instance for each individual zone
443 VehiclePropValue prop = {
444 .areaId = curArea,
445 .prop = cfg.prop,
446 .status = VehiclePropertyStatus::UNAVAILABLE,
447 };
448 // Allow the initial values to set status.
449 mPropStore->writeValue(prop, /*updateStatus=*/true);
450 }
451 }
452
453 mVehicleClient->triggerSendAllValues();
454
455 initObd2LiveFrame(mPropStore, *mPropStore->getConfigOrDie(OBD2_LIVE_FRAME));
456 initObd2FreezeFrame(mPropStore, *mPropStore->getConfigOrDie(OBD2_FREEZE_FRAME));
457
458 registerHeartBeatEvent();
459 }
460
~DefaultVehicleHal()461 DefaultVehicleHal::~DefaultVehicleHal() {
462 mRecurrentTimer.unregisterRecurrentEvent(static_cast<int32_t>(VehicleProperty::VHAL_HEARTBEAT));
463 }
464
registerHeartBeatEvent()465 void DefaultVehicleHal::registerHeartBeatEvent() {
466 mRecurrentTimer.registerRecurrentEvent(kHeartBeatIntervalNs,
467 static_cast<int32_t>(VehicleProperty::VHAL_HEARTBEAT));
468 }
469
doInternalHealthCheck()470 VehicleHal::VehiclePropValuePtr DefaultVehicleHal::doInternalHealthCheck() {
471 VehicleHal::VehiclePropValuePtr v = nullptr;
472
473 // This is an example of very simple health checking. VHAL is considered healthy if we can read
474 // PERF_VEHICLE_SPEED. The more comprehensive health checking is required.
475 VehiclePropValue propValue = {
476 .prop = static_cast<int32_t>(VehicleProperty::PERF_VEHICLE_SPEED),
477 };
478 auto internalPropValue = mPropStore->readValueOrNull(propValue);
479 if (internalPropValue != nullptr) {
480 v = createVhalHeartBeatProp();
481 } else {
482 ALOGW("VHAL health check failed");
483 }
484 return v;
485 }
486
onContinuousPropertyTimer(const std::vector<int32_t> & properties)487 void DefaultVehicleHal::onContinuousPropertyTimer(const std::vector<int32_t>& properties) {
488 auto& pool = *getValuePool();
489
490 for (int32_t property : properties) {
491 VehiclePropValuePtr v;
492 if (isContinuousProperty(property)) {
493 auto internalPropValue = mPropStore->readValueOrNull(property);
494 if (internalPropValue != nullptr) {
495 v = pool.obtain(*internalPropValue);
496 }
497 } else if (property == static_cast<int32_t>(VehicleProperty::VHAL_HEARTBEAT)) {
498 // VHAL_HEARTBEAT is not a continuous value, but it needs to be updated periodically.
499 // So, the update is done through onContinuousPropertyTimer.
500 v = doInternalHealthCheck();
501 } else {
502 ALOGE("Unexpected onContinuousPropertyTimer for property: 0x%x", property);
503 continue;
504 }
505
506 if (v.get()) {
507 v->timestamp = elapsedRealtimeNano();
508 doHalEvent(std::move(v));
509 }
510 }
511 }
512
getTimerAction()513 RecurrentTimer::Action DefaultVehicleHal::getTimerAction() {
514 return [this](const std::vector<int32_t>& properties) {
515 onContinuousPropertyTimer(properties);
516 };
517 }
518
subscribe(int32_t property,float sampleRate)519 StatusCode DefaultVehicleHal::subscribe(int32_t property, float sampleRate) {
520 ALOGI("%s propId: 0x%x, sampleRate: %f", __func__, property, sampleRate);
521
522 if (!isContinuousProperty(property)) {
523 return StatusCode::INVALID_ARG;
524 }
525
526 // If the config does not exist, isContinuousProperty should return false.
527 const VehiclePropConfig* config = mPropStore->getConfigOrNull(property);
528 if (sampleRate < config->minSampleRate || sampleRate > config->maxSampleRate) {
529 ALOGW("sampleRate out of range");
530 return StatusCode::INVALID_ARG;
531 }
532
533 mRecurrentTimer.registerRecurrentEvent(hertzToNanoseconds(sampleRate), property);
534 return StatusCode::OK;
535 }
536
unsubscribe(int32_t property)537 StatusCode DefaultVehicleHal::unsubscribe(int32_t property) {
538 ALOGI("%s propId: 0x%x", __func__, property);
539 if (!isContinuousProperty(property)) {
540 return StatusCode::INVALID_ARG;
541 }
542 // If the event was not registered before, this would do nothing.
543 mRecurrentTimer.unregisterRecurrentEvent(property);
544 return StatusCode::OK;
545 }
546
isContinuousProperty(int32_t propId) const547 bool DefaultVehicleHal::isContinuousProperty(int32_t propId) const {
548 const VehiclePropConfig* config = mPropStore->getConfigOrNull(propId);
549 if (config == nullptr) {
550 ALOGW("Config not found for property: 0x%x", propId);
551 return false;
552 }
553 return config->changeMode == VehiclePropertyChangeMode::CONTINUOUS;
554 }
555
onPropertyValue(const VehiclePropValue & value,bool updateStatus)556 void DefaultVehicleHal::onPropertyValue(const VehiclePropValue& value, bool updateStatus) {
557 VehiclePropValuePtr updatedPropValue = getValuePool()->obtain(value);
558
559 if (mPropStore->writeValue(*updatedPropValue, updateStatus)) {
560 doHalEvent(std::move(updatedPropValue));
561 }
562 }
563
initStaticConfig()564 void DefaultVehicleHal::initStaticConfig() {
565 auto configs = mVehicleClient->getAllPropertyConfig();
566 for (auto&& cfg : configs) {
567 VehiclePropertyStore::TokenFunction tokenFunction = nullptr;
568
569 switch (cfg.prop) {
570 case OBD2_FREEZE_FRAME: {
571 // We use timestamp as token for OBD2_FREEZE_FRAME
572 tokenFunction = [](const VehiclePropValue& propValue) {
573 return propValue.timestamp;
574 };
575 break;
576 }
577 default:
578 break;
579 }
580
581 mPropStore->registerProperty(cfg, tokenFunction);
582 }
583 }
584
585 } // namespace impl
586
587 } // namespace V2_0
588 } // namespace vehicle
589 } // namespace automotive
590 } // namespace hardware
591 } // namespace android
592