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 
17 #define LOG_TAG "[email protected]"
18 
19 #include "VehicleHalManager.h"
20 
21 #include <cmath>
22 #include <fstream>
23 
24 #include <android-base/parseint.h>
25 #include <android-base/strings.h>
26 #include <android/hardware/automotive/vehicle/2.0/BpHwVehicleCallback.h>
27 #include <android/log.h>
28 
29 #include <hwbinder/IPCThreadState.h>
30 #include <private/android_filesystem_config.h>
31 #include <utils/SystemClock.h>
32 
33 #include "VehicleUtils.h"
34 
35 namespace android {
36 namespace hardware {
37 namespace automotive {
38 namespace vehicle {
39 namespace V2_0 {
40 
41 using namespace std::placeholders;
42 
43 using ::android::base::EqualsIgnoreCase;
44 using ::android::hardware::hidl_handle;
45 using ::android::hardware::hidl_string;
46 
47 constexpr std::chrono::milliseconds kHalEventBatchingTimeWindow(10);
48 
49 const VehiclePropValue kEmptyValue{};
50 
51 /**
52  * Indicates what's the maximum size of hidl_vec<VehiclePropValue> we want
53  * to store in reusable object pool.
54  */
55 constexpr auto kMaxHidlVecOfVehiclPropValuePoolSize = 20;
56 
getAllPropConfigs(getAllPropConfigs_cb _hidl_cb)57 Return<void> VehicleHalManager::getAllPropConfigs(getAllPropConfigs_cb _hidl_cb) {
58     ALOGI("getAllPropConfigs called");
59     hidl_vec<VehiclePropConfig> hidlConfigs;
60     auto& halConfig = mConfigIndex->getAllConfigs();
61 
62     hidlConfigs.setToExternal(
63             const_cast<VehiclePropConfig *>(halConfig.data()),
64             halConfig.size());
65 
66     _hidl_cb(hidlConfigs);
67 
68     return Void();
69 }
70 
getPropConfigs(const hidl_vec<int32_t> & properties,getPropConfigs_cb _hidl_cb)71 Return<void> VehicleHalManager::getPropConfigs(const hidl_vec<int32_t> &properties,
72                                                getPropConfigs_cb _hidl_cb) {
73     std::vector<VehiclePropConfig> configs;
74     for (size_t i = 0; i < properties.size(); i++) {
75         auto prop = properties[i];
76         if (mConfigIndex->hasConfig(prop)) {
77             configs.push_back(mConfigIndex->getConfig(prop));
78         } else {
79             ALOGW("Requested config for undefined property: 0x%x", prop);
80             _hidl_cb(StatusCode::INVALID_ARG, hidl_vec<VehiclePropConfig>());
81         }
82     }
83 
84     _hidl_cb(StatusCode::OK, configs);
85 
86     return Void();
87 }
88 
get(const VehiclePropValue & requestedPropValue,get_cb _hidl_cb)89 Return<void> VehicleHalManager::get(const VehiclePropValue& requestedPropValue, get_cb _hidl_cb) {
90     const auto* config = getPropConfigOrNull(requestedPropValue.prop);
91     if (config == nullptr) {
92         ALOGE("Failed to get value: config not found, property: 0x%x",
93               requestedPropValue.prop);
94         _hidl_cb(StatusCode::INVALID_ARG, kEmptyValue);
95         return Void();
96     }
97 
98     if (!checkReadPermission(*config)) {
99         _hidl_cb(StatusCode::ACCESS_DENIED, kEmptyValue);
100         return Void();
101     }
102 
103     StatusCode status;
104     auto value = mHal->get(requestedPropValue, &status);
105     _hidl_cb(status, value.get() ? *value : kEmptyValue);
106 
107 
108     return Void();
109 }
110 
set(const VehiclePropValue & value)111 Return<StatusCode> VehicleHalManager::set(const VehiclePropValue &value) {
112     auto prop = value.prop;
113     const auto* config = getPropConfigOrNull(prop);
114     if (config == nullptr) {
115         ALOGE("Failed to set value: config not found, property: 0x%x", prop);
116         return StatusCode::INVALID_ARG;
117     }
118 
119     if (!checkWritePermission(*config)) {
120         return StatusCode::ACCESS_DENIED;
121     }
122 
123     handlePropertySetEvent(value);
124 
125     auto status = mHal->set(value);
126 
127     return Return<StatusCode>(status);
128 }
129 
subscribe(const sp<IVehicleCallback> & callback,const hidl_vec<SubscribeOptions> & options)130 Return<StatusCode> VehicleHalManager::subscribe(const sp<IVehicleCallback> &callback,
131                                                 const hidl_vec<SubscribeOptions> &options) {
132     hidl_vec<SubscribeOptions> verifiedOptions(options);
133     for (size_t i = 0; i < verifiedOptions.size(); i++) {
134         SubscribeOptions& ops = verifiedOptions[i];
135         auto prop = ops.propId;
136 
137         const auto* config = getPropConfigOrNull(prop);
138         if (config == nullptr) {
139             ALOGE("Failed to subscribe: config not found, property: 0x%x",
140                   prop);
141             return StatusCode::INVALID_ARG;
142         }
143 
144         if (ops.flags == SubscribeFlags::UNDEFINED) {
145             ALOGE("Failed to subscribe: undefined flag in options provided");
146             return StatusCode::INVALID_ARG;
147         }
148 
149         if (!isSubscribable(*config, ops.flags)) {
150             ALOGE("Failed to subscribe: property 0x%x is not subscribable",
151                   prop);
152             return StatusCode::INVALID_ARG;
153         }
154 
155         ops.sampleRate = checkSampleRate(*config, ops.sampleRate);
156     }
157 
158     std::list<SubscribeOptions> updatedOptions;
159     auto res = mSubscriptionManager.addOrUpdateSubscription(getClientId(callback),
160                                                             callback, verifiedOptions,
161                                                             &updatedOptions);
162     if (StatusCode::OK != res) {
163         ALOGW("%s failed to subscribe, error code: %d", __func__, res);
164         return res;
165     }
166 
167     for (auto opt : updatedOptions) {
168         mHal->subscribe(opt.propId, opt.sampleRate);
169     }
170 
171     return StatusCode::OK;
172 }
173 
unsubscribe(const sp<IVehicleCallback> & callback,int32_t propId)174 Return<StatusCode> VehicleHalManager::unsubscribe(const sp<IVehicleCallback>& callback,
175                                                   int32_t propId) {
176     mSubscriptionManager.unsubscribe(getClientId(callback), propId);
177     return StatusCode::OK;
178 }
179 
debugDump(IVehicle::debugDump_cb _hidl_cb)180 Return<void> VehicleHalManager::debugDump(IVehicle::debugDump_cb _hidl_cb) {
181     _hidl_cb("");
182     return Void();
183 }
184 
debug(const hidl_handle & fd,const hidl_vec<hidl_string> & options)185 Return<void> VehicleHalManager::debug(const hidl_handle& fd, const hidl_vec<hidl_string>& options) {
186     if (fd.getNativeHandle() == nullptr || fd->numFds == 0) {
187         ALOGE("Invalid parameters passed to debug()");
188         return Void();
189     }
190 
191     bool shouldContinue = mHal->dump(fd, options);
192     if (!shouldContinue) {
193         ALOGI("Dumped HAL only");
194         return Void();
195     }
196 
197     // Do our dump
198     cmdDump(fd->data[0], options);
199     return Void();
200 }
201 
cmdDump(int fd,const hidl_vec<hidl_string> & options)202 void VehicleHalManager::cmdDump(int fd, const hidl_vec<hidl_string>& options) {
203     if (options.size() == 0) {
204         cmdDumpAllProperties(fd);
205         return;
206     }
207     std::string option = options[0];
208     if (EqualsIgnoreCase(option, "--help")) {
209         cmdHelp(fd);
210     } else if (EqualsIgnoreCase(option, "--list")) {
211         cmdListAllProperties(fd);
212     } else if (EqualsIgnoreCase(option, "--get")) {
213         cmdDumpSpecificProperties(fd, options);
214     } else if (EqualsIgnoreCase(option, "--set")) {
215         cmdSetOneProperty(fd, options);
216     } else {
217         dprintf(fd, "Invalid option: %s\n", option.c_str());
218     }
219 }
220 
checkCallerHasWritePermissions(int fd)221 bool VehicleHalManager::checkCallerHasWritePermissions(int fd) {
222     // Double check that's only called by root - it should be be blocked at the HIDL debug() level,
223     // but it doesn't hurt to make sure...
224     if (hardware::IPCThreadState::self()->getCallingUid() != AID_ROOT) {
225         dprintf(fd, "Must be root\n");
226         return false;
227     }
228     return true;
229 }
230 
checkArgumentsSize(int fd,const hidl_vec<hidl_string> & options,size_t minSize)231 bool VehicleHalManager::checkArgumentsSize(int fd, const hidl_vec<hidl_string>& options,
232                                            size_t minSize) {
233     size_t size = options.size();
234     if (size >= minSize) {
235         return true;
236     }
237     dprintf(fd, "Invalid number of arguments: required at least %zu, got %zu\n", minSize, size);
238     return false;
239 }
240 
safelyParseInt(int fd,int index,std::string s,int * out)241 bool VehicleHalManager::safelyParseInt(int fd, int index, std::string s, int* out) {
242     if (!android::base::ParseInt(s, out)) {
243         dprintf(fd, "non-integer argument at index %d: %s\n", index, s.c_str());
244         return false;
245     }
246     return true;
247 }
248 
cmdHelp(int fd) const249 void VehicleHalManager::cmdHelp(int fd) const {
250     dprintf(fd, "Usage: \n\n");
251     dprintf(fd, "[no args]: dumps (id and value) all supported properties \n");
252     dprintf(fd, "--help: shows this help\n");
253     dprintf(fd, "--list: lists the ids of all supported properties\n");
254     dprintf(fd, "--get <PROP1> [PROP2] [PROPN]: dumps the value of specific properties \n");
255     // TODO: support other formats (int64, float, bytes)
256     dprintf(fd,
257             "--set <PROP> <i|s> <VALUE_1> [<i|s> <VALUE_N>] [a AREA_ID] : sets the value of "
258             "property PROP, using arbitrary number of key/value parameters (i for int32, "
259             "s for string) and an optional area.\n"
260             "Notice that the string value can be set just once, while the other can have multiple "
261             "values (so they're used in the respective array)\n");
262 }
263 
cmdListAllProperties(int fd) const264 void VehicleHalManager::cmdListAllProperties(int fd) const {
265     auto& halConfig = mConfigIndex->getAllConfigs();
266     size_t size = halConfig.size();
267     if (size == 0) {
268         dprintf(fd, "no properties to list\n");
269         return;
270     }
271     int i = 0;
272     dprintf(fd, "listing %zu properties\n", size);
273     for (const auto& config : halConfig) {
274         dprintf(fd, "%d: %d\n", ++i, config.prop);
275     }
276 }
277 
cmdDumpAllProperties(int fd)278 void VehicleHalManager::cmdDumpAllProperties(int fd) {
279     auto& halConfig = mConfigIndex->getAllConfigs();
280     size_t size = halConfig.size();
281     if (size == 0) {
282         dprintf(fd, "no properties to dump\n");
283         return;
284     }
285     int rowNumber = 0;
286     dprintf(fd, "dumping %zu properties\n", size);
287     for (auto& config : halConfig) {
288         cmdDumpOneProperty(fd, ++rowNumber, config);
289     }
290 }
291 
cmdDumpOneProperty(int fd,int rowNumber,const VehiclePropConfig & config)292 void VehicleHalManager::cmdDumpOneProperty(int fd, int rowNumber, const VehiclePropConfig& config) {
293     size_t numberAreas = config.areaConfigs.size();
294     if (numberAreas == 0) {
295         if (rowNumber > 0) {
296             dprintf(fd, "%d: ", rowNumber);
297         }
298         cmdDumpOneProperty(fd, config.prop, /* areaId= */ 0);
299         return;
300     }
301     for (size_t j = 0; j < numberAreas; ++j) {
302         if (rowNumber > 0) {
303             if (numberAreas > 1) {
304                 dprintf(fd, "%d/%zu: ", rowNumber, j);
305             } else {
306                 dprintf(fd, "%d: ", rowNumber);
307             }
308         }
309         cmdDumpOneProperty(fd, config.prop, config.areaConfigs[j].areaId);
310     }
311 }
312 
cmdDumpSpecificProperties(int fd,const hidl_vec<hidl_string> & options)313 void VehicleHalManager::cmdDumpSpecificProperties(int fd, const hidl_vec<hidl_string>& options) {
314     if (!checkArgumentsSize(fd, options, 2)) return;
315 
316     // options[0] is the command itself...
317     int rowNumber = 0;
318     size_t size = options.size();
319     for (size_t i = 1; i < size; ++i) {
320         int prop;
321         if (!safelyParseInt(fd, i, options[i], &prop)) return;
322         const auto* config = getPropConfigOrNull(prop);
323         if (config == nullptr) {
324             dprintf(fd, "No property %d\n", prop);
325             continue;
326         }
327         if (size > 2) {
328             // Only show row number if there's more than 1
329             rowNumber++;
330         }
331         cmdDumpOneProperty(fd, rowNumber, *config);
332     }
333 }
334 
cmdDumpOneProperty(int fd,int32_t prop,int32_t areaId)335 void VehicleHalManager::cmdDumpOneProperty(int fd, int32_t prop, int32_t areaId) {
336     VehiclePropValue input;
337     input.prop = prop;
338     input.areaId = areaId;
339     auto callback = [&](StatusCode status, const VehiclePropValue& output) {
340         if (status == StatusCode::OK) {
341             dprintf(fd, "%s\n", toString(output).c_str());
342         } else {
343             dprintf(fd, "Could not get property %d. Error: %s\n", prop, toString(status).c_str());
344         }
345     };
346     get(input, callback);
347 }
348 
cmdSetOneProperty(int fd,const hidl_vec<hidl_string> & options)349 void VehicleHalManager::cmdSetOneProperty(int fd, const hidl_vec<hidl_string>& options) {
350     if (!checkCallerHasWritePermissions(fd) || !checkArgumentsSize(fd, options, 3)) return;
351 
352     size_t size = options.size();
353 
354     // Syntax is --set PROP Type1 Value1 TypeN ValueN, so number of arguments must be even
355     if (size % 2 != 0) {
356         dprintf(fd, "must pass even number of arguments (passed %zu)\n", size);
357         return;
358     }
359     int numberValues = (size - 2) / 2;
360 
361     VehiclePropValue prop;
362     if (!safelyParseInt(fd, 1, options[1], &prop.prop)) return;
363     prop.timestamp = elapsedRealtimeNano();
364     prop.status = VehiclePropertyStatus::AVAILABLE;
365 
366     // First pass: calculate sizes
367     int sizeInt32 = 0;
368     int stringIndex = 0;
369     int areaIndex = 0;
370     for (int i = 2, kv = 1; kv <= numberValues; kv++) {
371         // iterate through the kv=1..n key/value pairs, accessing indexes i / i+1 at each step
372         std::string type = options[i];
373         std::string value = options[i + 1];
374         if (EqualsIgnoreCase(type, "i")) {
375             sizeInt32++;
376         } else if (EqualsIgnoreCase(type, "s")) {
377             if (stringIndex != 0) {
378                 dprintf(fd,
379                         "defining string value (%s) again at index %d (already defined at %d=%s"
380                         ")\n",
381                         value.c_str(), i, stringIndex, options[stringIndex + 1].c_str());
382                 return;
383             }
384             stringIndex = i;
385         } else if (EqualsIgnoreCase(type, "a")) {
386             if (areaIndex != 0) {
387                 dprintf(fd,
388                         "defining area value (%s) again at index %d (already defined at %d=%s"
389                         ")\n",
390                         value.c_str(), i, areaIndex, options[areaIndex + 1].c_str());
391                 return;
392             }
393             areaIndex = i;
394         } else {
395             dprintf(fd, "invalid (%s) type at index %d\n", type.c_str(), i);
396             return;
397         }
398         i += 2;
399     }
400     prop.value.int32Values.resize(sizeInt32);
401 
402     // Second pass: populate it
403     int indexInt32 = 0;
404     for (int i = 2, kv = 1; kv <= numberValues; kv++) {
405         // iterate through the kv=1..n key/value pairs, accessing indexes i / i+1 at each step
406         int valueIndex = i + 1;
407         std::string type = options[i];
408         std::string value = options[valueIndex];
409         if (EqualsIgnoreCase(type, "i")) {
410             int safeInt;
411             if (!safelyParseInt(fd, valueIndex, value, &safeInt)) return;
412             prop.value.int32Values[indexInt32++] = safeInt;
413         } else if (EqualsIgnoreCase(type, "s")) {
414             prop.value.stringValue = value;
415         } else if (EqualsIgnoreCase(type, "a")) {
416             if (!safelyParseInt(fd, valueIndex, value, &prop.areaId)) return;
417         }
418         i += 2;
419     }
420     ALOGD("Setting prop %s", toString(prop).c_str());
421     auto status = set(prop);
422     if (status == StatusCode::OK) {
423         dprintf(fd, "Set property %s\n", toString(prop).c_str());
424     } else {
425         dprintf(fd, "Failed to set property %s: %s\n", toString(prop).c_str(),
426                 toString(status).c_str());
427     }
428 }
429 
init()430 void VehicleHalManager::init() {
431     ALOGI("VehicleHalManager::init");
432 
433     mHidlVecOfVehiclePropValuePool.resize(kMaxHidlVecOfVehiclPropValuePoolSize);
434 
435 
436     mBatchingConsumer.run(&mEventQueue,
437                           kHalEventBatchingTimeWindow,
438                           std::bind(&VehicleHalManager::onBatchHalEvent,
439                                     this, _1));
440 
441     mHal->init(&mValueObjectPool,
442                std::bind(&VehicleHalManager::onHalEvent, this, _1),
443                std::bind(&VehicleHalManager::onHalPropertySetError, this,
444                          _1, _2, _3));
445 
446     // Initialize index with vehicle configurations received from VehicleHal.
447     auto supportedPropConfigs = mHal->listProperties();
448     mConfigIndex.reset(new VehiclePropConfigIndex(supportedPropConfigs));
449 
450     std::vector<int32_t> supportedProperties(
451         supportedPropConfigs.size());
452     for (const auto& config : supportedPropConfigs) {
453         supportedProperties.push_back(config.prop);
454     }
455 }
456 
~VehicleHalManager()457 VehicleHalManager::~VehicleHalManager() {
458     mBatchingConsumer.requestStop();
459     mEventQueue.deactivate();
460     // We have to wait until consumer thread is fully stopped because it may
461     // be in a state of running callback (onBatchHalEvent).
462     mBatchingConsumer.waitStopped();
463     ALOGI("VehicleHalManager::dtor");
464 }
465 
onHalEvent(VehiclePropValuePtr v)466 void VehicleHalManager::onHalEvent(VehiclePropValuePtr v) {
467     mEventQueue.push(std::move(v));
468 }
469 
onHalPropertySetError(StatusCode errorCode,int32_t property,int32_t areaId)470 void VehicleHalManager::onHalPropertySetError(StatusCode errorCode,
471                                               int32_t property,
472                                               int32_t areaId) {
473     const auto& clients =
474         mSubscriptionManager.getSubscribedClients(property, SubscribeFlags::EVENTS_FROM_CAR);
475 
476     for (const auto& client : clients) {
477         client->getCallback()->onPropertySetError(errorCode, property, areaId);
478     }
479 }
480 
onBatchHalEvent(const std::vector<VehiclePropValuePtr> & values)481 void VehicleHalManager::onBatchHalEvent(const std::vector<VehiclePropValuePtr>& values) {
482     const auto& clientValues =
483         mSubscriptionManager.distributeValuesToClients(values, SubscribeFlags::EVENTS_FROM_CAR);
484 
485     for (const HalClientValues& cv : clientValues) {
486         auto vecSize = cv.values.size();
487         hidl_vec<VehiclePropValue> vec;
488         if (vecSize < kMaxHidlVecOfVehiclPropValuePoolSize) {
489             vec.setToExternal(&mHidlVecOfVehiclePropValuePool[0], vecSize);
490         } else {
491             vec.resize(vecSize);
492         }
493 
494         int i = 0;
495         for (VehiclePropValue* pValue : cv.values) {
496             shallowCopy(&(vec)[i++], *pValue);
497         }
498         auto status = cv.client->getCallback()->onPropertyEvent(vec);
499         if (!status.isOk()) {
500             ALOGE("Failed to notify client %s, err: %s",
501                   toString(cv.client->getCallback()).c_str(),
502                   status.description().c_str());
503         }
504     }
505 }
506 
isSampleRateFixed(VehiclePropertyChangeMode mode)507 bool VehicleHalManager::isSampleRateFixed(VehiclePropertyChangeMode mode) {
508     return (mode & VehiclePropertyChangeMode::ON_CHANGE);
509 }
510 
checkSampleRate(const VehiclePropConfig & config,float sampleRate)511 float VehicleHalManager::checkSampleRate(const VehiclePropConfig &config,
512                                          float sampleRate) {
513     if (isSampleRateFixed(config.changeMode)) {
514         if (std::abs(sampleRate) > std::numeric_limits<float>::epsilon()) {
515             ALOGW("Sample rate is greater than zero for on change type. "
516                       "Ignoring it.");
517         }
518         return 0.0;
519     } else {
520         if (sampleRate > config.maxSampleRate) {
521             ALOGW("Sample rate %f is higher than max %f. Setting sampling rate "
522                       "to max.", sampleRate, config.maxSampleRate);
523             return config.maxSampleRate;
524         }
525         if (sampleRate < config.minSampleRate) {
526             ALOGW("Sample rate %f is lower than min %f. Setting sampling rate "
527                       "to min.", sampleRate, config.minSampleRate);
528             return config.minSampleRate;
529         }
530     }
531     return sampleRate;  // Provided sample rate was good, no changes.
532 }
533 
isSubscribable(const VehiclePropConfig & config,SubscribeFlags flags)534 bool VehicleHalManager::isSubscribable(const VehiclePropConfig& config,
535                                        SubscribeFlags flags) {
536     bool isReadable = config.access & VehiclePropertyAccess::READ;
537 
538     if (!isReadable && (SubscribeFlags::EVENTS_FROM_CAR & flags)) {
539         ALOGW("Cannot subscribe, property 0x%x is not readable", config.prop);
540         return false;
541     }
542     if (config.changeMode == VehiclePropertyChangeMode::STATIC) {
543         ALOGW("Cannot subscribe, property 0x%x is static", config.prop);
544         return false;
545     }
546     return true;
547 }
548 
checkWritePermission(const VehiclePropConfig & config) const549 bool VehicleHalManager::checkWritePermission(const VehiclePropConfig &config) const {
550     if (!(config.access & VehiclePropertyAccess::WRITE)) {
551         ALOGW("Property 0%x has no write access", config.prop);
552         return false;
553     } else {
554         return true;
555     }
556 }
557 
checkReadPermission(const VehiclePropConfig & config) const558 bool VehicleHalManager::checkReadPermission(const VehiclePropConfig &config) const {
559     if (!(config.access & VehiclePropertyAccess::READ)) {
560         ALOGW("Property 0%x has no read access", config.prop);
561         return false;
562     } else {
563         return true;
564     }
565 }
566 
handlePropertySetEvent(const VehiclePropValue & value)567 void VehicleHalManager::handlePropertySetEvent(const VehiclePropValue& value) {
568     auto clients =
569         mSubscriptionManager.getSubscribedClients(value.prop, SubscribeFlags::EVENTS_FROM_ANDROID);
570     for (const auto& client : clients) {
571         client->getCallback()->onPropertySet(value);
572     }
573 }
574 
getPropConfigOrNull(int32_t prop) const575 const VehiclePropConfig* VehicleHalManager::getPropConfigOrNull(
576         int32_t prop) const {
577     return mConfigIndex->hasConfig(prop)
578            ? &mConfigIndex->getConfig(prop) : nullptr;
579 }
580 
onAllClientsUnsubscribed(int32_t propertyId)581 void VehicleHalManager::onAllClientsUnsubscribed(int32_t propertyId) {
582     mHal->unsubscribe(propertyId);
583 }
584 
getClientId(const sp<IVehicleCallback> & callback)585 ClientId VehicleHalManager::getClientId(const sp<IVehicleCallback>& callback) {
586     //TODO(b/32172906): rework this to get some kind of unique id for callback interface when this
587     // feature is ready in HIDL.
588 
589     if (callback->isRemote()) {
590         BpHwVehicleCallback* hwCallback = static_cast<BpHwVehicleCallback*>(callback.get());
591         return static_cast<ClientId>(reinterpret_cast<intptr_t>(hwCallback->onAsBinder()));
592     } else {
593         return static_cast<ClientId>(reinterpret_cast<intptr_t>(callback.get()));
594     }
595 }
596 
597 }  // namespace V2_0
598 }  // namespace vehicle
599 }  // namespace automotive
600 }  // namespace hardware
601 }  // namespace android
602