/* * Copyright (C) 2017 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. */ #define LOG_TAG "tv_input_hidl_hal_test" #include #include #include #include #include #include #include #include #include #include using ::android::hardware::tv::input::V1_0::ITvInput; using ::android::hardware::tv::input::V1_0::ITvInputCallback; using ::android::hardware::tv::input::V1_0::Result; using ::android::hardware::tv::input::V1_0::TvInputType; using ::android::hardware::tv::input::V1_0::TvInputDeviceInfo; using ::android::hardware::tv::input::V1_0::TvInputEventType; using ::android::hardware::tv::input::V1_0::TvInputEvent; using ::android::hardware::tv::input::V1_0::TvStreamConfig; using ::android::hardware::Return; using ::android::hardware::Void; using ::android::hardware::hidl_vec; using ::android::sp; #define WAIT_FOR_EVENT_TIMEOUT 5 #define DEFAULT_ID INT32_MIN /* The main test class for TV Input HIDL HAL. */ class TvInputHidlTest : public testing::TestWithParam { public: virtual void SetUp() override { tv_input_ = ITvInput::getService(GetParam()); tv_input_callback_ = new TvInputCallback(*this); ASSERT_NE(tv_input_callback_, nullptr); tv_input_->setCallback(tv_input_callback_); // All events received within the timeout should be handled. sleep(WAIT_FOR_EVENT_TIMEOUT); } virtual void TearDown() override {} /* Called when a DEVICE_AVAILABLE event is received. */ void onDeviceAvailable(const TvInputDeviceInfo& deviceInfo) { device_info_.add(deviceInfo.deviceId, deviceInfo); } /* Called when a DEVICE_UNAVAILABLE event is received. */ void onDeviceUnavailable(int32_t deviceId) { device_info_.removeItem(deviceId); } /* Called when a DEVICE_CONFIGURATIONS_CHANGED event is received. */ Result onStreamConfigurationsChanged(int32_t deviceId) { return updateStreamConfigurations(deviceId); } /* Gets and updates the stream configurations for a device. */ Result updateStreamConfigurations(int32_t deviceId) { stream_config_.removeItem(deviceId); Result result = Result::UNKNOWN; hidl_vec list; tv_input_->getStreamConfigurations( deviceId, [&result, &list](Result res, hidl_vec configs) { result = res; if (res == Result::OK) { list = configs; } }); if (result == Result::OK) { stream_config_.add(deviceId, list); } return result; } /* Gets and updates the stream configurations for all existing devices. */ void updateAllStreamConfigurations() { for (size_t i = 0; i < device_info_.size(); i++) { int32_t device_id = device_info_.keyAt(i); updateStreamConfigurations(device_id); } } /* Returns a list of indices of stream_config_ whose corresponding values are not empty. */ std::vector getConfigIndices() { std::vector indices; for (size_t i = 0; i < stream_config_.size(); i++) { if (stream_config_.valueAt(i).size() != 0) { indices.push_back(i); } } return indices; } /* * Returns DEFAULT_ID if there is no missing integer in the range [0, the size of nums). * Otherwise, returns the smallest missing non-negative integer. */ int32_t getNumNotIn(std::vector& nums) { int32_t result = DEFAULT_ID; int32_t size = static_cast(nums.size()); for (int32_t i = 0; i < size; i++) { // Put every element to its target position, if possible. int32_t target_pos = nums[i]; while (target_pos >= 0 && target_pos < size && i != target_pos && nums[i] != nums[target_pos]) { std::swap(nums[i], nums[target_pos]); target_pos = nums[i]; } } for (int32_t i = 0; i < size; i++) { if (nums[i] != i) { return i; } } return result; } /* A simple test implementation of TvInputCallback for TV Input Events. */ class TvInputCallback : public ITvInputCallback { public: TvInputCallback(TvInputHidlTest& parent) : parent_(parent){}; virtual ~TvInputCallback() = default; /* * Notifies the client that an event has occurred. For possible event types, * check TvInputEventType.      */ Return notify(const TvInputEvent& event) override { std::unique_lock lock(parent_.mutex_); switch (event.type) { case TvInputEventType::DEVICE_AVAILABLE: parent_.onDeviceAvailable(event.deviceInfo); break; case TvInputEventType::DEVICE_UNAVAILABLE: parent_.onDeviceUnavailable(event.deviceInfo.deviceId); break; case TvInputEventType::STREAM_CONFIGURATIONS_CHANGED: parent_.onStreamConfigurationsChanged(event.deviceInfo.deviceId); break; } return Void(); }; private: /* The test contains this callback instance. */ TvInputHidlTest& parent_; }; /* The TvInput used for the test. */ sp tv_input_; /* The TvInputCallback used for the test. */ sp tv_input_callback_; /* * A KeyedVector stores device information of every available device. * A key is a device ID and the corresponding value is the TvInputDeviceInfo. */ android::KeyedVector device_info_; /* * A KeyedVector stores a list of stream configurations of every available device. * A key is a device ID and the corresponding value is the stream configuration list. */ android::KeyedVector> stream_config_; /* The mutex controls the access of shared data. */ std::mutex mutex_; }; /* * GetStreamConfigTest: * Calls updateStreamConfigurations() for each existing device * Checks returned results */ TEST_P(TvInputHidlTest, GetStreamConfigTest) { std::unique_lock lock(mutex_); for (size_t i = 0; i < device_info_.size(); i++) { int32_t device_id = device_info_.keyAt(i); Result result = updateStreamConfigurations(device_id); EXPECT_EQ(Result::OK, result); } } /* * OpenAndCloseStreamTest: * Calls openStream() and then closeStream() for each existing stream * Checks returned results */ TEST_P(TvInputHidlTest, OpenAndCloseStreamTest) { std::unique_lock lock(mutex_); updateAllStreamConfigurations(); for (size_t j = 0; j < stream_config_.size(); j++) { int32_t device_id = stream_config_.keyAt(j); hidl_vec config = stream_config_.valueAt(j); for (size_t i = 0; i < config.size(); i++) { Result result = Result::UNKNOWN; int32_t stream_id = config[i].streamId; tv_input_->openStream(device_id, stream_id, [&result](Result res, const native_handle_t*) { result = res; }); EXPECT_EQ(Result::OK, result); result = Result::UNKNOWN; result = tv_input_->closeStream(device_id, stream_id); EXPECT_EQ(Result::OK, result); } } } /* * InvalidDeviceIdTest: * Calls updateStreamConfigurations(), openStream(), and closeStream() * for a non-existing device * Checks returned results * The results should be Result::INVALID_ARGUMENTS */ TEST_P(TvInputHidlTest, InvalidDeviceIdTest) { std::unique_lock lock(mutex_); std::vector device_ids; for (size_t i = 0; i < device_info_.size(); i++) { device_ids.push_back(device_info_.keyAt(i)); } // Get a non-existing device ID. int32_t id = getNumNotIn(device_ids); EXPECT_EQ(Result::INVALID_ARGUMENTS, updateStreamConfigurations(id)); Result result = Result::UNKNOWN; int32_t stream_id = 0; tv_input_->openStream(id, stream_id, [&result](Result res, const native_handle_t*) { result = res; }); EXPECT_EQ(Result::INVALID_ARGUMENTS, result); result = Result::UNKNOWN; result = tv_input_->closeStream(id, stream_id); EXPECT_EQ(Result::INVALID_ARGUMENTS, result); } /* * InvalidStreamIdTest: * Calls openStream(), and closeStream() for a non-existing stream * Checks returned results * The results should be Result::INVALID_ARGUMENTS */ TEST_P(TvInputHidlTest, InvalidStreamIdTest) { std::unique_lock lock(mutex_); if (device_info_.isEmpty()) { return; } updateAllStreamConfigurations(); int32_t device_id = device_info_.keyAt(0); // Get a non-existing stream ID. int32_t id = DEFAULT_ID; if (stream_config_.indexOfKey(device_id) >= 0) { std::vector stream_ids; hidl_vec config = stream_config_.valueFor(device_id); for (size_t i = 0; i < config.size(); i++) { stream_ids.push_back(config[i].streamId); } id = getNumNotIn(stream_ids); } Result result = Result::UNKNOWN; tv_input_->openStream(device_id, id, [&result](Result res, const native_handle_t*) { result = res; }); EXPECT_EQ(Result::INVALID_ARGUMENTS, result); result = Result::UNKNOWN; result = tv_input_->closeStream(device_id, id); EXPECT_EQ(Result::INVALID_ARGUMENTS, result); } /* * OpenAnOpenedStreamsTest: * Calls openStream() twice for a stream (if any) * Checks returned results * The result of the second call should be Result::INVALID_STATE */ TEST_P(TvInputHidlTest, OpenAnOpenedStreamsTest) { std::unique_lock lock(mutex_); updateAllStreamConfigurations(); std::vector indices = getConfigIndices(); if (indices.empty()) { return; } int32_t device_id = stream_config_.keyAt(indices[0]); int32_t stream_id = stream_config_.valueAt(indices[0])[0].streamId; Result result = Result::UNKNOWN; tv_input_->openStream(device_id, stream_id, [&result](Result res, const native_handle_t*) { result = res; }); EXPECT_EQ(Result::OK, result); tv_input_->openStream(device_id, stream_id, [&result](Result res, const native_handle_t*) { result = res; }); EXPECT_EQ(Result::INVALID_STATE, result); // close stream as subsequent tests assume no open streams EXPECT_EQ(Result::OK, tv_input_->closeStream(device_id, stream_id)); } /* * CloseStreamBeforeOpenTest: * Calls closeStream() without calling openStream() for a stream (if any) * Checks the returned result * The result should be Result::INVALID_STATE */ TEST_P(TvInputHidlTest, CloseStreamBeforeOpenTest) { std::unique_lock lock(mutex_); updateAllStreamConfigurations(); std::vector indices = getConfigIndices(); if (indices.empty()) { return; } int32_t device_id = stream_config_.keyAt(indices[0]); int32_t stream_id = stream_config_.valueAt(indices[0])[0].streamId; EXPECT_EQ(Result::INVALID_STATE, tv_input_->closeStream(device_id, stream_id)); } INSTANTIATE_TEST_SUITE_P( PerInstance, TvInputHidlTest, testing::ValuesIn(android::hardware::getAllHalInstanceNames(ITvInput::descriptor)), android::hardware::PrintInstanceNameToString); // TODO remove from the allow list once the cf tv target is enabled for testing GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TvInputHidlTest);