Merge changes I5a4669b1,I78613b1d into main

* changes:
  Refactor RecurrentTimer to use Looper.
  Use timestamp from property store when get prop.
This commit is contained in:
Yu Shan
2023-12-02 02:22:35 +00:00
committed by Android (Google) Code Review
8 changed files with 225 additions and 164 deletions

View File

@@ -70,6 +70,16 @@ public:
* example wasn't registered. */
bool writeValue(const VehiclePropValue& propValue, bool updateStatus);
/*
* Stores provided value. Returns true if value was written returns false if config for
* example wasn't registered.
*
* The property value's timestamp will be set to the current ElapsedRealTimeNano.
*/
bool writeValueWithCurrentTimestamp(VehiclePropValue* propValuePtr, bool updateStatus);
std::unique_ptr<VehiclePropValue> refreshTimestamp(int32_t propId, int32_t areaId);
void removeValue(const VehiclePropValue& propValue);
void removeValuesForProperty(int32_t propId);
@@ -94,6 +104,8 @@ private:
std::unordered_map<int32_t /* VehicleProperty */, RecordConfig> mConfigs;
PropertyMap mPropertyValues; // Sorted map of RecordId : VehiclePropValue.
bool writeValueLocked(const VehiclePropValue& propValue, bool updateStatus);
};
} // namespace V2_0

View File

