1 /* 2 * Copyright 2019 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 package android.media.cts; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertNotEquals; 21 import static org.junit.Assert.assertNotNull; 22 import static org.junit.Assert.assertNull; 23 import static org.junit.Assert.assertFalse; 24 import static org.junit.Assert.assertSame; 25 import static org.junit.Assert.assertTrue; 26 import static org.junit.Assert.fail; 27 28 import android.app.PendingIntent; 29 import android.content.ComponentName; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.media.MediaController2; 33 import android.media.MediaMetadata; 34 import android.media.MediaSession2; 35 import android.media.Session2Command; 36 import android.media.Session2CommandGroup; 37 import android.media.Session2Token; 38 import android.media.session.MediaSessionManager; 39 import android.os.Bundle; 40 import android.os.Handler; 41 import android.os.HandlerThread; 42 import android.os.Parcel; 43 import android.os.Parcelable; 44 import android.os.Process; 45 46 import androidx.test.InstrumentationRegistry; 47 import androidx.test.filters.SmallTest; 48 import androidx.test.runner.AndroidJUnit4; 49 50 import org.junit.AfterClass; 51 import org.junit.Before; 52 import org.junit.BeforeClass; 53 import org.junit.Test; 54 import org.junit.runner.RunWith; 55 56 import java.util.concurrent.CountDownLatch; 57 import java.util.concurrent.Executor; 58 import java.util.concurrent.TimeUnit; 59 import java.util.List; 60 import java.util.Objects; 61 62 /** 63 * Tests {@link android.media.MediaSession2}. 64 */ 65 @RunWith(AndroidJUnit4.class) 66 @SmallTest 67 public class MediaSession2Test { 68 private static final long WAIT_TIME_MS = 300L; 69 70 private static final String TEST_KEY = "test_key"; 71 private static final String TEST_VALUE = "test_value"; 72 73 static Handler sHandler; 74 static Executor sHandlerExecutor; 75 final static Object sTestLock = new Object(); 76 77 private Context mContext; 78 79 @BeforeClass setUpThread()80 public static void setUpThread() { 81 synchronized (MediaSession2Test.class) { 82 if (sHandler != null) { 83 return; 84 } 85 HandlerThread handlerThread = new HandlerThread("MediaSessionTestBase"); 86 handlerThread.start(); 87 sHandler = new Handler(handlerThread.getLooper()); 88 sHandlerExecutor = (runnable) -> { 89 Handler handler; 90 synchronized (MediaSession2Test.class) { 91 handler = sHandler; 92 } 93 if (handler != null) { 94 handler.post(() -> { 95 synchronized (sTestLock) { 96 runnable.run(); 97 } 98 }); 99 } 100 }; 101 } 102 } 103 104 @AfterClass cleanUpThread()105 public static void cleanUpThread() { 106 synchronized (MediaSession2Test.class) { 107 if (sHandler == null) { 108 return; 109 } 110 sHandler.getLooper().quitSafely(); 111 sHandler = null; 112 sHandlerExecutor = null; 113 } 114 } 115 116 @Before setUp()117 public void setUp() throws Exception { 118 mContext = InstrumentationRegistry.getContext(); 119 } 120 121 @Test testBuilder_setIllegalArguments()122 public void testBuilder_setIllegalArguments() { 123 MediaSession2.Builder builder; 124 try { 125 builder = new MediaSession2.Builder(null); 126 fail("null context shouldn't be allowed"); 127 } catch (IllegalArgumentException e) { 128 // expected. pass-through 129 } 130 try { 131 builder = new MediaSession2.Builder(mContext); 132 builder.setId(null); 133 fail("null id shouldn't be allowed"); 134 } catch (IllegalArgumentException e) { 135 // expected. pass-through 136 } 137 } 138 139 @Test testBuilder_setSessionActivity()140 public void testBuilder_setSessionActivity() { 141 Intent intent = new Intent(Intent.ACTION_MAIN); 142 PendingIntent pendingIntent = PendingIntent.getActivity( 143 mContext, 0 /* requestCode */, intent, 0 /* flags */); 144 try (MediaSession2 session = new MediaSession2.Builder(mContext) 145 .setSessionActivity(pendingIntent) 146 .build()) { 147 // Note: The pendingIntent is set but is never used inside of MediaSession2. 148 // TODO: If getter is created, put assertEquals() here. 149 } 150 } 151 152 @Test testBuilder_createSessionWithoutId()153 public void testBuilder_createSessionWithoutId() { 154 try (MediaSession2 session = new MediaSession2.Builder(mContext).build()) { 155 assertEquals("", session.getId()); 156 } 157 } 158 159 @Test testBuilder_createSessionWithDupId()160 public void testBuilder_createSessionWithDupId() { 161 final String dupSessionId = "TEST_SESSION_DUP_ID"; 162 MediaSession2.Builder builder = new MediaSession2.Builder(mContext).setId(dupSessionId); 163 try ( 164 MediaSession2 session1 = builder.build(); 165 MediaSession2 session2 = builder.build() 166 ) { 167 fail("Duplicated id shouldn't be allowed"); 168 } catch (IllegalStateException e) { 169 // expected. pass-through 170 } 171 } 172 173 @Test testBuilder_setExtras_withFrameworkParcelable()174 public void testBuilder_setExtras_withFrameworkParcelable() { 175 final String testKey = "test_key"; 176 final Session2Token frameworkParcelable = new Session2Token(mContext, 177 new ComponentName(mContext, this.getClass())); 178 179 Bundle extras = new Bundle(); 180 extras.putParcelable(testKey, frameworkParcelable); 181 182 try (MediaSession2 session = new MediaSession2.Builder(mContext) 183 .setExtras(extras) 184 .build()) { 185 Bundle extrasOut = session.getToken().getExtras(); 186 assertNotNull(extrasOut); 187 assertTrue(extrasOut.containsKey(testKey)); 188 assertEquals(frameworkParcelable, extrasOut.getParcelable(testKey)); 189 } 190 } 191 192 @Test testBuilder_setExtras_withCustomParcelable()193 public void testBuilder_setExtras_withCustomParcelable() { 194 final String testKey = "test_key"; 195 final CustomParcelable customParcelable = new CustomParcelable(1); 196 197 Bundle extras = new Bundle(); 198 extras.putParcelable(testKey, customParcelable); 199 200 try (MediaSession2 session = new MediaSession2.Builder(mContext) 201 .setExtras(extras) 202 .build()) { 203 fail("Custom Parcelables shouldn't be accepted!"); 204 } catch (IllegalArgumentException e) { 205 // Expected 206 } 207 } 208 209 @Test testSession2Token()210 public void testSession2Token() { 211 final Bundle extras = new Bundle(); 212 try (MediaSession2 session = new MediaSession2.Builder(mContext) 213 .setExtras(extras) 214 .build()) { 215 Session2Token token = session.getToken(); 216 assertEquals(Process.myUid(), token.getUid()); 217 assertEquals(mContext.getPackageName(), token.getPackageName()); 218 assertNull(token.getServiceName()); 219 assertEquals(Session2Token.TYPE_SESSION, token.getType()); 220 assertEquals(0, token.describeContents()); 221 assertTrue(token.getExtras().isEmpty()); 222 } 223 } 224 225 @Test testSession2Token_extrasNotSet()226 public void testSession2Token_extrasNotSet() { 227 try (MediaSession2 session = new MediaSession2.Builder(mContext) 228 .build()) { 229 Session2Token token = session.getToken(); 230 assertTrue(token.getExtras().isEmpty()); 231 } 232 } 233 234 @Test testGetConnectedControllers_newController()235 public void testGetConnectedControllers_newController() throws Exception { 236 Session2Callback sessionCallback = new Session2Callback(); 237 try (MediaSession2 session = new MediaSession2.Builder(mContext) 238 .setSessionCallback(sHandlerExecutor, sessionCallback) 239 .build()) { 240 Controller2Callback callback = new Controller2Callback(); 241 MediaController2 controller = 242 new MediaController2.Builder(mContext, session.getToken()) 243 .setControllerCallback(sHandlerExecutor, callback) 244 .build(); 245 assertTrue(callback.awaitOnConnected(WAIT_TIME_MS)); 246 247 List<MediaSession2.ControllerInfo> controllers = session.getConnectedControllers(); 248 boolean found = false; 249 for (MediaSession2.ControllerInfo controllerInfo : controllers) { 250 if (Objects.equals(sessionCallback.mController, controllerInfo)) { 251 assertEquals(Process.myUid(), controllerInfo.getUid()); 252 found = true; 253 break; 254 } 255 } 256 assertTrue(found); 257 } 258 } 259 260 @Test testGetConnectedControllers_closedController()261 public void testGetConnectedControllers_closedController() throws Exception { 262 Session2Callback sessionCallback = new Session2Callback(); 263 try (MediaSession2 session = new MediaSession2.Builder(mContext) 264 .setSessionCallback(sHandlerExecutor, sessionCallback) 265 .build()) { 266 Controller2Callback callback = new Controller2Callback(); 267 MediaController2 controller = 268 new MediaController2.Builder(mContext, session.getToken()) 269 .setControllerCallback(sHandlerExecutor, callback) 270 .build(); 271 assertTrue(callback.awaitOnConnected(WAIT_TIME_MS)); 272 controller.close(); 273 assertTrue(sessionCallback.awaitOnDisconnect(WAIT_TIME_MS)); 274 275 List<MediaSession2.ControllerInfo> controllers = session.getConnectedControllers(); 276 for (MediaSession2.ControllerInfo controllerInfo : controllers) { 277 assertNotEquals(sessionCallback.mController, controllerInfo); 278 } 279 } 280 } 281 282 @Test testSession2Token_writeToParcel()283 public void testSession2Token_writeToParcel() { 284 final Bundle extras = new Bundle(); 285 extras.putString(TEST_KEY, TEST_VALUE); 286 287 try (MediaSession2 session = new MediaSession2.Builder(mContext) 288 .setExtras(extras) 289 .build()) { 290 Session2Token token = session.getToken(); 291 292 Parcel parcel = Parcel.obtain(); 293 token.writeToParcel(parcel, 0 /* flags */); 294 parcel.setDataPosition(0); 295 Session2Token tokenOut = Session2Token.CREATOR.createFromParcel(parcel); 296 parcel.recycle(); 297 298 assertEquals(Process.myUid(), tokenOut.getUid()); 299 assertEquals(mContext.getPackageName(), tokenOut.getPackageName()); 300 assertNull(tokenOut.getServiceName()); 301 assertEquals(Session2Token.TYPE_SESSION, tokenOut.getType()); 302 303 Bundle extrasOut = tokenOut.getExtras(); 304 assertNotNull(extrasOut); 305 assertEquals(TEST_VALUE, extrasOut.getString(TEST_KEY)); 306 } 307 } 308 309 @Test testBroadcastSessionCommand()310 public void testBroadcastSessionCommand() throws Exception { 311 Session2Callback sessionCallback = new Session2Callback(); 312 313 String commandStr = "test_command"; 314 Session2Command command = new Session2Command(commandStr, null); 315 316 int resultCode = 100; 317 Session2Command.Result commandResult = new Session2Command.Result(resultCode, null); 318 319 try (MediaSession2 session = new MediaSession2.Builder(mContext) 320 .setSessionCallback(sHandlerExecutor, sessionCallback) 321 .build()) { 322 323 // 1. Create two controllers with each latch. 324 final CountDownLatch latch1 = new CountDownLatch(1); 325 Controller2Callback callback1 = new Controller2Callback() { 326 @Override 327 public Session2Command.Result onSessionCommand(MediaController2 controller, 328 Session2Command command, Bundle args) { 329 if (commandStr.equals(command.getCustomAction()) 330 && command.getCustomExtras() == null) { 331 latch1.countDown(); 332 } 333 return commandResult; 334 } 335 }; 336 337 MediaController2 controller1 = 338 new MediaController2.Builder(mContext, session.getToken()) 339 .setControllerCallback(sHandlerExecutor, callback1) 340 .build(); 341 342 final CountDownLatch latch2 = new CountDownLatch(1); 343 Controller2Callback callback2 = new Controller2Callback() { 344 @Override 345 public Session2Command.Result onSessionCommand(MediaController2 controller, 346 Session2Command command, Bundle args) { 347 if (commandStr.equals(command.getCustomAction()) 348 && command.getCustomExtras() == null) { 349 latch2.countDown(); 350 } 351 return commandResult; 352 } 353 }; 354 MediaController2 controller2 = 355 new MediaController2.Builder(mContext, session.getToken()) 356 .setControllerCallback(sHandlerExecutor, callback2) 357 .build(); 358 359 // 2. Wait until all the controllers are connected. 360 assertTrue(callback1.awaitOnConnected(WAIT_TIME_MS)); 361 assertTrue(callback2.awaitOnConnected(WAIT_TIME_MS)); 362 363 // 3. Call MediaSession2#broadcastSessionCommand() and check both controller's 364 // onSessionCommand is called. 365 session.broadcastSessionCommand(command, null); 366 assertTrue(latch1.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 367 assertTrue(latch2.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 368 } 369 } 370 371 @Test testCallback_onConnect_onDisconnect()372 public void testCallback_onConnect_onDisconnect() throws Exception { 373 Session2Callback sessionCallback = new Session2Callback(); 374 try (MediaSession2 session = new MediaSession2.Builder(mContext) 375 .setSessionCallback(sHandlerExecutor, sessionCallback) 376 .build()) { 377 // Test onConnect 378 Controller2Callback controllerCallback = new Controller2Callback(); 379 Bundle testConnectionHints = new Bundle(); 380 testConnectionHints.putString("test_key", "test_value"); 381 382 MediaController2 controller = 383 new MediaController2.Builder(mContext, session.getToken()) 384 .setConnectionHints(testConnectionHints) 385 .setControllerCallback(sHandlerExecutor, controllerCallback) 386 .build(); 387 assertTrue(controllerCallback.awaitOnConnected(WAIT_TIME_MS)); 388 assertTrue(sessionCallback.awaitOnConnect(WAIT_TIME_MS)); 389 assertEquals(session, sessionCallback.mSession); 390 MediaSession2.ControllerInfo controllerInfo = sessionCallback.mController; 391 392 // Check whether the controllerInfo is the right one. 393 assertEquals(mContext.getPackageName(), controllerInfo.getPackageName()); 394 MediaSessionManager.RemoteUserInfo remoteUserInfo = controllerInfo.getRemoteUserInfo(); 395 assertEquals(Process.myPid(), remoteUserInfo.getPid()); 396 assertEquals(Process.myUid(), remoteUserInfo.getUid()); 397 assertEquals(mContext.getPackageName(), remoteUserInfo.getPackageName()); 398 assertTrue(TestUtils.equals(testConnectionHints, controllerInfo.getConnectionHints())); 399 400 // Test onDisconnect 401 controller.close(); 402 assertTrue(controllerCallback.awaitOnDisconnected(WAIT_TIME_MS)); 403 assertTrue(sessionCallback.awaitOnDisconnect(WAIT_TIME_MS)); 404 assertEquals(session, sessionCallback.mSession); 405 assertEquals(controllerInfo, sessionCallback.mController); 406 } 407 } 408 409 @Test testCallback_onPostConnect_connected()410 public void testCallback_onPostConnect_connected() throws Exception { 411 Session2Callback sessionCallback = new Session2Callback(); 412 try (MediaSession2 session = new MediaSession2.Builder(mContext) 413 .setSessionCallback(sHandlerExecutor, sessionCallback) 414 .build()) { 415 Controller2Callback controllerCallback = new Controller2Callback(); 416 MediaController2 controller = 417 new MediaController2.Builder(mContext, session.getToken()) 418 .setControllerCallback(sHandlerExecutor, controllerCallback) 419 .build(); 420 assertTrue(controllerCallback.awaitOnConnected(WAIT_TIME_MS)); 421 assertTrue(sessionCallback.awaitOnPostConnect(WAIT_TIME_MS)); 422 assertEquals(Process.myUid(), sessionCallback.mController.getUid()); 423 } 424 } 425 426 @Test testCallback_onPostConnect_rejected()427 public void testCallback_onPostConnect_rejected() throws Exception { 428 Session2Callback sessionCallback = new Session2Callback() { 429 @Override 430 public Session2CommandGroup onConnect(MediaSession2 session, 431 MediaSession2.ControllerInfo controller) { 432 // Reject all 433 return null; 434 } 435 }; 436 try (MediaSession2 session = new MediaSession2.Builder(mContext) 437 .setSessionCallback(sHandlerExecutor, sessionCallback) 438 .build()) { 439 Controller2Callback callback = new Controller2Callback(); 440 441 MediaController2 controller = 442 new MediaController2.Builder(mContext, session.getToken()) 443 .setControllerCallback(sHandlerExecutor, callback) 444 .build(); 445 assertFalse(sessionCallback.awaitOnPostConnect(WAIT_TIME_MS)); 446 } 447 } 448 449 @Test testCallback_onSessionCommand()450 public void testCallback_onSessionCommand() { 451 Session2Callback sessionCallback = new Session2Callback(); 452 453 try (MediaSession2 session = new MediaSession2.Builder(mContext) 454 .setSessionCallback(sHandlerExecutor, sessionCallback) 455 .build()) { 456 Controller2Callback controllerCallback = new Controller2Callback(); 457 MediaController2 controller = 458 new MediaController2.Builder(mContext, session.getToken()) 459 .setControllerCallback(sHandlerExecutor, controllerCallback) 460 .build(); 461 // Wait for connection 462 assertTrue(controllerCallback.awaitOnConnected(WAIT_TIME_MS)); 463 assertTrue(sessionCallback.awaitOnConnect(WAIT_TIME_MS)); 464 MediaSession2.ControllerInfo controllerInfo = sessionCallback.mController; 465 466 // Test onSessionCommand 467 String commandStr = "test_command"; 468 String commandExtraKey = "test_extra_key"; 469 String commandExtraValue = "test_extra_value"; 470 Bundle commandExtra = new Bundle(); 471 commandExtra.putString(commandExtraKey, commandExtraValue); 472 Session2Command command = new Session2Command(commandStr, commandExtra); 473 474 String commandArgKey = "test_arg_key"; 475 String commandArgValue = "test_arg_value"; 476 Bundle commandArg = new Bundle(); 477 commandArg.putString(commandArgKey, commandArgValue); 478 controller.sendSessionCommand(command, commandArg); 479 480 assertTrue(sessionCallback.awaitOnSessionCommand(WAIT_TIME_MS)); 481 assertEquals(session, sessionCallback.mSession); 482 assertEquals(controllerInfo, sessionCallback.mController); 483 assertEquals(commandStr, sessionCallback.mCommand.getCustomAction()); 484 assertEquals(commandExtraValue, 485 sessionCallback.mCommand.getCustomExtras().getString(commandExtraKey)); 486 assertEquals(commandArgValue, sessionCallback.mCommandArgs.getString(commandArgKey)); 487 488 controller.close(); 489 assertTrue(controllerCallback.awaitOnDisconnected(WAIT_TIME_MS)); 490 } 491 } 492 493 @Test testCallback_onCommandResult()494 public void testCallback_onCommandResult() { 495 Session2Callback sessionCallback = new Session2Callback(); 496 497 int resultCode = 100; 498 String commandResultKey = "test_result_key"; 499 String commandResultValue = "test_result_value"; 500 Bundle resultData = new Bundle(); 501 resultData.putString(commandResultKey, commandResultValue); 502 Session2Command.Result commandResult = new Session2Command.Result(resultCode, resultData); 503 504 try (MediaSession2 session = new MediaSession2.Builder(mContext) 505 .setSessionCallback(sHandlerExecutor, sessionCallback) 506 .build()) { 507 Controller2Callback controllerCallback = new Controller2Callback() { 508 @Override 509 public Session2Command.Result onSessionCommand(MediaController2 controller, 510 Session2Command command, Bundle args) { 511 return commandResult; 512 } 513 }; 514 MediaController2 controller = 515 new MediaController2.Builder(mContext, session.getToken()) 516 .setControllerCallback(sHandlerExecutor, controllerCallback) 517 .build(); 518 // Wait for connection 519 assertTrue(sessionCallback.awaitOnConnect(WAIT_TIME_MS)); 520 MediaSession2.ControllerInfo controllerInfo = sessionCallback.mController; 521 522 // Test onCommandResult 523 String commandStr = "test_command"; 524 String commandExtraKey = "test_extra_key"; 525 String commandExtraValue = "test_extra_value"; 526 Bundle commandExtra = new Bundle(); 527 commandExtra.putString(commandExtraKey, commandExtraValue); 528 Session2Command command = new Session2Command(commandStr, commandExtra); 529 530 String commandArgKey = "test_arg_key"; 531 String commandArgValue = "test_arg_value"; 532 Bundle commandArg = new Bundle(); 533 commandArg.putString(commandArgKey, commandArgValue); 534 session.sendSessionCommand(controllerInfo, command, commandArg); 535 536 assertTrue(sessionCallback.awaitOnCommandResult(WAIT_TIME_MS)); 537 assertEquals(session, sessionCallback.mSession); 538 assertEquals(controllerInfo, sessionCallback.mController); 539 assertEquals(resultCode, sessionCallback.mCommandResult.getResultCode()); 540 assertEquals(commandResultValue, 541 sessionCallback.mCommandResult.getResultData().getString(commandResultKey)); 542 543 controller.close(); 544 assertTrue(controllerCallback.awaitOnDisconnected(WAIT_TIME_MS)); 545 } 546 } 547 548 @Test testSetPlaybackActive()549 public void testSetPlaybackActive() { 550 final boolean testInitialPlaybackActive = true; 551 final boolean testPlaybackActive = false; 552 Session2Callback sessionCallback = new Session2Callback(); 553 try (MediaSession2 session = new MediaSession2.Builder(mContext) 554 .setSessionCallback(sHandlerExecutor, sessionCallback) 555 .build()) { 556 session.setPlaybackActive(testInitialPlaybackActive); 557 assertEquals(testInitialPlaybackActive, session.isPlaybackActive()); 558 559 Controller2Callback controllerCallback = new Controller2Callback(); 560 MediaController2 controller = 561 new MediaController2.Builder(mContext, session.getToken()) 562 .setControllerCallback(sHandlerExecutor, controllerCallback) 563 .build(); 564 // Wait for connection 565 assertTrue(controllerCallback.awaitOnConnected(WAIT_TIME_MS)); 566 567 // Check initial value 568 assertEquals(testInitialPlaybackActive, controller.isPlaybackActive()); 569 570 // Change playback active change and wait for changes 571 session.setPlaybackActive(testPlaybackActive); 572 assertEquals(testPlaybackActive, session.isPlaybackActive()); 573 assertTrue(controllerCallback.awaitOnPlaybackActiveChanged(WAIT_TIME_MS)); 574 575 assertEquals(testPlaybackActive, controllerCallback.getNotifiedPlaybackActive()); 576 assertEquals(testPlaybackActive, controller.isPlaybackActive()); 577 578 controller.close(); 579 assertTrue(controllerCallback.awaitOnDisconnected(WAIT_TIME_MS)); 580 } 581 } 582 583 @Test testCancelSessionCommand()584 public void testCancelSessionCommand() { 585 Session2Callback sessionCallback = new Session2Callback(); 586 try (MediaSession2 session = new MediaSession2.Builder(mContext) 587 .setSessionCallback(sHandlerExecutor, sessionCallback) 588 .build()) { 589 Controller2Callback controllerCallback = new Controller2Callback(); 590 MediaController2 controller = 591 new MediaController2.Builder(mContext, session.getToken()) 592 .setControllerCallback(sHandlerExecutor, controllerCallback) 593 .build(); 594 // Wait for connection 595 assertTrue(sessionCallback.awaitOnConnect(WAIT_TIME_MS)); 596 MediaSession2.ControllerInfo controllerInfo = sessionCallback.mController; 597 598 String commandStr = "test_command_"; 599 String commandExtraKey = "test_extra_key_"; 600 String commandExtraValue = "test_extra_value_"; 601 Bundle commandExtra = new Bundle(); 602 commandExtra.putString(commandExtraKey, commandExtraValue); 603 Session2Command command = new Session2Command(commandStr, commandExtra); 604 605 String commandArgKey = "test_arg_key_"; 606 String commandArgValue = "test_arg_value_"; 607 Bundle commandArg = new Bundle(); 608 commandArg.putString(commandArgKey, commandArgValue); 609 synchronized (sTestLock) { 610 Object token = session.sendSessionCommand(controllerInfo, command, commandArg); 611 session.cancelSessionCommand(controllerInfo, token); 612 } 613 assertTrue(sessionCallback.awaitOnCommandResult(WAIT_TIME_MS)); 614 assertEquals(Session2Command.Result.RESULT_INFO_SKIPPED, 615 sessionCallback.mCommandResult.getResultCode()); 616 617 controller.close(); 618 assertTrue(controllerCallback.awaitOnDisconnected(WAIT_TIME_MS)); 619 } 620 } 621 622 class Controller2Callback extends MediaController2.ControllerCallback { 623 private final CountDownLatch mOnConnectedLatch = new CountDownLatch(1); 624 private final CountDownLatch mOnDisconnectedLatch = new CountDownLatch(1); 625 private final CountDownLatch mOnPlaybackActiveChangedLatch = new CountDownLatch(1); 626 627 private boolean mPlaybackActive; 628 629 @Override onConnected(MediaController2 controller, Session2CommandGroup allowedCommands)630 public void onConnected(MediaController2 controller, 631 Session2CommandGroup allowedCommands) { 632 mOnConnectedLatch.countDown(); 633 } 634 635 @Override onDisconnected(MediaController2 controller)636 public void onDisconnected(MediaController2 controller) { 637 mOnDisconnectedLatch.countDown(); 638 } 639 640 @Override onPlaybackActiveChanged(MediaController2 controller, boolean playbackActive)641 public void onPlaybackActiveChanged(MediaController2 controller, boolean playbackActive) { 642 mPlaybackActive = playbackActive; 643 mOnPlaybackActiveChangedLatch.countDown(); 644 } 645 awaitOnConnected(long waitTimeMs)646 public boolean awaitOnConnected(long waitTimeMs) { 647 try { 648 return mOnConnectedLatch.await(waitTimeMs, TimeUnit.MILLISECONDS); 649 } catch (InterruptedException e) { 650 return false; 651 } 652 } 653 awaitOnDisconnected(long waitTimeMs)654 public boolean awaitOnDisconnected(long waitTimeMs) { 655 try { 656 return mOnDisconnectedLatch.await(waitTimeMs, TimeUnit.MILLISECONDS); 657 } catch (InterruptedException e) { 658 return false; 659 } 660 } 661 awaitOnPlaybackActiveChanged(long waitTimeMs)662 public boolean awaitOnPlaybackActiveChanged(long waitTimeMs) { 663 try { 664 return mOnPlaybackActiveChangedLatch.await(waitTimeMs, TimeUnit.MILLISECONDS); 665 } catch (InterruptedException e) { 666 return false; 667 } 668 } 669 getNotifiedPlaybackActive()670 public boolean getNotifiedPlaybackActive() { 671 return mPlaybackActive; 672 } 673 } 674 675 class Session2Callback extends MediaSession2.SessionCallback { 676 private final CountDownLatch mOnConnectLatch = new CountDownLatch(1); 677 private final CountDownLatch mOnPostConnectLatch = new CountDownLatch(1); 678 private final CountDownLatch mOnDisconnectLatch = new CountDownLatch(1); 679 private final CountDownLatch mOnSessionCommandLatch = new CountDownLatch(1); 680 private final CountDownLatch mOnCommandResultLatch = new CountDownLatch(1); 681 682 MediaSession2 mSession; 683 MediaSession2.ControllerInfo mController; 684 Session2Command mCommand; 685 Bundle mCommandArgs; 686 Session2Command.Result mCommandResult; 687 688 @Override onConnect(MediaSession2 session, MediaSession2.ControllerInfo controller)689 public Session2CommandGroup onConnect(MediaSession2 session, 690 MediaSession2.ControllerInfo controller) { 691 super.onConnect(session, controller); 692 if (controller.getUid() != Process.myUid()) { 693 return null; 694 } 695 mSession = session; 696 mController = controller; 697 mOnConnectLatch.countDown(); 698 return new Session2CommandGroup.Builder().build(); 699 } 700 701 @Override onPostConnect(MediaSession2 session, MediaSession2.ControllerInfo controller)702 public void onPostConnect(MediaSession2 session, MediaSession2.ControllerInfo controller) { 703 super.onPostConnect(session, controller); 704 if (controller.getUid() != Process.myUid()) { 705 return; 706 } 707 mSession = session; 708 mController = controller; 709 mOnPostConnectLatch.countDown(); 710 } 711 712 @Override onDisconnected(MediaSession2 session, MediaSession2.ControllerInfo controller)713 public void onDisconnected(MediaSession2 session, MediaSession2.ControllerInfo controller) { 714 super.onDisconnected(session, controller); 715 if (controller.getUid() != Process.myUid()) { 716 return; 717 } 718 mSession = session; 719 mController = controller; 720 mOnDisconnectLatch.countDown(); 721 } 722 723 @Override onSessionCommand(MediaSession2 session, MediaSession2.ControllerInfo controller, Session2Command command, Bundle args)724 public Session2Command.Result onSessionCommand(MediaSession2 session, 725 MediaSession2.ControllerInfo controller, Session2Command command, Bundle args) { 726 super.onSessionCommand(session, controller, command, args); 727 if (controller.getUid() != Process.myUid()) { 728 return null; 729 } 730 mSession = session; 731 mController = controller; 732 mCommand = command; 733 mCommandArgs = args; 734 mOnSessionCommandLatch.countDown(); 735 736 int resultCode = 100; 737 String commandResultKey = "test_result_key"; 738 String commandResultValue = "test_result_value"; 739 Bundle resultData = new Bundle(); 740 resultData.putString(commandResultKey, commandResultValue); 741 Session2Command.Result commandResult = 742 new Session2Command.Result(resultCode, resultData); 743 return commandResult; 744 } 745 746 @Override onCommandResult(MediaSession2 session, MediaSession2.ControllerInfo controller, Object token, Session2Command command, Session2Command.Result result)747 public void onCommandResult(MediaSession2 session, MediaSession2.ControllerInfo controller, 748 Object token, Session2Command command, Session2Command.Result result) { 749 super.onCommandResult(session, controller, token, command, result); 750 if (controller.getUid() != Process.myUid()) { 751 return; 752 } 753 mSession = session; 754 mController = controller; 755 mCommand = command; 756 mCommandResult = result; 757 mOnCommandResultLatch.countDown(); 758 } 759 awaitOnConnect(long waitTimeMs)760 public boolean awaitOnConnect(long waitTimeMs) { 761 try { 762 return mOnConnectLatch.await(waitTimeMs, TimeUnit.MILLISECONDS); 763 } catch (InterruptedException e) { 764 return false; 765 } 766 } 767 awaitOnPostConnect(long waitTimeMs)768 public boolean awaitOnPostConnect(long waitTimeMs) { 769 try { 770 return mOnPostConnectLatch.await(waitTimeMs, TimeUnit.MILLISECONDS); 771 } catch (InterruptedException e) { 772 return false; 773 } 774 } 775 awaitOnDisconnect(long waitTimeMs)776 public boolean awaitOnDisconnect(long waitTimeMs) { 777 try { 778 return mOnDisconnectLatch.await(waitTimeMs, TimeUnit.MILLISECONDS); 779 } catch (InterruptedException e) { 780 return false; 781 } 782 } 783 awaitOnSessionCommand(long waitTimeMs)784 public boolean awaitOnSessionCommand(long waitTimeMs) { 785 try { 786 return mOnSessionCommandLatch.await(waitTimeMs, TimeUnit.MILLISECONDS); 787 } catch (InterruptedException e) { 788 return false; 789 } 790 } 791 awaitOnCommandResult(long waitTimeMs)792 public boolean awaitOnCommandResult(long waitTimeMs) { 793 try { 794 return mOnCommandResultLatch.await(waitTimeMs, TimeUnit.MILLISECONDS); 795 } catch (InterruptedException e) { 796 return false; 797 } 798 } 799 } 800 801 static class CustomParcelable implements Parcelable { 802 public int mValue; 803 CustomParcelable(int value)804 public CustomParcelable(int value) { 805 mValue = value; 806 } 807 808 @Override describeContents()809 public int describeContents() { 810 return 0; 811 } 812 813 @Override writeToParcel(Parcel dest, int flags)814 public void writeToParcel(Parcel dest, int flags) { 815 dest.writeInt(mValue); 816 } 817 818 public static final Parcelable.Creator<CustomParcelable> CREATOR = 819 new Parcelable.Creator<CustomParcelable>() { 820 @Override 821 public CustomParcelable createFromParcel(Parcel in) { 822 int value = in.readInt(); 823 return new CustomParcelable(value); 824 } 825 826 @Override 827 public CustomParcelable[] newArray(int size) { 828 return new CustomParcelable[size]; 829 } 830 }; 831 } 832 } 833