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