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