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