1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "tv_input_hidl_hal_test"
18 #include <android-base/logging.h>
19 
20 #include <android/hardware/tv/input/1.0/ITvInput.h>
21 #include <android/hardware/tv/input/1.0/ITvInputCallback.h>
22 #include <android/hardware/tv/input/1.0/types.h>
23 #include <gtest/gtest.h>
24 #include <hidl/GtestPrinter.h>
25 #include <hidl/ServiceManagement.h>
26 #include <utils/KeyedVector.h>
27 #include <mutex>
28 #include <vector>
29 
30 using ::android::hardware::tv::input::V1_0::ITvInput;
31 using ::android::hardware::tv::input::V1_0::ITvInputCallback;
32 using ::android::hardware::tv::input::V1_0::Result;
33 using ::android::hardware::tv::input::V1_0::TvInputType;
34 using ::android::hardware::tv::input::V1_0::TvInputDeviceInfo;
35 using ::android::hardware::tv::input::V1_0::TvInputEventType;
36 using ::android::hardware::tv::input::V1_0::TvInputEvent;
37 using ::android::hardware::tv::input::V1_0::TvStreamConfig;
38 using ::android::hardware::Return;
39 using ::android::hardware::Void;
40 using ::android::hardware::hidl_vec;
41 using ::android::sp;
42 
43 #define WAIT_FOR_EVENT_TIMEOUT 5
44 #define DEFAULT_ID INT32_MIN
45 
46 /* The main test class for TV Input HIDL HAL. */
47 class TvInputHidlTest : public testing::TestWithParam<std::string> {
48   public:
SetUp()49     virtual void SetUp() override {
50         tv_input_ = ITvInput::getService(GetParam());
51         tv_input_callback_ = new TvInputCallback(*this);
52         ASSERT_NE(tv_input_callback_, nullptr);
53         tv_input_->setCallback(tv_input_callback_);
54         // All events received within the timeout should be handled.
55         sleep(WAIT_FOR_EVENT_TIMEOUT);
56     }
57 
TearDown()58     virtual void TearDown() override {}
59 
60     /* Called when a DEVICE_AVAILABLE event is received. */
onDeviceAvailable(const TvInputDeviceInfo & deviceInfo)61     void onDeviceAvailable(const TvInputDeviceInfo& deviceInfo) {
62         device_info_.add(deviceInfo.deviceId, deviceInfo);
63     }
64 
65     /* Called when a DEVICE_UNAVAILABLE event is received. */
onDeviceUnavailable(int32_t deviceId)66     void onDeviceUnavailable(int32_t deviceId) { device_info_.removeItem(deviceId); }
67 
68     /* Called when a DEVICE_CONFIGURATIONS_CHANGED event is received. */
onStreamConfigurationsChanged(int32_t deviceId)69     Result onStreamConfigurationsChanged(int32_t deviceId) {
70         return updateStreamConfigurations(deviceId);
71     }
72 
73     /* Gets and updates the stream configurations for a device. */
updateStreamConfigurations(int32_t deviceId)74     Result updateStreamConfigurations(int32_t deviceId) {
75         stream_config_.removeItem(deviceId);
76         Result result = Result::UNKNOWN;
77         hidl_vec<TvStreamConfig> list;
78         tv_input_->getStreamConfigurations(
79                 deviceId, [&result, &list](Result res, hidl_vec<TvStreamConfig> configs) {
80                     result = res;
81                     if (res == Result::OK) {
82                         list = configs;
83                     }
84                 });
85         if (result == Result::OK) {
86             stream_config_.add(deviceId, list);
87         }
88         return result;
89     }
90 
91     /* Gets and updates the stream configurations for all existing devices. */
updateAllStreamConfigurations()92     void updateAllStreamConfigurations() {
93         for (size_t i = 0; i < device_info_.size(); i++) {
94             int32_t device_id = device_info_.keyAt(i);
95             updateStreamConfigurations(device_id);
96         }
97     }
98 
99     /* Returns a list of indices of stream_config_ whose corresponding values are not empty. */
getConfigIndices()100     std::vector<size_t> getConfigIndices() {
101         std::vector<size_t> indices;
102         for (size_t i = 0; i < stream_config_.size(); i++) {
103             if (stream_config_.valueAt(i).size() != 0) {
104                 indices.push_back(i);
105             }
106         }
107         return indices;
108     }
109 
110     /*
111      * Returns DEFAULT_ID if there is no missing integer in the range [0, the size of nums).
112      * Otherwise, returns the smallest missing non-negative integer.
113      */
getNumNotIn(std::vector<int32_t> & nums)114     int32_t getNumNotIn(std::vector<int32_t>& nums) {
115         int32_t result = DEFAULT_ID;
116         int32_t size = static_cast<int32_t>(nums.size());
117         for (int32_t i = 0; i < size; i++) {
118             // Put every element to its target position, if possible.
119             int32_t target_pos = nums[i];
120             while (target_pos >= 0 && target_pos < size && i != target_pos &&
121                    nums[i] != nums[target_pos]) {
122                 std::swap(nums[i], nums[target_pos]);
123                 target_pos = nums[i];
124             }
125         }
126 
127         for (int32_t i = 0; i < size; i++) {
128             if (nums[i] != i) {
129                 return i;
130             }
131         }
132         return result;
133     }
134 
135     /* A simple test implementation of TvInputCallback for TV Input Events. */
136     class TvInputCallback : public ITvInputCallback {
137       public:
TvInputCallback(TvInputHidlTest & parent)138         TvInputCallback(TvInputHidlTest& parent) : parent_(parent){};
139 
140         virtual ~TvInputCallback() = default;
141 
142         /*
143          * Notifies the client that an event has occurred. For possible event types,
144          * check TvInputEventType.
145          */
notify(const TvInputEvent & event)146         Return<void> notify(const TvInputEvent& event) override {
147             std::unique_lock<std::mutex> lock(parent_.mutex_);
148             switch (event.type) {
149                 case TvInputEventType::DEVICE_AVAILABLE:
150                     parent_.onDeviceAvailable(event.deviceInfo);
151                     break;
152                 case TvInputEventType::DEVICE_UNAVAILABLE:
153                     parent_.onDeviceUnavailable(event.deviceInfo.deviceId);
154                     break;
155                 case TvInputEventType::STREAM_CONFIGURATIONS_CHANGED:
156                     parent_.onStreamConfigurationsChanged(event.deviceInfo.deviceId);
157                     break;
158             }
159             return Void();
160         };
161 
162       private:
163         /* The test contains this callback instance. */
164         TvInputHidlTest& parent_;
165     };
166 
167     /* The TvInput used for the test. */
168     sp<ITvInput> tv_input_;
169 
170     /* The TvInputCallback used for the test. */
171     sp<ITvInputCallback> tv_input_callback_;
172 
173     /*
174      * A KeyedVector stores device information of every available device.
175      * A key is a device ID and the corresponding value is the TvInputDeviceInfo.
176      */
177     android::KeyedVector<int32_t, TvInputDeviceInfo> device_info_;
178 
179     /*
180      * A KeyedVector stores a list of stream configurations of every available device.
181      * A key is a device ID and the corresponding value is the stream configuration list.
182      */
183     android::KeyedVector<int32_t, hidl_vec<TvStreamConfig>> stream_config_;
184 
185     /* The mutex controls the access of shared data. */
186     std::mutex mutex_;
187 };
188 
189 /*
190  * GetStreamConfigTest:
191  * Calls updateStreamConfigurations() for each existing device
192  * Checks returned results
193  */
TEST_P(TvInputHidlTest,GetStreamConfigTest)194 TEST_P(TvInputHidlTest, GetStreamConfigTest) {
195     std::unique_lock<std::mutex> lock(mutex_);
196     for (size_t i = 0; i < device_info_.size(); i++) {
197         int32_t device_id = device_info_.keyAt(i);
198         Result result = updateStreamConfigurations(device_id);
199         EXPECT_EQ(Result::OK, result);
200     }
201 }
202 
203 /*
204  * OpenAndCloseStreamTest:
205  * Calls openStream() and then closeStream() for each existing stream
206  * Checks returned results
207  */
TEST_P(TvInputHidlTest,OpenAndCloseStreamTest)208 TEST_P(TvInputHidlTest, OpenAndCloseStreamTest) {
209     std::unique_lock<std::mutex> lock(mutex_);
210     updateAllStreamConfigurations();
211     for (size_t j = 0; j < stream_config_.size(); j++) {
212         int32_t device_id = stream_config_.keyAt(j);
213         hidl_vec<TvStreamConfig> config = stream_config_.valueAt(j);
214         for (size_t i = 0; i < config.size(); i++) {
215             Result result = Result::UNKNOWN;
216             int32_t stream_id = config[i].streamId;
217             tv_input_->openStream(device_id, stream_id,
218                                   [&result](Result res, const native_handle_t*) { result = res; });
219             EXPECT_EQ(Result::OK, result);
220 
221             result = Result::UNKNOWN;
222             result = tv_input_->closeStream(device_id, stream_id);
223             EXPECT_EQ(Result::OK, result);
224         }
225     }
226 }
227 
228 /*
229  * InvalidDeviceIdTest:
230  * Calls updateStreamConfigurations(), openStream(), and closeStream()
231  * for a non-existing device
232  * Checks returned results
233  * The results should be Result::INVALID_ARGUMENTS
234  */
TEST_P(TvInputHidlTest,InvalidDeviceIdTest)235 TEST_P(TvInputHidlTest, InvalidDeviceIdTest) {
236     std::unique_lock<std::mutex> lock(mutex_);
237 
238     std::vector<int32_t> device_ids;
239     for (size_t i = 0; i < device_info_.size(); i++) {
240         device_ids.push_back(device_info_.keyAt(i));
241     }
242     // Get a non-existing device ID.
243     int32_t id = getNumNotIn(device_ids);
244     EXPECT_EQ(Result::INVALID_ARGUMENTS, updateStreamConfigurations(id));
245 
246     Result result = Result::UNKNOWN;
247     int32_t stream_id = 0;
248     tv_input_->openStream(id, stream_id,
249                           [&result](Result res, const native_handle_t*) { result = res; });
250     EXPECT_EQ(Result::INVALID_ARGUMENTS, result);
251 
252     result = Result::UNKNOWN;
253     result = tv_input_->closeStream(id, stream_id);
254     EXPECT_EQ(Result::INVALID_ARGUMENTS, result);
255 }
256 
257 /*
258  * InvalidStreamIdTest:
259  * Calls openStream(), and closeStream() for a non-existing stream
260  * Checks returned results
261  * The results should be Result::INVALID_ARGUMENTS
262  */
TEST_P(TvInputHidlTest,InvalidStreamIdTest)263 TEST_P(TvInputHidlTest, InvalidStreamIdTest) {
264     std::unique_lock<std::mutex> lock(mutex_);
265     if (device_info_.isEmpty()) {
266         return;
267     }
268     updateAllStreamConfigurations();
269 
270     int32_t device_id = device_info_.keyAt(0);
271     // Get a non-existing stream ID.
272     int32_t id = DEFAULT_ID;
273     if (stream_config_.indexOfKey(device_id) >= 0) {
274         std::vector<int32_t> stream_ids;
275         hidl_vec<TvStreamConfig> config = stream_config_.valueFor(device_id);
276         for (size_t i = 0; i < config.size(); i++) {
277             stream_ids.push_back(config[i].streamId);
278         }
279         id = getNumNotIn(stream_ids);
280     }
281 
282     Result result = Result::UNKNOWN;
283     tv_input_->openStream(device_id, id,
284                           [&result](Result res, const native_handle_t*) { result = res; });
285     EXPECT_EQ(Result::INVALID_ARGUMENTS, result);
286 
287     result = Result::UNKNOWN;
288     result = tv_input_->closeStream(device_id, id);
289     EXPECT_EQ(Result::INVALID_ARGUMENTS, result);
290 }
291 
292 /*
293  * OpenAnOpenedStreamsTest:
294  * Calls openStream() twice for a stream (if any)
295  * Checks returned results
296  * The result of the second call should be Result::INVALID_STATE
297  */
TEST_P(TvInputHidlTest,OpenAnOpenedStreamsTest)298 TEST_P(TvInputHidlTest, OpenAnOpenedStreamsTest) {
299     std::unique_lock<std::mutex> lock(mutex_);
300     updateAllStreamConfigurations();
301     std::vector<size_t> indices = getConfigIndices();
302     if (indices.empty()) {
303         return;
304     }
305     int32_t device_id = stream_config_.keyAt(indices[0]);
306     int32_t stream_id = stream_config_.valueAt(indices[0])[0].streamId;
307 
308     Result result = Result::UNKNOWN;
309     tv_input_->openStream(device_id, stream_id,
310                           [&result](Result res, const native_handle_t*) { result = res; });
311     EXPECT_EQ(Result::OK, result);
312 
313     tv_input_->openStream(device_id, stream_id,
314                           [&result](Result res, const native_handle_t*) { result = res; });
315     EXPECT_EQ(Result::INVALID_STATE, result);
316 
317     // close stream as subsequent tests assume no open streams
318     EXPECT_EQ(Result::OK, tv_input_->closeStream(device_id, stream_id));
319 }
320 
321 /*
322  * CloseStreamBeforeOpenTest:
323  * Calls closeStream() without calling openStream() for a stream (if any)
324  * Checks the returned result
325  * The result should be Result::INVALID_STATE
326  */
TEST_P(TvInputHidlTest,CloseStreamBeforeOpenTest)327 TEST_P(TvInputHidlTest, CloseStreamBeforeOpenTest) {
328     std::unique_lock<std::mutex> lock(mutex_);
329     updateAllStreamConfigurations();
330     std::vector<size_t> indices = getConfigIndices();
331     if (indices.empty()) {
332         return;
333     }
334     int32_t device_id = stream_config_.keyAt(indices[0]);
335     int32_t stream_id = stream_config_.valueAt(indices[0])[0].streamId;
336     EXPECT_EQ(Result::INVALID_STATE, tv_input_->closeStream(device_id, stream_id));
337 }
338 
339 INSTANTIATE_TEST_SUITE_P(
340         PerInstance, TvInputHidlTest,
341         testing::ValuesIn(android::hardware::getAllHalInstanceNames(ITvInput::descriptor)),
342         android::hardware::PrintInstanceNameToString);
343 
344 // TODO remove from the allow list once the cf tv target is enabled for testing
345 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TvInputHidlTest);
346