blob: f81b1a29980c310e303609a9192222b90153c045 [file] [log] [blame]
/*
* 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 <VehicleHalTypes.h>
#include <aidl/android/hardware/automotive/vehicle/BnVehicleCallback.h>
#include <android-base/thread_annotations.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <float.h>
#include <chrono>
#include <list>
#include <memory>
#include <mutex>
#include <thread>
#include <vector>
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<std::mutex> 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<VehiclePropValue> getEvents() {
std::scoped_lock<std::mutex> lockGuard(mLock);
return mEvents;
}
void clearEvents() {
std::scoped_lock<std::mutex> lockGuard(mLock);
mEvents.clear();
}
private:
std::mutex mLock;
std::list<VehiclePropValue> mEvents GUARDED_BY(mLock);
};
class SubscriptionManagerTest : public ::testing::Test {
public:
void SetUp() override {
mManager = std::make_unique<SubscriptionManager>(
[](const std::shared_ptr<IVehicleCallback>& callback,
const VehiclePropValue& value) {
callback->onPropertyEvent(
VehiclePropValues{
.payloads = {value},
},
0);
});
mCallback = ::ndk::SharedRefBase::make<PropertyCallback>();
// Keep the local binder alive.
mBinder = mCallback->asBinder();
mCallbackClient = IVehicleCallback::fromBinder(mBinder);
}
SubscriptionManager* getManager() { return mManager.get(); }
std::shared_ptr<IVehicleCallback> getCallbackClient() { return mCallbackClient; }
PropertyCallback* getCallback() { return mCallback.get(); }
std::list<VehiclePropValue> getEvents() { return getCallback()->getEvents(); }
void clearEvents() { return getCallback()->clearEvents(); }
private:
std::unique_ptr<SubscriptionManager> mManager;
std::shared_ptr<PropertyCallback> mCallback;
std::shared_ptr<IVehicleCallback> mCallbackClient;
SpAIBinder mBinder;
};
TEST_F(SubscriptionManagerTest, testSubscribeGlobalContinuous) {
std::vector<SubscribeOptions> 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<size_t>(9));
EXPECT_EQ(getEvents().back().prop, 0);
EXPECT_EQ(getEvents().back().areaId, 0);
}
TEST_F(SubscriptionManagerTest, testSubscribeMultiplePropsGlobalContinuous) {
std::vector<SubscribeOptions> 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<size_t>(9));
// Theoretically trigger 20 times, but check for at least 15 times to be stable.
EXPECT_GE(event1Count, static_cast<size_t>(15));
}
TEST_F(SubscriptionManagerTest, testOverrideSubscriptionContinuous) {
std::vector<SubscribeOptions> 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<size_t>(9));
EXPECT_LE(getEvents().size(), static_cast<size_t>(15));
}
TEST_F(SubscriptionManagerTest, testSubscribeMultipleAreasContinuous) {
std::vector<SubscribeOptions> 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<size_t>(9));
// Theoretically trigger 10 times, but check for at least 9 times to be stable.
EXPECT_GE(area1Count, static_cast<size_t>(9));
}
TEST_F(SubscriptionManagerTest, testUnsubscribeGlobalContinuous) {
std::vector<SubscribeOptions> 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<SubscribeOptions> 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<int32_t>({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<size_t>(9));
for (const auto& event : getEvents()) {
EXPECT_EQ(event.prop, 1);
}
}
TEST_F(SubscriptionManagerTest, testUnsubscribeByCallback) {
std::vector<SubscribeOptions> 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<SubscribeOptions> 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<int32_t>({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<int32_t>({0, 1}));
ASSERT_TRUE(result.ok()) << "a failed unsubscription must not unsubscribe any properties"
<< result.error().message();
}
TEST_F(SubscriptionManagerTest, testSubscribeOnchange) {
std::vector<SubscribeOptions> options1 = {
{
.propId = 0,
.areaIds = {0, 1},
},
{
.propId = 1,
.areaIds = {0},
},
};
std::vector<SubscribeOptions> options2 = {
{
.propId = 0,
.areaIds = {0},
},
};
SpAIBinder binder1 = ::ndk::SharedRefBase::make<PropertyCallback>()->asBinder();
std::shared_ptr<IVehicleCallback> client1 = IVehicleCallback::fromBinder(binder1);
SpAIBinder binder2 = ::ndk::SharedRefBase::make<PropertyCallback>()->asBinder();
std::shared_ptr<IVehicleCallback> 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<VehiclePropValue> 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<SubscribeOptions> 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<SubscribeOptions> 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<SubscribeOptions> 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<int32_t>({0}));
ASSERT_TRUE(result.ok()) << "failed to unsubscribe: " << result.error().message();
std::vector<VehiclePropValue> 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