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