@@ -15,6 +15,7 @@
*/
#define LOG_TAG "VehiclePropertyStore"
#include <log/log.h>
#include <utils/SystemClock.h>
#include <common/include/vhal_v2_0/VehicleUtils.h>
#include "VehiclePropertyStore.h"
@@ -41,9 +42,7 @@ void VehiclePropertyStore::registerProperty(const VehiclePropConfig& config,
mConfigs.insert({ config.prop, RecordConfig { config, tokenFunc } });
}
bool VehiclePropertyStore::writeValue(const VehiclePropValue& propValue,
bool updateStatus) {
MuxGuard g(mLock);
bool VehiclePropertyStore::writeValueLocked(const VehiclePropValue& propValue, bool updateStatus) {
if (!mConfigs.count(propValue.prop)) return false;
RecordId recId = getRecordIdLocked(propValue);
@@ -68,6 +67,36 @@ bool VehiclePropertyStore::writeValue(const VehiclePropValue& propValue,
return true;
}
bool VehiclePropertyStore::writeValue(const VehiclePropValue& propValue, bool updateStatus) {
MuxGuard g(mLock);
return writeValueLocked(propValue, updateStatus);
}
bool VehiclePropertyStore::writeValueWithCurrentTimestamp(VehiclePropValue* propValuePtr,
bool updateStatus) {
MuxGuard g(mLock);
propValuePtr->timestamp = elapsedRealtimeNano();
return writeValueLocked(*propValuePtr, updateStatus);
}
std::unique_ptr<VehiclePropValue> VehiclePropertyStore::refreshTimestamp(int32_t propId,
int32_t areaId) {
MuxGuard g(mLock);
RecordId recId = getRecordIdLocked(VehiclePropValue{
.prop = propId,
.areaId = areaId,
});
auto it = mPropertyValues.find(recId);
if (it == mPropertyValues.end()) {
return nullptr;
}
it->second.timestamp = elapsedRealtimeNano();
return std::make_unique<VehiclePropValue>(it->second);
}
void VehiclePropertyStore::removeValue(const VehiclePropValue& propValue) {
MuxGuard g(mLock);
RecordId recId = getRecordIdLocked(propValue);

View File

@@ -57,12 +57,6 @@ const VehicleAreaConfig* getAreaConfig(const VehiclePropValue& propValue,
return nullptr;
}
VehicleHal::VehiclePropValuePtr addTimestamp(VehicleHal::VehiclePropValuePtr v) {
if (v.get()) {
v->timestamp = elapsedRealtimeNano();
}
return v;
}
} // namespace
VehicleHal::VehiclePropValuePtr DefaultVehicleHal::createVhalHeartBeatProp() {
@@ -102,7 +96,7 @@ VehicleHal::VehiclePropValuePtr DefaultVehicleHal::getUserHalProp(
*outStatus = StatusCode::INTERNAL_ERROR;
}
}
return addTimestamp(std::move(v));
return v;
}
VehicleHal::VehiclePropValuePtr DefaultVehicleHal::get(const VehiclePropValue& requestedPropValue,
@@ -118,13 +112,13 @@ VehicleHal::VehiclePropValuePtr DefaultVehicleHal::get(const VehiclePropValue& r
if (propId == OBD2_FREEZE_FRAME) {
v = getValuePool()->obtainComplex();
*outStatus = fillObd2FreezeFrame(mPropStore, requestedPropValue, v.get());
return addTimestamp(std::move(v));
return v;
}
if (propId == OBD2_FREEZE_FRAME_INFO) {
v = getValuePool()->obtainComplex();
*outStatus = fillObd2DtcInfo(mPropStore, v.get());
return addTimestamp(std::move(v));
return v;
}
auto internalPropValue = mPropStore->readValueOrNull(requestedPropValue);
@@ -139,7 +133,7 @@ VehicleHal::VehiclePropValuePtr DefaultVehicleHal::get(const VehiclePropValue& r
} else {
*outStatus = StatusCode::TRY_AGAIN;
}
return addTimestamp(std::move(v));
return v;
}
std::vector<VehiclePropConfig> DefaultVehicleHal::listProperties() {
@@ -486,26 +480,42 @@ VehicleHal::VehiclePropValuePtr DefaultVehicleHal::doInternalHealthCheck() {
void DefaultVehicleHal::onContinuousPropertyTimer(const std::vector<int32_t>& properties) {
auto& pool = *getValuePool();
for (int32_t property : properties) {
VehiclePropValuePtr v;
std::vector<VehiclePropValuePtr> events;
if (isContinuousProperty(property)) {
auto internalPropValue = mPropStore->readValueOrNull(property);
if (internalPropValue != nullptr) {
v = pool.obtain(*internalPropValue);
const VehiclePropConfig* config = mPropStore->getConfigOrNull(property);
std::vector<int32_t> areaIds;
if (isGlobalProp(property)) {
areaIds.push_back(0);
} else {
for (auto& c : config->areaConfigs) {
areaIds.push_back(c.areaId);
}
}
for (int areaId : areaIds) {
auto v = pool.obtain(*mPropStore->refreshTimestamp(property, areaId));
if (v.get()) {
events.push_back(std::move(v));
}
}
} else if (property == static_cast<int32_t>(VehicleProperty::VHAL_HEARTBEAT)) {
// VHAL_HEARTBEAT is not a continuous value, but it needs to be updated periodically.
// So, the update is done through onContinuousPropertyTimer.
v = doInternalHealthCheck();
auto v = doInternalHealthCheck();
if (!v.get()) {
// Internal health check failed.
continue;
}
mPropStore->writeValueWithCurrentTimestamp(v.get(), /*updateStatus=*/true);
events.push_back(std::move(v));
} else {
ALOGE("Unexpected onContinuousPropertyTimer for property: 0x%x", property);
continue;
}
if (v.get()) {
v->timestamp = elapsedRealtimeNano();
doHalEvent(std::move(v));
for (VehiclePropValuePtr& event : events) {
doHalEvent(std::move(event));
}
}
}
@@ -556,7 +566,7 @@ bool DefaultVehicleHal::isContinuousProperty(int32_t propId) const {
void DefaultVehicleHal::onPropertyValue(const VehiclePropValue& value, bool updateStatus) {
VehiclePropValuePtr updatedPropValue = getValuePool()->obtain(value);
if (mPropStore->writeValue(*updatedPropValue, updateStatus)) {
if (mPropStore->writeValueWithCurrentTimestamp(updatedPropValue.get(), updateStatus)) {
doHalEvent(std::move(updatedPropValue));
}
}

View File

@@ -81,8 +81,13 @@ using ::android::hardware::automotive::vehicle::V2_0::impl::OBD2_FREEZE_FRAME;
using ::android::hardware::automotive::vehicle::V2_0::impl::OBD2_FREEZE_FRAME_CLEAR;
using ::android::hardware::automotive::vehicle::V2_0::impl::OBD2_FREEZE_FRAME_INFO;
using ::android::hardware::automotive::vehicle::V2_0::impl::OBD2_LIVE_FRAME;
using ::android::hardware::automotive::vehicle::V2_0::impl::WHEEL_FRONT_LEFT;
using ::android::hardware::automotive::vehicle::V2_0::impl::WHEEL_FRONT_RIGHT;
using ::android::hardware::automotive::vehicle::V2_0::impl::WHEEL_REAR_LEFT;
using ::android::hardware::automotive::vehicle::V2_0::impl::WHEEL_REAR_RIGHT;
using ::testing::HasSubstr;
using ::testing::UnorderedElementsAre;
using VehiclePropValuePtr = recyclable_ptr<VehiclePropValue>;
@@ -346,6 +351,38 @@ TEST_F(DefaultVhalImplTest, testSubscribe) {
EXPECT_EQ(1.0f, lastEvent->value.floatValues[0]);
}
TEST_F(DefaultVhalImplTest, testSubscribeContinuous_withMultipleAreaIds) {
// Clear existing events.
mEventQueue.flush();
int propId = toInt(VehicleProperty::TIRE_PRESSURE);
auto status = mHal->subscribe(propId, 1);
ASSERT_EQ(StatusCode::OK, status);
std::vector<VehiclePropValuePtr> receivedEvents;
// Wait for 2 updates, each for 4 area IDs.
waitForEvents(&receivedEvents, 4 * 2);
std::vector<int> areasForUpdate1;
std::vector<int> areasForUpdate2;
for (size_t i = 0; i < receivedEvents.size(); i++) {
ASSERT_EQ(receivedEvents[i]->prop, propId);
if (i < 4) {
areasForUpdate1.push_back(receivedEvents[i]->areaId);
} else {
areasForUpdate2.push_back(receivedEvents[i]->areaId);
}
}
ASSERT_THAT(areasForUpdate1, UnorderedElementsAre(WHEEL_FRONT_LEFT, WHEEL_FRONT_RIGHT,
WHEEL_REAR_LEFT, WHEEL_REAR_RIGHT));
ASSERT_THAT(areasForUpdate2, UnorderedElementsAre(WHEEL_FRONT_LEFT, WHEEL_FRONT_RIGHT,
WHEEL_REAR_LEFT, WHEEL_REAR_RIGHT));
}
TEST_F(DefaultVhalImplTest, testSubscribeInvalidProp) {
EXPECT_EQ(StatusCode::INVALID_ARG, mHal->subscribe(toInt(VehicleProperty::INFO_MAKE), 10));
}
@@ -1318,7 +1355,6 @@ TEST_F(DefaultVhalImplTest, testDebugSetInt) {
ASSERT_EQ((size_t)1, events.size());
ASSERT_EQ((size_t)1, events[0]->value.int32Values.size());
EXPECT_EQ(2022, events[0]->value.int32Values[0]);
EXPECT_EQ(1000, events[0]->timestamp);
VehiclePropValue value;
StatusCode status;
@@ -1352,7 +1388,6 @@ TEST_F(DefaultVhalImplTest, testDebugSetBool) {
ASSERT_EQ((size_t)1, events.size());
EXPECT_EQ(0, events[0]->value.int32Values[0]);
EXPECT_EQ(DOOR_1_LEFT, events[0]->areaId);
EXPECT_EQ(1000, events[0]->timestamp);
VehiclePropValue value;
StatusCode status;
@@ -1391,7 +1426,6 @@ TEST_F(DefaultVhalImplTest, testDebugSetFloat) {
ASSERT_EQ((size_t)1, events.size());
ASSERT_EQ((size_t)1, events[0]->value.floatValues.size());
EXPECT_EQ(10.5, events[0]->value.floatValues[0]);
EXPECT_EQ(1000, events[0]->timestamp);
VehiclePropValue value;
StatusCode status;

View File

@@ -19,6 +19,8 @@
#include <android-base/thread_annotations.h>
#include <utils/Looper.h>
#include <atomic>
#include <memory>
#include <mutex>
#include <queue>
@@ -31,6 +33,9 @@ namespace hardware {
namespace automotive {
namespace vehicle {
// Forward declaration
class RecurrentMessageHandler;
// A thread-safe recurrent timer.
class RecurrentTimer final {
public:
@@ -43,49 +48,44 @@ class RecurrentTimer final {
// Registers a recurrent callback for a given interval.
// Registering the same callback twice will override the interval provided before.
void registerTimerCallback(int64_t intervalInNano, std::shared_ptr<Callback> callback);
void registerTimerCallback(int64_t intervalInNanos, std::shared_ptr<Callback> callback);
// Unregisters a previously registered recurrent callback.
void unregisterTimerCallback(std::shared_ptr<Callback> callback);
private:
// friend class for unit testing.
friend class RecurrentMessageHandler;
// For unit test
friend class RecurrentTimerTest;
struct CallbackInfo {
std::shared_ptr<Callback> callback;
int64_t interval;
int64_t nextTime;
// A flag to indicate whether this CallbackInfo is already outdated and should be ignored.
// The reason we need this flag is because we cannot easily remove an element from a heap.
bool outdated = false;
static bool cmp(const std::unique_ptr<CallbackInfo>& lhs,
const std::unique_ptr<CallbackInfo>& rhs);
int64_t intervalInNanos;
int64_t nextTimeInNanos;
};
android::sp<Looper> mLooper;
android::sp<RecurrentMessageHandler> mHandler;
std::atomic<bool> mStopRequested = false;
std::atomic<int> mCallbackId = 0;
std::mutex mLock;
std::thread mThread;
std::condition_variable mCond;
bool mStopRequested GUARDED_BY(mLock) = false;
// A map to map each callback to its current active CallbackInfo in the mCallbackQueue.
std::unordered_map<std::shared_ptr<Callback>, CallbackInfo*> mCallbacks GUARDED_BY(mLock);
// A min-heap sorted by nextTime. Note that because we cannot remove arbitrary element from the
// heap, a single Callback can have multiple entries in this queue, all but one should be valid.
// The rest should be mark as outdated. The valid one is one stored in mCallbacks.
std::vector<std::unique_ptr<CallbackInfo>> mCallbackQueue GUARDED_BY(mLock);
std::unordered_map<std::shared_ptr<Callback>, int> mIdByCallback GUARDED_BY(mLock);
std::unordered_map<int, std::unique_ptr<CallbackInfo>> mCallbackInfoById GUARDED_BY(mLock);
void loop();
void handleMessage(const android::Message& message) EXCLUDES(mLock);
int getCallbackIdLocked(std::shared_ptr<Callback> callback) REQUIRES(mLock);
};
// Mark the callbackInfo as outdated and should be ignored when popped from the heap.
void markOutdatedLocked(CallbackInfo* callback) REQUIRES(mLock);
// Remove all outdated callbackInfos from the top of the heap. This function must be called
// each time we might introduce outdated elements to the top. We must make sure the heap is
// always valid from the top.
void removeInvalidCallbackLocked() REQUIRES(mLock);
// Gets the next calblack to run (must be valid) from the heap, update its nextTime and put
// it back to the heap.
std::shared_ptr<Callback> getNextCallbackLocked(int64_t now) REQUIRES(mLock);
class RecurrentMessageHandler final : public android::MessageHandler {
public:
RecurrentMessageHandler(RecurrentTimer* timer) { mTimer = timer; }
void handleMessage(const android::Message& message) override;
private:
RecurrentTimer* mTimer;
};
} // namespace vehicle

View File

@@ -17,6 +17,7 @@
#include "RecurrentTimer.h"
#include <utils/Log.h>
#include <utils/Looper.h>
#include <utils/SystemClock.h>
#include <inttypes.h>
@@ -27,153 +28,119 @@ namespace hardware {
namespace automotive {
namespace vehicle {
namespace {
using ::android::base::ScopedLockAssertion;
constexpr int INVALID_ID = -1;
} // namespace
RecurrentTimer::RecurrentTimer() {
mThread = std::thread(&RecurrentTimer::loop, this);
mHandler = sp<RecurrentMessageHandler>::make(this);
mLooper = sp<Looper>::make(/*allowNonCallbacks=*/false);
mThread = std::thread([this] {
Looper::setForThread(mLooper);
while (!mStopRequested) {
mLooper->pollOnce(/*timeoutMillis=*/-1);
}
});
}
RecurrentTimer::~RecurrentTimer() {
{
std::scoped_lock<std::mutex> lockGuard(mLock);
mStopRequested = true;
}
mCond.notify_one();
mStopRequested = true;
mLooper->removeMessages(mHandler);
mLooper->wake();
if (mThread.joinable()) {
mThread.join();
}
}
void RecurrentTimer::registerTimerCallback(int64_t intervalInNano,
int RecurrentTimer::getCallbackIdLocked(std::shared_ptr<RecurrentTimer::Callback> callback) {
const auto& it = mIdByCallback.find(callback);
if (it != mIdByCallback.end()) {
return it->second;
}
return INVALID_ID;
}
void RecurrentTimer::registerTimerCallback(int64_t intervalInNanos,
std::shared_ptr<RecurrentTimer::Callback> callback) {
{
std::scoped_lock<std::mutex> lockGuard(mLock);
int callbackId = getCallbackIdLocked(callback);
if (callbackId == INVALID_ID) {
callbackId = mCallbackId++;
mIdByCallback.insert({callback, callbackId});
} else {
ALOGI("Replacing an existing timer callback with a new interval, current: %" PRId64
" ns, new: %" PRId64 " ns",
mCallbackInfoById[callbackId]->intervalInNanos, intervalInNanos);
mLooper->removeMessages(mHandler, callbackId);
}
// Aligns the nextTime to multiply of interval.
int64_t nextTime = ceil(uptimeNanos() / intervalInNano) * intervalInNano;
int64_t nextTimeInNanos = ceil(uptimeNanos() / intervalInNanos) * intervalInNanos;
std::unique_ptr<CallbackInfo> info = std::make_unique<CallbackInfo>();
info->callback = callback;
info->interval = intervalInNano;
info->nextTime = nextTime;
info->intervalInNanos = intervalInNanos;
info->nextTimeInNanos = nextTimeInNanos;
mCallbackInfoById.insert({callbackId, std::move(info)});
auto it = mCallbacks.find(callback);
if (it != mCallbacks.end()) {
ALOGI("Replacing an existing timer callback with a new interval, current: %" PRId64
" ns, new: %" PRId64 " ns",
it->second->interval, intervalInNano);
markOutdatedLocked(it->second);
}
mCallbacks[callback] = info.get();
mCallbackQueue.push_back(std::move(info));
// Insert the last element into the heap.
std::push_heap(mCallbackQueue.begin(), mCallbackQueue.end(), CallbackInfo::cmp);
mLooper->sendMessageAtTime(nextTimeInNanos, mHandler, Message(callbackId));
}
mCond.notify_one();
}
void RecurrentTimer::unregisterTimerCallback(std::shared_ptr<RecurrentTimer::Callback> callback) {
{
std::scoped_lock<std::mutex> lockGuard(mLock);
auto it = mCallbacks.find(callback);
if (it == mCallbacks.end()) {
int callbackId = getCallbackIdLocked(callback);
if (callbackId == INVALID_ID) {
ALOGE("No event found to unregister");
return;
}
markOutdatedLocked(it->second);
mCallbacks.erase(it);
}
mCond.notify_one();
}
void RecurrentTimer::markOutdatedLocked(RecurrentTimer::CallbackInfo* info) {
info->outdated = true;
info->callback = nullptr;
// Make sure the first element is always valid.
removeInvalidCallbackLocked();
}
void RecurrentTimer::removeInvalidCallbackLocked() {
while (mCallbackQueue.size() != 0 && mCallbackQueue[0]->outdated) {
std::pop_heap(mCallbackQueue.begin(), mCallbackQueue.end(), CallbackInfo::cmp);
mCallbackQueue.pop_back();
mLooper->removeMessages(mHandler, callbackId);
mCallbackInfoById.erase(callbackId);
mIdByCallback.erase(callback);
}
}
std::shared_ptr<RecurrentTimer::Callback> RecurrentTimer::getNextCallbackLocked(int64_t now) {
std::pop_heap(mCallbackQueue.begin(), mCallbackQueue.end(), CallbackInfo::cmp);
auto& callbackInfo = mCallbackQueue[mCallbackQueue.size() - 1];
auto nextCallback = callbackInfo->callback;
// intervalCount is the number of interval we have to advance until we pass now.
size_t intervalCount = (now - callbackInfo->nextTime) / callbackInfo->interval + 1;
callbackInfo->nextTime += intervalCount * callbackInfo->interval;
std::push_heap(mCallbackQueue.begin(), mCallbackQueue.end(), CallbackInfo::cmp);
void RecurrentTimer::handleMessage(const Message& message) {
std::shared_ptr<RecurrentTimer::Callback> callback;
{
std::scoped_lock<std::mutex> lockGuard(mLock);
// Make sure the first element is always valid.
removeInvalidCallbackLocked();
int callbackId = message.what;
return nextCallback;
}
void RecurrentTimer::loop() {
std::vector<std::shared_ptr<Callback>> callbacksToRun;
while (true) {
{
std::unique_lock<std::mutex> uniqueLock(mLock);
ScopedLockAssertion lockAssertion(mLock);
// Wait until the timer exits or we have at least one recurrent callback.
mCond.wait(uniqueLock, [this] {
ScopedLockAssertion lockAssertion(mLock);
return mStopRequested || mCallbackQueue.size() != 0;
});
int64_t interval;
if (mStopRequested) {
return;
}
// The first element is the nearest next event.
int64_t nextTime = mCallbackQueue[0]->nextTime;
int64_t now = uptimeNanos();
if (nextTime > now) {
interval = nextTime - now;
} else {
interval = 0;
}
// Wait for the next event or the timer exits.
if (mCond.wait_for(uniqueLock, std::chrono::nanoseconds(interval), [this] {
ScopedLockAssertion lockAssertion(mLock);
return mStopRequested;
})) {
return;
}
now = uptimeNanos();
callbacksToRun.clear();
while (mCallbackQueue.size() > 0) {
int64_t nextTime = mCallbackQueue[0]->nextTime;
if (nextTime > now) {
break;
}
callbacksToRun.push_back(getNextCallbackLocked(now));
}
auto it = mCallbackInfoById.find(callbackId);
if (it == mCallbackInfoById.end()) {
ALOGW("The event for callback ID: %d is outdated, ignore", callbackId);
return;
}
// Do not execute the callback while holding the lock.
for (size_t i = 0; i < callbacksToRun.size(); i++) {
(*callbacksToRun[i])();
}
CallbackInfo* callbackInfo = it->second.get();
callback = callbackInfo->callback;
int64_t nowNanos = uptimeNanos();
// intervalCount is the number of interval we have to advance until we pass now.
size_t intervalCount =
(nowNanos - callbackInfo->nextTimeInNanos) / callbackInfo->intervalInNanos + 1;
callbackInfo->nextTimeInNanos += intervalCount * callbackInfo->intervalInNanos;
mLooper->sendMessageAtTime(callbackInfo->nextTimeInNanos, mHandler, Message(callbackId));
}
(*callback)();
}
bool RecurrentTimer::CallbackInfo::cmp(const std::unique_ptr<RecurrentTimer::CallbackInfo>& lhs,
const std::unique_ptr<RecurrentTimer::CallbackInfo>& rhs) {
return lhs->nextTime > rhs->nextTime;
void RecurrentMessageHandler::handleMessage(const Message& message) {
mTimer->handleMessage(message);
}
} // namespace vehicle

View File

@@ -60,9 +60,14 @@ class RecurrentTimerTest : public testing::Test {
mCallbacks.clear();
}
size_t countTimerCallbackQueue(RecurrentTimer* timer) {
size_t countCallbackInfoById(RecurrentTimer* timer) {
std::scoped_lock<std::mutex> lockGuard(timer->mLock);
return timer->mCallbackQueue.size();
return timer->mCallbackInfoById.size();
}
size_t countIdByCallback(RecurrentTimer* timer) {
std::scoped_lock<std::mutex> lockGuard(timer->mLock);
return timer->mIdByCallback.size();
}
private:
@@ -109,6 +114,9 @@ TEST_F(RecurrentTimerTest, testRegisterUnregisterRegister) {
<< "Not enough callbacks called before timeout";
timer.unregisterTimerCallback(action);
ASSERT_EQ(countCallbackInfoById(&timer), 0u);
ASSERT_EQ(countIdByCallback(&timer), 0u);
}
TEST_F(RecurrentTimerTest, testDestroyTimerWithCallback) {
@@ -198,8 +206,8 @@ TEST_F(RecurrentTimerTest, testRegisterSameCallbackMultipleTimes) {
timer.unregisterTimerCallback(action);
// Make sure there is no item in the callback queue.
ASSERT_EQ(countTimerCallbackQueue(&timer), static_cast<size_t>(0));
ASSERT_EQ(countCallbackInfoById(&timer), 0u);
ASSERT_EQ(countIdByCallback(&timer), 0u);
}
TEST_F(RecurrentTimerTest, testRegisterCallbackMultipleTimesNoDeadLock) {

View File

@@ -59,6 +59,7 @@ using ::aidl::android::hardware::automotive::vehicle::SetValueResult;
using ::aidl::android::hardware::automotive::vehicle::SetValueResults;
using ::aidl::android::hardware::automotive::vehicle::StatusCode;
using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions;
using ::aidl::android::hardware::automotive::vehicle::VehicleAreaConfig;
using ::aidl::android::hardware::automotive::vehicle::VehicleAreaWindow;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfigs;