1/* 2 * Copyright 2018 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 17package androidx.media; 18 19import static androidx.media.MockMediaLibraryService2.EXTRAS; 20import static androidx.media.MockMediaLibraryService2.ROOT_ID; 21 22import static junit.framework.Assert.assertEquals; 23import static junit.framework.Assert.assertFalse; 24import static junit.framework.Assert.assertNotNull; 25import static junit.framework.Assert.assertNull; 26import static junit.framework.Assert.assertTrue; 27import static junit.framework.Assert.fail; 28 29import static org.junit.Assert.assertNotEquals; 30 31import android.content.Context; 32import android.os.Build; 33import android.os.Bundle; 34import android.os.Process; 35import android.os.ResultReceiver; 36import android.support.test.filters.LargeTest; 37import android.support.test.filters.SdkSuppress; 38import android.support.test.filters.SmallTest; 39import android.support.test.runner.AndroidJUnit4; 40 41import androidx.annotation.CallSuper; 42import androidx.annotation.GuardedBy; 43import androidx.annotation.NonNull; 44import androidx.annotation.Nullable; 45import androidx.media.MediaBrowser2.BrowserCallback; 46import androidx.media.MediaController2.ControllerCallback; 47import androidx.media.MediaLibraryService2.MediaLibrarySession; 48import androidx.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback; 49import androidx.media.MediaSession2.CommandButton; 50import androidx.media.MediaSession2.ControllerInfo; 51 52import junit.framework.Assert; 53 54import org.junit.Ignore; 55import org.junit.Test; 56import org.junit.runner.RunWith; 57 58import java.lang.reflect.Method; 59import java.util.List; 60import java.util.concurrent.CountDownLatch; 61import java.util.concurrent.TimeUnit; 62import java.util.concurrent.atomic.AtomicReference; 63 64/** 65 * Tests {@link MediaBrowser2}. 66 * <p> 67 * This test inherits {@link MediaController2Test} to ensure that inherited APIs from 68 * {@link MediaController2} works cleanly. 69 */ 70// TODO(jaewan): Implement host-side test so browser and service can run in different processes. 71@SdkSuppress(minSdkVersion = Build.VERSION_CODES.JELLY_BEAN) 72@RunWith(AndroidJUnit4.class) 73@SmallTest 74public class MediaBrowser2Test extends MediaController2Test { 75 private static final String TAG = "MediaBrowser2Test"; 76 77 @Override 78 TestControllerInterface onCreateController(final @NonNull SessionToken2 token, 79 final @Nullable ControllerCallback callback) throws InterruptedException { 80 final AtomicReference<TestControllerInterface> controller = new AtomicReference<>(); 81 sHandler.postAndSync(new Runnable() { 82 @Override 83 public void run() { 84 // Create controller on the test handler, for changing MediaBrowserCompat's Handler 85 // Looper. Otherwise, MediaBrowserCompat will post all the commands to the handler 86 // and commands wouldn't be run if tests codes waits on the test handler. 87 controller.set(new TestMediaBrowser( 88 mContext, token, new TestBrowserCallback(callback))); 89 } 90 }); 91 return controller.get(); 92 } 93 94 /** 95 * Test if the {@link TestBrowserCallback} wraps the callback proxy without missing any method. 96 */ 97 @Test 98 public void testTestBrowserCallback() { 99 prepareLooper(); 100 Method[] methods = TestBrowserCallback.class.getMethods(); 101 assertNotNull(methods); 102 for (int i = 0; i < methods.length; i++) { 103 // For any methods in the controller callback, TestControllerCallback should have 104 // overriden the method and call matching API in the callback proxy. 105 assertNotEquals("TestBrowserCallback should override " + methods[i] 106 + " and call callback proxy", 107 BrowserCallback.class, methods[i].getDeclaringClass()); 108 } 109 } 110 111 @Test 112 public void testGetLibraryRoot() throws InterruptedException { 113 prepareLooper(); 114 final Bundle param = new Bundle(); 115 param.putString(TAG, TAG); 116 117 final CountDownLatch latch = new CountDownLatch(1); 118 final BrowserCallback callback = new BrowserCallback() { 119 @Override 120 public void onGetLibraryRootDone(MediaBrowser2 browser, 121 Bundle rootHints, String rootMediaId, Bundle rootExtra) { 122 assertTrue(TestUtils.equals(param, rootHints)); 123 assertEquals(ROOT_ID, rootMediaId); 124 // Note that TestUtils#equals() cannot be used for this because 125 // MediaBrowserServiceCompat adds extra_client_version to the rootHints. 126 assertTrue(TestUtils.contains(rootExtra, EXTRAS)); 127 latch.countDown(); 128 } 129 }; 130 131 final SessionToken2 token = MockMediaLibraryService2.getToken(mContext); 132 MediaBrowser2 browser = 133 (MediaBrowser2) createController(token, true, callback); 134 browser.getLibraryRoot(param); 135 assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 136 } 137 138 @Test 139 public void testGetItem() throws InterruptedException { 140 prepareLooper(); 141 final String mediaId = MockMediaLibraryService2.MEDIA_ID_GET_ITEM; 142 143 final CountDownLatch latch = new CountDownLatch(1); 144 final BrowserCallback callback = new BrowserCallback() { 145 @Override 146 public void onGetItemDone(MediaBrowser2 browser, String mediaIdOut, MediaItem2 result) { 147 assertEquals(mediaId, mediaIdOut); 148 assertNotNull(result); 149 assertEquals(mediaId, result.getMediaId()); 150 latch.countDown(); 151 } 152 }; 153 154 final SessionToken2 token = MockMediaLibraryService2.getToken(mContext); 155 MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback); 156 browser.getItem(mediaId); 157 assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 158 } 159 160 @Test 161 public void testGetItemNullResult() throws InterruptedException { 162 prepareLooper(); 163 final String mediaId = "random_media_id"; 164 165 final CountDownLatch latch = new CountDownLatch(1); 166 final BrowserCallback callback = new BrowserCallback() { 167 @Override 168 public void onGetItemDone(MediaBrowser2 browser, String mediaIdOut, MediaItem2 result) { 169 assertEquals(mediaId, mediaIdOut); 170 assertNull(result); 171 latch.countDown(); 172 } 173 }; 174 175 final SessionToken2 token = MockMediaLibraryService2.getToken(mContext); 176 MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback); 177 browser.getItem(mediaId); 178 assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 179 } 180 181 @Test 182 public void testGetChildren() throws InterruptedException { 183 prepareLooper(); 184 final String parentId = MockMediaLibraryService2.PARENT_ID; 185 final int page = 4; 186 final int pageSize = 10; 187 final Bundle extras = new Bundle(); 188 extras.putString(TAG, TAG); 189 190 final CountDownLatch latch = new CountDownLatch(1); 191 final BrowserCallback callback = new BrowserCallback() { 192 @Override 193 public void onGetChildrenDone(MediaBrowser2 browser, String parentIdOut, int pageOut, 194 int pageSizeOut, List<MediaItem2> result, Bundle extrasOut) { 195 assertEquals(parentId, parentIdOut); 196 assertEquals(page, pageOut); 197 assertEquals(pageSize, pageSizeOut); 198 assertTrue(TestUtils.equals(extras, extrasOut)); 199 assertNotNull(result); 200 201 int fromIndex = (page - 1) * pageSize; 202 int toIndex = Math.min(page * pageSize, MockMediaLibraryService2.CHILDREN_COUNT); 203 204 // Compare the given results with originals. 205 for (int originalIndex = fromIndex; originalIndex < toIndex; originalIndex++) { 206 int relativeIndex = originalIndex - fromIndex; 207 Assert.assertEquals( 208 MockMediaLibraryService2.GET_CHILDREN_RESULT.get(originalIndex) 209 .getMediaId(), 210 result.get(relativeIndex).getMediaId()); 211 } 212 latch.countDown(); 213 } 214 }; 215 216 final SessionToken2 token = MockMediaLibraryService2.getToken(mContext); 217 MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback); 218 browser.getChildren(parentId, page, pageSize, extras); 219 assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 220 } 221 222 @Test 223 public void testGetChildrenEmptyResult() throws InterruptedException { 224 prepareLooper(); 225 final String parentId = MockMediaLibraryService2.PARENT_ID_NO_CHILDREN; 226 227 final CountDownLatch latch = new CountDownLatch(1); 228 final BrowserCallback callback = new BrowserCallback() { 229 @Override 230 public void onGetChildrenDone(MediaBrowser2 browser, String parentIdOut, 231 int pageOut, int pageSizeOut, List<MediaItem2> result, Bundle extrasOut) { 232 assertNotNull(result); 233 assertEquals(0, result.size()); 234 latch.countDown(); 235 } 236 }; 237 238 final SessionToken2 token = MockMediaLibraryService2.getToken(mContext); 239 MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback); 240 browser.getChildren(parentId, 1, 1, null); 241 assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 242 } 243 244 @Test 245 public void testGetChildrenNullResult() throws InterruptedException { 246 prepareLooper(); 247 final String parentId = MockMediaLibraryService2.PARENT_ID_ERROR; 248 249 final CountDownLatch latch = new CountDownLatch(1); 250 final BrowserCallback callback = new BrowserCallback() { 251 @Override 252 public void onGetChildrenDone(MediaBrowser2 browser, String parentIdOut, 253 int pageOut, int pageSizeOut, List<MediaItem2> result, Bundle extrasOut) { 254 assertNull(result); 255 latch.countDown(); 256 } 257 }; 258 259 final SessionToken2 token = MockMediaLibraryService2.getToken(mContext); 260 MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback); 261 browser.getChildren(parentId, 1, 1, null); 262 assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 263 } 264 265 @Test 266 public void testSearch() throws InterruptedException { 267 prepareLooper(); 268 final String query = MockMediaLibraryService2.SEARCH_QUERY; 269 final int page = 4; 270 final int pageSize = 10; 271 final Bundle extras = new Bundle(); 272 extras.putString(TAG, TAG); 273 274 final CountDownLatch latchForSearch = new CountDownLatch(1); 275 final CountDownLatch latchForGetSearchResult = new CountDownLatch(1); 276 final BrowserCallback callback = new BrowserCallback() { 277 @Override 278 public void onSearchResultChanged(MediaBrowser2 browser, 279 String queryOut, int itemCount, Bundle extrasOut) { 280 assertEquals(query, queryOut); 281 assertTrue(TestUtils.equals(extras, extrasOut)); 282 assertEquals(MockMediaLibraryService2.SEARCH_RESULT_COUNT, itemCount); 283 latchForSearch.countDown(); 284 } 285 286 @Override 287 public void onGetSearchResultDone(MediaBrowser2 browser, String queryOut, 288 int pageOut, int pageSizeOut, List<MediaItem2> result, Bundle extrasOut) { 289 assertEquals(query, queryOut); 290 assertEquals(page, pageOut); 291 assertEquals(pageSize, pageSizeOut); 292 assertTrue(TestUtils.equals(extras, extrasOut)); 293 assertNotNull(result); 294 295 int fromIndex = (page - 1) * pageSize; 296 int toIndex = Math.min( 297 page * pageSize, MockMediaLibraryService2.SEARCH_RESULT_COUNT); 298 299 // Compare the given results with originals. 300 for (int originalIndex = fromIndex; originalIndex < toIndex; originalIndex++) { 301 int relativeIndex = originalIndex - fromIndex; 302 Assert.assertEquals( 303 MockMediaLibraryService2.SEARCH_RESULT.get(originalIndex).getMediaId(), 304 result.get(relativeIndex).getMediaId()); 305 } 306 latchForGetSearchResult.countDown(); 307 } 308 }; 309 310 // Request the search. 311 final SessionToken2 token = MockMediaLibraryService2.getToken(mContext); 312 MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback); 313 browser.search(query, extras); 314 assertTrue(latchForSearch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 315 316 // Get the search result. 317 browser.getSearchResult(query, page, pageSize, extras); 318 assertTrue(latchForGetSearchResult.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 319 } 320 321 @Test 322 @LargeTest 323 public void testSearchTakesTime() throws InterruptedException { 324 prepareLooper(); 325 final String query = MockMediaLibraryService2.SEARCH_QUERY_TAKES_TIME; 326 final Bundle extras = new Bundle(); 327 extras.putString(TAG, TAG); 328 329 final CountDownLatch latch = new CountDownLatch(1); 330 final BrowserCallback callback = new BrowserCallback() { 331 @Override 332 public void onSearchResultChanged( 333 MediaBrowser2 browser, String queryOut, int itemCount, Bundle extrasOut) { 334 assertEquals(query, queryOut); 335 assertTrue(TestUtils.equals(extras, extrasOut)); 336 assertEquals(MockMediaLibraryService2.SEARCH_RESULT_COUNT, itemCount); 337 latch.countDown(); 338 } 339 }; 340 341 final SessionToken2 token = MockMediaLibraryService2.getToken(mContext); 342 MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback); 343 browser.search(query, extras); 344 assertTrue(latch.await( 345 MockMediaLibraryService2.SEARCH_TIME_IN_MS + WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 346 } 347 348 @Test 349 public void testSearchEmptyResult() throws InterruptedException { 350 prepareLooper(); 351 final String query = MockMediaLibraryService2.SEARCH_QUERY_EMPTY_RESULT; 352 final Bundle extras = new Bundle(); 353 extras.putString(TAG, TAG); 354 355 final CountDownLatch latch = new CountDownLatch(1); 356 final BrowserCallback callback = new BrowserCallback() { 357 @Override 358 public void onSearchResultChanged( 359 MediaBrowser2 browser, String queryOut, int itemCount, Bundle extrasOut) { 360 assertEquals(query, queryOut); 361 assertTrue(TestUtils.equals(extras, extrasOut)); 362 assertEquals(0, itemCount); 363 latch.countDown(); 364 } 365 }; 366 367 final SessionToken2 token = MockMediaLibraryService2.getToken(mContext); 368 MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback); 369 browser.search(query, extras); 370 assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 371 } 372 373 @Test 374 public void testSubscribe() throws InterruptedException { 375 prepareLooper(); 376 final String testParentId = "testSubscribeId"; 377 final Bundle testExtras = new Bundle(); 378 testExtras.putString(testParentId, testParentId); 379 380 final CountDownLatch latch = new CountDownLatch(1); 381 final MediaLibrarySessionCallback callback = new MediaLibrarySessionCallback() { 382 @Override 383 public void onSubscribe(@NonNull MediaLibraryService2.MediaLibrarySession session, 384 @NonNull MediaSession2.ControllerInfo info, @NonNull String parentId, 385 @Nullable Bundle extras) { 386 if (Process.myUid() == info.getUid()) { 387 assertEquals(testParentId, parentId); 388 assertTrue(TestUtils.equals(testExtras, extras)); 389 latch.countDown(); 390 } 391 } 392 }; 393 TestServiceRegistry.getInstance().setSessionCallback(callback); 394 final SessionToken2 token = MockMediaLibraryService2.getToken(mContext); 395 MediaBrowser2 browser = (MediaBrowser2) createController(token); 396 browser.subscribe(testParentId, testExtras); 397 assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 398 } 399 400 @Ignore 401 @Test 402 public void testUnsubscribe() throws InterruptedException { 403 prepareLooper(); 404 final String testParentId = "testUnsubscribeId"; 405 final CountDownLatch latch = new CountDownLatch(1); 406 final MediaLibrarySessionCallback callback = new MediaLibrarySessionCallback() { 407 @Override 408 public void onUnsubscribe(@NonNull MediaLibrarySession session, 409 @NonNull ControllerInfo info, @NonNull String parentId) { 410 if (Process.myUid() == info.getUid()) { 411 assertEquals(testParentId, parentId); 412 latch.countDown(); 413 } 414 } 415 }; 416 TestServiceRegistry.getInstance().setSessionCallback(callback); 417 final SessionToken2 token = MockMediaLibraryService2.getToken(mContext); 418 MediaBrowser2 browser = (MediaBrowser2) createController(token); 419 browser.unsubscribe(testParentId); 420 assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 421 } 422 423 @Test 424 public void testBrowserCallback_onChildrenChangedIsNotCalledWhenNotSubscribed() 425 throws InterruptedException { 426 // This test uses MediaLibrarySession.notifyChildrenChanged(). 427 prepareLooper(); 428 final String subscribedMediaId = "subscribedMediaId"; 429 final String anotherMediaId = "anotherMediaId"; 430 final int testChildrenCount = 101; 431 final CountDownLatch latch = new CountDownLatch(1); 432 433 final MediaLibrarySessionCallback sessionCallback = new MediaLibrarySessionCallback() { 434 @Override 435 public void onSubscribe(@NonNull MediaLibrarySession session, 436 @NonNull ControllerInfo controller, @NonNull String parentId, 437 @Nullable Bundle extras) { 438 if (Process.myUid() == controller.getUid()) { 439 // Shouldn't trigger onChildrenChanged() for the browser, 440 // because the browser didn't subscribe this media id. 441 session.notifyChildrenChanged(anotherMediaId, testChildrenCount, null); 442 } 443 } 444 445 @Override 446 public List<MediaItem2> onGetChildren(MediaLibrarySession session, 447 ControllerInfo controller, String parentId, int page, int pageSize, 448 Bundle extras) { 449 return TestUtils.createPlaylist(testChildrenCount); 450 } 451 }; 452 final BrowserCallback controllerCallbackProxy = new BrowserCallback() { 453 @Override 454 public void onChildrenChanged(MediaBrowser2 browser, String parentId, 455 int itemCount, Bundle extras) { 456 // Unexpected call. 457 fail(); 458 } 459 }; 460 461 TestServiceRegistry.getInstance().setSessionCallback(sessionCallback); 462 final SessionToken2 token = MockMediaLibraryService2.getToken(mContext); 463 final MediaBrowser2 browser = (MediaBrowser2) createController( 464 token, true, controllerCallbackProxy); 465 browser.subscribe(subscribedMediaId, null); 466 467 // onChildrenChanged() should not be called. 468 assertFalse(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 469 } 470 471 @Test 472 public void testBrowserCallback_onChildrenChangedIsCalledWhenSubscribed() 473 throws InterruptedException { 474 // This test uses MediaLibrarySession.notifyChildrenChanged(). 475 prepareLooper(); 476 final String expectedParentId = "expectedParentId"; 477 final int testChildrenCount = 101; 478 final Bundle testExtras = TestUtils.createTestBundle(); 479 480 final CountDownLatch latch = new CountDownLatch(1); 481 final MediaLibrarySessionCallback sessionCallback = new MediaLibrarySessionCallback() { 482 @Override 483 public void onSubscribe(@NonNull MediaLibrarySession session, 484 @NonNull ControllerInfo controller, @NonNull String parentId, 485 @Nullable Bundle extras) { 486 if (Process.myUid() == controller.getUid()) { 487 // Should trigger onChildrenChanged() for the browser. 488 session.notifyChildrenChanged(expectedParentId, testChildrenCount, testExtras); 489 } 490 } 491 492 @Override 493 public List<MediaItem2> onGetChildren(MediaLibrarySession session, 494 ControllerInfo controller, String parentId, int page, int pageSize, 495 Bundle extras) { 496 return TestUtils.createPlaylist(testChildrenCount); 497 } 498 }; 499 final BrowserCallback controllerCallbackProxy = new BrowserCallback() { 500 @Override 501 public void onChildrenChanged(MediaBrowser2 browser, String parentId, 502 int itemCount, Bundle extras) { 503 assertEquals(expectedParentId, parentId); 504 assertEquals(testChildrenCount, itemCount); 505 assertTrue(TestUtils.equals(testExtras, extras)); 506 latch.countDown(); 507 } 508 }; 509 510 TestServiceRegistry.getInstance().setSessionCallback(sessionCallback); 511 final SessionToken2 token = MockMediaLibraryService2.getToken(mContext); 512 final MediaBrowser2 browser = (MediaBrowser2) createController( 513 token, true, controllerCallbackProxy); 514 browser.subscribe(expectedParentId, null); 515 516 // onChildrenChanged() should be called. 517 assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 518 } 519 520 @Test 521 public void testBrowserCallback_onChildrenChangedIsNotCalledWhenNotSubscribed2() 522 throws InterruptedException { 523 // This test uses MediaLibrarySession.notifyChildrenChanged(ControllerInfo). 524 prepareLooper(); 525 final String subscribedMediaId = "subscribedMediaId"; 526 final String anotherMediaId = "anotherMediaId"; 527 final int testChildrenCount = 101; 528 final CountDownLatch latch = new CountDownLatch(1); 529 530 final MediaLibrarySessionCallback sessionCallback = new MediaLibrarySessionCallback() { 531 @Override 532 public void onSubscribe(@NonNull MediaLibrarySession session, 533 @NonNull ControllerInfo controller, @NonNull String parentId, 534 @Nullable Bundle extras) { 535 if (Process.myUid() == controller.getUid()) { 536 // Shouldn't trigger onChildrenChanged() for the browser, 537 // because the browser didn't subscribe this media id. 538 session.notifyChildrenChanged( 539 controller, anotherMediaId, testChildrenCount, null); 540 } 541 } 542 543 @Override 544 public List<MediaItem2> onGetChildren(MediaLibrarySession session, 545 ControllerInfo controller, String parentId, int page, int pageSize, 546 Bundle extras) { 547 return TestUtils.createPlaylist(testChildrenCount); 548 } 549 }; 550 final BrowserCallback controllerCallbackProxy = new BrowserCallback() { 551 @Override 552 public void onChildrenChanged(MediaBrowser2 browser, String parentId, 553 int itemCount, Bundle extras) { 554 // Unexpected call. 555 fail(); 556 } 557 }; 558 559 TestServiceRegistry.getInstance().setSessionCallback(sessionCallback); 560 final SessionToken2 token = MockMediaLibraryService2.getToken(mContext); 561 final MediaBrowser2 browser = (MediaBrowser2) createController( 562 token, true, controllerCallbackProxy); 563 browser.subscribe(subscribedMediaId, null); 564 565 // onChildrenChanged() should not be called. 566 assertFalse(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 567 } 568 569 @Test 570 public void testBrowserCallback_onChildrenChangedIsCalledWhenSubscribed2() 571 throws InterruptedException { 572 // This test uses MediaLibrarySession.notifyChildrenChanged(ControllerInfo). 573 prepareLooper(); 574 final String expectedParentId = "expectedParentId"; 575 final int testChildrenCount = 101; 576 final Bundle testExtras = TestUtils.createTestBundle(); 577 578 final CountDownLatch latch = new CountDownLatch(1); 579 final MediaLibrarySessionCallback sessionCallback = new MediaLibrarySessionCallback() { 580 @Override 581 public void onSubscribe(@NonNull MediaLibrarySession session, 582 @NonNull ControllerInfo controller, @NonNull String parentId, 583 @Nullable Bundle extras) { 584 if (Process.myUid() == controller.getUid()) { 585 // Should trigger onChildrenChanged() for the browser. 586 session.notifyChildrenChanged( 587 controller, expectedParentId, testChildrenCount, testExtras); 588 } 589 } 590 591 @Override 592 public List<MediaItem2> onGetChildren(MediaLibrarySession session, 593 ControllerInfo controller, String parentId, int page, int pageSize, 594 Bundle extras) { 595 return TestUtils.createPlaylist(testChildrenCount); 596 } 597 }; 598 final BrowserCallback controllerCallbackProxy = new BrowserCallback() { 599 @Override 600 public void onChildrenChanged(MediaBrowser2 browser, String parentId, 601 int itemCount, Bundle extras) { 602 assertEquals(expectedParentId, parentId); 603 assertEquals(testChildrenCount, itemCount); 604 assertTrue(TestUtils.equals(testExtras, extras)); 605 latch.countDown(); 606 } 607 }; 608 609 TestServiceRegistry.getInstance().setSessionCallback(sessionCallback); 610 final SessionToken2 token = MockMediaLibraryService2.getToken(mContext); 611 final MediaBrowser2 browser = (MediaBrowser2) createController( 612 token, true, controllerCallbackProxy); 613 browser.subscribe(expectedParentId, null); 614 615 // onChildrenChanged() should be called. 616 assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 617 } 618 619 public static class TestBrowserCallback extends BrowserCallback 620 implements TestControllerCallbackInterface { 621 private final ControllerCallback mCallbackProxy; 622 public final CountDownLatch connectLatch = new CountDownLatch(1); 623 public final CountDownLatch disconnectLatch = new CountDownLatch(1); 624 @GuardedBy("this") 625 private Runnable mOnCustomCommandRunnable; 626 627 TestBrowserCallback(ControllerCallback callbackProxy) { 628 if (callbackProxy == null) { 629 callbackProxy = new BrowserCallback() {}; 630 } 631 mCallbackProxy = callbackProxy; 632 } 633 634 @CallSuper 635 @Override 636 public void onConnected(MediaController2 controller, SessionCommandGroup2 commands) { 637 connectLatch.countDown(); 638 } 639 640 @CallSuper 641 @Override 642 public void onDisconnected(MediaController2 controller) { 643 disconnectLatch.countDown(); 644 } 645 646 @Override 647 public void waitForConnect(boolean expect) throws InterruptedException { 648 if (expect) { 649 assertTrue(connectLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 650 } else { 651 assertFalse(connectLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 652 } 653 } 654 655 @Override 656 public void waitForDisconnect(boolean expect) throws InterruptedException { 657 if (expect) { 658 assertTrue(disconnectLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 659 } else { 660 assertFalse(disconnectLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 661 } 662 } 663 664 @Override 665 public void onPlaybackInfoChanged(MediaController2 controller, 666 MediaController2.PlaybackInfo info) { 667 mCallbackProxy.onPlaybackInfoChanged(controller, info); 668 } 669 670 @Override 671 public void onCustomCommand(MediaController2 controller, SessionCommand2 command, 672 Bundle args, ResultReceiver receiver) { 673 mCallbackProxy.onCustomCommand(controller, command, args, receiver); 674 synchronized (this) { 675 if (mOnCustomCommandRunnable != null) { 676 mOnCustomCommandRunnable.run(); 677 } 678 } 679 } 680 681 @Override 682 public void onCustomLayoutChanged(MediaController2 controller, List<CommandButton> layout) { 683 mCallbackProxy.onCustomLayoutChanged(controller, layout); 684 } 685 686 @Override 687 public void onAllowedCommandsChanged(MediaController2 controller, 688 SessionCommandGroup2 commands) { 689 mCallbackProxy.onAllowedCommandsChanged(controller, commands); 690 } 691 692 @Override 693 public void onPlayerStateChanged(MediaController2 controller, int state) { 694 mCallbackProxy.onPlayerStateChanged(controller, state); 695 } 696 697 @Override 698 public void onSeekCompleted(MediaController2 controller, long position) { 699 mCallbackProxy.onSeekCompleted(controller, position); 700 } 701 702 @Override 703 public void onPlaybackSpeedChanged(MediaController2 controller, float speed) { 704 mCallbackProxy.onPlaybackSpeedChanged(controller, speed); 705 } 706 707 @Override 708 public void onBufferingStateChanged(MediaController2 controller, MediaItem2 item, 709 int state) { 710 mCallbackProxy.onBufferingStateChanged(controller, item, state); 711 } 712 713 @Override 714 public void onError(MediaController2 controller, int errorCode, Bundle extras) { 715 mCallbackProxy.onError(controller, errorCode, extras); 716 } 717 718 @Override 719 public void onCurrentMediaItemChanged(MediaController2 controller, MediaItem2 item) { 720 mCallbackProxy.onCurrentMediaItemChanged(controller, item); 721 } 722 723 @Override 724 public void onPlaylistChanged(MediaController2 controller, 725 List<MediaItem2> list, MediaMetadata2 metadata) { 726 mCallbackProxy.onPlaylistChanged(controller, list, metadata); 727 } 728 729 @Override 730 public void onPlaylistMetadataChanged(MediaController2 controller, 731 MediaMetadata2 metadata) { 732 mCallbackProxy.onPlaylistMetadataChanged(controller, metadata); 733 } 734 735 @Override 736 public void onShuffleModeChanged(MediaController2 controller, int shuffleMode) { 737 mCallbackProxy.onShuffleModeChanged(controller, shuffleMode); 738 } 739 740 @Override 741 public void onRepeatModeChanged(MediaController2 controller, int repeatMode) { 742 mCallbackProxy.onRepeatModeChanged(controller, repeatMode); 743 } 744 745 @Override 746 public void onGetLibraryRootDone(MediaBrowser2 browser, Bundle rootHints, 747 String rootMediaId, Bundle rootExtra) { 748 super.onGetLibraryRootDone(browser, rootHints, rootMediaId, rootExtra); 749 if (mCallbackProxy instanceof BrowserCallback) { 750 ((BrowserCallback) mCallbackProxy) 751 .onGetLibraryRootDone(browser, rootHints, rootMediaId, rootExtra); 752 } 753 } 754 755 @Override 756 public void onGetItemDone(MediaBrowser2 browser, String mediaId, MediaItem2 result) { 757 super.onGetItemDone(browser, mediaId, result); 758 if (mCallbackProxy instanceof BrowserCallback) { 759 ((BrowserCallback) mCallbackProxy).onGetItemDone(browser, mediaId, result); 760 } 761 } 762 763 @Override 764 public void onGetChildrenDone(MediaBrowser2 browser, String parentId, int page, 765 int pageSize, List<MediaItem2> result, Bundle extras) { 766 super.onGetChildrenDone(browser, parentId, page, pageSize, result, extras); 767 if (mCallbackProxy instanceof BrowserCallback) { 768 ((BrowserCallback) mCallbackProxy) 769 .onGetChildrenDone(browser, parentId, page, pageSize, result, extras); 770 } 771 } 772 773 @Override 774 public void onSearchResultChanged(MediaBrowser2 browser, String query, int itemCount, 775 Bundle extras) { 776 super.onSearchResultChanged(browser, query, itemCount, extras); 777 if (mCallbackProxy instanceof BrowserCallback) { 778 ((BrowserCallback) mCallbackProxy) 779 .onSearchResultChanged(browser, query, itemCount, extras); 780 } 781 } 782 783 @Override 784 public void onGetSearchResultDone(MediaBrowser2 browser, String query, int page, 785 int pageSize, List<MediaItem2> result, Bundle extras) { 786 super.onGetSearchResultDone(browser, query, page, pageSize, result, extras); 787 if (mCallbackProxy instanceof BrowserCallback) { 788 ((BrowserCallback) mCallbackProxy) 789 .onGetSearchResultDone(browser, query, page, pageSize, result, extras); 790 } 791 } 792 793 @Override 794 public void onChildrenChanged(MediaBrowser2 browser, String parentId, int itemCount, 795 Bundle extras) { 796 super.onChildrenChanged(browser, parentId, itemCount, extras); 797 if (mCallbackProxy instanceof BrowserCallback) { 798 ((BrowserCallback) mCallbackProxy) 799 .onChildrenChanged(browser, parentId, itemCount, extras); 800 } 801 } 802 803 @Override 804 public void setRunnableForOnCustomCommand(Runnable runnable) { 805 synchronized (this) { 806 mOnCustomCommandRunnable = runnable; 807 } 808 } 809 } 810 811 public class TestMediaBrowser extends MediaBrowser2 implements TestControllerInterface { 812 private final BrowserCallback mCallback; 813 814 public TestMediaBrowser(@NonNull Context context, @NonNull SessionToken2 token, 815 @NonNull BrowserCallback callback) { 816 super(context, token, sHandlerExecutor, callback); 817 mCallback = callback; 818 } 819 820 @Override 821 public BrowserCallback getCallback() { 822 return mCallback; 823 } 824 } 825} 826