1 /* 2 * Copyright (C) 2014 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 package android.media.cts; 17 18 import com.android.compatibility.common.util.SystemUtil; 19 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.pm.PackageManager; 23 import android.media.AudioManager; 24 import android.media.session.MediaController; 25 import android.media.session.MediaSession; 26 import android.media.session.MediaSessionManager; 27 import android.media.session.PlaybackState; 28 import android.os.Handler; 29 import android.os.HandlerThread; 30 import android.os.Looper; 31 import android.test.InstrumentationTestCase; 32 import android.test.UiThreadTest; 33 import android.view.KeyEvent; 34 35 import java.io.IOException; 36 import java.util.ArrayList; 37 import java.util.List; 38 import java.util.concurrent.CountDownLatch; 39 import java.util.concurrent.TimeUnit; 40 41 public class MediaSessionManagerTest extends InstrumentationTestCase { 42 private static final String TAG = "MediaSessionManagerTest"; 43 private static final int TIMEOUT_MS = 3000; 44 private static final int WAIT_MS = 500; 45 46 private MediaSessionManager mSessionManager; 47 48 @Override setUp()49 protected void setUp() throws Exception { 50 super.setUp(); 51 mSessionManager = (MediaSessionManager) getInstrumentation().getTargetContext() 52 .getSystemService(Context.MEDIA_SESSION_SERVICE); 53 } 54 testGetActiveSessions()55 public void testGetActiveSessions() throws Exception { 56 try { 57 List<MediaController> controllers = mSessionManager.getActiveSessions(null); 58 fail("Expected security exception for unauthorized call to getActiveSessions"); 59 } catch (SecurityException e) { 60 // Expected 61 } 62 // TODO enable a notification listener, test again, disable, test again 63 } 64 65 @UiThreadTest testAddOnActiveSessionsListener()66 public void testAddOnActiveSessionsListener() throws Exception { 67 try { 68 mSessionManager.addOnActiveSessionsChangedListener(null, null); 69 fail("Expected IAE for call to addOnActiveSessionsChangedListener"); 70 } catch (IllegalArgumentException e) { 71 // Expected 72 } 73 74 MediaSessionManager.OnActiveSessionsChangedListener listener 75 = new MediaSessionManager.OnActiveSessionsChangedListener() { 76 @Override 77 public void onActiveSessionsChanged(List<MediaController> controllers) { 78 79 } 80 }; 81 try { 82 mSessionManager.addOnActiveSessionsChangedListener(listener, null); 83 fail("Expected security exception for call to addOnActiveSessionsChangedListener"); 84 } catch (SecurityException e) { 85 // Expected 86 } 87 88 // TODO enable a notification listener, test again, disable, verify 89 // updates stopped 90 } 91 assertKeyEventEquals(KeyEvent lhs, int keyCode, int action, int repeatCount)92 private void assertKeyEventEquals(KeyEvent lhs, int keyCode, int action, int repeatCount) { 93 assertTrue(lhs.getKeyCode() == keyCode 94 && lhs.getAction() == action 95 && lhs.getRepeatCount() == repeatCount); 96 } 97 injectInputEvent(int keyCode, boolean longPress)98 private void injectInputEvent(int keyCode, boolean longPress) throws IOException { 99 // Injecting key with instrumentation requires a window/view, but we don't have it. 100 // Inject key event through the adb commend to workaround. 101 final String command = "input keyevent " + (longPress ? "--longpress " : "") + keyCode; 102 SystemUtil.runShellCommand(getInstrumentation(), command); 103 } 104 testSetOnVolumeKeyLongPressListener()105 public void testSetOnVolumeKeyLongPressListener() throws Exception { 106 Context context = getInstrumentation().getTargetContext(); 107 if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) { 108 // Skip this test on TV platform because the PhoneWindowManager dispatches volume key 109 // events directly to the audio service to change the system volume. 110 return; 111 } 112 113 HandlerThread handlerThread = new HandlerThread(TAG); 114 handlerThread.start(); 115 Handler handler = new Handler(handlerThread.getLooper()); 116 117 // Ensure that the listener is called for long-press. 118 VolumeKeyLongPressListener listener = new VolumeKeyLongPressListener(3, handler); 119 mSessionManager.setOnVolumeKeyLongPressListener(listener, handler); 120 injectInputEvent(KeyEvent.KEYCODE_VOLUME_DOWN, true); 121 assertTrue(listener.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 122 assertEquals(listener.mKeyEvents.size(), 3); 123 assertKeyEventEquals(listener.mKeyEvents.get(0), 124 KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.ACTION_DOWN, 0); 125 assertKeyEventEquals(listener.mKeyEvents.get(1), 126 KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.ACTION_DOWN, 1); 127 assertKeyEventEquals(listener.mKeyEvents.get(2), 128 KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.ACTION_UP, 0); 129 130 // Ensure the the listener isn't called for short-press. 131 listener = new VolumeKeyLongPressListener(1, handler); 132 mSessionManager.setOnVolumeKeyLongPressListener(listener, handler); 133 injectInputEvent(KeyEvent.KEYCODE_VOLUME_DOWN, false); 134 assertFalse(listener.mCountDownLatch.await(WAIT_MS, TimeUnit.MILLISECONDS)); 135 assertEquals(listener.mKeyEvents.size(), 0); 136 137 // Ensure that the listener isn't called anymore. 138 mSessionManager.setOnVolumeKeyLongPressListener(null, handler); 139 injectInputEvent(KeyEvent.KEYCODE_VOLUME_DOWN, true); 140 assertFalse(listener.mCountDownLatch.await(WAIT_MS, TimeUnit.MILLISECONDS)); 141 assertEquals(listener.mKeyEvents.size(), 0); 142 } 143 testSetOnMediaKeyListener()144 public void testSetOnMediaKeyListener() throws Exception { 145 HandlerThread handlerThread = new HandlerThread(TAG); 146 handlerThread.start(); 147 Handler handler = new Handler(handlerThread.getLooper()); 148 149 MediaSessionCallback callback = new MediaSessionCallback(2); 150 MediaSession session = new MediaSession(getInstrumentation().getTargetContext(), TAG); 151 session.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS 152 | MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS); 153 session.setCallback(callback, handler); 154 PlaybackState state = new PlaybackState.Builder() 155 .setState(PlaybackState.STATE_PLAYING, 0, 1.0f).build(); 156 // Fake the media session service so this session can take the media key events. 157 session.setPlaybackState(state); 158 session.setActive(true); 159 160 // A media playback is also needed to receive media key events. 161 Utils.assertMediaPlaybackStarted(getInstrumentation().getTargetContext()); 162 163 // Ensure that the listener is called for media key event, 164 // and any other media sessions don't get the key. 165 MediaKeyListener listener = new MediaKeyListener(2, true, handler); 166 mSessionManager.setOnMediaKeyListener(listener, handler); 167 injectInputEvent(KeyEvent.KEYCODE_HEADSETHOOK, false); 168 assertTrue(listener.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 169 assertEquals(listener.mKeyEvents.size(), 2); 170 assertKeyEventEquals(listener.mKeyEvents.get(0), 171 KeyEvent.KEYCODE_HEADSETHOOK, KeyEvent.ACTION_DOWN, 0); 172 assertKeyEventEquals(listener.mKeyEvents.get(1), 173 KeyEvent.KEYCODE_HEADSETHOOK, KeyEvent.ACTION_UP, 0); 174 assertFalse(callback.mCountDownLatch.await(WAIT_MS, TimeUnit.MILLISECONDS)); 175 assertEquals(callback.mKeyEvents.size(), 0); 176 177 // Ensure that the listener is called for media key event, 178 // and another media session gets the key. 179 listener = new MediaKeyListener(2, false, handler); 180 mSessionManager.setOnMediaKeyListener(listener, handler); 181 injectInputEvent(KeyEvent.KEYCODE_HEADSETHOOK, false); 182 assertTrue(listener.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 183 assertEquals(listener.mKeyEvents.size(), 2); 184 assertKeyEventEquals(listener.mKeyEvents.get(0), 185 KeyEvent.KEYCODE_HEADSETHOOK, KeyEvent.ACTION_DOWN, 0); 186 assertKeyEventEquals(listener.mKeyEvents.get(1), 187 KeyEvent.KEYCODE_HEADSETHOOK, KeyEvent.ACTION_UP, 0); 188 assertTrue(callback.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 189 assertEquals(callback.mKeyEvents.size(), 2); 190 assertKeyEventEquals(callback.mKeyEvents.get(0), 191 KeyEvent.KEYCODE_HEADSETHOOK, KeyEvent.ACTION_DOWN, 0); 192 assertKeyEventEquals(callback.mKeyEvents.get(1), 193 KeyEvent.KEYCODE_HEADSETHOOK, KeyEvent.ACTION_UP, 0); 194 195 // Ensure that the listener isn't called anymore. 196 listener = new MediaKeyListener(1, true, handler); 197 mSessionManager.setOnMediaKeyListener(listener, handler); 198 mSessionManager.setOnMediaKeyListener(null, handler); 199 injectInputEvent(KeyEvent.KEYCODE_HEADSETHOOK, false); 200 assertFalse(listener.mCountDownLatch.await(WAIT_MS, TimeUnit.MILLISECONDS)); 201 assertEquals(listener.mKeyEvents.size(), 0); 202 } 203 204 private class VolumeKeyLongPressListener 205 implements MediaSessionManager.OnVolumeKeyLongPressListener { 206 private final List<KeyEvent> mKeyEvents = new ArrayList<>(); 207 private final CountDownLatch mCountDownLatch; 208 private final Handler mHandler; 209 VolumeKeyLongPressListener(int count, Handler handler)210 public VolumeKeyLongPressListener(int count, Handler handler) { 211 mCountDownLatch = new CountDownLatch(count); 212 mHandler = handler; 213 } 214 215 @Override onVolumeKeyLongPress(KeyEvent event)216 public void onVolumeKeyLongPress(KeyEvent event) { 217 mKeyEvents.add(event); 218 // Ensure the listener is called on the thread. 219 assertEquals(mHandler.getLooper(), Looper.myLooper()); 220 mCountDownLatch.countDown(); 221 } 222 } 223 224 private class MediaKeyListener implements MediaSessionManager.OnMediaKeyListener { 225 private final CountDownLatch mCountDownLatch; 226 private final boolean mConsume; 227 private final Handler mHandler; 228 private final List<KeyEvent> mKeyEvents = new ArrayList<>(); 229 MediaKeyListener(int count, boolean consume, Handler handler)230 public MediaKeyListener(int count, boolean consume, Handler handler) { 231 mCountDownLatch = new CountDownLatch(count); 232 mConsume = consume; 233 mHandler = handler; 234 } 235 236 @Override onMediaKey(KeyEvent event)237 public boolean onMediaKey(KeyEvent event) { 238 mKeyEvents.add(event); 239 // Ensure the listener is called on the thread. 240 assertEquals(mHandler.getLooper(), Looper.myLooper()); 241 mCountDownLatch.countDown(); 242 return mConsume; 243 } 244 } 245 246 private class MediaSessionCallback extends MediaSession.Callback { 247 private final CountDownLatch mCountDownLatch; 248 private final List<KeyEvent> mKeyEvents = new ArrayList<>(); 249 MediaSessionCallback(int count)250 private MediaSessionCallback(int count) { 251 mCountDownLatch = new CountDownLatch(count); 252 } 253 onMediaButtonEvent(Intent mediaButtonIntent)254 public boolean onMediaButtonEvent(Intent mediaButtonIntent) { 255 KeyEvent event = (KeyEvent) mediaButtonIntent.getParcelableExtra( 256 Intent.EXTRA_KEY_EVENT); 257 assertNotNull(event); 258 mKeyEvents.add(event); 259 mCountDownLatch.countDown(); 260 return true; 261 } 262 } 263 } 264