/* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include // NOLINT #include // NOLINT #include // NOLINT namespace android { namespace frameworks { namespace automotive { namespace vhal { namespace aidl_test { using ::android::hardware::automotive::vehicle::toInt; using ::aidl::android::hardware::automotive::vehicle::BnVehicle; using ::aidl::android::hardware::automotive::vehicle::GetValueRequest; using ::aidl::android::hardware::automotive::vehicle::GetValueRequests; using ::aidl::android::hardware::automotive::vehicle::GetValueResult; using ::aidl::android::hardware::automotive::vehicle::GetValueResults; using ::aidl::android::hardware::automotive::vehicle::HasSupportedValueInfo; using ::aidl::android::hardware::automotive::vehicle::IVehicle; using ::aidl::android::hardware::automotive::vehicle::IVehicleCallback; using ::aidl::android::hardware::automotive::vehicle::MinMaxSupportedValueResult; using ::aidl::android::hardware::automotive::vehicle::MinMaxSupportedValueResults; using ::aidl::android::hardware::automotive::vehicle::PropIdAreaId; using ::aidl::android::hardware::automotive::vehicle::RawPropValues; using ::aidl::android::hardware::automotive::vehicle::SetValueRequest; using ::aidl::android::hardware::automotive::vehicle::SetValueRequests; 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::SupportedValuesListResult; using ::aidl::android::hardware::automotive::vehicle::SupportedValuesListResults; using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig; using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfigs; using ::aidl::android::hardware::automotive::vehicle::VehiclePropError; using ::aidl::android::hardware::automotive::vehicle::VehiclePropErrors; using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyAccess; using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus; using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue; using ::aidl::android::hardware::automotive::vehicle::VehiclePropValues; using ::ndk::ScopedAStatus; using ::ndk::SharedRefBase; using ::testing::Gt; using ::testing::UnorderedElementsAre; class MockVhal final : public BnVehicle { public: using CallbackType = std::shared_ptr; ~MockVhal() { std::unique_lock lk(mLock); mCv.wait_for(lk, std::chrono::milliseconds(1000), [this] { return mThreadCount == 0; }); } ScopedAStatus getAllPropConfigs(VehiclePropConfigs* returnConfigs) override { if (mStatus != StatusCode::OK) { return ScopedAStatus::fromServiceSpecificError(toInt(mStatus)); } returnConfigs->payloads = mPropConfigs; return ScopedAStatus::ok(); } ScopedAStatus getValues(const CallbackType& callback, const GetValueRequests& requests) override { mGetValueRequests = requests.payloads; if (mStatus != StatusCode::OK) { return ScopedAStatus::fromServiceSpecificError(toInt(mStatus)); } if (mWaitTimeInMs == 0) { callback->onGetValues(GetValueResults{.payloads = mGetValueResults}); } else { mThreadCount++; std::thread t([this, callback]() { std::this_thread::sleep_for(std::chrono::milliseconds(mWaitTimeInMs)); callback->onGetValues(GetValueResults{.payloads = mGetValueResults}); mThreadCount--; mCv.notify_one(); }); // Detach the thread here so we do not have to maintain the thread object. mThreadCount // and mCv make sure we wait for all threads to end before we exit. t.detach(); } return ScopedAStatus::ok(); } ScopedAStatus setValues(const CallbackType& callback, const SetValueRequests& requests) override { mSetValueRequests = requests.payloads; if (mStatus != StatusCode::OK) { return ScopedAStatus::fromServiceSpecificError(toInt(mStatus)); } if (mWaitTimeInMs == 0) { callback->onSetValues(SetValueResults{.payloads = mSetValueResults}); } else { mThreadCount++; std::thread t([this, callback]() { std::this_thread::sleep_for(std::chrono::milliseconds(mWaitTimeInMs)); callback->onSetValues(SetValueResults{.payloads = mSetValueResults}); mThreadCount--; mCv.notify_one(); }); // Detach the thread here so we do not have to maintain the thread object. mThreadCount // and mCv make sure we wait for all threads to end before we exit. t.detach(); } return ScopedAStatus::ok(); } ScopedAStatus getPropConfigs(const std::vector& props, VehiclePropConfigs* returnConfigs) override { mGetPropConfigPropIds = props; if (mStatus != StatusCode::OK) { return ScopedAStatus::fromServiceSpecificError(toInt(mStatus)); } returnConfigs->payloads = mPropConfigs; return ScopedAStatus::ok(); } ScopedAStatus subscribe(const CallbackType& callback, const std::vector& options, [[maybe_unused]] int32_t maxSharedMemoryFileCount) override { mSubscriptionCallback = callback; mSubscriptionOptions = options; if (mStatus != StatusCode::OK) { return ScopedAStatus::fromServiceSpecificError(toInt(mStatus)); } return ScopedAStatus::ok(); } ScopedAStatus unsubscribe([[maybe_unused]] const CallbackType& callback, const std::vector& propIds) override { mUnsubscribedPropIds = propIds; if (mStatus != StatusCode::OK) { return ScopedAStatus::fromServiceSpecificError(toInt(mStatus)); } return ScopedAStatus::ok(); } ScopedAStatus returnSharedMemory([[maybe_unused]] const CallbackType& callback, [[maybe_unused]] int64_t sharedMemoryId) override { return ScopedAStatus::ok(); } MOCK_METHOD(ScopedAStatus, getSupportedValuesLists, (const std::vector&, SupportedValuesListResults*), (override)); MOCK_METHOD(ScopedAStatus, getMinMaxSupportedValue, (const std::vector&, MinMaxSupportedValueResults*), (override)); ScopedAStatus registerSupportedValueChangeCallback(const std::shared_ptr&, const std::vector&) { return ScopedAStatus::ok(); } ScopedAStatus unregisterSupportedValueChangeCallback(const std::shared_ptr&, const std::vector&) { return ScopedAStatus::ok(); } // Test Functions void setGetValueResults(std::vector results) { mGetValueResults = results; } std::vector getGetValueRequests() { return mGetValueRequests; } void setSetValueResults(std::vector results) { mSetValueResults = results; } std::vector getSetValueRequests() { return mSetValueRequests; } void setWaitTimeInMs(int64_t waitTimeInMs) { mWaitTimeInMs = waitTimeInMs; } void setStatus(StatusCode status) { mStatus = status; } void setPropConfigs(std::vector configs) { mPropConfigs = configs; } std::vector getGetPropConfigPropIds() { return mGetPropConfigPropIds; } std::vector getSubscriptionOptions() { return mSubscriptionOptions; } void triggerOnPropertyEvent(const std::vector& values) { VehiclePropValues propValues = { .payloads = values, }; mSubscriptionCallback->onPropertyEvent(propValues, /*sharedMemoryCount=*/0); } void triggerSetErrorEvent(const std::vector& errors) { VehiclePropErrors propErrors = { .payloads = errors, }; mSubscriptionCallback->onPropertySetError(propErrors); } std::vector getUnsubscribedPropIds() { return mUnsubscribedPropIds; } void resetUnsubscribedPropIds() { mUnsubscribedPropIds.clear(); } private: std::mutex mLock; std::vector mGetValueResults; std::vector mGetValueRequests; std::vector mSetValueResults; std::vector mSetValueRequests; std::vector mPropConfigs; std::vector mGetPropConfigPropIds; int64_t mWaitTimeInMs = 0; StatusCode mStatus = StatusCode::OK; std::condition_variable mCv; std::atomic mThreadCount = 0; CallbackType mSubscriptionCallback; std::vector mSubscriptionOptions; std::vector mUnsubscribedPropIds; }; class MockSubscriptionCallback : public ISubscriptionCallback { public: void onPropertyEvent(const std::vector>& values) override { for (const auto& value : values) { mEventPropIds.push_back(value->getPropId()); } } void onPropertySetError(const std::vector& errors) override { mErrors = errors; } std::vector getEventPropIds() { return mEventPropIds; } std::vector getErrors() { return mErrors; } private: std::vector mEventPropIds; std::vector mErrors; }; class AidlVhalClientTest : public ::testing::Test { protected: class TestLinkUnlinkImpl final : public AidlVhalClient::ILinkUnlinkToDeath { public: binder_status_t linkToDeath([[maybe_unused]] AIBinder* binder, [[maybe_unused]] AIBinder_DeathRecipient* recipient, void* cookie) override { mCookie = cookie; mDeathRecipient = recipient; return STATUS_OK; } void deleteDeathRecipient(AIBinder_DeathRecipient* recipient) override { if (mDeathRecipient != recipient) { return; } if (mOnBinderDiedOngoing) { // If onBinderDied is ongoing, we do not call unlink until the function // returns. mToUnlink = true; return; } triggerBinderUnlinked(); } void setOnUnlinked([[maybe_unused]] AIBinder_DeathRecipient* recipient, AIBinder_DeathRecipient_onBinderUnlinked onUnlinked) override { mOnUnlinked = onUnlinked; } void* getCookie() { return mCookie; } void triggerBinderUnlinked() { if (mDeathRecipient == nullptr) { // Already unlinked, do nothing. return; } (*mOnUnlinked)(mCookie); mDeathRecipient = nullptr; } void triggerBinderDied() { mOnBinderDiedOngoing = true; } void endBinderDied() { mOnBinderDiedOngoing = false; if (mToUnlink) { triggerBinderUnlinked(); } } private: void* mCookie; AIBinder_DeathRecipient_onBinderUnlinked mOnUnlinked; AIBinder_DeathRecipient* mDeathRecipient; bool mOnBinderDiedOngoing = false; bool mToUnlink = false; }; constexpr static int32_t TEST_PROP_ID = 1; constexpr static int32_t TEST_AREA_ID = 2; constexpr static int32_t TEST_PROP_ID_2 = 3; constexpr static int32_t TEST_AREA_ID_2 = 4; constexpr static VehiclePropertyAccess TEST_GLOBAL_ACCESS = VehiclePropertyAccess::READ_WRITE; constexpr static VehiclePropertyAccess TEST_AREA_ACCESS = VehiclePropertyAccess::READ; constexpr static int64_t TEST_TIMEOUT_IN_MS = 100; void SetUp() override { mVhal = SharedRefBase::make(); auto impl = std::make_unique(); // We are sure impl would be alive when we use mLinkUnlinkImpl. mLinkUnlinkImpl = impl.get(); mVhalClient = std::unique_ptr( new AidlVhalClient(mVhal, TEST_TIMEOUT_IN_MS, std::move(impl))); mVhalClient->linkToDeath(); } AidlVhalClient* getClient() { return mVhalClient.get(); } void resetClient() { mVhalClient.reset(); } MockVhal* getVhal() { return mVhal.get(); } void triggerBinderDied() { // We cannot directly trigger onBinderDied inside mLinkUnlinkImpl because the recipient // implementation is private. mLinkUnlinkImpl->triggerBinderDied(); AidlVhalClient::onBinderDied(mLinkUnlinkImpl->getCookie()); mLinkUnlinkImpl->endBinderDied(); } size_t countOnBinderDiedCallbacks() { return mVhalClient->countOnBinderDiedCallbacks(); } void setTestRemoteInterfaceVersion(int32_t version) { mVhalClient->setTestRemoteInterfaceVersion(version); } private: std::shared_ptr mVhal; std::unique_ptr mVhalClient; TestLinkUnlinkImpl* mLinkUnlinkImpl; }; TEST_F(AidlVhalClientTest, testIsAidl) { ASSERT_TRUE(getClient()->isAidlVhal()); } TEST_F(AidlVhalClientTest, testGetValueNormal) { VehiclePropValue testProp{ .prop = TEST_PROP_ID, .areaId = TEST_AREA_ID, }; getVhal()->setWaitTimeInMs(10); getVhal()->setGetValueResults({ GetValueResult{ .requestId = 0, .status = StatusCode::OK, .prop = VehiclePropValue{ .prop = TEST_PROP_ID, .areaId = TEST_AREA_ID, .value = RawPropValues{ .int32Values = {1}, }, }, }, }); AidlHalPropValue propValue(TEST_PROP_ID, TEST_AREA_ID); std::mutex lock; std::condition_variable cv; VhalClientResult> result; VhalClientResult>* resultPtr = &result; bool gotResult = false; bool* gotResultPtr = &gotResult; auto callback = std::make_shared( [&lock, &cv, resultPtr, gotResultPtr](VhalClientResult> r) { { std::lock_guard lockGuard(lock); *resultPtr = std::move(r); *gotResultPtr = true; } cv.notify_one(); }); getClient()->getValue(propValue, callback); std::unique_lock lk(lock); cv.wait_for(lk, std::chrono::milliseconds(1000), [&gotResult] { return gotResult; }); ASSERT_TRUE(gotResult); ASSERT_EQ(getVhal()->getGetValueRequests(), std::vector({GetValueRequest{.requestId = 0, .prop = testProp}})); ASSERT_TRUE(result.ok()); auto gotValue = std::move(result.value()); ASSERT_EQ(gotValue->getPropId(), TEST_PROP_ID); ASSERT_EQ(gotValue->getAreaId(), TEST_AREA_ID); ASSERT_EQ(gotValue->getStatus(), VehiclePropertyStatus::AVAILABLE); ASSERT_EQ(gotValue->getInt32Values(), std::vector({1})); } TEST_F(AidlVhalClientTest, testGetValueSync) { VehiclePropValue testProp{ .prop = TEST_PROP_ID, .areaId = TEST_AREA_ID, }; getVhal()->setWaitTimeInMs(10); getVhal()->setGetValueResults({ GetValueResult{ .requestId = 0, .status = StatusCode::OK, .prop = VehiclePropValue{ .prop = TEST_PROP_ID, .areaId = TEST_AREA_ID, .value = RawPropValues{ .int32Values = {1}, }, }, }, }); AidlHalPropValue propValue(TEST_PROP_ID, TEST_AREA_ID); VhalClientResult> result = getClient()->getValueSync(propValue); ASSERT_EQ(getVhal()->getGetValueRequests(), std::vector({GetValueRequest{.requestId = 0, .prop = testProp}})); ASSERT_TRUE(result.ok()); auto gotValue = std::move(result.value()); ASSERT_EQ(gotValue->getPropId(), TEST_PROP_ID); ASSERT_EQ(gotValue->getAreaId(), TEST_AREA_ID); ASSERT_EQ(gotValue->getStatus(), VehiclePropertyStatus::AVAILABLE); ASSERT_EQ(gotValue->getInt32Values(), std::vector({1})); } TEST_F(AidlVhalClientTest, testGetValueUnavailableStatusSync) { VehiclePropValue testProp{ .prop = TEST_PROP_ID, .areaId = TEST_AREA_ID, }; getVhal()->setWaitTimeInMs(10); getVhal()->setGetValueResults({ GetValueResult{ .requestId = 0, .status = StatusCode::OK, .prop = VehiclePropValue{ .prop = TEST_PROP_ID, .areaId = TEST_AREA_ID, .status = VehiclePropertyStatus::UNAVAILABLE, }, }, }); AidlHalPropValue propValue(TEST_PROP_ID, TEST_AREA_ID); VhalClientResult> result = getClient()->getValueSync(propValue); ASSERT_EQ(getVhal()->getGetValueRequests(), std::vector({GetValueRequest{.requestId = 0, .prop = testProp}})); ASSERT_TRUE(result.ok()); auto gotValue = std::move(result.value()); ASSERT_EQ(gotValue->getPropId(), TEST_PROP_ID); ASSERT_EQ(gotValue->getAreaId(), TEST_AREA_ID); ASSERT_EQ(gotValue->getStatus(), VehiclePropertyStatus::UNAVAILABLE); } TEST_F(AidlVhalClientTest, testGetValueTimeout) { VehiclePropValue testProp{ .prop = TEST_PROP_ID, .areaId = TEST_AREA_ID, }; // The request will time-out before the response. getVhal()->setWaitTimeInMs(200); getVhal()->setGetValueResults({ GetValueResult{ .requestId = 0, .status = StatusCode::OK, .prop = VehiclePropValue{ .prop = TEST_PROP_ID, .areaId = TEST_AREA_ID, .value = RawPropValues{ .int32Values = {1}, }, }, }, }); AidlHalPropValue propValue(TEST_PROP_ID, TEST_AREA_ID); std::mutex lock; std::condition_variable cv; VhalClientResult> result; VhalClientResult>* resultPtr = &result; bool gotResult = false; bool* gotResultPtr = &gotResult; auto callback = std::make_shared( [&lock, &cv, resultPtr, gotResultPtr](VhalClientResult> r) { { std::lock_guard lockGuard(lock); *resultPtr = std::move(r); *gotResultPtr = true; } cv.notify_one(); }); getClient()->getValue(propValue, callback); std::unique_lock lk(lock); cv.wait_for(lk, std::chrono::milliseconds(1000), [&gotResult] { return gotResult; }); ASSERT_TRUE(gotResult); ASSERT_EQ(getVhal()->getGetValueRequests(), std::vector({GetValueRequest{.requestId = 0, .prop = testProp}})); ASSERT_FALSE(result.ok()); ASSERT_EQ(result.error().code(), ErrorCode::TIMEOUT); } TEST_F(AidlVhalClientTest, testGetValueErrorStatus) { VehiclePropValue testProp{ .prop = TEST_PROP_ID, .areaId = TEST_AREA_ID, }; getVhal()->setStatus(StatusCode::INTERNAL_ERROR); AidlHalPropValue propValue(TEST_PROP_ID, TEST_AREA_ID); VhalClientResult> result; VhalClientResult>* resultPtr = &result; getClient()->getValue(propValue, std::make_shared( [resultPtr](VhalClientResult> r) { *resultPtr = std::move(r); })); ASSERT_EQ(getVhal()->getGetValueRequests(), std::vector({GetValueRequest{.requestId = 0, .prop = testProp}})); ASSERT_FALSE(result.ok()); ASSERT_EQ(result.error().code(), ErrorCode::INTERNAL_ERROR_FROM_VHAL); } TEST_F(AidlVhalClientTest, testGetValueNonOkayResult) { VehiclePropValue testProp{ .prop = TEST_PROP_ID, .areaId = TEST_AREA_ID, }; getVhal()->setGetValueResults({ GetValueResult{ .requestId = 0, .status = StatusCode::INTERNAL_ERROR, }, }); AidlHalPropValue propValue(TEST_PROP_ID, TEST_AREA_ID); VhalClientResult> result; VhalClientResult>* resultPtr = &result; getClient()->getValue(propValue, std::make_shared( [resultPtr](VhalClientResult> r) { *resultPtr = std::move(r); })); ASSERT_EQ(getVhal()->getGetValueRequests(), std::vector({GetValueRequest{.requestId = 0, .prop = testProp}})); ASSERT_FALSE(result.ok()); ASSERT_EQ(result.error().code(), ErrorCode::INTERNAL_ERROR_FROM_VHAL); } TEST_F(AidlVhalClientTest, testGetValueIgnoreInvalidRequestId) { VehiclePropValue testProp{ .prop = TEST_PROP_ID, .areaId = TEST_AREA_ID, }; getVhal()->setGetValueResults({ GetValueResult{ .requestId = 0, .status = StatusCode::OK, .prop = VehiclePropValue{ .prop = TEST_PROP_ID, .areaId = TEST_AREA_ID, .value = RawPropValues{ .int32Values = {1}, }, }, }, // This result has invalid request ID and should be ignored. GetValueResult{ .requestId = 1, .status = StatusCode::INTERNAL_ERROR, }, }); AidlHalPropValue propValue(TEST_PROP_ID, TEST_AREA_ID); VhalClientResult> result; VhalClientResult>* resultPtr = &result; getClient()->getValue(propValue, std::make_shared( [resultPtr](VhalClientResult> r) { *resultPtr = std::move(r); })); ASSERT_EQ(getVhal()->getGetValueRequests(), std::vector({GetValueRequest{.requestId = 0, .prop = testProp}})); ASSERT_TRUE(result.ok()); auto gotValue = std::move(result.value()); ASSERT_EQ(gotValue->getPropId(), TEST_PROP_ID); ASSERT_EQ(gotValue->getAreaId(), TEST_AREA_ID); ASSERT_EQ(gotValue->getInt32Values(), std::vector({1})); } TEST_F(AidlVhalClientTest, testSetValueNormal) { VehiclePropValue testProp{ .prop = TEST_PROP_ID, .areaId = TEST_AREA_ID, }; getVhal()->setWaitTimeInMs(10); getVhal()->setSetValueResults({ SetValueResult{ .requestId = 0, .status = StatusCode::OK, }, }); AidlHalPropValue propValue(TEST_PROP_ID, TEST_AREA_ID); std::mutex lock; std::condition_variable cv; VhalClientResult result; VhalClientResult* resultPtr = &result; bool gotResult = false; bool* gotResultPtr = &gotResult; auto callback = std::make_shared( [&lock, &cv, resultPtr, gotResultPtr](VhalClientResult r) { { std::lock_guard lockGuard(lock); *resultPtr = std::move(r); *gotResultPtr = true; } cv.notify_one(); }); getClient()->setValue(propValue, callback); std::unique_lock lk(lock); cv.wait_for(lk, std::chrono::milliseconds(1000), [&gotResult] { return gotResult; }); ASSERT_TRUE(gotResult); ASSERT_EQ(getVhal()->getSetValueRequests(), std::vector({SetValueRequest{.requestId = 0, .value = testProp}})); ASSERT_TRUE(result.ok()); } TEST_F(AidlVhalClientTest, testSetValueSync) { VehiclePropValue testProp{ .prop = TEST_PROP_ID, .areaId = TEST_AREA_ID, }; getVhal()->setWaitTimeInMs(10); getVhal()->setSetValueResults({ SetValueResult{ .requestId = 0, .status = StatusCode::OK, }, }); AidlHalPropValue propValue(TEST_PROP_ID, TEST_AREA_ID); VhalClientResult result = getClient()->setValueSync(propValue); ASSERT_EQ(getVhal()->getSetValueRequests(), std::vector({SetValueRequest{.requestId = 0, .value = testProp}})); ASSERT_TRUE(result.ok()); } TEST_F(AidlVhalClientTest, testSetValueTimeout) { VehiclePropValue testProp{ .prop = TEST_PROP_ID, .areaId = TEST_AREA_ID, }; // The request will time-out before the response. getVhal()->setWaitTimeInMs(200); getVhal()->setSetValueResults({ SetValueResult{ .requestId = 0, .status = StatusCode::OK, }, }); AidlHalPropValue propValue(TEST_PROP_ID, TEST_AREA_ID); std::mutex lock; std::condition_variable cv; VhalClientResult result; VhalClientResult* resultPtr = &result; bool gotResult = false; bool* gotResultPtr = &gotResult; auto callback = std::make_shared( [&lock, &cv, resultPtr, gotResultPtr](VhalClientResult r) { { std::lock_guard lockGuard(lock); *resultPtr = std::move(r); *gotResultPtr = true; } cv.notify_one(); }); getClient()->setValue(propValue, callback); std::unique_lock lk(lock); cv.wait_for(lk, std::chrono::milliseconds(1000), [&gotResult] { return gotResult; }); ASSERT_TRUE(gotResult); ASSERT_EQ(getVhal()->getSetValueRequests(), std::vector({SetValueRequest{.requestId = 0, .value = testProp}})); ASSERT_FALSE(result.ok()); ASSERT_EQ(result.error().code(), ErrorCode::TIMEOUT); } TEST_F(AidlVhalClientTest, testSetValueErrorStatus) { VehiclePropValue testProp{ .prop = TEST_PROP_ID, .areaId = TEST_AREA_ID, }; getVhal()->setStatus(StatusCode::INTERNAL_ERROR); AidlHalPropValue propValue(TEST_PROP_ID, TEST_AREA_ID); VhalClientResult result; VhalClientResult* resultPtr = &result; getClient()->setValue(propValue, std::make_shared( [resultPtr](VhalClientResult r) { *resultPtr = std::move(r); })); ASSERT_EQ(getVhal()->getSetValueRequests(), std::vector({SetValueRequest{.requestId = 0, .value = testProp}})); ASSERT_FALSE(result.ok()); ASSERT_EQ(result.error().code(), ErrorCode::INTERNAL_ERROR_FROM_VHAL); } TEST_F(AidlVhalClientTest, testSetValueNonOkayResult) { VehiclePropValue testProp{ .prop = TEST_PROP_ID, .areaId = TEST_AREA_ID, }; getVhal()->setSetValueResults({ SetValueResult{ .requestId = 0, .status = StatusCode::INTERNAL_ERROR, }, }); AidlHalPropValue propValue(TEST_PROP_ID, TEST_AREA_ID); VhalClientResult result; VhalClientResult* resultPtr = &result; getClient()->setValue(propValue, std::make_shared( [resultPtr](VhalClientResult r) { *resultPtr = std::move(r); })); ASSERT_EQ(getVhal()->getSetValueRequests(), std::vector({SetValueRequest{.requestId = 0, .value = testProp}})); ASSERT_FALSE(result.ok()); ASSERT_EQ(result.error().code(), ErrorCode::INTERNAL_ERROR_FROM_VHAL); } TEST_F(AidlVhalClientTest, testSetValueIgnoreInvalidRequestId) { VehiclePropValue testProp{ .prop = TEST_PROP_ID, .areaId = TEST_AREA_ID, }; getVhal()->setSetValueResults({ SetValueResult{ .requestId = 0, .status = StatusCode::OK, }, // This result has invalid request ID and should be ignored. SetValueResult{ .requestId = 1, .status = StatusCode::INTERNAL_ERROR, }, }); AidlHalPropValue propValue(TEST_PROP_ID, TEST_AREA_ID); VhalClientResult result; VhalClientResult* resultPtr = &result; getClient()->setValue(propValue, std::make_shared( [resultPtr](VhalClientResult r) { *resultPtr = std::move(r); })); ASSERT_EQ(getVhal()->getSetValueRequests(), std::vector({SetValueRequest{.requestId = 0, .value = testProp}})); ASSERT_TRUE(result.ok()); } TEST_F(AidlVhalClientTest, testAddOnBinderDiedCallback) { struct Result { bool callbackOneCalled = false; bool callbackTwoCalled = false; } result; getClient()->addOnBinderDiedCallback(std::make_shared( [&result] { result.callbackOneCalled = true; })); getClient()->addOnBinderDiedCallback(std::make_shared( [&result] { result.callbackTwoCalled = true; })); triggerBinderDied(); ASSERT_TRUE(result.callbackOneCalled); ASSERT_TRUE(result.callbackTwoCalled); ASSERT_EQ(countOnBinderDiedCallbacks(), static_cast(2)); } TEST_F(AidlVhalClientTest, testOnBinderDied_noDeadLock) { getClient()->addOnBinderDiedCallback( std::make_shared([this] { // This will trigger the destructor for AidlVhalClient. This must not cause dead // lock. resetClient(); })); triggerBinderDied(); } TEST_F(AidlVhalClientTest, testRemoveOnBinderDiedCallback) { struct Result { bool callbackOneCalled = false; bool callbackTwoCalled = false; } result; auto callbackOne = std::make_shared( [&result] { result.callbackOneCalled = true; }); auto callbackTwo = std::make_shared( [&result] { result.callbackTwoCalled = true; }); getClient()->addOnBinderDiedCallback(callbackOne); getClient()->addOnBinderDiedCallback(callbackTwo); getClient()->removeOnBinderDiedCallback(callbackOne); triggerBinderDied(); ASSERT_FALSE(result.callbackOneCalled); ASSERT_TRUE(result.callbackTwoCalled); ASSERT_EQ(countOnBinderDiedCallbacks(), static_cast(1)); } TEST_F(AidlVhalClientTest, testGetAllPropConfigs) { getVhal()->setPropConfigs({ VehiclePropConfig{ .prop = TEST_PROP_ID, .access = TEST_GLOBAL_ACCESS, .areaConfigs = {{ .areaId = TEST_AREA_ID, .minInt32Value = 0, .maxInt32Value = 1, .supportVariableUpdateRate = true, .hasSupportedValueInfo = HasSupportedValueInfo{ .hasMinSupportedValue = true, .hasMaxSupportedValue = true, .hasSupportedValuesList = false, }, .supportedEnumValues = std::vector({1L, 2L, 3L}), }, { .areaId = TEST_AREA_ID_2, .access = TEST_AREA_ACCESS, .minInt32Value = 2, .maxInt32Value = 3, }}, }, VehiclePropConfig{ .prop = TEST_PROP_ID_2, }, }); auto result = getClient()->getAllPropConfigs(); ASSERT_TRUE(result.ok()); std::vector> configs = std::move(result.value()); ASSERT_EQ(configs.size(), static_cast(2)); ASSERT_EQ(configs[0]->getPropId(), TEST_PROP_ID); ASSERT_EQ(configs[0]->getAccess(), toInt(TEST_GLOBAL_ACCESS)); ASSERT_EQ(configs[0]->getAreaConfigSize(), static_cast(2)); const std::unique_ptr& areaConfig0 = configs[0]->getAreaConfigs()[0]; ASSERT_EQ(areaConfig0->getAreaId(), TEST_AREA_ID); ASSERT_EQ(areaConfig0->getAccess(), toInt(TEST_GLOBAL_ACCESS)); ASSERT_EQ(areaConfig0->getMinInt32Value(), 0); ASSERT_EQ(areaConfig0->getMaxInt32Value(), 1); ASSERT_TRUE(areaConfig0->isVariableUpdateRateSupported()); ASSERT_TRUE(areaConfig0->getHasSupportedValueInfo().has_value()); ASSERT_TRUE(areaConfig0->getHasSupportedValueInfo()->hasMinSupportedValue); ASSERT_TRUE(areaConfig0->getHasSupportedValueInfo()->hasMaxSupportedValue); ASSERT_FALSE(areaConfig0->getHasSupportedValueInfo()->hasSupportedValuesList); ASSERT_TRUE(areaConfig0->getSupportedEnumValues().has_value()); ASSERT_THAT(areaConfig0->getSupportedEnumValues().value(), ::testing::ElementsAre(1L, 2L, 3L)); const std::unique_ptr& areaConfig1 = configs[0]->getAreaConfigs()[1]; ASSERT_EQ(areaConfig1->getAreaId(), TEST_AREA_ID_2); ASSERT_EQ(areaConfig1->getAccess(), toInt(TEST_AREA_ACCESS)); ASSERT_EQ(areaConfig1->getMinInt32Value(), 2); ASSERT_EQ(areaConfig1->getMaxInt32Value(), 3); ASSERT_FALSE(areaConfig1->isVariableUpdateRateSupported()); ASSERT_FALSE(areaConfig1->getHasSupportedValueInfo().has_value()); ASSERT_FALSE(areaConfig1->getSupportedEnumValues().has_value()); ASSERT_EQ(configs[1]->getPropId(), TEST_PROP_ID_2); ASSERT_EQ(configs[1]->getAccess(), 0); ASSERT_EQ(configs[1]->getAreaConfigSize(), static_cast(1)); const std::unique_ptr& areaConfig2 = configs[1]->getAreaConfigs()[0]; ASSERT_EQ(areaConfig2->getAreaId(), 0); ASSERT_EQ(areaConfig2->getAccess(), 0); ASSERT_FALSE(areaConfig2->isVariableUpdateRateSupported()); } TEST_F(AidlVhalClientTest, testGetAllPropConfigsError) { getVhal()->setStatus(StatusCode::INTERNAL_ERROR); auto result = getClient()->getAllPropConfigs(); ASSERT_FALSE(result.ok()); ASSERT_EQ(result.error().code(), ErrorCode::INTERNAL_ERROR_FROM_VHAL); } TEST_F(AidlVhalClientTest, testGetPropConfigs) { getVhal()->setPropConfigs({ VehiclePropConfig{ .prop = TEST_PROP_ID, .access = TEST_GLOBAL_ACCESS, .areaConfigs = {{ .areaId = TEST_AREA_ID, .minInt32Value = 0, .maxInt32Value = 1, .supportVariableUpdateRate = true, }, { .areaId = TEST_AREA_ID_2, .access = TEST_AREA_ACCESS, .minInt32Value = 2, .maxInt32Value = 3, }}, }, VehiclePropConfig{ .prop = TEST_PROP_ID_2, }, }); std::vector propIds = {TEST_PROP_ID, TEST_PROP_ID_2}; auto result = getClient()->getPropConfigs(propIds); ASSERT_EQ(getVhal()->getGetPropConfigPropIds(), propIds); ASSERT_TRUE(result.ok()); std::vector> configs = std::move(result.value()); ASSERT_EQ(configs.size(), static_cast(2)); ASSERT_EQ(configs[0]->getPropId(), TEST_PROP_ID); ASSERT_EQ(configs[0]->getAccess(), toInt(TEST_GLOBAL_ACCESS)); ASSERT_EQ(configs[0]->getAreaConfigSize(), static_cast(2)); const std::unique_ptr& areaConfig0 = configs[0]->getAreaConfigs()[0]; ASSERT_EQ(areaConfig0->getAreaId(), TEST_AREA_ID); ASSERT_EQ(areaConfig0->getAccess(), toInt(TEST_GLOBAL_ACCESS)); ASSERT_EQ(areaConfig0->getMinInt32Value(), 0); ASSERT_EQ(areaConfig0->getMaxInt32Value(), 1); ASSERT_TRUE(areaConfig0->isVariableUpdateRateSupported()); const std::unique_ptr& areaConfig1 = configs[0]->getAreaConfigs()[1]; ASSERT_EQ(areaConfig1->getAreaId(), TEST_AREA_ID_2); ASSERT_EQ(areaConfig1->getAccess(), toInt(TEST_AREA_ACCESS)); ASSERT_EQ(areaConfig1->getMinInt32Value(), 2); ASSERT_EQ(areaConfig1->getMaxInt32Value(), 3); ASSERT_FALSE(areaConfig1->isVariableUpdateRateSupported()); ASSERT_EQ(configs[1]->getPropId(), TEST_PROP_ID_2); ASSERT_EQ(configs[1]->getAccess(), 0); ASSERT_EQ(configs[1]->getAreaConfigSize(), static_cast(1)); const std::unique_ptr& areaConfig2 = configs[1]->getAreaConfigs()[0]; ASSERT_EQ(areaConfig2->getAreaId(), 0); ASSERT_EQ(areaConfig2->getAccess(), 0); ASSERT_FALSE(areaConfig2->isVariableUpdateRateSupported()); } TEST_F(AidlVhalClientTest, testGetPropConfigsError) { getVhal()->setStatus(StatusCode::INTERNAL_ERROR); std::vector propIds = {TEST_PROP_ID, TEST_PROP_ID_2}; auto result = getClient()->getPropConfigs(propIds); ASSERT_FALSE(result.ok()); } TEST_F(AidlVhalClientTest, testSubscribe) { std::vector options = { { .propId = TEST_PROP_ID, .areaIds = {TEST_AREA_ID}, .sampleRate = 1.0, }, { .propId = TEST_PROP_ID_2, .sampleRate = 2.0, }, }; auto callback = std::make_shared(); auto subscriptionClient = getClient()->getSubscriptionClient(callback); auto result = subscriptionClient->subscribe(options); ASSERT_TRUE(result.ok()); ASSERT_EQ(getVhal()->getSubscriptionOptions(), options); getVhal()->triggerOnPropertyEvent(std::vector{ { .prop = TEST_PROP_ID, .areaId = TEST_AREA_ID, .value.int32Values = {1}, }, }); ASSERT_EQ(callback->getEventPropIds(), std::vector({TEST_PROP_ID})); getVhal()->triggerSetErrorEvent(std::vector({ { .propId = TEST_PROP_ID, .areaId = TEST_AREA_ID, .errorCode = StatusCode::INTERNAL_ERROR, }, })); auto errors = callback->getErrors(); ASSERT_EQ(errors.size(), static_cast(1)); ASSERT_EQ(errors[0].propId, TEST_PROP_ID); ASSERT_EQ(errors[0].areaId, TEST_AREA_ID); ASSERT_EQ(errors[0].status, StatusCode::INTERNAL_ERROR); } TEST_F(AidlVhalClientTest, testSubscribeError) { std::vector options = { { .propId = TEST_PROP_ID, .areaIds = {TEST_AREA_ID}, .sampleRate = 1.0, }, { .propId = TEST_PROP_ID_2, .sampleRate = 2.0, }, }; getVhal()->setStatus(StatusCode::INTERNAL_ERROR); auto callback = std::make_shared(); auto subscriptionClient = getClient()->getSubscriptionClient(callback); auto result = subscriptionClient->subscribe(options); ASSERT_FALSE(result.ok()); } TEST_F(AidlVhalClientTest, testUnubscribe) { auto callback = std::make_shared(); auto subscriptionClient = getClient()->getSubscriptionClient(callback); auto result = subscriptionClient->unsubscribe({TEST_PROP_ID}); ASSERT_TRUE(result.ok()); ASSERT_EQ(getVhal()->getUnsubscribedPropIds(), std::vector({TEST_PROP_ID})); } TEST_F(AidlVhalClientTest, testUnubscribeError) { getVhal()->setStatus(StatusCode::INTERNAL_ERROR); auto callback = std::make_shared(); auto subscriptionClient = getClient()->getSubscriptionClient(callback); auto result = subscriptionClient->unsubscribe({TEST_PROP_ID}); ASSERT_FALSE(result.ok()); } TEST_F(AidlVhalClientTest, testUnsubscribeAll) { std::vector options = { { .propId = TEST_PROP_ID, .areaIds = {TEST_AREA_ID}, .sampleRate = 1.0, }, { .propId = TEST_PROP_ID_2, .sampleRate = 2.0, }, }; auto callback = std::make_shared(); auto subscriptionClient = getClient()->getSubscriptionClient(callback); subscriptionClient->subscribe(options); subscriptionClient->unsubscribeAll(); ASSERT_THAT(getVhal()->getUnsubscribedPropIds(), UnorderedElementsAre(TEST_PROP_ID, TEST_PROP_ID_2)); getVhal()->resetUnsubscribedPropIds(); subscriptionClient->unsubscribeAll(); ASSERT_EQ(getVhal()->getUnsubscribedPropIds().size(), 0u); } TEST_F(AidlVhalClientTest, testUnsubscribeAll_AfterUnsubscribe) { std::vector options = { { .propId = TEST_PROP_ID, .areaIds = {TEST_AREA_ID}, .sampleRate = 1.0, }, { .propId = TEST_PROP_ID_2, .sampleRate = 2.0, }, }; auto callback = std::make_shared(); auto subscriptionClient = getClient()->getSubscriptionClient(callback); subscriptionClient->subscribe(options); subscriptionClient->unsubscribe({TEST_PROP_ID}); getVhal()->resetUnsubscribedPropIds(); subscriptionClient->unsubscribeAll(); ASSERT_THAT(getVhal()->getUnsubscribedPropIds(), UnorderedElementsAre(TEST_PROP_ID_2)); } TEST_F(AidlVhalClientTest, testGetRemoteInterfaceVersion) { // The AIDL VHAL should be v2 or higher. ASSERT_THAT(getClient()->getRemoteInterfaceVersion(), Gt(1)); } TEST_F(AidlVhalClientTest, testSubscribeOptionsBuilder) { auto optionsBuilder = SubscribeOptionsBuilder(TEST_PROP_ID); optionsBuilder.setSampleRate(1.23f); optionsBuilder.addAreaId(1); optionsBuilder.addAreaId(2); optionsBuilder.setResolution(2.34f); auto options = optionsBuilder.build(); ASSERT_EQ(options, (SubscribeOptions{ .propId = TEST_PROP_ID, .areaIds = {1, 2}, .sampleRate = 1.23f, .resolution = 2.34f, // VUR is true by default .enableVariableUpdateRate = true, })); } TEST_F(AidlVhalClientTest, testSubscribeOptionsBuilder_disableVur) { auto optionsBuilder = SubscribeOptionsBuilder(TEST_PROP_ID); optionsBuilder.setSampleRate(1.23f); optionsBuilder.addAreaId(1); optionsBuilder.addAreaId(2); optionsBuilder.setResolution(2.34f); optionsBuilder.setEnableVariableUpdateRate(false); auto options = optionsBuilder.build(); ASSERT_EQ(options, (SubscribeOptions{ .propId = TEST_PROP_ID, .areaIds = {1, 2}, .sampleRate = 1.23f, .resolution = 2.34f, .enableVariableUpdateRate = false, })); } TEST_F(AidlVhalClientTest, testAidlHalPropValueClone_valueIsTheSame) { VehiclePropValue testProp{.prop = TEST_PROP_ID, .areaId = TEST_AREA_ID, .value = { .int32Values = {1, 2}, .floatValues = {1.1, 2.2}, }}; auto testPropCopy = testProp; std::unique_ptr halPropValue = std::make_unique(std::move(testPropCopy)); auto halPropValueClone = halPropValue->clone(); EXPECT_EQ(halPropValueClone->getPropId(), TEST_PROP_ID); EXPECT_EQ(halPropValueClone->getAreaId(), TEST_AREA_ID); EXPECT_EQ(halPropValueClone->getInt32Values(), std::vector({1, 2})); EXPECT_EQ(halPropValueClone->getFloatValues(), std::vector({1.1, 2.2})); } TEST_F(AidlVhalClientTest, testAidlHalPropValueClone_modifyCloneDoesNotAffectOrig) { std::vector int32Values1 = {1, 2}; std::vector floatValues1 = {1.1, 2.2}; std::vector int32Values2 = {5, 4, 3, 2, 1}; std::vector floatValues2 = {3.3, 2.2, 1.1}; VehiclePropValue testProp{.prop = TEST_PROP_ID, .areaId = TEST_AREA_ID, .value = { .int32Values = int32Values1, .floatValues = floatValues1, }}; auto testPropCopy = testProp; std::unique_ptr halPropValue = std::make_unique(std::move(testPropCopy)); auto halPropValueClone = halPropValue->clone(); halPropValueClone->setInt32Values(int32Values2); halPropValueClone->setFloatValues(floatValues2); EXPECT_EQ(halPropValue->getInt32Values(), int32Values1); EXPECT_EQ(halPropValue->getFloatValues(), floatValues1); EXPECT_EQ(halPropValueClone->getInt32Values(), int32Values2); EXPECT_EQ(halPropValueClone->getFloatValues(), floatValues2); } TEST_F(AidlVhalClientTest, testGetMinMaxSupportedValue) { setTestRemoteInterfaceVersion(4); PropIdAreaId propIdAreaId = { .propId = TEST_PROP_ID, .areaId = TEST_AREA_ID, }; MinMaxSupportedValueResult vhalResult = {.minSupportedValue = RawPropValues{.int32Values = {1}}, .maxSupportedValue = RawPropValues{.int32Values = {10}}}; EXPECT_CALL(*getVhal(), getMinMaxSupportedValue) .WillOnce([&propIdAreaId, &vhalResult](const std::vector& propIdAreaIds, MinMaxSupportedValueResults* results) { EXPECT_THAT(propIdAreaIds, ::testing::ElementsAre(propIdAreaId)); results->payloads = {vhalResult}; return ScopedAStatus::ok(); }); auto result = getClient()->getMinMaxSupportedValue({propIdAreaId}); ASSERT_TRUE(result.ok()); ASSERT_THAT(result.value(), ::testing::SizeIs(1)); const MinMaxSupportedValueResult& propIdAreaIdResult = result.value()[0]; EXPECT_EQ(propIdAreaIdResult.status, StatusCode::OK); EXPECT_EQ(propIdAreaIdResult, vhalResult); } TEST_F(AidlVhalClientTest, testGetMinMaxSupportedValue_vhalReturnsError) { setTestRemoteInterfaceVersion(4); PropIdAreaId propIdAreaId = { .propId = TEST_PROP_ID, .areaId = TEST_AREA_ID, }; EXPECT_CALL(*getVhal(), getMinMaxSupportedValue) .WillOnce(::testing::Return( ScopedAStatus::fromServiceSpecificError(toInt(StatusCode::INTERNAL_ERROR)))); auto result = getClient()->getMinMaxSupportedValue({propIdAreaId}); ASSERT_FALSE(result.ok()); ASSERT_EQ(result.error().code().value(), ErrorCode::INTERNAL_ERROR_FROM_VHAL); } TEST_F(AidlVhalClientTest, testGetMinMaxSupportedValue_notSupportedVersionTooLow) { PropIdAreaId propIdAreaId = { .propId = TEST_PROP_ID, .areaId = TEST_AREA_ID, }; setTestRemoteInterfaceVersion(3); auto result = getClient()->getMinMaxSupportedValue({propIdAreaId}); ASSERT_FALSE(result.ok()); ASSERT_EQ(result.error().code().value(), ErrorCode::NOT_SUPPORTED); } TEST_F(AidlVhalClientTest, testGetSupportedValuesLists) { setTestRemoteInterfaceVersion(4); PropIdAreaId propIdAreaId = { .propId = TEST_PROP_ID, .areaId = TEST_AREA_ID, }; SupportedValuesListResult vhalResult = { .supportedValuesList = std::vector>({RawPropValues{.int32Values = {1}}})}; EXPECT_CALL(*getVhal(), getSupportedValuesLists) .WillOnce([&propIdAreaId, &vhalResult](const std::vector& propIdAreaIds, SupportedValuesListResults* results) { EXPECT_THAT(propIdAreaIds, ::testing::ElementsAre(propIdAreaId)); results->payloads = {vhalResult}; return ScopedAStatus::ok(); }); auto result = getClient()->getSupportedValuesLists({propIdAreaId}); ASSERT_TRUE(result.ok()); ASSERT_THAT(result.value(), ::testing::SizeIs(1)); const SupportedValuesListResult& propIdAreaIdResult = result.value()[0]; EXPECT_EQ(propIdAreaIdResult.status, StatusCode::OK); EXPECT_EQ(propIdAreaIdResult, vhalResult); } TEST_F(AidlVhalClientTest, testGetSupportedValuesLists_vhalReturnsError) { setTestRemoteInterfaceVersion(4); PropIdAreaId propIdAreaId = { .propId = TEST_PROP_ID, .areaId = TEST_AREA_ID, }; EXPECT_CALL(*getVhal(), getSupportedValuesLists) .WillOnce(::testing::Return( ScopedAStatus::fromServiceSpecificError(toInt(StatusCode::INTERNAL_ERROR)))); auto result = getClient()->getSupportedValuesLists({propIdAreaId}); ASSERT_FALSE(result.ok()); ASSERT_EQ(result.error().code().value(), ErrorCode::INTERNAL_ERROR_FROM_VHAL); } TEST_F(AidlVhalClientTest, testGetSupportedValuesLists_notSupportedVersionTooLow) { PropIdAreaId propIdAreaId = { .propId = TEST_PROP_ID, .areaId = TEST_AREA_ID, }; setTestRemoteInterfaceVersion(3); auto result = getClient()->getSupportedValuesLists({propIdAreaId}); ASSERT_FALSE(result.ok()); ASSERT_EQ(result.error().code().value(), ErrorCode::NOT_SUPPORTED); } } // namespace aidl_test } // namespace vhal } // namespace automotive } // namespace frameworks } // namespace android