| /* |
| * Copyright 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package androidx.media2.test.client.tests; |
| |
| import static androidx.media.AudioAttributesCompat.CONTENT_TYPE_MUSIC; |
| import static androidx.media.VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE; |
| import static androidx.media.VolumeProviderCompat.VOLUME_CONTROL_FIXED; |
| import static androidx.media2.test.common.CommonConstants.DEFAULT_TEST_NAME; |
| import static androidx.media2.test.common.CommonConstants.SERVICE_PACKAGE_NAME; |
| import static androidx.media2.test.common.MediaSessionConstants.TEST_GET_SESSION_ACTIVITY; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| |
| import android.app.PendingIntent; |
| import android.content.Context; |
| import android.media.AudioManager; |
| import android.os.Build; |
| import android.os.Bundle; |
| import android.util.Log; |
| |
| import androidx.media.AudioAttributesCompat; |
| import androidx.media2.common.MediaItem; |
| import androidx.media2.common.SessionPlayer; |
| import androidx.media2.session.MediaController; |
| import androidx.media2.session.MediaController.ControllerCallback; |
| import androidx.media2.session.MediaController.PlaybackInfo; |
| import androidx.media2.session.SessionToken; |
| import androidx.media2.test.client.MediaTestUtils; |
| import androidx.media2.test.client.RemoteMediaSession; |
| import androidx.media2.test.common.PollingCheck; |
| import androidx.media2.test.common.TestUtils; |
| import androidx.test.ext.junit.runners.AndroidJUnit4; |
| import androidx.test.filters.LargeTest; |
| import androidx.test.filters.SdkSuppress; |
| |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| /** |
| * Tests {@link MediaController}. |
| */ |
| @SdkSuppress(minSdkVersion = Build.VERSION_CODES.JELLY_BEAN) |
| @RunWith(AndroidJUnit4.class) |
| @LargeTest |
| public class MediaControllerTest extends MediaSessionTestBase { |
| |
| static final String TAG = "MediaControllerTest"; |
| private static final long VOLUME_CHANGE_TIMEOUT_MS = 5000L; |
| |
| final List<RemoteMediaSession> mRemoteSessionList = new ArrayList<>(); |
| |
| AudioManager mAudioManager; |
| RemoteMediaSession mRemoteSession; |
| |
| @Before |
| @Override |
| public void setUp() throws Exception { |
| super.setUp(); |
| mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); |
| mRemoteSession = createRemoteMediaSession(DEFAULT_TEST_NAME, null); |
| } |
| |
| @After |
| @Override |
| public void cleanUp() throws Exception { |
| super.cleanUp(); |
| for (int i = 0; i < mRemoteSessionList.size(); i++) { |
| RemoteMediaSession session = mRemoteSessionList.get(i); |
| if (session != null) { |
| session.cleanUp(); |
| } |
| } |
| } |
| |
| @Test |
| public void testBuilder() { |
| prepareLooper(); |
| MediaController.Builder builder; |
| |
| try { |
| builder = new MediaController.Builder(null); |
| fail("null context shouldn't be allowed"); |
| } catch (IllegalArgumentException e) { |
| // expected. pass-through |
| } |
| |
| try { |
| builder = new MediaController.Builder(mContext); |
| builder.setSessionToken(null); |
| fail("null token shouldn't be allowed"); |
| } catch (IllegalArgumentException e) { |
| // expected. pass-through |
| } |
| |
| try { |
| builder = new MediaController.Builder(mContext); |
| builder.setSessionCompatToken(null); |
| fail("null compat token shouldn't be allowed"); |
| } catch (IllegalArgumentException e) { |
| // expected. pass-through |
| } |
| |
| try { |
| builder = new MediaController.Builder(mContext); |
| builder.setControllerCallback(null, null); |
| fail("null executor or null callback shouldn't be allowed"); |
| } catch (IllegalArgumentException e) { |
| // expected. pass-through |
| } |
| |
| MediaController controller = new MediaController.Builder(mContext) |
| .setSessionToken(mRemoteSession.getToken()) |
| .setControllerCallback(sHandlerExecutor, new ControllerCallback() {}) |
| .build(); |
| controller.close(); |
| } |
| |
| @Test |
| public void testGetSessionActivity() throws InterruptedException { |
| prepareLooper(); |
| RemoteMediaSession session = createRemoteMediaSession(TEST_GET_SESSION_ACTIVITY, null); |
| |
| MediaController controller = createController(session.getToken()); |
| PendingIntent sessionActivity = controller.getSessionActivity(); |
| assertNotNull(sessionActivity); |
| if (Build.VERSION.SDK_INT >= 17) { |
| // PendingIntent#getCreatorPackage() is added in API 17. |
| assertEquals(SERVICE_PACKAGE_NAME, sessionActivity.getCreatorPackage()); |
| |
| // TODO: Add getPid/getUid in MediaControllerProviderService and compare them. |
| // assertEquals(mRemoteSession.getUid(), sessionActivity.getCreatorUid()); |
| } |
| session.cleanUp(); |
| } |
| |
| @Test |
| public void testSetVolumeWithLocalVolume() throws Exception { |
| prepareLooper(); |
| if (Build.VERSION.SDK_INT >= 21 && mAudioManager.isVolumeFixed()) { |
| // This test is not eligible for this device. |
| return; |
| } |
| |
| MediaController controller = createController(mRemoteSession.getToken()); |
| |
| // Here, we intentionally choose STREAM_ALARM in order not to consider |
| // 'Do Not Disturb' or 'Volume limit'. |
| final int stream = AudioManager.STREAM_ALARM; |
| final int maxVolume = mAudioManager.getStreamMaxVolume(stream); |
| final int minVolume = |
| Build.VERSION.SDK_INT >= 28 ? mAudioManager.getStreamMinVolume(stream) : 0; |
| Log.d(TAG, "maxVolume=" + maxVolume + ", minVolume=" + minVolume); |
| if (maxVolume <= minVolume) { |
| return; |
| } |
| |
| AudioAttributesCompat attrs = new AudioAttributesCompat.Builder() |
| .setLegacyStreamType(stream).build(); |
| Bundle playerConfig = RemoteMediaSession.createMockPlayerConnectorConfig( |
| 0 /* state */, 0 /* buffState */, 0 /* position */, 0 /* buffPosition */, |
| 0f /* speed */, attrs); |
| mRemoteSession.updatePlayer(playerConfig); |
| |
| final int originalVolume = mAudioManager.getStreamVolume(stream); |
| final int targetVolume = originalVolume == minVolume |
| ? originalVolume + 1 : originalVolume - 1; |
| Log.d(TAG, "originalVolume=" + originalVolume + ", targetVolume=" + targetVolume); |
| |
| controller.setVolumeTo(targetVolume, AudioManager.FLAG_SHOW_UI); |
| new PollingCheck(VOLUME_CHANGE_TIMEOUT_MS) { |
| @Override |
| protected boolean check() { |
| return targetVolume == mAudioManager.getStreamVolume(stream); |
| } |
| }.run(); |
| |
| // Set back to original volume. |
| mAudioManager.setStreamVolume(stream, originalVolume, 0 /* flags */); |
| } |
| |
| @Test |
| public void testAdjustVolumeWithLocalVolume() throws Exception { |
| prepareLooper(); |
| if (Build.VERSION.SDK_INT >= 21 && mAudioManager.isVolumeFixed()) { |
| // This test is not eligible for this device. |
| return; |
| } |
| |
| MediaController controller = createController(mRemoteSession.getToken()); |
| |
| // Here, we intentionally choose STREAM_ALARM in order not to consider |
| // 'Do Not Disturb' or 'Volume limit'. |
| final int stream = AudioManager.STREAM_ALARM; |
| final int maxVolume = mAudioManager.getStreamMaxVolume(stream); |
| final int minVolume = |
| Build.VERSION.SDK_INT >= 28 ? mAudioManager.getStreamMinVolume(stream) : 0; |
| Log.d(TAG, "maxVolume=" + maxVolume + ", minVolume=" + minVolume); |
| if (maxVolume <= minVolume) { |
| return; |
| } |
| |
| AudioAttributesCompat attrs = new AudioAttributesCompat.Builder() |
| .setLegacyStreamType(stream).build(); |
| Bundle playerConfig = RemoteMediaSession.createMockPlayerConnectorConfig( |
| 0 /* state */, 0 /* buffState */, 0 /* position */, 0 /* buffPosition */, |
| 0f /* speed */, attrs); |
| mRemoteSession.updatePlayer(playerConfig); |
| |
| final int originalVolume = mAudioManager.getStreamVolume(stream); |
| final int direction = originalVolume == minVolume |
| ? AudioManager.ADJUST_RAISE : AudioManager.ADJUST_LOWER; |
| final int targetVolume = originalVolume + direction; |
| Log.d(TAG, "originalVolume=" + originalVolume + ", targetVolume=" + targetVolume); |
| |
| controller.adjustVolume(direction, AudioManager.FLAG_SHOW_UI); |
| new PollingCheck(VOLUME_CHANGE_TIMEOUT_MS) { |
| @Override |
| protected boolean check() { |
| return targetVolume == mAudioManager.getStreamVolume(stream); |
| } |
| }.run(); |
| |
| // Set back to original volume. |
| mAudioManager.setStreamVolume(stream, originalVolume, 0 /* flags */); |
| } |
| |
| @Test |
| public void testGetPackageName() throws Exception { |
| prepareLooper(); |
| MediaController controller = createController(mRemoteSession.getToken()); |
| assertEquals(SERVICE_PACKAGE_NAME, controller.getConnectedToken().getPackageName()); |
| } |
| |
| @Test |
| public void testGetTokenExtras() throws Exception { |
| prepareLooper(); |
| Bundle testTokenExtras = TestUtils.createTestBundle(); |
| RemoteMediaSession session = createRemoteMediaSession("testGetExtras", testTokenExtras); |
| |
| MediaController controller = createController(session.getToken()); |
| SessionToken connectedToken = controller.getConnectedToken(); |
| assertNotNull(connectedToken); |
| assertTrue(TestUtils.equals(testTokenExtras, connectedToken.getExtras())); |
| } |
| |
| @Test |
| public void testIsConnected() throws InterruptedException { |
| prepareLooper(); |
| MediaController controller = createController(mRemoteSession.getToken()); |
| assertTrue(controller.isConnected()); |
| |
| mRemoteSession.close(); |
| waitForDisconnect(controller, true); |
| assertFalse(controller.isConnected()); |
| } |
| |
| @Test |
| public void testClose_beforeConnected() throws InterruptedException { |
| prepareLooper(); |
| MediaController controller = createController(mRemoteSession.getToken(), |
| false /* waitForConnect */, null, null /* callback */); |
| controller.close(); |
| } |
| |
| @Test |
| public void testClose_twice() throws InterruptedException { |
| prepareLooper(); |
| MediaController controller = createController(mRemoteSession.getToken()); |
| controller.close(); |
| controller.close(); |
| } |
| |
| @Test |
| public void testGettersAfterConnected() throws InterruptedException { |
| prepareLooper(); |
| final int state = SessionPlayer.PLAYER_STATE_PLAYING; |
| final int bufferingState = SessionPlayer.BUFFERING_STATE_COMPLETE; |
| final long position = 150000; |
| final long bufferedPosition = 900000; |
| final float speed = 0.5f; |
| final long timeDiff = 102; |
| final MediaItem currentMediaItem = MediaTestUtils.createFileMediaItemWithMetadata(); |
| |
| Bundle config = RemoteMediaSession.createMockPlayerConnectorConfig( |
| state, bufferingState, position, bufferedPosition, speed, null /* audioAttrs */, |
| null /* playlist */, currentMediaItem, null /* metadata */); |
| mRemoteSession.updatePlayer(config); |
| |
| MediaController controller = createController(mRemoteSession.getToken()); |
| controller.setTimeDiff(timeDiff); |
| assertEquals(state, controller.getPlayerState()); |
| assertEquals(bufferedPosition, controller.getBufferedPosition()); |
| assertEquals(speed, controller.getPlaybackSpeed(), 0.0f); |
| assertEquals(position + (long) (speed * timeDiff), controller.getCurrentPosition()); |
| MediaTestUtils.assertNotMediaItemSubclass(controller.getCurrentMediaItem()); |
| MediaTestUtils.assertMediaIdEquals(currentMediaItem, controller.getCurrentMediaItem()); |
| } |
| |
| @Test |
| public void testGetPlaybackInfo() throws Exception { |
| prepareLooper(); |
| final AudioAttributesCompat attrs = new AudioAttributesCompat.Builder() |
| .setContentType(CONTENT_TYPE_MUSIC) |
| .build(); |
| |
| Bundle playerConfig = RemoteMediaSession.createMockPlayerConnectorConfig( |
| 0 /* state */, 0 /* buffState */, 0 /* position */, 0 /* buffPosition */, |
| 0f /* speed */, attrs); |
| mRemoteSession.updatePlayer(playerConfig); |
| |
| final MediaController controller = createController(mRemoteSession.getToken()); |
| PlaybackInfo info = controller.getPlaybackInfo(); |
| assertNotNull(info); |
| assertEquals(PlaybackInfo.PLAYBACK_TYPE_LOCAL, info.getPlaybackType()); |
| assertEquals(attrs, info.getAudioAttributes()); |
| |
| int localVolumeControlType = VOLUME_CONTROL_ABSOLUTE; |
| if (Build.VERSION.SDK_INT >= 21 && mAudioManager.isVolumeFixed()) { |
| localVolumeControlType = VOLUME_CONTROL_FIXED; |
| } |
| assertEquals(localVolumeControlType, info.getControlType()); |
| assertEquals(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC), |
| info.getMaxVolume()); |
| assertEquals(mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC), |
| info.getCurrentVolume()); |
| } |
| |
| RemoteMediaSession createRemoteMediaSession(String id, Bundle tokenExtras) { |
| RemoteMediaSession session = new RemoteMediaSession(id, mContext, tokenExtras); |
| mRemoteSessionList.add(session); |
| return session; |
| } |
| } |