gamepad_service_unittest.cc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543
  1. // Copyright 2014 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "device/gamepad/gamepad_service.h"
  5. #include <string.h>
  6. #include <memory>
  7. #include "base/barrier_closure.h"
  8. #include "base/memory/raw_ptr.h"
  9. #include "base/run_loop.h"
  10. #include "base/test/gmock_callback_support.h"
  11. #include "base/test/task_environment.h"
  12. #include "device/gamepad/gamepad_consumer.h"
  13. #include "device/gamepad/gamepad_test_helpers.h"
  14. #include "testing/gmock/include/gmock/gmock.h"
  15. #include "testing/gtest/include/gtest/gtest.h"
  16. namespace device {
  17. namespace {
  18. using ::base::test::RunClosure;
  19. // The number of simulated gamepads that will be connected and disconnected in
  20. // tests.
  21. constexpr int kNumberOfGamepads = Gamepads::kItemsLengthCap;
  22. } // namespace
  23. class MockGamepadConsumer : public GamepadConsumer {
  24. public:
  25. MockGamepadConsumer() {
  26. // Expect no connections or disconnections by default.
  27. EXPECT_CALL(*this, OnGamepadConnected).Times(0);
  28. EXPECT_CALL(*this, OnGamepadDisconnected).Times(0);
  29. }
  30. MockGamepadConsumer(MockGamepadConsumer&) = delete;
  31. MockGamepadConsumer& operator=(MockGamepadConsumer&) = delete;
  32. ~MockGamepadConsumer() override = default;
  33. MOCK_METHOD2(OnGamepadConnected, void(uint32_t, const Gamepad&));
  34. MOCK_METHOD2(OnGamepadDisconnected, void(uint32_t, const Gamepad&));
  35. MOCK_METHOD1(OnGamepadChanged, void(const mojom::GamepadChanges&));
  36. };
  37. class GamepadServiceTest : public testing::Test {
  38. public:
  39. GamepadServiceTest(const GamepadServiceTest&) = delete;
  40. GamepadServiceTest& operator=(const GamepadServiceTest&) = delete;
  41. protected:
  42. GamepadServiceTest() {
  43. memset(&test_data_, 0, sizeof(test_data_));
  44. // Configure the pad to have one button. We need our mock gamepad
  45. // to have at least one input so we can simulate a user gesture.
  46. test_data_.items[0].buttons_length = 1;
  47. }
  48. ~GamepadServiceTest() override = default;
  49. GamepadService* service() const { return service_; }
  50. void SetUp() override {
  51. auto fetcher = std::make_unique<MockGamepadDataFetcher>(test_data_);
  52. fetcher_ = fetcher.get();
  53. service_ = new GamepadService(std::move(fetcher));
  54. service_->SetSanitizationEnabled(false);
  55. }
  56. void TearDown() override {
  57. // Calling SetInstance will destroy the GamepadService instance.
  58. GamepadService::SetInstance(nullptr);
  59. }
  60. MockGamepadConsumer* CreateConsumer() {
  61. consumers_.push_back(std::make_unique<MockGamepadConsumer>());
  62. return consumers_.back().get();
  63. }
  64. // Configure the first `connected_count` gamepads as connected and the rest as
  65. // disconnected.
  66. void SetPadsConnected(int connected_count) {
  67. for (int i = 0; i < kNumberOfGamepads; ++i)
  68. test_data_.items[i].connected = (i < connected_count);
  69. fetcher_->SetTestData(test_data_);
  70. }
  71. void SimulateUserGesture(bool has_gesture) {
  72. test_data_.items[0].buttons[0].value = has_gesture ? 1.0f : 0.0f;
  73. test_data_.items[0].buttons[0].pressed = has_gesture ? true : false;
  74. fetcher_->SetTestData(test_data_);
  75. }
  76. void SimulatePageReload(GamepadConsumer* consumer) {
  77. EXPECT_TRUE(service_->ConsumerBecameInactive(consumer));
  78. EXPECT_TRUE(service_->ConsumerBecameActive(consumer));
  79. }
  80. void WaitForData() {
  81. // Block until work on the polling thread is complete. The data fetcher will
  82. // read gamepad data on the polling thread, which may cause the provider to
  83. // post user gesture or gamepad connection callbacks to the main thread.
  84. fetcher_->WaitForDataReadAndCallbacksIssued();
  85. // Allow the user gesture and gamepad connection callbacks to run.
  86. base::RunLoop().RunUntilIdle();
  87. }
  88. private:
  89. base::test::SingleThreadTaskEnvironment task_environment_;
  90. raw_ptr<MockGamepadDataFetcher> fetcher_;
  91. raw_ptr<GamepadService> service_;
  92. std::vector<std::unique_ptr<MockGamepadConsumer>> consumers_;
  93. Gamepads test_data_;
  94. };
  95. TEST_F(GamepadServiceTest, ConnectionsTest) {
  96. // Create an active consumer.
  97. auto* consumer = CreateConsumer();
  98. EXPECT_TRUE(service()->ConsumerBecameActive(consumer));
  99. WaitForData();
  100. // Connect gamepads and simulate a user gesture. The consumer is notified for
  101. // each connected gamepad.
  102. {
  103. base::RunLoop loop;
  104. auto barrier = base::BarrierClosure(kNumberOfGamepads, loop.QuitClosure());
  105. EXPECT_CALL(*consumer, OnGamepadConnected)
  106. .Times(kNumberOfGamepads)
  107. .WillRepeatedly(RunClosure(barrier));
  108. SimulateUserGesture(/*has_gesture=*/true);
  109. SetPadsConnected(/*connected_count=*/kNumberOfGamepads);
  110. loop.Run();
  111. }
  112. // Disconnect all gamepads. The consumer is notified for each disconnected
  113. // gamepad.
  114. {
  115. base::RunLoop loop;
  116. auto barrier = base::BarrierClosure(kNumberOfGamepads, loop.QuitClosure());
  117. EXPECT_CALL(*consumer, OnGamepadDisconnected)
  118. .Times(kNumberOfGamepads)
  119. .WillRepeatedly(RunClosure(barrier));
  120. SetPadsConnected(/*connected_count=*/0);
  121. loop.Run();
  122. }
  123. }
  124. TEST_F(GamepadServiceTest, ConnectionThenGestureTest) {
  125. // Create an active consumer.
  126. auto* consumer = CreateConsumer();
  127. EXPECT_TRUE(service()->ConsumerBecameActive(consumer));
  128. WaitForData();
  129. // Connect gamepads. The consumer is not notified because there is no gesture.
  130. SetPadsConnected(/*connected_count=*/kNumberOfGamepads);
  131. WaitForData();
  132. // Simulate a user gesture. The consumer is notified for each connected
  133. // gamepad.
  134. {
  135. base::RunLoop loop;
  136. auto barrier = base::BarrierClosure(kNumberOfGamepads, loop.QuitClosure());
  137. EXPECT_CALL(*consumer, OnGamepadConnected)
  138. .Times(kNumberOfGamepads)
  139. .WillRepeatedly(RunClosure(barrier));
  140. SimulateUserGesture(/*has_gesture=*/true);
  141. loop.Run();
  142. }
  143. }
  144. TEST_F(GamepadServiceTest, ReloadTest) {
  145. // Create an active consumer.
  146. auto* consumer = CreateConsumer();
  147. EXPECT_TRUE(service()->ConsumerBecameActive(consumer));
  148. WaitForData();
  149. // Connect gamepads. The consumer is not notified because there is no gesture.
  150. SetPadsConnected(/*connected_count=*/kNumberOfGamepads);
  151. WaitForData();
  152. // Simulate a page reload. The consumer is not notified because there is still
  153. // no gesture.
  154. SimulatePageReload(consumer);
  155. WaitForData();
  156. // Simulate a user gesture. The consumer is notified for each gamepad.
  157. {
  158. base::RunLoop loop;
  159. auto barrier = base::BarrierClosure(kNumberOfGamepads, loop.QuitClosure());
  160. EXPECT_CALL(*consumer, OnGamepadConnected)
  161. .Times(kNumberOfGamepads)
  162. .WillRepeatedly(RunClosure(barrier));
  163. SimulateUserGesture(/*has_gesture=*/true);
  164. loop.Run();
  165. }
  166. // Simulate another page reload. The consumer is notified again for each
  167. // gamepad.
  168. {
  169. base::RunLoop loop;
  170. auto barrier = base::BarrierClosure(kNumberOfGamepads, loop.QuitClosure());
  171. EXPECT_CALL(*consumer, OnGamepadConnected)
  172. .Times(kNumberOfGamepads)
  173. .WillRepeatedly(RunClosure(barrier));
  174. SimulatePageReload(consumer);
  175. loop.Run();
  176. }
  177. }
  178. TEST_F(GamepadServiceTest, SecondConsumerGestureTest) {
  179. // Create two consumers and mark the first consumer active.
  180. auto* consumer1 = CreateConsumer();
  181. auto* consumer2 = CreateConsumer();
  182. EXPECT_TRUE(service()->ConsumerBecameActive(consumer1));
  183. WaitForData();
  184. // Connect gamepads and simulate a user gesture. The active consumer is
  185. // notified, the inactive consumer is not.
  186. {
  187. base::RunLoop loop;
  188. auto barrier = base::BarrierClosure(kNumberOfGamepads, loop.QuitClosure());
  189. EXPECT_CALL(*consumer1, OnGamepadConnected)
  190. .Times(kNumberOfGamepads)
  191. .WillRepeatedly(RunClosure(barrier));
  192. SetPadsConnected(/*connected_count=*/kNumberOfGamepads);
  193. SimulateUserGesture(/*has_gesture=*/true);
  194. loop.Run();
  195. }
  196. // Restore the default gamepad state (no gesture).
  197. SimulateUserGesture(/*has_gesture=*/false);
  198. // Mark the second consumer active. The second consumer is not notified
  199. // because it needs a new user gesture.
  200. EXPECT_TRUE(service()->ConsumerBecameActive(consumer2));
  201. WaitForData();
  202. // Simulate another user gesture. Only the second consumer is notified.
  203. {
  204. base::RunLoop loop;
  205. auto barrier = base::BarrierClosure(kNumberOfGamepads, loop.QuitClosure());
  206. EXPECT_CALL(*consumer2, OnGamepadConnected)
  207. .Times(kNumberOfGamepads)
  208. .WillRepeatedly(RunClosure(barrier));
  209. SetPadsConnected(/*connected_count=*/kNumberOfGamepads);
  210. SimulateUserGesture(/*has_gesture=*/true);
  211. loop.Run();
  212. }
  213. }
  214. TEST_F(GamepadServiceTest, ConnectWhileInactiveTest) {
  215. // Create two consumers and mark them both active.
  216. auto* consumer1 = CreateConsumer();
  217. auto* consumer2 = CreateConsumer();
  218. EXPECT_TRUE(service()->ConsumerBecameActive(consumer1));
  219. EXPECT_TRUE(service()->ConsumerBecameActive(consumer2));
  220. // Connect gamepads and simulate a user gesture. Each consumer is notified for
  221. // each connected gamepad.
  222. {
  223. base::RunLoop loop;
  224. auto barrier =
  225. base::BarrierClosure(2 * kNumberOfGamepads, loop.QuitClosure());
  226. EXPECT_CALL(*consumer1, OnGamepadConnected)
  227. .Times(kNumberOfGamepads)
  228. .WillRepeatedly(RunClosure(barrier));
  229. EXPECT_CALL(*consumer2, OnGamepadConnected)
  230. .Times(kNumberOfGamepads)
  231. .WillRepeatedly(RunClosure(barrier));
  232. SimulateUserGesture(/*has_gesture=*/true);
  233. SetPadsConnected(/*connected_count=*/kNumberOfGamepads);
  234. loop.Run();
  235. }
  236. // Disconnect gamepads. Each consumer is notified for each disconnected
  237. // gamepad.
  238. {
  239. base::RunLoop loop;
  240. auto barrier =
  241. base::BarrierClosure(2 * kNumberOfGamepads, loop.QuitClosure());
  242. EXPECT_CALL(*consumer1, OnGamepadDisconnected)
  243. .Times(kNumberOfGamepads)
  244. .WillRepeatedly(RunClosure(barrier));
  245. EXPECT_CALL(*consumer2, OnGamepadDisconnected)
  246. .Times(kNumberOfGamepads)
  247. .WillRepeatedly(RunClosure(barrier));
  248. SetPadsConnected(/*connected_count=*/0);
  249. loop.Run();
  250. }
  251. // Mark the second consumer inactive.
  252. EXPECT_TRUE(service()->ConsumerBecameInactive(consumer2));
  253. WaitForData();
  254. // Connect gamepads. Only the active consumer is notified.
  255. {
  256. base::RunLoop loop;
  257. auto barrier = base::BarrierClosure(kNumberOfGamepads, loop.QuitClosure());
  258. EXPECT_CALL(*consumer1, OnGamepadConnected)
  259. .Times(kNumberOfGamepads)
  260. .WillRepeatedly(RunClosure(barrier));
  261. SetPadsConnected(/*connected_count=*/kNumberOfGamepads);
  262. loop.Run();
  263. }
  264. // Mark the second consumer active again. The second consumer is notified
  265. // because it already received a gesture.
  266. {
  267. base::RunLoop loop;
  268. auto barrier = base::BarrierClosure(kNumberOfGamepads, loop.QuitClosure());
  269. EXPECT_CALL(*consumer2, OnGamepadConnected)
  270. .Times(kNumberOfGamepads)
  271. .WillRepeatedly(RunClosure(barrier));
  272. EXPECT_TRUE(service()->ConsumerBecameActive(consumer2));
  273. loop.Run();
  274. }
  275. }
  276. TEST_F(GamepadServiceTest, ConnectAndDisconnectWhileInactiveTest) {
  277. // Create two active consumers.
  278. auto* consumer1 = CreateConsumer();
  279. auto* consumer2 = CreateConsumer();
  280. EXPECT_TRUE(service()->ConsumerBecameActive(consumer1));
  281. EXPECT_TRUE(service()->ConsumerBecameActive(consumer2));
  282. // Connect a gamepad and simulate a user gesture.
  283. {
  284. base::RunLoop loop;
  285. auto barrier = base::BarrierClosure(2, loop.QuitClosure());
  286. EXPECT_CALL(*consumer1, OnGamepadConnected)
  287. .Times(1)
  288. .WillRepeatedly(RunClosure(barrier));
  289. EXPECT_CALL(*consumer2, OnGamepadConnected)
  290. .Times(1)
  291. .WillRepeatedly(RunClosure(barrier));
  292. SetPadsConnected(/*connected_count=*/1);
  293. SimulateUserGesture(/*has_gesture=*/true);
  294. loop.Run();
  295. }
  296. // Disconnect the gamepad.
  297. {
  298. base::RunLoop loop;
  299. auto barrier = base::BarrierClosure(2, loop.QuitClosure());
  300. EXPECT_CALL(*consumer1, OnGamepadDisconnected)
  301. .Times(1)
  302. .WillRepeatedly(RunClosure(barrier));
  303. EXPECT_CALL(*consumer2, OnGamepadDisconnected)
  304. .Times(1)
  305. .WillRepeatedly(RunClosure(barrier));
  306. SetPadsConnected(/*connected_count=*/0);
  307. loop.Run();
  308. }
  309. // Mark the second consumer inactive.
  310. EXPECT_TRUE(service()->ConsumerBecameInactive(consumer2));
  311. WaitForData();
  312. // Connect gamepads. Only the active consumer is notified.
  313. {
  314. base::RunLoop loop;
  315. auto barrier = base::BarrierClosure(kNumberOfGamepads, loop.QuitClosure());
  316. EXPECT_CALL(*consumer1, OnGamepadConnected)
  317. .Times(kNumberOfGamepads)
  318. .WillRepeatedly(RunClosure(barrier));
  319. SetPadsConnected(/*connected_count=*/kNumberOfGamepads);
  320. loop.Run();
  321. }
  322. // Disconnect gamepads. Only the active consumer is notified.
  323. {
  324. base::RunLoop loop;
  325. auto barrier = base::BarrierClosure(kNumberOfGamepads, loop.QuitClosure());
  326. EXPECT_CALL(*consumer1, OnGamepadDisconnected)
  327. .Times(kNumberOfGamepads)
  328. .WillRepeatedly(RunClosure(barrier));
  329. SetPadsConnected(/*connected_count=*/0);
  330. loop.Run();
  331. }
  332. // Mark the second consumer active again. The second consumer is not notified
  333. // because the connected gamepads were disconnected while the consumer was
  334. // still inactive.
  335. EXPECT_TRUE(service()->ConsumerBecameActive(consumer2));
  336. WaitForData();
  337. }
  338. // https://crbug.com/1346527 Flaky on Android and Linux.
  339. TEST_F(GamepadServiceTest, DISABLED_DisconnectWhileInactiveTest) {
  340. // Create two active consumers.
  341. auto* consumer1 = CreateConsumer();
  342. auto* consumer2 = CreateConsumer();
  343. EXPECT_TRUE(service()->ConsumerBecameActive(consumer1));
  344. EXPECT_TRUE(service()->ConsumerBecameActive(consumer2));
  345. // Connect gamepads and simulate a user gesture.
  346. {
  347. base::RunLoop loop;
  348. auto barrier =
  349. base::BarrierClosure(2 * kNumberOfGamepads, loop.QuitClosure());
  350. EXPECT_CALL(*consumer1, OnGamepadConnected)
  351. .Times(kNumberOfGamepads)
  352. .WillRepeatedly(RunClosure(barrier));
  353. EXPECT_CALL(*consumer2, OnGamepadConnected)
  354. .Times(kNumberOfGamepads)
  355. .WillRepeatedly(RunClosure(barrier));
  356. SetPadsConnected(/*connected_count=*/kNumberOfGamepads);
  357. SimulateUserGesture(/*has_gesture=*/true);
  358. loop.Run();
  359. }
  360. // Mark the second consumer inactive.
  361. EXPECT_TRUE(service()->ConsumerBecameInactive(consumer2));
  362. WaitForData();
  363. // Disconnect gamepads. Only the active consumer is notified.
  364. {
  365. base::RunLoop loop;
  366. auto barrier = base::BarrierClosure(kNumberOfGamepads, loop.QuitClosure());
  367. EXPECT_CALL(*consumer1, OnGamepadDisconnected)
  368. .Times(kNumberOfGamepads)
  369. .WillRepeatedly(RunClosure(barrier));
  370. SetPadsConnected(/*connected_count=*/0);
  371. loop.Run();
  372. }
  373. // Mark the second consumer active again. The second consumer is notified for
  374. // gamepads that were disconnected while it was inactive.
  375. {
  376. base::RunLoop loop;
  377. auto barrier = base::BarrierClosure(kNumberOfGamepads, loop.QuitClosure());
  378. EXPECT_CALL(*consumer2, OnGamepadDisconnected)
  379. .Times(kNumberOfGamepads)
  380. .WillRepeatedly(RunClosure(barrier));
  381. EXPECT_TRUE(service()->ConsumerBecameActive(consumer2));
  382. loop.Run();
  383. }
  384. }
  385. TEST_F(GamepadServiceTest, DisconnectAndConnectWhileInactiveTest) {
  386. // Create two active consumers.
  387. auto* consumer1 = CreateConsumer();
  388. auto* consumer2 = CreateConsumer();
  389. EXPECT_TRUE(service()->ConsumerBecameActive(consumer1));
  390. EXPECT_TRUE(service()->ConsumerBecameActive(consumer2));
  391. // Connect gamepads and simulate a user gesture.
  392. {
  393. base::RunLoop loop;
  394. auto barrier =
  395. base::BarrierClosure(2 * kNumberOfGamepads, loop.QuitClosure());
  396. EXPECT_CALL(*consumer1, OnGamepadConnected)
  397. .Times(kNumberOfGamepads)
  398. .WillRepeatedly(RunClosure(barrier));
  399. EXPECT_CALL(*consumer2, OnGamepadConnected)
  400. .Times(kNumberOfGamepads)
  401. .WillRepeatedly(RunClosure(barrier));
  402. SetPadsConnected(/*connected_count=*/kNumberOfGamepads);
  403. SimulateUserGesture(/*has_gesture=*/true);
  404. loop.Run();
  405. }
  406. // Mark the second consumer inactive.
  407. EXPECT_TRUE(service()->ConsumerBecameInactive(consumer2));
  408. WaitForData();
  409. // Disconnect gamepads. Only the active consumer is notified.
  410. {
  411. base::RunLoop loop;
  412. auto barrier = base::BarrierClosure(kNumberOfGamepads, loop.QuitClosure());
  413. EXPECT_CALL(*consumer1, OnGamepadDisconnected)
  414. .Times(kNumberOfGamepads)
  415. .WillRepeatedly(RunClosure(barrier));
  416. SetPadsConnected(/*connected_count=*/0);
  417. loop.Run();
  418. }
  419. // Connect gamepads. Only the active consumer is notified.
  420. {
  421. base::RunLoop loop;
  422. auto barrier = base::BarrierClosure(kNumberOfGamepads, loop.QuitClosure());
  423. EXPECT_CALL(*consumer1, OnGamepadConnected)
  424. .Times(kNumberOfGamepads)
  425. .WillRepeatedly(RunClosure(barrier));
  426. SetPadsConnected(/*connected_count=*/kNumberOfGamepads);
  427. loop.Run();
  428. }
  429. // Mark the second consumer active again. The second consumer is notified for
  430. // gamepads that were connected while it was inactive.
  431. {
  432. base::RunLoop loop;
  433. auto barrier = base::BarrierClosure(kNumberOfGamepads, loop.QuitClosure());
  434. EXPECT_CALL(*consumer2, OnGamepadConnected)
  435. .Times(kNumberOfGamepads)
  436. .WillRepeatedly(RunClosure(barrier));
  437. EXPECT_TRUE(service()->ConsumerBecameActive(consumer2));
  438. loop.Run();
  439. }
  440. }
  441. TEST_F(GamepadServiceTest, ActiveConsumerBecameActive) {
  442. // Mark |consumer| active.
  443. auto* consumer = CreateConsumer();
  444. EXPECT_TRUE(service()->ConsumerBecameActive(consumer));
  445. // Mark |consumer| active a second time. ConsumerBecameActive should fail.
  446. EXPECT_FALSE(service()->ConsumerBecameActive(consumer));
  447. }
  448. TEST_F(GamepadServiceTest, InactiveConsumerBecameInactive) {
  449. // Mark |consumer| active.
  450. auto* consumer = CreateConsumer();
  451. EXPECT_TRUE(service()->ConsumerBecameActive(consumer));
  452. // Mark |consumer| inactive.
  453. EXPECT_TRUE(service()->ConsumerBecameInactive(consumer));
  454. // Mark |consumer| inactive a second time. ConsumerBecameInactive should fail.
  455. EXPECT_FALSE(service()->ConsumerBecameInactive(consumer));
  456. }
  457. TEST_F(GamepadServiceTest, UnregisteredConsumerBecameInactive) {
  458. auto* consumer = CreateConsumer();
  459. // |consumer| has not yet been added to the gamepad service through a call to
  460. // ConsumerBecameActive. ConsumerBecameInactive should fail.
  461. EXPECT_FALSE(service()->ConsumerBecameInactive(consumer));
  462. }
  463. TEST_F(GamepadServiceTest, RemoveUnregisteredConsumer) {
  464. auto* consumer = CreateConsumer();
  465. // |consumer| has not yet been added to the gamepad service through a call to
  466. // ConsumerBecameActive. RemoveConsumer should fail.
  467. EXPECT_FALSE(service()->RemoveConsumer(consumer));
  468. }
  469. } // namespace device