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 "VtsHalUsbV1_0TargetTest"
18 #include <android-base/logging.h>
19 
20 #include <android/hardware/usb/1.0/IUsb.h>
21 #include <android/hardware/usb/1.0/IUsbCallback.h>
22 #include <android/hardware/usb/1.0/types.h>
23 #include <gtest/gtest.h>
24 #include <hidl/GtestPrinter.h>
25 #include <hidl/ServiceManagement.h>
26 
27 #include <log/log.h>
28 #include <stdlib.h>
29 #include <chrono>
30 #include <condition_variable>
31 #include <mutex>
32 
33 #define TIMEOUT_PERIOD 10
34 
35 using ::android::hardware::usb::V1_0::IUsbCallback;
36 using ::android::hardware::usb::V1_0::IUsb;
37 using ::android::hardware::usb::V1_0::PortDataRole;
38 using ::android::hardware::usb::V1_0::PortMode;
39 using ::android::hardware::usb::V1_0::PortPowerRole;
40 using ::android::hardware::usb::V1_0::PortRole;
41 using ::android::hardware::usb::V1_0::PortRoleType;
42 using ::android::hardware::usb::V1_0::PortStatus;
43 using ::android::hardware::usb::V1_0::Status;
44 using ::android::hidl::base::V1_0::IBase;
45 using ::android::hardware::hidl_array;
46 using ::android::hardware::hidl_memory;
47 using ::android::hardware::hidl_string;
48 using ::android::hardware::hidl_vec;
49 using ::android::hardware::Return;
50 using ::android::hardware::Void;
51 using ::android::sp;
52 
53 // The main test class for the USB hidl HAL
54 class UsbHidlTest : public testing::TestWithParam<std::string> {
55  public:
56   // Callback class for the USB HIDL hal.
57   // Usb Hal will call this object upon role switch or port query.
58   class UsbCallback : public IUsbCallback {
59     UsbHidlTest& parent_;
60     int cookie;
61 
62    public:
UsbCallback(UsbHidlTest & parent,int cookie)63     UsbCallback(UsbHidlTest& parent, int cookie)
64         : parent_(parent), cookie(cookie){};
65 
66     virtual ~UsbCallback() = default;
67 
68     // Callback method for the port status.
notifyPortStatusChange(const hidl_vec<PortStatus> & currentPortStatus,Status retval)69     Return<void> notifyPortStatusChange(
70         const hidl_vec<PortStatus>& currentPortStatus, Status retval) override {
71       if (retval == Status::SUCCESS) {
72         parent_.usb_last_port_status.portName =
73             currentPortStatus[0].portName.c_str();
74         parent_.usb_last_port_status.currentDataRole =
75             currentPortStatus[0].currentDataRole;
76         parent_.usb_last_port_status.currentPowerRole =
77             currentPortStatus[0].currentPowerRole;
78         parent_.usb_last_port_status.currentMode =
79             currentPortStatus[0].currentMode;
80       }
81       parent_.usb_last_cookie = cookie;
82       parent_.notify();
83       return Void();
84     };
85 
86     // Callback method for the status of role switch operation.
notifyRoleSwitchStatus(const hidl_string &,const PortRole & newRole,Status retval)87     Return<void> notifyRoleSwitchStatus(const hidl_string& /*portName*/,
88                                         const PortRole& newRole,
89                                         Status retval) override {
90       parent_.usb_last_status = retval;
91       parent_.usb_last_cookie = cookie;
92       parent_.usb_last_port_role = newRole;
93       parent_.usb_role_switch_done = true;
94       parent_.notify();
95       return Void();
96     };
97   };
98 
SetUp()99   virtual void SetUp() override {
100     ALOGI("Setup");
101     usb = IUsb::getService(GetParam());
102     ASSERT_NE(usb, nullptr);
103 
104     usb_cb_2 = new UsbCallback(*this, 2);
105     ASSERT_NE(usb_cb_2, nullptr);
106     Return<void> ret = usb->setCallback(usb_cb_2);
107     ASSERT_TRUE(ret.isOk());
108   }
109 
TearDown()110   virtual void TearDown() override { ALOGI("Teardown"); }
111 
112   // Used as a mechanism to inform the test about data/event callback.
notify()113   inline void notify() {
114     std::unique_lock<std::mutex> lock(usb_mtx);
115     usb_count++;
116     usb_cv.notify_one();
117   }
118 
119   // Test code calls this function to wait for data/event callback.
wait()120   inline std::cv_status wait() {
121     std::unique_lock<std::mutex> lock(usb_mtx);
122 
123     std::cv_status status = std::cv_status::no_timeout;
124     auto now = std::chrono::system_clock::now();
125     while (usb_count == 0) {
126       status =
127           usb_cv.wait_until(lock, now + std::chrono::seconds(TIMEOUT_PERIOD));
128       if (status == std::cv_status::timeout) {
129         ALOGI("timeout");
130         return status;
131       }
132     }
133     usb_count--;
134     return status;
135   }
136 
137   // USB hidl hal Proxy
138   sp<IUsb> usb;
139 
140   // Callback objects for usb hidl
141   // Methods of these objects are called to notify port status updates.
142   sp<IUsbCallback> usb_cb_1, usb_cb_2;
143 
144   // The last conveyed status of the USB ports.
145   // Stores information of currentt_data_role, power_role for all the USB ports
146   PortStatus usb_last_port_status;
147 
148   // Status of the last role switch operation.
149   Status usb_last_status;
150 
151   // Port role information of the last role switch operation.
152   PortRole usb_last_port_role;
153 
154   // Flag to indicate the invocation of role switch callback.
155   bool usb_role_switch_done;
156 
157   // Identifier for the usb callback object.
158   // Stores the cookie of the last invoked usb callback object.
159   int usb_last_cookie;
160 
161   // synchronization primitives to coordinate between main test thread
162   // and the callback thread.
163   std::mutex usb_mtx;
164   std::condition_variable usb_cv;
165   int usb_count = 0;
166 };
167 
168 /*
169  * Test to see if setCallback succeeds.
170  * Callback oject is created and registered.
171  * Check to see if the hidl transaction succeeded.
172  */
TEST_P(UsbHidlTest,setCallback)173 TEST_P(UsbHidlTest, setCallback) {
174   usb_cb_1 = new UsbCallback(*this, 1);
175   ASSERT_NE(usb_cb_1, nullptr);
176   Return<void> ret = usb->setCallback(usb_cb_1);
177   ASSERT_TRUE(ret.isOk());
178 }
179 
180 /*
181  * Check to see if querying type-c
182  * port status succeeds.
183  */
TEST_P(UsbHidlTest,queryPortStatus)184 TEST_P(UsbHidlTest, queryPortStatus) {
185   Return<void> ret = usb->queryPortStatus();
186   ASSERT_TRUE(ret.isOk());
187   EXPECT_EQ(std::cv_status::no_timeout, wait());
188   EXPECT_EQ(2, usb_last_cookie);
189   ALOGI("rightafter: %s", usb_last_port_status.portName.c_str());
190 }
191 
192 /*
193  * Trying to switch a non-existent port should fail.
194  * This test case tried to switch the port with empty
195  * name which is expected to fail.
196  */
TEST_P(UsbHidlTest,switchEmptyPort)197 TEST_P(UsbHidlTest, switchEmptyPort) {
198   struct PortRole role;
199   role.type = PortRoleType::DATA_ROLE;
200 
201   Return<void> ret = usb->switchRole("", role);
202   ASSERT_TRUE(ret.isOk());
203   EXPECT_EQ(std::cv_status::no_timeout, wait());
204   EXPECT_EQ(Status::ERROR, usb_last_status);
205   EXPECT_EQ(2, usb_last_cookie);
206 }
207 
208 /*
209  * Test switching the power role of usb port.
210  * Test case queries the usb ports present in device.
211  * If there is atleast one usb port, a power role switch
212  * to SOURCE is attempted for the port.
213  * The callback parametes are checked to see if the power role
214  * switch was successfull. Upon success, Status::SUCCESS
215  * is expected to be returned.
216  */
217 
TEST_P(UsbHidlTest,switchPowerRole)218 TEST_P(UsbHidlTest, switchPowerRole) {
219   struct PortRole role;
220   role.type = PortRoleType::POWER_ROLE;
221   role.role = static_cast<uint32_t>(PortPowerRole::SOURCE);
222 
223   Return<void> ret = usb->queryPortStatus();
224   ASSERT_TRUE(ret.isOk());
225   EXPECT_EQ(std::cv_status::no_timeout, wait());
226   EXPECT_EQ(2, usb_last_cookie);
227 
228   if (!usb_last_port_status.portName.empty()) {
229     hidl_string portBeingSwitched = usb_last_port_status.portName;
230     ALOGI("switchPower role portname:%s", portBeingSwitched.c_str());
231     usb_role_switch_done = false;
232     Return<void> ret = usb->switchRole(portBeingSwitched.c_str(), role);
233     ASSERT_TRUE(ret.isOk());
234 
235     std::cv_status waitStatus = wait();
236     while (waitStatus == std::cv_status::no_timeout &&
237            usb_role_switch_done == false)
238       waitStatus = wait();
239 
240     EXPECT_EQ(std::cv_status::no_timeout, waitStatus);
241     EXPECT_EQ(2, usb_last_cookie);
242 
243     EXPECT_EQ(static_cast<uint32_t>(PortRoleType::POWER_ROLE),
244               static_cast<uint32_t>(usb_last_port_role.type));
245     if (usb_last_status == Status::SUCCESS) {
246       EXPECT_EQ(static_cast<uint32_t>(PortPowerRole::SOURCE),
247                 static_cast<uint32_t>(usb_last_port_role.role));
248     } else {
249       EXPECT_NE(static_cast<uint32_t>(PortPowerRole::SINK),
250                 static_cast<uint32_t>(usb_last_port_role.role));
251     }
252   }
253 }
254 
255 /*
256  * Test switching the data role of usb port.
257  * Test case queries the usb ports present in device.
258  * If there is atleast one usb port, a power role switch
259  * to HOST is attempted for the port.
260  * The callback parametes are checked to see if the power role
261  * switch was successfull. Upon success, Status::SUCCESS
262  * is expected to be returned.
263  */
TEST_P(UsbHidlTest,switchDataRole)264 TEST_P(UsbHidlTest, switchDataRole) {
265   struct PortRole role;
266   role.type = PortRoleType::DATA_ROLE;
267   role.role = static_cast<uint32_t>(PortDataRole::HOST);
268 
269   Return<void> ret = usb->queryPortStatus();
270   ASSERT_TRUE(ret.isOk());
271   EXPECT_EQ(std::cv_status::no_timeout, wait());
272   EXPECT_EQ(2, usb_last_cookie);
273 
274   if (!usb_last_port_status.portName.empty()) {
275     hidl_string portBeingSwitched = usb_last_port_status.portName;
276     ALOGI("portname:%s", portBeingSwitched.c_str());
277     usb_role_switch_done = false;
278     Return<void> ret = usb->switchRole(portBeingSwitched.c_str(), role);
279     ASSERT_TRUE(ret.isOk());
280 
281     std::cv_status waitStatus = wait();
282     while (waitStatus == std::cv_status::no_timeout &&
283            usb_role_switch_done == false)
284       waitStatus = wait();
285 
286     EXPECT_EQ(std::cv_status::no_timeout, waitStatus);
287     EXPECT_EQ(2, usb_last_cookie);
288 
289     EXPECT_EQ(static_cast<uint32_t>(PortRoleType::DATA_ROLE),
290               static_cast<uint32_t>(usb_last_port_role.type));
291     if (usb_last_status == Status::SUCCESS) {
292       EXPECT_EQ(static_cast<uint32_t>(PortDataRole::HOST),
293                 static_cast<uint32_t>(usb_last_port_role.role));
294     } else {
295       EXPECT_NE(static_cast<uint32_t>(PortDataRole::DEVICE),
296                 static_cast<uint32_t>(usb_last_port_role.role));
297     }
298   }
299 }
300 
301 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(UsbHidlTest);
302 INSTANTIATE_TEST_SUITE_P(
303         PerInstance, UsbHidlTest,
304         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IUsb::descriptor)),
305         android::hardware::PrintInstanceNameToString);
306