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 "media_omx_hidl_store_test"
18 #ifdef __LP64__
19 #define OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS
20 #endif
21
22 #include <android-base/logging.h>
23 #include <android-base/strings.h>
24
25 #include <android/hardware/media/omx/1.0/IOmx.h>
26 #include <android/hardware/media/omx/1.0/IOmxNode.h>
27 #include <android/hardware/media/omx/1.0/IOmxObserver.h>
28 #include <android/hardware/media/omx/1.0/IOmxStore.h>
29 #include <android/hardware/media/omx/1.0/types.h>
30 #include <android/hidl/allocator/1.0/IAllocator.h>
31 #include <android/hidl/memory/1.0/IMapper.h>
32 #include <android/hidl/memory/1.0/IMemory.h>
33 #include <gtest/gtest.h>
34 #include <hidl/GtestPrinter.h>
35 #include <hidl/ServiceManagement.h>
36 #include <media/stagefright/omx/OMXUtils.h>
37
38 using ::android::sp;
39 using ::android::base::Join;
40 using ::android::hardware::hidl_string;
41 using ::android::hardware::hidl_vec;
42 using ::android::hardware::Return;
43 using ::android::hardware::Void;
44 using ::android::hardware::media::omx::V1_0::CodecBuffer;
45 using ::android::hardware::media::omx::V1_0::IOmx;
46 using ::android::hardware::media::omx::V1_0::IOmxNode;
47 using ::android::hardware::media::omx::V1_0::IOmxObserver;
48 using ::android::hardware::media::omx::V1_0::IOmxStore;
49 using ::android::hardware::media::omx::V1_0::Message;
50 using ::android::hardware::media::omx::V1_0::PortMode;
51 using ::android::hidl::allocator::V1_0::IAllocator;
52 using ::android::hidl::memory::V1_0::IMapper;
53 using ::android::hidl::memory::V1_0::IMemory;
54
55 #include <getopt.h>
56 #include <media_hidl_test_common.h>
57
58 class StoreHidlTest : public ::testing::TestWithParam<std::string> {
59 public:
SetUp()60 virtual void SetUp() override {
61 omxStore = IOmxStore::getService(GetParam());
62 ASSERT_NE(omxStore, nullptr);
63 omx = IOmx::getService(GetParam());
64 ASSERT_NE(omx, nullptr);
65 }
66
67 sp<IOmxStore> omxStore;
68 sp<IOmx> omx;
69
70 protected:
description(const std::string & description)71 static void description(const std::string& description) {
72 RecordProperty("description", description);
73 }
74 };
75
76 struct AttributePattern {
77 const testing::internal::RE key;
78 const testing::internal::RE value;
79 };
80
displayComponentInfo(hidl_vec<IOmx::ComponentInfo> & nodeList)81 void displayComponentInfo(hidl_vec<IOmx::ComponentInfo>& nodeList) {
82 for (size_t i = 0; i < nodeList.size(); i++) {
83 printf("%s | ", nodeList[i].mName.c_str());
84 for (size_t j = 0; j < ((nodeList[i]).mRoles).size(); j++) {
85 printf("%s ", nodeList[i].mRoles[j].c_str());
86 }
87 printf("\n");
88 }
89 }
90
validateAttributes(const std::map<const std::string,const testing::internal::RE> & knownPatterns,const std::vector<const struct AttributePattern> & unknownPatterns,hidl_vec<IOmxStore::Attribute> attributes)91 void validateAttributes(
92 const std::map<const std::string, const testing::internal::RE>& knownPatterns,
93 const std::vector<const struct AttributePattern>& unknownPatterns,
94 hidl_vec<IOmxStore::Attribute> attributes) {
95 std::set<const std::string> attributeKeys;
96 for (const auto& attr : attributes) {
97 // Make sure there are no duplicates
98 const auto [nodeIter, inserted] = attributeKeys.insert(attr.key);
99 EXPECT_EQ(inserted, true) << "Attribute \"" << attr.key << "\" has duplicates.";
100
101 // Check the value against the corresponding regular
102 // expression.
103 const auto knownPattern = knownPatterns.find(attr.key);
104 if (knownPattern != knownPatterns.end()) {
105 EXPECT_EQ(testing::internal::RE::FullMatch(attr.value, knownPattern->second), true)
106 << "Attribute \"" << attr.key << "\" has invalid value \"" << attr.value << ".";
107 ;
108 } else {
109 // Failed to find exact attribute, check against
110 // possible patterns.
111 bool keyFound = false;
112 for (const auto& unknownPattern : unknownPatterns) {
113 if (testing::internal::RE::PartialMatch(attr.key, unknownPattern.key)) {
114 keyFound = true;
115 EXPECT_EQ(testing::internal::RE::FullMatch(attr.value, unknownPattern.value),
116 true)
117 << "Attribute \"" << attr.key << "\" has invalid value \"" << attr.value
118 << ".";
119 }
120 }
121 if (!keyFound) {
122 std::cout << "Warning, Unrecognized attribute \"" << attr.key << "\" with value \""
123 << attr.value << "\"." << std::endl;
124 }
125 }
126 }
127 }
128
129 // Make sure IOmx and IOmxStore have the same set of instances.
TEST(StoreHidlTest,instanceMatchValidation)130 TEST(StoreHidlTest, instanceMatchValidation) {
131 auto omxInstances = android::hardware::getAllHalInstanceNames(IOmx::descriptor);
132 auto omxStoreInstances = android::hardware::getAllHalInstanceNames(IOmxStore::descriptor);
133 ASSERT_EQ(omxInstances.size(), omxInstances.size());
134 for (const std::string& omxInstance : omxInstances) {
135 EXPECT_TRUE(std::find(omxStoreInstances.begin(), omxStoreInstances.end(), omxInstance) !=
136 omxStoreInstances.end());
137 }
138 }
139
140 // list service attributes and verify expected formats
TEST_P(StoreHidlTest,ListServiceAttr)141 TEST_P(StoreHidlTest, ListServiceAttr) {
142 description("list service attributes");
143 android::hardware::media::omx::V1_0::Status status;
144 hidl_vec<IOmxStore::Attribute> attributes;
145 EXPECT_TRUE(omxStore
146 ->listServiceAttributes([&status, &attributes](
147 android::hardware::media::omx::V1_0::Status _s,
148 hidl_vec<IOmxStore::Attribute> const& _nl) {
149 status = _s;
150 attributes = _nl;
151 })
152 .isOk());
153 ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
154 if (attributes.size() == 0) {
155 std::cout << "Warning, Attribute list empty" << std::endl;
156 } else {
157 /*
158 * knownPatterns is a map whose keys are the known "key" for a service
159 * attribute pair (see IOmxStore::Attribute), and whose values are the
160 * corresponding regular expressions that will have to match with the
161 * "value" of the attribute pair. If listServiceAttributes() returns an
162 * attribute that has a matching key but an unmatched value, the test
163 * will fail.
164 */
165 const std::map<const std::string, const testing::internal::RE> knownPatterns = {
166 {"max-video-encoder-input-buffers", "0|[1-9][0-9]*"},
167 {"supports-multiple-secure-codecs", "0|1"},
168 {"supports-secure-with-non-secure-codec", "0|1"},
169 };
170 /*
171 * unknownPatterns is a vector of pairs of regular expressions.
172 * For each attribute whose key is not known (i.e., does not match any
173 * of the keys in the "knownPatterns" variable defined above), that key will be
174 * tried for a match with the first element of each pair of the variable
175 * "unknownPatterns". If a match occurs, the value of that same attribute will be
176 * tried for a match with the second element of the pair. If this second
177 * match fails, the test will fail.
178 */
179 const std::vector<const struct AttributePattern> unknownPatterns = {
180 {"supports-[a-z0-9-]*", "0|1"}};
181
182 validateAttributes(knownPatterns, unknownPatterns, attributes);
183 }
184 }
185
186 // get node prefix
TEST_P(StoreHidlTest,getNodePrefix)187 TEST_P(StoreHidlTest, getNodePrefix) {
188 description("get node prefix");
189 hidl_string prefix;
190 omxStore->getNodePrefix(
191 [&prefix](hidl_string const& _nl) { prefix = _nl; });
192 if (prefix.empty()) std::cout << "Warning, Node Prefix empty" << std::endl;
193 }
194
195 // list roles and validate all RoleInfo objects
TEST_P(StoreHidlTest,ListRoles)196 TEST_P(StoreHidlTest, ListRoles) {
197 description("list roles");
198 hidl_vec<IOmxStore::RoleInfo> roleList;
199 omxStore->listRoles([&roleList](hidl_vec<IOmxStore::RoleInfo> const& _nl) {
200 roleList = _nl;
201 });
202 if (roleList.size() == 0) {
203 GTEST_SKIP() << "Warning, RoleInfo list empty";
204 return;
205 }
206
207 // Basic patterns for matching
208 const std::string toggle = "(0|1)";
209 const std::string string = "(.*)";
210 const std::string num = "(0|([1-9][0-9]*))";
211 const std::string size = "(" + num + "x" + num + ")";
212 const std::string ratio = "(" + num + ":" + num + ")";
213 const std::string range_num = "((" + num + "-" + num + ")|" + num + ")";
214 const std::string range_size = "((" + size + "-" + size + ")|" + size + ")";
215 const std::string range_ratio = "((" + ratio + "-" + ratio + ")|" + ratio + ")";
216 const std::string list_range_num = "(" + range_num + "(," + range_num + ")*)";
217
218 // Matching rules for node attributes with fixed keys
219 const std::map<const std::string, const testing::internal::RE> knownPatterns = {
220 {"alignment", size},
221 {"bitrate-range", range_num},
222 {"block-aspect-ratio-range", range_ratio},
223 {"block-count-range", range_num},
224 {"block-size", size},
225 {"blocks-per-second-range", range_num},
226 {"complexity-default", num},
227 {"complexity-range", range_num},
228 {"feature-adaptive-playback", toggle},
229 {"feature-bitrate-control", "(VBR|CBR|CQ)[,(VBR|CBR|CQ)]*"},
230 {"feature-can-swap-width-height", toggle},
231 {"feature-intra-refresh", toggle},
232 {"feature-partial-frame", toggle},
233 {"feature-secure-playback", toggle},
234 {"feature-tunneled-playback", toggle},
235 {"frame-rate-range", range_num},
236 {"max-channel-count", num},
237 {"max-concurrent-instances", num},
238 {"max-supported-instances", num},
239 {"pixel-aspect-ratio-range", range_ratio},
240 {"quality-default", num},
241 {"quality-range", range_num},
242 {"quality-scale", string},
243 {"sample-rate-ranges", list_range_num},
244 {"size-range", range_size},
245 };
246
247 // Strings for matching rules for node attributes with key patterns
248 const std::vector<const struct AttributePattern> unknownPatterns = {
249 {"measured-frame-rate-" + size + "-range", range_num},
250 {"feature-[a-zA-Z0-9_-]+", string},
251 };
252
253 // Matching rules for node names and owners
254 const testing::internal::RE nodeNamePattern = "[a-zA-Z0-9._-]+";
255 const testing::internal::RE nodeOwnerPattern = "[a-zA-Z0-9._-]+";
256
257 std::set<const std::string> roleKeys;
258 std::map<const std::string, std::set<const std::string>> nodeToRoles;
259 std::map<const std::string, std::set<const std::string>> ownerToNodes;
260 for (const IOmxStore::RoleInfo& role : roleList) {
261 // Make sure there are no duplicates
262 const auto [roleIter, inserted] = roleKeys.insert(role.role);
263 EXPECT_EQ(inserted, true) << "Role \"" << role.role << "\" has duplicates.";
264
265 // Make sure role name follows expected format based on type and
266 // isEncoder
267 const std::string role_name(
268 ::android::GetComponentRole(role.isEncoder, role.type.c_str()));
269 EXPECT_EQ(role_name, role.role) << "Role \"" << role.role << "\" does not match "
270 << (role.isEncoder ? "an encoder " : "a decoder ")
271 << "for mime type \"" << role.type << ".";
272
273 // Check the nodes for this role
274 std::set<const std::string> nodeKeys;
275 for (const IOmxStore::NodeInfo& node : role.nodes) {
276 // Make sure there are no duplicates
277 const auto [nodeIter, inserted] = nodeKeys.insert(node.name);
278 EXPECT_EQ(inserted, true) << "Node \"" << node.name << "\" has duplicates.";
279
280 // Check the format of node name
281 EXPECT_EQ(testing::internal::RE::FullMatch(node.name, nodeNamePattern), true)
282 << "Node name \"" << node.name << " is invalid.";
283 // Check the format of node owner
284 EXPECT_EQ(testing::internal::RE::FullMatch(node.owner, nodeOwnerPattern), true)
285 << "Node owner \"" << node.owner << " is invalid.";
286
287 validateAttributes(knownPatterns, unknownPatterns, node.attributes);
288
289 ownerToNodes[node.owner].insert(node.name);
290 nodeToRoles[node.name].insert(role.role);
291 }
292 }
293
294 // Verify the information with IOmx::listNodes().
295 // IOmxStore::listRoles() and IOmx::listNodes() should give consistent
296 // information about nodes and roles.
297 for (const auto& [owner, nodes] : ownerToNodes) {
298 // Obtain the IOmx instance for each "owner"
299 const sp<IOmx> omx = omxStore->getOmx(owner);
300 EXPECT_NE(nullptr, omx);
301
302 // Invoke IOmx::listNodes()
303 android::hardware::media::omx::V1_0::Status status;
304 hidl_vec<IOmx::ComponentInfo> nodeList;
305 EXPECT_TRUE(
306 omx->listNodes([&status, &nodeList](android::hardware::media::omx::V1_0::Status _s,
307 hidl_vec<IOmx::ComponentInfo> const& _nl) {
308 status = _s;
309 nodeList = _nl;
310 }).isOk());
311 ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
312
313 // Verify that roles for each node match with the information from
314 // IOmxStore::listRoles().
315 std::set<const std::string> nodeKeys;
316 for (IOmx::ComponentInfo node : nodeList) {
317 // Make sure there are no duplicates
318 const auto [nodeIter, inserted] = nodeKeys.insert(node.mName);
319 EXPECT_EQ(inserted, true)
320 << "IOmx::listNodes() lists duplicate nodes \"" << node.mName << "\".";
321
322 // Skip "hidden" nodes, i.e. those that are not advertised by
323 // IOmxStore::listRoles().
324 if (nodes.find(node.mName) == nodes.end()) {
325 std::cout << "Warning, IOmx::listNodes() lists unknown node \"" << node.mName
326 << "\" for IOmx instance \"" << owner << "\"." << std::endl;
327 continue;
328 }
329
330 // All the roles advertised by IOmxStore::listRoles() for this
331 // node must be included in roleKeys.
332 std::set<const std::string> difference;
333 std::set_difference(nodeToRoles[node.mName].begin(), nodeToRoles[node.mName].end(),
334 roleKeys.begin(), roleKeys.end(),
335 std::inserter(difference, difference.begin()));
336 EXPECT_EQ(difference.empty(), true) << "IOmx::listNodes() for IOmx "
337 "instance \""
338 << owner
339 << "\" does not report some "
340 "expected nodes: "
341 << android::base::Join(difference, ", ") << ".";
342 }
343 // Check that all nodes obtained from IOmxStore::listRoles() are
344 // supported by the their corresponding IOmx instances.
345 std::set<const std::string> difference;
346 std::set_difference(nodes.begin(), nodes.end(), nodeKeys.begin(), nodeKeys.end(),
347 std::inserter(difference, difference.begin()));
348 EXPECT_EQ(difference.empty(), true) << "IOmx::listNodes() for IOmx "
349 "instance \""
350 << owner
351 << "\" does not report some "
352 "expected nodes: "
353 << android::base::Join(difference, ", ") << ".";
354 }
355
356 if (!nodeToRoles.empty()) {
357 // Check that the prefix is a sensible string.
358 hidl_string prefix;
359 omxStore->getNodePrefix([&prefix](hidl_string const& _nl) { prefix = _nl; });
360 EXPECT_EQ(testing::internal::RE::PartialMatch(prefix, nodeNamePattern), true)
361 << "\"" << prefix << "\" is not a valid prefix for node names.";
362
363 // Check that all node names have the said prefix.
364 for (const auto& node : nodeToRoles) {
365 EXPECT_NE(node.first.rfind(prefix, 0), std::string::npos)
366 << "Node \"" << node.first << "\" does not start with prefix \"" << prefix
367 << "\".";
368 }
369 }
370 }
371
372 // list components and roles.
TEST_P(StoreHidlTest,ListNodes)373 TEST_P(StoreHidlTest, ListNodes) {
374 description("enumerate component and roles");
375 android::hardware::media::omx::V1_0::Status status;
376 hidl_vec<IOmx::ComponentInfo> nodeList;
377 bool isPass = true;
378 EXPECT_TRUE(
379 omx->listNodes([&status, &nodeList](
380 android::hardware::media::omx::V1_0::Status _s,
381 hidl_vec<IOmx::ComponentInfo> const& _nl) {
382 status = _s;
383 nodeList = _nl;
384 })
385 .isOk());
386 ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
387 if (nodeList.size() == 0)
388 std::cout << "Warning, ComponentInfo list empty" << std::endl;
389 else {
390 // displayComponentInfo(nodeList);
391 for (size_t i = 0; i < nodeList.size(); i++) {
392 sp<CodecObserver> observer = nullptr;
393 sp<IOmxNode> omxNode = nullptr;
394 observer = new CodecObserver(nullptr);
395 ASSERT_NE(observer, nullptr);
396 EXPECT_TRUE(
397 omx->allocateNode(
398 nodeList[i].mName, observer,
399 [&](android::hardware::media::omx::V1_0::Status _s,
400 sp<IOmxNode> const& _nl) {
401 status = _s;
402 omxNode = _nl;
403 })
404 .isOk());
405 ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
406 if (omxNode == nullptr) {
407 isPass = false;
408 std::cerr << "[ !OK ] " << nodeList[i].mName.c_str()
409 << "\n";
410 } else {
411 EXPECT_TRUE((omxNode->freeNode()).isOk());
412 omxNode = nullptr;
413 // std::cout << "[ OK ] " << nodeList[i].mName.c_str() <<
414 // "\n";
415 }
416 }
417 }
418 EXPECT_TRUE(isPass);
419 }
420
421 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(StoreHidlTest);
422 INSTANTIATE_TEST_CASE_P(
423 PerInstance, StoreHidlTest,
424 testing::ValuesIn(android::hardware::getAllHalInstanceNames(IOmxStore::descriptor)),
425 android::hardware::PrintInstanceNameToString);
426