blob: afc272ece7709b8dd631e6f3d4bf132c5793093f [file] [log] [blame]
/*
* Copyright (C) 2017 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 android.media.cts;
import android.media.AudioAttributes;
import android.media.AudioFocusRequest;
import android.media.AudioManager;
import android.media.AudioManager.OnAudioFocusChangeListener;
import android.os.Handler;
import android.os.HandlerThread;
import com.android.compatibility.common.util.CtsAndroidTestCase;
public class AudioFocusTest extends CtsAndroidTestCase {
private static final String TAG = "AudioFocusTest";
private static final int TEST_TIMING_TOLERANCE_MS = 100;
private static final AudioAttributes ATTR_DRIVE_DIR = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
.build();
private static final AudioAttributes ATTR_MEDIA = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build();
public void testInvalidAudioFocusRequestDelayNoListener() throws Exception {
AudioFocusRequest req = null;
Exception ex = null;
try {
req = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
.setAcceptsDelayedFocusGain(true).build();
} catch (Exception e) {
// expected
ex = e;
}
assertNotNull("No exception was thrown for an invalid build", ex);
assertEquals("Wrong exception thrown", ex.getClass(), IllegalStateException.class);
assertNull("Shouldn't be able to create delayed request without listener", req);
}
public void testInvalidAudioFocusRequestPauseOnDuckNoListener() throws Exception {
AudioFocusRequest req = null;
Exception ex = null;
try {
req = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
.setWillPauseWhenDucked(true).build();
} catch (Exception e) {
// expected
ex = e;
}
assertNotNull("No exception was thrown for an invalid build", ex);
assertEquals("Wrong exception thrown", ex.getClass(), IllegalStateException.class);
assertNull("Shouldn't be able to create pause-on-duck request without listener", req);
}
public void testAudioFocusRequestBuilderDefault() throws Exception {
final AudioFocusRequest reqDefaults =
new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN).build();
assertEquals("Focus gain differs", AudioManager.AUDIOFOCUS_GAIN,
reqDefaults.getFocusGain());
assertEquals("Listener differs", null, reqDefaults.getOnAudioFocusChangeListener());
assertEquals("Handler differs", null, reqDefaults.getOnAudioFocusChangeListenerHandler());
assertEquals("Duck behavior differs", false, reqDefaults.willPauseWhenDucked());
assertEquals("Delayed focus differs", false, reqDefaults.acceptsDelayedFocusGain());
}
public void testAudioFocusRequestCopyBuilder() throws Exception {
final FocusChangeListener focusListener = new FocusChangeListener();
final int focusGain = AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK;
final AudioFocusRequest reqToCopy =
new AudioFocusRequest.Builder(focusGain)
.setAudioAttributes(ATTR_DRIVE_DIR)
.setOnAudioFocusChangeListener(focusListener)
.setAcceptsDelayedFocusGain(true)
.setWillPauseWhenDucked(true)
.build();
AudioFocusRequest newReq = new AudioFocusRequest.Builder(reqToCopy).build();
assertEquals("AudioAttributes differ", ATTR_DRIVE_DIR, newReq.getAudioAttributes());
assertEquals("Listener differs", focusListener, newReq.getOnAudioFocusChangeListener());
assertEquals("Focus gain differs", focusGain, newReq.getFocusGain());
assertEquals("Duck behavior differs", true, newReq.willPauseWhenDucked());
assertEquals("Delayed focus differs", true, newReq.acceptsDelayedFocusGain());
newReq = new AudioFocusRequest.Builder(reqToCopy)
.setWillPauseWhenDucked(false)
.setFocusGain(AudioManager.AUDIOFOCUS_GAIN)
.build();
assertEquals("AudioAttributes differ", ATTR_DRIVE_DIR, newReq.getAudioAttributes());
assertEquals("Listener differs", focusListener, newReq.getOnAudioFocusChangeListener());
assertEquals("Focus gain differs", AudioManager.AUDIOFOCUS_GAIN, newReq.getFocusGain());
assertEquals("Duck behavior differs", false, newReq.willPauseWhenDucked());
assertEquals("Delayed focus differs", true, newReq.acceptsDelayedFocusGain());
}
public void testNullListenerHandlerNpe() throws Exception {
final AudioFocusRequest.Builder afBuilder =
new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN);
try {
afBuilder.setOnAudioFocusChangeListener(null);
fail("no NPE when setting a null listener");
} catch (NullPointerException e) {
}
final HandlerThread handlerThread = new HandlerThread(TAG);
handlerThread.start();
final Handler h = new Handler(handlerThread.getLooper());
final AudioFocusRequest.Builder afBuilderH =
new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN);
try {
afBuilderH.setOnAudioFocusChangeListener(null, h);
fail("no NPE when setting a null listener with non-null Handler");
} catch (NullPointerException e) {
}
final AudioFocusRequest.Builder afBuilderL =
new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN);
try {
afBuilderL.setOnAudioFocusChangeListener(new FocusChangeListener(), null);
fail("no NPE when setting a non-null listener with null Handler");
} catch (NullPointerException e) {
}
}
public void testAudioFocusRequestGainLoss() throws Exception {
final AudioAttributes[] attributes = { ATTR_DRIVE_DIR, ATTR_MEDIA };
doTestTwoPlayersGainLoss(AudioManager.AUDIOFOCUS_GAIN, attributes, false /*no handler*/);
}
public void testAudioFocusRequestGainLossHandler() throws Exception {
final AudioAttributes[] attributes = { ATTR_DRIVE_DIR, ATTR_MEDIA };
doTestTwoPlayersGainLoss(AudioManager.AUDIOFOCUS_GAIN, attributes, true /*with handler*/);
}
public void testAudioFocusRequestGainLossTransient() throws Exception {
final AudioAttributes[] attributes = { ATTR_DRIVE_DIR, ATTR_MEDIA };
doTestTwoPlayersGainLoss(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, attributes,
false /*no handler*/);
}
public void testAudioFocusRequestGainLossTransientHandler() throws Exception {
final AudioAttributes[] attributes = { ATTR_DRIVE_DIR, ATTR_MEDIA };
doTestTwoPlayersGainLoss(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, attributes,
true /*with handler*/);
}
public void testAudioFocusRequestGainLossTransientDuck() throws Exception {
final AudioAttributes[] attributes = { ATTR_DRIVE_DIR, ATTR_MEDIA };
doTestTwoPlayersGainLoss(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, attributes,
false /*no handler*/);
}
public void testAudioFocusRequestGainLossTransientDuckHandler() throws Exception {
final AudioAttributes[] attributes = { ATTR_DRIVE_DIR, ATTR_MEDIA };
doTestTwoPlayersGainLoss(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, attributes,
true /*with handler*/);
}
/**
* Test focus request and abandon between two focus owners
* @param gainType focus gain of the focus owner on top (== 2nd focus requester)
*/
private void doTestTwoPlayersGainLoss(int gainType, AudioAttributes[] attributes,
boolean useHandlerInListener) throws Exception {
final int NB_FOCUS_OWNERS = 2;
if (NB_FOCUS_OWNERS != attributes.length) {
throw new IllegalArgumentException("Invalid test: invalid number of attributes");
}
final AudioFocusRequest[] focusRequests = new AudioFocusRequest[NB_FOCUS_OWNERS];
final FocusChangeListener[] focusListeners = new FocusChangeListener[NB_FOCUS_OWNERS];
final int[] focusGains = { AudioManager.AUDIOFOCUS_GAIN, gainType };
int expectedLoss = 0;
switch (gainType) {
case AudioManager.AUDIOFOCUS_GAIN:
expectedLoss = AudioManager.AUDIOFOCUS_LOSS;
break;
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
expectedLoss = AudioManager.AUDIOFOCUS_LOSS_TRANSIENT;
break;
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
expectedLoss = AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK;
break;
default:
fail("invalid focus gain used in test");
}
final AudioManager am = new AudioManager(getContext());
final Handler h;
if (useHandlerInListener) {
HandlerThread handlerThread = new HandlerThread(TAG);
handlerThread.start();
h = new Handler(handlerThread.getLooper());
} else {
h = null;
}
try {
for (int i = 0 ; i < NB_FOCUS_OWNERS ; i++) {
focusListeners[i] = new FocusChangeListener();
if (h != null) {
focusRequests[i] = new AudioFocusRequest.Builder(focusGains[i])
.setAudioAttributes(attributes[i])
.setOnAudioFocusChangeListener(focusListeners[i], h /*handler*/)
.build();
} else {
focusRequests[i] = new AudioFocusRequest.Builder(focusGains[i])
.setAudioAttributes(attributes[i])
.setOnAudioFocusChangeListener(focusListeners[i])
.build();
}
}
// focus owner 0 requests focus with GAIN,
// then focus owner 1 requests focus with gainType
// then 1 abandons focus, then 0 abandons focus
int res = am.requestAudioFocus(focusRequests[0]);
assertEquals("1st focus request failed",
AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
res = am.requestAudioFocus(focusRequests[1]);
assertEquals("2nd focus request failed", AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
Thread.sleep(TEST_TIMING_TOLERANCE_MS);
assertEquals("Focus loss not dispatched", expectedLoss,
focusListeners[0].getFocusChangeAndReset());
res = am.abandonAudioFocusRequest(focusRequests[1]);
assertEquals("1st abandon failed", AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
focusRequests[1] = null;
Thread.sleep(TEST_TIMING_TOLERANCE_MS);
assertEquals("Focus gain not dispatched", AudioManager.AUDIOFOCUS_GAIN,
focusListeners[0].getFocusChangeAndReset());
res = am.abandonAudioFocusRequest(focusRequests[0]);
assertEquals("2nd abandon failed", AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
focusRequests[0] = null;
}
finally {
for (int i = 0 ; i < NB_FOCUS_OWNERS ; i++) {
if (focusRequests[i] != null) {
am.abandonAudioFocusRequest(focusRequests[i]);
}
}
if (h != null) {
h.getLooper().quit();
}
}
}
private static class FocusChangeListener implements OnAudioFocusChangeListener {
private final Object mLock = new Object();
private int mFocusChange = AudioManager.AUDIOFOCUS_NONE;
int getFocusChangeAndReset() {
final int change;
synchronized (mLock) {
change = mFocusChange;
mFocusChange = AudioManager.AUDIOFOCUS_NONE;
}
return change;
}
@Override
public void onAudioFocusChange(int focusChange) {
synchronized (mLock) {
mFocusChange = focusChange;
}
}
}
}