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