/* * Copyright (C) 2021 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 "SubscriptionManager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace android { namespace hardware { namespace automotive { namespace vehicle { using ::aidl::android::hardware::automotive::vehicle::BnVehicleCallback; using ::aidl::android::hardware::automotive::vehicle::GetValueResults; using ::aidl::android::hardware::automotive::vehicle::IVehicleCallback; using ::aidl::android::hardware::automotive::vehicle::SetValueResults; using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions; using ::aidl::android::hardware::automotive::vehicle::VehiclePropErrors; using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue; using ::aidl::android::hardware::automotive::vehicle::VehiclePropValues; using ::ndk::ScopedAStatus; using ::ndk::SpAIBinder; using ::testing::ElementsAre; using ::testing::WhenSorted; class PropertyCallback final : public BnVehicleCallback { public: ScopedAStatus onGetValues(const GetValueResults&) override { return ScopedAStatus::ok(); } ScopedAStatus onSetValues(const SetValueResults&) override { return ScopedAStatus::ok(); } ScopedAStatus onPropertyEvent(const VehiclePropValues& values, int32_t) override { std::scoped_lock lockGuard(mLock); for (const auto& value : values.payloads) { mEvents.push_back(value); } return ScopedAStatus::ok(); } ScopedAStatus onPropertySetError(const VehiclePropErrors&) override { return ScopedAStatus::ok(); } // Test functions. std::list getEvents() { std::scoped_lock lockGuard(mLock); return mEvents; } void clearEvents() { std::scoped_lock lockGuard(mLock); mEvents.clear(); } private: std::mutex mLock; std::list mEvents GUARDED_BY(mLock); }; class SubscriptionManagerTest : public testing::Test { public: void SetUp() override { mHardware = std::make_shared(); mManager = std::make_unique(mHardware.get()); mCallback = ndk::SharedRefBase::make(); // Keep the local binder alive. mBinder = mCallback->asBinder(); mCallbackClient = IVehicleCallback::fromBinder(mBinder); std::shared_ptr callbackClient = mCallbackClient; mHardware->registerOnPropertyChangeEvent( std::make_unique( [callbackClient](std::vector updatedValues) { VehiclePropValues values = { .payloads = std::move(updatedValues), }; callbackClient->onPropertyEvent(values, 0); })); } SubscriptionManager* getManager() { return mManager.get(); } std::shared_ptr getCallbackClient() { return mCallbackClient; } PropertyCallback* getCallback() { return mCallback.get(); } std::list getEvents() { return getCallback()->getEvents(); } void clearEvents() { return getCallback()->clearEvents(); } private: std::unique_ptr mManager; std::shared_ptr mCallback; std::shared_ptr mCallbackClient; std::shared_ptr mHardware; SpAIBinder mBinder; }; TEST_F(SubscriptionManagerTest, testSubscribeGlobalContinuous) { std::vector options = {{ .propId = 0, .areaIds = {0}, .sampleRate = 10.0, }}; auto result = getManager()->subscribe(getCallbackClient(), options, true); ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); std::this_thread::sleep_for(std::chrono::seconds(1)); // Theoretically trigger 10 times, but check for at least 9 times to be stable. ASSERT_GE(getEvents().size(), static_cast(9)); EXPECT_EQ(getEvents().back().prop, 0); EXPECT_EQ(getEvents().back().areaId, 0); } TEST_F(SubscriptionManagerTest, testSubscribeMultiplePropsGlobalContinuous) { std::vector options = {{ .propId = 0, .areaIds = {0}, .sampleRate = 10.0, }, { .propId = 1, .areaIds = {0}, .sampleRate = 20.0, }}; auto result = getManager()->subscribe(getCallbackClient(), options, true); ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); std::this_thread::sleep_for(std::chrono::seconds(1)); size_t event0Count = 0; size_t event1Count = 0; for (const auto& event : getEvents()) { if (event.prop == 0) { event0Count++; } else { event1Count++; } } // Theoretically trigger 10 times, but check for at least 9 times to be stable. EXPECT_GE(event0Count, static_cast(9)); // Theoretically trigger 20 times, but check for at least 15 times to be stable. EXPECT_GE(event1Count, static_cast(15)); } TEST_F(SubscriptionManagerTest, testOverrideSubscriptionContinuous) { std::vector options = {{ .propId = 0, .areaIds = {0}, .sampleRate = 20.0, }}; auto result = getManager()->subscribe(getCallbackClient(), options, true); ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); // Override sample rate to be 10.0. options[0].sampleRate = 10.0; result = getManager()->subscribe(getCallbackClient(), options, true); ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); std::this_thread::sleep_for(std::chrono::seconds(1)); // Theoretically trigger 10 times, but check for at least 9 times to be stable. EXPECT_GE(getEvents().size(), static_cast(9)); EXPECT_LE(getEvents().size(), static_cast(15)); } TEST_F(SubscriptionManagerTest, testSubscribeMultipleAreasContinuous) { std::vector options = { { .propId = 0, .areaIds = {0, 1}, .sampleRate = 10.0, }, }; auto result = getManager()->subscribe(getCallbackClient(), options, true); ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); std::this_thread::sleep_for(std::chrono::seconds(1)); size_t area0Count = 0; size_t area1Count = 0; for (const auto& event : getEvents()) { if (event.areaId == 0) { area0Count++; } else { area1Count++; } } // Theoretically trigger 10 times, but check for at least 9 times to be stable. EXPECT_GE(area0Count, static_cast(9)); // Theoretically trigger 10 times, but check for at least 9 times to be stable. EXPECT_GE(area1Count, static_cast(9)); } TEST_F(SubscriptionManagerTest, testUnsubscribeGlobalContinuous) { std::vector options = {{ .propId = 0, .areaIds = {0}, .sampleRate = 10.0, }}; auto result = getManager()->subscribe(getCallbackClient(), options, true); ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); result = getManager()->unsubscribe(getCallbackClient()->asBinder().get()); ASSERT_TRUE(result.ok()) << "failed to unsubscribe: " << result.error().message(); clearEvents(); std::this_thread::sleep_for(std::chrono::milliseconds(200)); // Theoretically trigger 10 times, but check for at least 9 times to be stable. ASSERT_TRUE(getEvents().empty()); } TEST_F(SubscriptionManagerTest, testUnsubscribeMultipleAreas) { std::vector options = { { .propId = 0, .areaIds = {0, 1, 2, 3, 4}, .sampleRate = 10.0, }, { .propId = 1, .areaIds = {0}, .sampleRate = 10.0, }, }; auto result = getManager()->subscribe(getCallbackClient(), options, true); ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); result = getManager()->unsubscribe(getCallbackClient()->asBinder().get(), std::vector({0})); ASSERT_TRUE(result.ok()) << "failed to unsubscribe: " << result.error().message(); clearEvents(); std::this_thread::sleep_for(std::chrono::seconds(1)); // Theoretically trigger 10 times, but check for at least 9 times to be stable. EXPECT_GE(getEvents().size(), static_cast(9)); for (const auto& event : getEvents()) { EXPECT_EQ(event.prop, 1); } } TEST_F(SubscriptionManagerTest, testUnsubscribeByCallback) { std::vector options = { { .propId = 0, .areaIds = {0, 1, 2, 3, 4}, .sampleRate = 10.0, }, { .propId = 1, .areaIds = {0}, .sampleRate = 10.0, }, }; auto result = getManager()->subscribe(getCallbackClient(), options, true); ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); result = getManager()->unsubscribe(getCallbackClient()->asBinder().get()); ASSERT_TRUE(result.ok()) << "failed to unsubscribe: " << result.error().message(); clearEvents(); std::this_thread::sleep_for(std::chrono::seconds(1)); EXPECT_TRUE(getEvents().empty()); } TEST_F(SubscriptionManagerTest, testUnsubscribeFailure) { std::vector options = { { .propId = 0, .areaIds = {0, 1, 2, 3, 4}, }, { .propId = 1, .areaIds = {0}, }, }; auto result = getManager()->subscribe(getCallbackClient(), options, false); ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); // Property ID: 2 was not subscribed. result = getManager()->unsubscribe(getCallbackClient()->asBinder().get(), std::vector({0, 1, 2})); ASSERT_FALSE(result.ok()) << "unsubscribe an unsubscribed property must fail"; // Since property 0 and property 1 was not unsubscribed successfully, we should be able to // unsubscribe them again. result = getManager()->unsubscribe(getCallbackClient()->asBinder().get(), std::vector({0, 1})); ASSERT_TRUE(result.ok()) << "a failed unsubscription must not unsubscribe any properties" << result.error().message(); } TEST_F(SubscriptionManagerTest, testSubscribeOnchange) { std::vector options1 = { { .propId = 0, .areaIds = {0, 1}, }, { .propId = 1, .areaIds = {0}, }, }; std::vector options2 = { { .propId = 0, .areaIds = {0}, }, }; SpAIBinder binder1 = ndk::SharedRefBase::make()->asBinder(); std::shared_ptr client1 = IVehicleCallback::fromBinder(binder1); SpAIBinder binder2 = ndk::SharedRefBase::make()->asBinder(); std::shared_ptr client2 = IVehicleCallback::fromBinder(binder2); auto result = getManager()->subscribe(client1, options1, false); ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); result = getManager()->subscribe(client2, options2, false); ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); std::vector updatedValues = { { .prop = 0, .areaId = 0, }, { .prop = 0, .areaId = 1, }, { .prop = 1, .areaId = 0, }, { .prop = 1, .areaId = 1, }, }; auto clients = getManager()->getSubscribedClients(updatedValues); ASSERT_THAT(clients[client1], WhenSorted(ElementsAre(&updatedValues[0], &updatedValues[1], &updatedValues[2]))); ASSERT_THAT(clients[client2], ElementsAre(&updatedValues[0])); } TEST_F(SubscriptionManagerTest, testSubscribeInvalidOption) { std::vector options = { { .propId = 0, .areaIds = {0, 1, 2, 3, 4}, // invalid sample rate. .sampleRate = 0.0, }, { .propId = 1, .areaIds = {0}, .sampleRate = 10.0, }, }; auto result = getManager()->subscribe(getCallbackClient(), options, true); ASSERT_FALSE(result.ok()) << "subscribe with invalid sample rate must fail"; ASSERT_TRUE(getManager() ->getSubscribedClients({{ .prop = 0, .areaId = 0, }, { .prop = 1, .areaId = 0, }}) .empty()) << "no property should be subscribed if error is returned"; } TEST_F(SubscriptionManagerTest, testSubscribeNoAreaIds) { std::vector options = { { .propId = 0, .areaIds = {}, .sampleRate = 1.0, }, { .propId = 1, .areaIds = {0}, .sampleRate = 10.0, }, }; auto result = getManager()->subscribe(getCallbackClient(), options, true); ASSERT_FALSE(result.ok()) << "subscribe with invalid sample rate must fail"; ASSERT_TRUE(getManager() ->getSubscribedClients({{ .prop = 1, .areaId = 0, }}) .empty()) << "no property should be subscribed if error is returned"; } TEST_F(SubscriptionManagerTest, testUnsubscribeOnchange) { std::vector options = { { .propId = 0, .areaIds = {0, 1}, }, { .propId = 1, .areaIds = {0}, }, }; auto result = getManager()->subscribe(getCallbackClient(), options, false); ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); result = getManager()->unsubscribe(getCallbackClient()->asBinder().get(), std::vector({0})); ASSERT_TRUE(result.ok()) << "failed to unsubscribe: " << result.error().message(); std::vector updatedValues = { { .prop = 0, .areaId = 0, }, { .prop = 1, .areaId = 0, }, }; auto clients = getManager()->getSubscribedClients(updatedValues); ASSERT_THAT(clients[getCallbackClient()], ElementsAre(&updatedValues[1])); } TEST_F(SubscriptionManagerTest, testCheckSampleRateValid) { ASSERT_TRUE(SubscriptionManager::checkSampleRate(1.0)); } TEST_F(SubscriptionManagerTest, testCheckSampleRateInvalidTooSmall) { ASSERT_FALSE(SubscriptionManager::checkSampleRate(FLT_MIN)); } TEST_F(SubscriptionManagerTest, testCheckSampleRateInvalidZero) { ASSERT_FALSE(SubscriptionManager::checkSampleRate(0)); } } // namespace vehicle } // namespace automotive } // namespace hardware } // namespace android