/* * Copyright 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. */ #define LOG_TAG "tv_input_aidl_hal_test" #include "VtsHalTvInputTargetTest.h" #include #include #include #include using namespace VtsHalTvInputTargetTest; TvInputAidlTest::TvInputCallback::TvInputCallback(shared_ptr parent) : parent_(parent) {} ::ndk::ScopedAStatus TvInputAidlTest::TvInputCallback::notify(const TvInputEvent& in_event) { unique_lock lock(parent_->mutex_); switch (in_event.type) { case TvInputEventType::DEVICE_AVAILABLE: parent_->onDeviceAvailable(in_event.deviceInfo); break; case TvInputEventType::DEVICE_UNAVAILABLE: parent_->onDeviceUnavailable(in_event.deviceInfo.deviceId); break; case TvInputEventType::STREAM_CONFIGURATIONS_CHANGED: parent_->onStreamConfigurationsChanged(in_event.deviceInfo.deviceId); break; } return ::ndk::ScopedAStatus::ok(); } ::ndk::ScopedAStatus TvInputAidlTest::TvInputCallback::notifyTvMessageEvent( const TvMessageEvent& in_event) { return ::ndk::ScopedAStatus::ok(); } void TvInputAidlTest::SetUp() { if (AServiceManager_isDeclared(GetParam().c_str())) { ::ndk::SpAIBinder binder(AServiceManager_waitForService(GetParam().c_str())); tv_input_ = ITvInput::fromBinder(binder); } else { tv_input_ = nullptr; } ASSERT_NE(tv_input_, nullptr); tv_input_callback_ = ::ndk::SharedRefBase::make(shared_ptr(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); } void TvInputAidlTest::TearDown() { tv_input_ = nullptr; } void TvInputAidlTest::onDeviceAvailable(const TvInputDeviceInfo& deviceInfo) { ALOGD("onDeviceAvailable for device id %d", deviceInfo.deviceId); device_info_.add(deviceInfo.deviceId, deviceInfo); } void TvInputAidlTest::onDeviceUnavailable(int32_t deviceId) { ALOGD("onDeviceUnavailable for device id %d", deviceId); device_info_.removeItem(deviceId); stream_config_.removeItem(deviceId); } ::ndk::ScopedAStatus TvInputAidlTest::onStreamConfigurationsChanged(int32_t deviceId) { ALOGD("onStreamConfigurationsChanged for device id %d", deviceId); return updateStreamConfigurations(deviceId); } ::ndk::ScopedAStatus TvInputAidlTest::updateStreamConfigurations(int32_t deviceId) { stream_config_.removeItem(deviceId); vector list; ::ndk::ScopedAStatus status = tv_input_->getStreamConfigurations(deviceId, &list); if (status.isOk()) { stream_config_.add(deviceId, list); } return status; } void TvInputAidlTest::updateAllStreamConfigurations() { for (size_t i = 0; i < device_info_.size(); i++) { int32_t device_id = device_info_.keyAt(i); updateStreamConfigurations(device_id); } } vector TvInputAidlTest::getConfigIndices() { 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; } int32_t TvInputAidlTest::getNumNotIn(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]) { 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; } bool TvInputAidlTest::isValidHandle(NativeHandle& handle) { if (handle.fds.empty() && handle.ints.empty()) { return false; } if (!(handle.fds.empty())) { for (size_t i = 0; i < handle.fds.size(); i++) { int fd = handle.fds[i].get(); if (fcntl(fd, F_GETFL) < 0) { return false; } } } return true; } /* * GetStreamConfigTest: * Calls updateStreamConfigurations() for each existing device * Checks returned results */ TEST_P(TvInputAidlTest, GetStreamConfigTest) { unique_lock lock(mutex_); for (size_t i = 0; i < device_info_.size(); i++) { int32_t device_id = device_info_.keyAt(i); ALOGD("GetStreamConfigTest: device_id=%d", device_id); ASSERT_TRUE(updateStreamConfigurations(device_id).isOk()); } } /* * OpenAndCloseStreamTest: * Calls openStream() and then closeStream() for each existing stream * Checks returned results */ TEST_P(TvInputAidlTest, OpenAndCloseStreamTest) { unique_lock lock(mutex_); updateAllStreamConfigurations(); for (size_t j = 0; j < stream_config_.size(); j++) { int32_t device_id = stream_config_.keyAt(j); vector config = stream_config_.valueAt(j); for (size_t i = 0; i < config.size(); i++) { NativeHandle handle; int32_t stream_id = config[i].streamId; ALOGD("OpenAndCloseStreamTest: open stream, device_id=%d, stream_id=%d", device_id, stream_id); ASSERT_TRUE(tv_input_->openStream(device_id, stream_id, &handle).isOk()); if (VERIFY_SIDEBAND_STREAM_HANDLE) { ASSERT_TRUE(isValidHandle(handle)); } ALOGD("OpenAndCloseStreamTest: close stream, device_id=%d, stream_id=%d", device_id, stream_id); ASSERT_TRUE(tv_input_->closeStream(device_id, stream_id).isOk()); } } } /* * InvalidDeviceIdTest: * Calls updateStreamConfigurations(), openStream(), and closeStream() * for a non-existing device * Checks returned results * The results should be ITvInput::STATUS_INVALID_ARGUMENTS */ TEST_P(TvInputAidlTest, InvalidDeviceIdTest) { unique_lock lock(mutex_); 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); ALOGD("InvalidDeviceIdTest: update stream config, device_id=%d", id); ASSERT_TRUE(updateStreamConfigurations(id).getServiceSpecificError() == ITvInput::STATUS_INVALID_ARGUMENTS); int32_t stream_id = 0; NativeHandle handle; ALOGD("InvalidDeviceIdTest: open stream, device_id=%d, stream_id=%d", id, stream_id); ASSERT_TRUE(tv_input_->openStream(id, stream_id, &handle).getServiceSpecificError() == ITvInput::STATUS_INVALID_ARGUMENTS); ALOGD("InvalidDeviceIdTest: close stream, device_id=%d, stream_id=%d", id, stream_id); ASSERT_TRUE(tv_input_->closeStream(id, stream_id).getServiceSpecificError() == ITvInput::STATUS_INVALID_ARGUMENTS); } /* * InvalidStreamIdTest: * Calls openStream(), and closeStream() for a non-existing stream * Checks returned results * The results should be ITvInput::STATUS_INVALID_ARGUMENTS */ TEST_P(TvInputAidlTest, InvalidStreamIdTest) { 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) { vector stream_ids; vector 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); } NativeHandle handle; ALOGD("InvalidStreamIdTest: open stream, device_id=%d, stream_id=%d", device_id, id); ASSERT_TRUE(tv_input_->openStream(device_id, id, &handle).getServiceSpecificError() == ITvInput::STATUS_INVALID_ARGUMENTS); ALOGD("InvalidStreamIdTest: close stream, device_id=%d, stream_id=%d", device_id, id); ASSERT_TRUE(tv_input_->closeStream(device_id, id).getServiceSpecificError() == ITvInput::STATUS_INVALID_ARGUMENTS); } /* * OpenAnOpenedStreamsTest: * Calls openStream() twice for a stream (if any) * Checks returned results * The result of the second call should be ITvInput::STATUS_INVALID_STATE */ TEST_P(TvInputAidlTest, OpenAnOpenedStreamsTest) { unique_lock lock(mutex_); updateAllStreamConfigurations(); vector indices = getConfigIndices(); if (indices.empty()) { return; } int32_t device_id = stream_config_.keyAt(indices[0]); vector streamConfigs = stream_config_.valueAt(indices[0]); if (streamConfigs.empty()) { return; } int32_t stream_id = streamConfigs[0].streamId; NativeHandle handle; ALOGD("OpenAnOpenedStreamsTest: open stream, device_id=%d, stream_id=%d", device_id, stream_id); ASSERT_TRUE(tv_input_->openStream(device_id, stream_id, &handle).isOk()); if (VERIFY_SIDEBAND_STREAM_HANDLE) { ASSERT_TRUE(isValidHandle(handle)); } ALOGD("OpenAnOpenedStreamsTest: open stream, device_id=%d, stream_id=%d", device_id, stream_id); ASSERT_TRUE(tv_input_->openStream(device_id, stream_id, &handle).getServiceSpecificError() == ITvInput::STATUS_INVALID_STATE); // close stream as subsequent tests assume no open streams ALOGD("OpenAnOpenedStreamsTest: close stream, device_id=%d, stream_id=%d", device_id, stream_id); ASSERT_TRUE(tv_input_->closeStream(device_id, stream_id).isOk()); } /* * CloseStreamBeforeOpenTest: * Calls closeStream() without calling openStream() for a stream (if any) * Checks the returned result * The result should be ITvInput::STATUS_INVALID_STATE */ TEST_P(TvInputAidlTest, CloseStreamBeforeOpenTest) { unique_lock lock(mutex_); updateAllStreamConfigurations(); vector indices = getConfigIndices(); if (indices.empty()) { return; } int32_t device_id = stream_config_.keyAt(indices[0]); vector streamConfigs = stream_config_.valueAt(indices[0]); if (streamConfigs.empty()) { return; } int32_t stream_id = streamConfigs[0].streamId; ALOGD("CloseStreamBeforeOpenTest: close stream, device_id=%d, stream_id=%d", device_id, stream_id); ASSERT_TRUE(tv_input_->closeStream(device_id, stream_id).getServiceSpecificError() == ITvInput::STATUS_INVALID_STATE); } TEST_P(TvInputAidlTest, SetTvMessageEnabledTest) { unique_lock lock(mutex_); updateAllStreamConfigurations(); vector indices = getConfigIndices(); if (indices.empty()) { return; } int32_t device_id = stream_config_.keyAt(indices[0]); vector streamConfigs = stream_config_.valueAt(indices[0]); if (streamConfigs.empty()) { return; } int32_t stream_id = streamConfigs[0].streamId; ALOGD("SetTvMessageEnabledTest: device_id=%d, stream_id=%d", device_id, stream_id); tv_input_->setTvMessageEnabled(device_id, stream_id, TvMessageEventType::WATERMARK, true); } TEST_P(TvInputAidlTest, GetTvMessageQueueTest) { unique_lock lock(mutex_); updateAllStreamConfigurations(); vector indices = getConfigIndices(); if (indices.empty()) { return; } int32_t device_id = stream_config_.keyAt(indices[0]); vector streamConfigs = stream_config_.valueAt(indices[0]); if (streamConfigs.empty()) { return; } int32_t stream_id = streamConfigs[0].streamId; ALOGD("GetTvMessageQueueTest: device_id=%d, stream_id=%d", device_id, stream_id); MQDescriptor queueDescriptor; AidlMessageQueue* queue; tv_input_->getTvMessageQueueDesc(&queueDescriptor, device_id, stream_id); queue = new (std::nothrow) AidlMessageQueue(queueDescriptor); ASSERT_TRUE(queue->isValid()); delete queue; } INSTANTIATE_TEST_SUITE_P(PerInstance, TvInputAidlTest, testing::ValuesIn(android::getAidlHalInstanceNames(ITvInput::descriptor)), android::PrintInstanceNameToString); // TODO remove from the allow list once the cf tv target is enabled for testing GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TvInputAidlTest);