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