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