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 char* role_name = ::android::GetComponentRole(role.isEncoder, role.type.c_str());
268         if (role_name != nullptr) {
269             EXPECT_EQ(std::string(role_name), role.role)
270                     << "Role \"" << role.role << "\" does not match "
271                     << (role.isEncoder ? "an encoder " : "a decoder ") << "for media type \""
272                     << role.type << ".";
273         }
274 
275         // Check the nodes for this role
276         std::set<const std::string> nodeKeys;
277         for (const IOmxStore::NodeInfo& node : role.nodes) {
278             // Make sure there are no duplicates
279             const auto [nodeIter, inserted] = nodeKeys.insert(node.name);
280             EXPECT_EQ(inserted, true) << "Node \"" << node.name << "\" has duplicates.";
281 
282             // Check the format of node name
283             EXPECT_EQ(testing::internal::RE::FullMatch(node.name, nodeNamePattern), true)
284                     << "Node name \"" << node.name << " is invalid.";
285             // Check the format of node owner
286             EXPECT_EQ(testing::internal::RE::FullMatch(node.owner, nodeOwnerPattern), true)
287                     << "Node owner \"" << node.owner << " is invalid.";
288 
289             validateAttributes(knownPatterns, unknownPatterns, node.attributes);
290 
291             ownerToNodes[node.owner].insert(node.name);
292             nodeToRoles[node.name].insert(role.role);
293         }
294     }
295 
296     // Verify the information with IOmx::listNodes().
297     // IOmxStore::listRoles() and IOmx::listNodes() should give consistent
298     // information about nodes and roles.
299     for (const auto& [owner, nodes] : ownerToNodes) {
300         // Obtain the IOmx instance for each "owner"
301         const sp<IOmx> omx = omxStore->getOmx(owner);
302         EXPECT_NE(nullptr, omx);
303 
304         // Invoke IOmx::listNodes()
305         android::hardware::media::omx::V1_0::Status status;
306         hidl_vec<IOmx::ComponentInfo> nodeList;
307         EXPECT_TRUE(
308                 omx->listNodes([&status, &nodeList](android::hardware::media::omx::V1_0::Status _s,
309                                                     hidl_vec<IOmx::ComponentInfo> const& _nl) {
310                        status = _s;
311                        nodeList = _nl;
312                    }).isOk());
313         ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
314 
315         // Verify that roles for each node match with the information from
316         // IOmxStore::listRoles().
317         std::set<const std::string> nodeKeys;
318         for (IOmx::ComponentInfo node : nodeList) {
319             // Make sure there are no duplicates
320             const auto [nodeIter, inserted] = nodeKeys.insert(node.mName);
321             EXPECT_EQ(inserted, true)
322                     << "IOmx::listNodes() lists duplicate nodes \"" << node.mName << "\".";
323 
324             // Skip "hidden" nodes, i.e. those that are not advertised by
325             // IOmxStore::listRoles().
326             if (nodes.find(node.mName) == nodes.end()) {
327                 std::cout << "Warning, IOmx::listNodes() lists unknown node \"" << node.mName
328                           << "\" for IOmx instance \"" << owner << "\"." << std::endl;
329                 continue;
330             }
331 
332             // All the roles advertised by IOmxStore::listRoles() for this
333             // node must be included in roleKeys.
334             std::set<const std::string> difference;
335             std::set_difference(nodeToRoles[node.mName].begin(), nodeToRoles[node.mName].end(),
336                                 roleKeys.begin(), roleKeys.end(),
337                                 std::inserter(difference, difference.begin()));
338             EXPECT_EQ(difference.empty(), true) << "IOmx::listNodes() for IOmx "
339                                                    "instance \""
340                                                 << owner
341                                                 << "\" does not report some "
342                                                    "expected nodes: "
343                                                 << android::base::Join(difference, ", ") << ".";
344         }
345         // Check that all nodes obtained from IOmxStore::listRoles() are
346         // supported by the their corresponding IOmx instances.
347         std::set<const std::string> difference;
348         std::set_difference(nodes.begin(), nodes.end(), nodeKeys.begin(), nodeKeys.end(),
349                             std::inserter(difference, difference.begin()));
350         EXPECT_EQ(difference.empty(), true) << "IOmx::listNodes() for IOmx "
351                                                "instance \""
352                                             << owner
353                                             << "\" does not report some "
354                                                "expected nodes: "
355                                             << android::base::Join(difference, ", ") << ".";
356     }
357 
358     if (!nodeToRoles.empty()) {
359         // Check that the prefix is a sensible string.
360         hidl_string prefix;
361         omxStore->getNodePrefix([&prefix](hidl_string const& _nl) { prefix = _nl; });
362         EXPECT_EQ(testing::internal::RE::PartialMatch(prefix, nodeNamePattern), true)
363                 << "\"" << prefix << "\" is not a valid prefix for node names.";
364 
365         // Check that all node names have the said prefix.
366         for (const auto& node : nodeToRoles) {
367             EXPECT_NE(node.first.rfind(prefix, 0), std::string::npos)
368                     << "Node \"" << node.first << "\" does not start with prefix \"" << prefix
369                     << "\".";
370         }
371     }
372 }
373 
374 // list components and roles.
TEST_P(StoreHidlTest,ListNodes)375 TEST_P(StoreHidlTest, ListNodes) {
376     description("enumerate component and roles");
377     android::hardware::media::omx::V1_0::Status status;
378     hidl_vec<IOmx::ComponentInfo> nodeList;
379     bool isPass = true;
380     EXPECT_TRUE(
381         omx->listNodes([&status, &nodeList](
382                            android::hardware::media::omx::V1_0::Status _s,
383                            hidl_vec<IOmx::ComponentInfo> const& _nl) {
384                status = _s;
385                nodeList = _nl;
386            })
387             .isOk());
388     ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
389     if (nodeList.size() == 0)
390         std::cout << "Warning, ComponentInfo list empty" << std::endl;
391     else {
392         // displayComponentInfo(nodeList);
393         for (size_t i = 0; i < nodeList.size(); i++) {
394             sp<CodecObserver> observer = nullptr;
395             sp<IOmxNode> omxNode = nullptr;
396             observer = new CodecObserver(nullptr);
397             ASSERT_NE(observer, nullptr);
398             EXPECT_TRUE(
399                 omx->allocateNode(
400                        nodeList[i].mName, observer,
401                        [&](android::hardware::media::omx::V1_0::Status _s,
402                            sp<IOmxNode> const& _nl) {
403                            status = _s;
404                            omxNode = _nl;
405                        })
406                     .isOk());
407             ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
408             if (omxNode == nullptr) {
409                 isPass = false;
410                 std::cerr << "[    !OK   ] " << nodeList[i].mName.c_str()
411                           << "\n";
412             } else {
413                 EXPECT_TRUE((omxNode->freeNode()).isOk());
414                 omxNode = nullptr;
415                 // std::cout << "[     OK   ] " << nodeList[i].mName.c_str() <<
416                 // "\n";
417             }
418         }
419     }
420     EXPECT_TRUE(isPass);
421 }
422 
423 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(StoreHidlTest);
424 INSTANTIATE_TEST_CASE_P(
425         PerInstance, StoreHidlTest,
426         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IOmxStore::descriptor)),
427         android::hardware::PrintInstanceNameToString);
428