summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/audio/FadeOutManager.java140
-rw-r--r--services/core/java/com/android/server/audio/FocusRequester.java3
-rw-r--r--services/core/java/com/android/server/audio/MediaFocusControl.java37
-rw-r--r--services/core/java/com/android/server/audio/PlaybackActivityMonitor.java14
-rw-r--r--services/core/java/com/android/server/audio/PlayerFocusEnforcer.java15
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/FadeOutManagerTest.java214
6 files changed, 363 insertions, 60 deletions
diff --git a/services/core/java/com/android/server/audio/FadeOutManager.java b/services/core/java/com/android/server/audio/FadeOutManager.java
index 5f6f4b125710..dde98b466ef7 100644
--- a/services/core/java/com/android/server/audio/FadeOutManager.java
+++ b/services/core/java/com/android/server/audio/FadeOutManager.java
@@ -21,8 +21,10 @@ import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.AudioPlaybackConfiguration;
import android.media.VolumeShaper;
-import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
import com.android.server.utils.EventLogger;
@@ -38,7 +40,7 @@ public final class FadeOutManager {
public static final String TAG = "AudioService.FadeOutManager";
/** duration of the fade out curve */
- /*package*/ static final long FADE_OUT_DURATION_MS = 2000;
+ private static final long FADE_OUT_DURATION_MS = 2000;
/**
* delay after which a faded out player will be faded back in. This will be heard by the user
* only in the case of unmuting players that didn't respect audio focus and didn't stop/pause
@@ -46,7 +48,7 @@ public final class FadeOutManager {
* This is the amount of time between the app being notified of
* the focus loss (when its muted by the fade out), and the time fade in (to unmute) starts
*/
- /*package*/ static final long DELAY_FADE_IN_OFFENDERS_MS = 2000;
+ private static final long DELAY_FADE_IN_OFFENDERS_MS = 2000;
private static final boolean DEBUG = PlaybackActivityMonitor.DEBUG;
@@ -81,6 +83,14 @@ public final class FadeOutManager {
private static final VolumeShaper.Operation PLAY_SKIP_RAMP =
new VolumeShaper.Operation.Builder(PLAY_CREATE_IF_NEEDED).setXOffset(1.0f).build();
+ private final Object mLock = new Object();
+
+ /**
+ * Map of uid (key) to faded out apps (value)
+ */
+ @GuardedBy("mLock")
+ private final SparseArray<FadedOutApp> mUidToFadedAppsMap = new SparseArray<>();
+
// TODO explore whether a shorter fade out would be a better UX instead of not fading out at all
// (legacy behavior)
@@ -90,15 +100,18 @@ public final class FadeOutManager {
* @param requester the parameters for the focus request
* @return true if there can be a fade out over the requester starting to play
*/
- static boolean canCauseFadeOut(@NonNull FocusRequester requester,
- @NonNull FocusRequester loser) {
+ boolean canCauseFadeOut(@NonNull FocusRequester requester, @NonNull FocusRequester loser) {
if (requester.getAudioAttributes().getContentType() == AudioAttributes.CONTENT_TYPE_SPEECH)
{
- if (DEBUG) { Log.i(TAG, "not fading out: new focus is for speech"); }
+ if (DEBUG) {
+ Slog.i(TAG, "not fading out: new focus is for speech");
+ }
return false;
}
if ((loser.getGrantFlags() & AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS) != 0) {
- if (DEBUG) { Log.i(TAG, "not fading out: loser has PAUSES_ON_DUCKABLE_LOSS"); }
+ if (DEBUG) {
+ Slog.i(TAG, "not fading out: loser has PAUSES_ON_DUCKABLE_LOSS");
+ }
return false;
}
@@ -110,29 +123,36 @@ public final class FadeOutManager {
* @param apc the configuration of the player
* @return true if player type and AudioAttributes are compatible with fade out
*/
- static boolean canBeFadedOut(@NonNull AudioPlaybackConfiguration apc) {
+ boolean canBeFadedOut(@NonNull AudioPlaybackConfiguration apc) {
if (ArrayUtils.contains(UNFADEABLE_PLAYER_TYPES, apc.getPlayerType())) {
- if (DEBUG) { Log.i(TAG, "not fading: player type:" + apc.getPlayerType()); }
+ if (DEBUG) {
+ Slog.i(TAG, "not fading: player type:" + apc.getPlayerType());
+ }
return false;
}
if (ArrayUtils.contains(UNFADEABLE_CONTENT_TYPES,
apc.getAudioAttributes().getContentType())) {
if (DEBUG) {
- Log.i(TAG, "not fading: content type:"
+ Slog.i(TAG, "not fading: content type:"
+ apc.getAudioAttributes().getContentType());
}
return false;
}
if (!ArrayUtils.contains(FADEABLE_USAGES, apc.getAudioAttributes().getUsage())) {
if (DEBUG) {
- Log.i(TAG, "not fading: usage:" + apc.getAudioAttributes().getUsage());
+ Slog.i(TAG, "not fading: usage:" + apc.getAudioAttributes().getUsage());
}
return false;
}
return true;
}
- static long getFadeOutDurationOnFocusLossMillis(AudioAttributes aa) {
+ /**
+ * Get the duration to fade-out after losing audio focus
+ * @param aa The {@link android.media.AudioAttributes} of the player
+ * @return duration in milliseconds
+ */
+ long getFadeOutDurationOnFocusLossMillis(@NonNull AudioAttributes aa) {
if (ArrayUtils.contains(UNFADEABLE_CONTENT_TYPES, aa.getContentType())) {
return 0;
}
@@ -143,18 +163,24 @@ public final class FadeOutManager {
}
/**
- * Map of uid (key) to faded out apps (value)
+ * Get the delay to fade-in the offending players that do not stop after losing audio focus
+ * @param aa The {@link android.media.AudioAttributes}
+ * @return duration in milliseconds
*/
- private final HashMap<Integer, FadedOutApp> mFadedApps = new HashMap<Integer, FadedOutApp>();
+ long getFadeInDelayForOffendersMillis(@NonNull AudioAttributes aa) {
+ return DELAY_FADE_IN_OFFENDERS_MS;
+ }
- synchronized void fadeOutUid(int uid, ArrayList<AudioPlaybackConfiguration> players) {
- Log.i(TAG, "fadeOutUid() uid:" + uid);
- if (!mFadedApps.containsKey(uid)) {
- mFadedApps.put(uid, new FadedOutApp(uid));
- }
- final FadedOutApp fa = mFadedApps.get(uid);
- for (AudioPlaybackConfiguration apc : players) {
- fa.addFade(apc, false /*skipRamp*/);
+ void fadeOutUid(int uid, ArrayList<AudioPlaybackConfiguration> players) {
+ Slog.i(TAG, "fadeOutUid() uid:" + uid);
+ synchronized (mLock) {
+ if (!mUidToFadedAppsMap.contains(uid)) {
+ mUidToFadedAppsMap.put(uid, new FadedOutApp(uid));
+ }
+ final FadedOutApp fa = mUidToFadedAppsMap.get(uid);
+ for (AudioPlaybackConfiguration apc : players) {
+ fa.addFade(apc, /* skipRamp= */ false);
+ }
}
}
@@ -163,49 +189,69 @@ public final class FadeOutManager {
* @param uid the uid for the app to unfade out
* @param players map of current available players (so we can get an APC from piid)
*/
- synchronized void unfadeOutUid(int uid, HashMap<Integer, AudioPlaybackConfiguration> players) {
- Log.i(TAG, "unfadeOutUid() uid:" + uid);
- final FadedOutApp fa = mFadedApps.remove(uid);
- if (fa == null) {
- return;
+ void unfadeOutUid(int uid, HashMap<Integer, AudioPlaybackConfiguration> players) {
+ Slog.i(TAG, "unfadeOutUid() uid:" + uid);
+ synchronized (mLock) {
+ final FadedOutApp fa = mUidToFadedAppsMap.get(uid);
+ if (fa == null) {
+ return;
+ }
+ mUidToFadedAppsMap.remove(uid);
+ fa.removeUnfadeAll(players);
}
- fa.removeUnfadeAll(players);
}
// pre-condition: apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED
// see {@link PlaybackActivityMonitor#playerEvent}
- synchronized void checkFade(@NonNull AudioPlaybackConfiguration apc) {
+ void checkFade(@NonNull AudioPlaybackConfiguration apc) {
if (DEBUG) {
- Log.v(TAG, "checkFade() player piid:"
+ Slog.v(TAG, "checkFade() player piid:"
+ apc.getPlayerInterfaceId() + " uid:" + apc.getClientUid());
}
- final FadedOutApp fa = mFadedApps.get(apc.getClientUid());
- if (fa == null) {
- return;
+ synchronized (mLock) {
+ final FadedOutApp fa = mUidToFadedAppsMap.get(apc.getClientUid());
+ if (fa == null) {
+ return;
+ }
+ fa.addFade(apc, /* skipRamp= */ true);
}
- fa.addFade(apc, true);
}
/**
* Remove the player from the list of faded out players because it has been released
* @param apc the released player
*/
- synchronized void removeReleased(@NonNull AudioPlaybackConfiguration apc) {
+ void removeReleased(@NonNull AudioPlaybackConfiguration apc) {
final int uid = apc.getClientUid();
if (DEBUG) {
- Log.v(TAG, "removedReleased() player piid: "
+ Slog.v(TAG, "removedReleased() player piid: "
+ apc.getPlayerInterfaceId() + " uid:" + uid);
}
- final FadedOutApp fa = mFadedApps.get(uid);
- if (fa == null) {
- return;
+ synchronized (mLock) {
+ final FadedOutApp fa = mUidToFadedAppsMap.get(uid);
+ if (fa == null) {
+ return;
+ }
+ fa.removeReleased(apc);
}
- fa.removeReleased(apc);
}
- synchronized void dump(PrintWriter pw) {
- for (FadedOutApp da : mFadedApps.values()) {
- da.dump(pw);
+ /**
+ * Check if uid is currently faded out
+ * @param uid Client id
+ * @return true if uid is currently faded out. Othwerwise, false.
+ */
+ boolean isUidFadedOut(int uid) {
+ synchronized (mLock) {
+ return mUidToFadedAppsMap.contains(uid);
+ }
+ }
+
+ void dump(PrintWriter pw) {
+ synchronized (mLock) {
+ for (int index = 0; index < mUidToFadedAppsMap.size(); index++) {
+ mUidToFadedAppsMap.valueAt(index).dump(pw);
+ }
}
}
@@ -240,7 +286,7 @@ public final class FadeOutManager {
final int piid = new Integer(apc.getPlayerInterfaceId());
if (mFadedPlayers.contains(piid)) {
if (DEBUG) {
- Log.v(TAG, "player piid:" + piid + " already faded out");
+ Slog.v(TAG, "player piid:" + piid + " already faded out");
}
return;
}
@@ -252,7 +298,7 @@ public final class FadeOutManager {
skipRamp ? PLAY_SKIP_RAMP : PLAY_CREATE_IF_NEEDED);
mFadedPlayers.add(piid);
} catch (Exception e) {
- Log.e(TAG, "Error fading out player piid:" + piid
+ Slog.e(TAG, "Error fading out player piid:" + piid
+ " uid:" + apc.getClientUid(), e);
}
}
@@ -269,12 +315,12 @@ public final class FadeOutManager {
FADEOUT_VSHAPE,
VolumeShaper.Operation.REVERSE);
} catch (Exception e) {
- Log.e(TAG, "Error unfading out player piid:" + piid + " uid:" + mUid, e);
+ Slog.e(TAG, "Error unfading out player piid:" + piid + " uid:" + mUid, e);
}
} else {
// this piid was in the list of faded players, but wasn't found
if (DEBUG) {
- Log.v(TAG, "Error unfading out player piid:" + piid
+ Slog.v(TAG, "Error unfading out player piid:" + piid
+ ", player not found for uid " + mUid);
}
}
diff --git a/services/core/java/com/android/server/audio/FocusRequester.java b/services/core/java/com/android/server/audio/FocusRequester.java
index 010d5f41bc7d..00c04ff12c89 100644
--- a/services/core/java/com/android/server/audio/FocusRequester.java
+++ b/services/core/java/com/android/server/audio/FocusRequester.java
@@ -495,7 +495,8 @@ public class FocusRequester {
// will be dispatched later, it is now in limbo mode
mFocusLossFadeLimbo = true;
mFocusController.postDelayedLossAfterFade(this,
- FadeOutManager.FADE_OUT_DURATION_MS);
+ mFocusController.getFadeOutDurationOnFocusLossMillis(
+ this.getAudioAttributes()));
return true;
}
}
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index 65f6c9b8d459..58f5d5e21cf0 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -94,7 +94,7 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
private final Context mContext;
private final AppOpsManager mAppOps;
- private PlayerFocusEnforcer mFocusEnforcer; // never null
+ private final @NonNull PlayerFocusEnforcer mFocusEnforcer;
private boolean mMultiAudioFocusEnabled = false;
private boolean mRingOrCallActive = false;
@@ -128,7 +128,8 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
* @return the fade out duration in ms
*/
public long getFocusFadeOutDurationForTest() {
- return FadeOutManager.FADE_OUT_DURATION_MS;
+ return getFadeOutDurationMillis(
+ new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build());
}
/**
@@ -137,7 +138,8 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
* @return the time gap after a fade out completion on focus loss, and fade in start in ms
*/
public long getFocusUnmuteDelayAfterFadeOutForTest() {
- return FadeOutManager.DELAY_FADE_IN_OFFENDERS_MS;
+ return getFadeInDelayForOffendersMillis(
+ new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build());
}
//=================================================================
@@ -178,6 +180,21 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
mFocusEnforcer.forgetUid(uid);
}
+ @Override
+ public long getFadeOutDurationMillis(@NonNull AudioAttributes aa) {
+ if (aa == null) {
+ return 0;
+ }
+ return mFocusEnforcer.getFadeOutDurationMillis(aa);
+ }
+
+ @Override
+ public long getFadeInDelayForOffendersMillis(@NonNull AudioAttributes aa) {
+ if (aa == null) {
+ return 0;
+ }
+ return mFocusEnforcer.getFadeInDelayForOffendersMillis(aa);
+ }
//==========================================================================================
// AudioFocus
//==========================================================================================
@@ -1401,7 +1418,7 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
if (!ENFORCE_FADEOUT_FOR_FOCUS_LOSS) {
return 0;
}
- return FadeOutManager.getFadeOutDurationOnFocusLossMillis(aa);
+ return getFadeOutDurationMillis(aa);
}
private void dumpMultiAudioFocus(PrintWriter pw) {
@@ -1423,14 +1440,14 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
Log.v(TAG, "postDelayedLossAfterFade loser=" + focusLoser.getPackageName());
}
mFocusHandler.sendMessageDelayed(
- mFocusHandler.obtainMessage(MSG_L_FOCUS_LOSS_AFTER_FADE, focusLoser),
- FadeOutManager.FADE_OUT_DURATION_MS);
+ mFocusHandler.obtainMessage(MSG_L_FOCUS_LOSS_AFTER_FADE, focusLoser), delayMs);
}
- private void postForgetUidLater(int uid) {
+ private void postForgetUidLater(FocusRequester focusRequester) {
mFocusHandler.sendMessageDelayed(
- mFocusHandler.obtainMessage(MSL_L_FORGET_UID, new ForgetFadeUidInfo(uid)),
- FadeOutManager.DELAY_FADE_IN_OFFENDERS_MS);
+ mFocusHandler.obtainMessage(MSL_L_FORGET_UID,
+ new ForgetFadeUidInfo(focusRequester.getClientUid())),
+ getFadeInDelayForOffendersMillis(focusRequester.getAudioAttributes()));
}
//=================================================================
@@ -1466,7 +1483,7 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
if (loser.isInFocusLossLimbo()) {
loser.dispatchFocusChange(AudioManager.AUDIOFOCUS_LOSS);
loser.release();
- postForgetUidLater(loser.getClientUid());
+ postForgetUidLater(loser);
}
}
break;
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 54fa6fbc3bfd..26e1248c087e 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -914,7 +914,7 @@ public final class PlaybackActivityMonitor
if (DEBUG) { Log.v(TAG, "no players to fade out"); }
return false;
}
- if (!FadeOutManager.canCauseFadeOut(winner, loser)) {
+ if (!mFadingManager.canCauseFadeOut(winner, loser)) {
return false;
}
// check if this UID needs to be faded out (return false if not), and gather list of
@@ -928,7 +928,7 @@ public final class PlaybackActivityMonitor
&& loser.hasSameUid(apc.getClientUid())
&& apc.getPlayerState()
== AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
- if (!FadeOutManager.canBeFadedOut(apc)) {
+ if (!mFadingManager.canBeFadedOut(apc)) {
// the player is not eligible to be faded out, bail
Log.v(TAG, "not fading out player " + apc.getPlayerInterfaceId()
+ " uid:" + apc.getClientUid() + " pid:" + apc.getClientPid()
@@ -960,6 +960,16 @@ public final class PlaybackActivityMonitor
mDuckingManager.unduckUid(uid, players);
}
+ @Override
+ public long getFadeOutDurationMillis(@NonNull AudioAttributes aa) {
+ return mFadingManager.getFadeOutDurationOnFocusLossMillis(aa);
+ }
+
+ @Override
+ public long getFadeInDelayForOffendersMillis(@NonNull AudioAttributes aa) {
+ return mFadingManager.getFadeInDelayForOffendersMillis(aa);
+ }
+
//=================================================================
// Track playback activity listeners
diff --git a/services/core/java/com/android/server/audio/PlayerFocusEnforcer.java b/services/core/java/com/android/server/audio/PlayerFocusEnforcer.java
index fb72ac282e8d..f1d42f3571a9 100644
--- a/services/core/java/com/android/server/audio/PlayerFocusEnforcer.java
+++ b/services/core/java/com/android/server/audio/PlayerFocusEnforcer.java
@@ -17,6 +17,7 @@
package com.android.server.audio;
import android.annotation.NonNull;
+import android.media.AudioAttributes;
public interface PlayerFocusEnforcer {
@@ -64,4 +65,18 @@ public interface PlayerFocusEnforcer {
* @param uid
*/
void forgetUid(int uid);
+
+ /**
+ * Get the fade out duration currently active for the given usage
+ * @param aa The {@link android.media.AudioAttributes}
+ * @return fade out duration in milliseconds
+ */
+ long getFadeOutDurationMillis(@NonNull AudioAttributes aa);
+
+ /**
+ * Returns the delay to fade-in the offending players
+ * @param aa The {@link android.media.AudioAttributes}
+ * @return delay in milliseconds
+ */
+ long getFadeInDelayForOffendersMillis(@NonNull AudioAttributes aa);
} \ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/audio/FadeOutManagerTest.java b/services/tests/servicestests/src/com/android/server/audio/FadeOutManagerTest.java
new file mode 100644
index 000000000000..fa94821d4ff2
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/audio/FadeOutManagerTest.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2023 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 com.android.server.audio;
+
+import static android.media.AudioAttributes.USAGE_MEDIA;
+import static android.media.AudioAttributes.USAGE_GAME;
+import static android.media.AudioAttributes.CONTENT_TYPE_SPEECH;
+import static android.media.AudioPlaybackConfiguration.PLAYER_TYPE_AAUDIO;
+import static android.media.AudioPlaybackConfiguration.PLAYER_TYPE_JAM_AUDIOTRACK;
+import static android.media.AudioPlaybackConfiguration.PLAYER_TYPE_UNKNOWN;
+
+import android.content.Context;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.media.AudioPlaybackConfiguration;
+import android.media.PlayerBase;
+import android.os.Parcel;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.google.common.truth.Expect;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+@RunWith(JUnit4.class)
+public final class FadeOutManagerTest {
+ private static final int TEST_UID_SYSTEM = 1000;
+ private static final int TEST_UID_USER = 10100;
+ private static final int TEST_SESSION_ID = 10;
+ private static final int TEST_PIID_1 = 101;
+ private static final int TEST_PIID_2 = 102;
+ private static final int TEST_PIID_3 = 103;
+ private static final int TEST_PID = 10101;
+
+ private static final AudioAttributes TEST_MEDIA_AUDIO_ATTRIBUTE =
+ new AudioAttributes.Builder().setUsage(USAGE_MEDIA).build();
+ private static final AudioAttributes TEST_GAME_AUDIO_ATTRIBUTE =
+ new AudioAttributes.Builder().setUsage(USAGE_GAME).build();
+ private static final AudioAttributes TEST_SPEECH_AUDIO_ATTRIBUTE =
+ new AudioAttributes.Builder().setContentType(CONTENT_TYPE_SPEECH).build();
+ private FadeOutManager mFadeOutManager;
+ private Context mContext;
+
+ @Mock
+ PlayerBase.PlayerIdCard mMockPlayerIdCard;
+ @Mock
+ AudioPlaybackConfiguration mMockPlaybackConfiguration;
+
+ @Rule
+ public final Expect expect = Expect.create();
+
+ @Before
+ public void setUp() {
+ mFadeOutManager = new FadeOutManager();
+ mContext = ApplicationProvider.getApplicationContext();
+ }
+
+ @Test
+ public void testCanCauseFadeOut_forFaders_returnsTrue() {
+ FocusRequester winner = createFocusRequester(TEST_MEDIA_AUDIO_ATTRIBUTE, "winning-client",
+ "unit-test", TEST_UID_USER,
+ AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS);
+ FocusRequester loser = createFocusRequester(TEST_SPEECH_AUDIO_ATTRIBUTE, "losing-client",
+ "unit-test", TEST_UID_USER, AudioManager.AUDIOFOCUS_FLAG_TEST);
+
+ expect.withMessage("Can cause fade out").that(mFadeOutManager.canCauseFadeOut(
+ winner, loser)).isTrue();
+ }
+
+ @Test
+ public void testCanCauseFadeOut_forUnfaderSpeechAttribute_returnsFalse() {
+ FocusRequester winner = createFocusRequester(TEST_SPEECH_AUDIO_ATTRIBUTE, "winning-client",
+ "unit-test", TEST_UID_USER, AudioManager.AUDIOFOCUS_FLAG_TEST);
+ FocusRequester loser = createFocusRequester(TEST_SPEECH_AUDIO_ATTRIBUTE, "losing-client",
+ "unit-test", TEST_UID_USER, AudioManager.AUDIOFOCUS_FLAG_TEST);
+
+ expect.withMessage("Can cause fade out for speech attribute")
+ .that(mFadeOutManager.canCauseFadeOut(winner, loser)).isFalse();
+ }
+
+ @Test
+ public void testCanCauseFadeOut_forUnfaderFlag_returnsFalse() {
+ FocusRequester winner = createFocusRequester(TEST_SPEECH_AUDIO_ATTRIBUTE, "winning-client",
+ "unit-test", TEST_UID_USER, AudioManager.AUDIOFOCUS_FLAG_TEST);
+ FocusRequester loser = createFocusRequester(TEST_MEDIA_AUDIO_ATTRIBUTE, "losing-client",
+ "unit-test", TEST_UID_USER,
+ AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS);
+
+ expect.withMessage("Can cause fade out for flag pause on duckable loss")
+ .that(mFadeOutManager.canCauseFadeOut(winner, loser)).isFalse();
+ }
+
+ @Test
+ public void testCanBeFadedOut_forFadableConfig_returnsTrue() {
+ AudioPlaybackConfiguration apcMedia = createAudioPlaybackConfiguration(
+ TEST_MEDIA_AUDIO_ATTRIBUTE, TEST_UID_USER, TEST_PIID_1, TEST_PID,
+ PLAYER_TYPE_JAM_AUDIOTRACK);
+ AudioPlaybackConfiguration apcGame = createAudioPlaybackConfiguration(
+ TEST_GAME_AUDIO_ATTRIBUTE, TEST_UID_USER, TEST_PIID_2, TEST_PID,
+ PLAYER_TYPE_UNKNOWN);
+
+ expect.withMessage("Fadability for media audio attribute")
+ .that(mFadeOutManager.canBeFadedOut(apcMedia)).isTrue();
+ expect.withMessage("Fadability for game audio attribute")
+ .that(mFadeOutManager.canBeFadedOut(apcGame)).isTrue();
+ }
+
+ @Test
+ public void testCanBeFadedOut_forUnFadableConfig_returnsFalse() {
+ AudioPlaybackConfiguration apcSpeech = createAudioPlaybackConfiguration(
+ TEST_SPEECH_AUDIO_ATTRIBUTE, TEST_UID_USER, TEST_PIID_1, TEST_PID,
+ PLAYER_TYPE_JAM_AUDIOTRACK);
+ AudioPlaybackConfiguration apcAAudio = createAudioPlaybackConfiguration(
+ TEST_GAME_AUDIO_ATTRIBUTE, TEST_UID_USER, TEST_PIID_2, TEST_PID,
+ PLAYER_TYPE_AAUDIO);
+
+ expect.withMessage("Fadability for speech audio attribute")
+ .that(mFadeOutManager.canBeFadedOut(apcSpeech)).isFalse();
+ expect.withMessage("Fadability for AAudio player type")
+ .that(mFadeOutManager.canBeFadedOut(apcAAudio)).isFalse();
+ }
+
+ @Test
+ public void testFadeOutUid_getsFadedOut_isFadedReturnsTrue() {
+ final ArrayList<AudioPlaybackConfiguration> apcsToFadeOut =
+ new ArrayList<AudioPlaybackConfiguration>();
+ apcsToFadeOut.add(createAudioPlaybackConfiguration(
+ TEST_MEDIA_AUDIO_ATTRIBUTE, TEST_UID_USER, TEST_PIID_1, TEST_PID,
+ PLAYER_TYPE_JAM_AUDIOTRACK));
+
+ mFadeOutManager.fadeOutUid(TEST_UID_USER, apcsToFadeOut);
+
+ expect.withMessage("Fade out state for uid")
+ .that(mFadeOutManager.isUidFadedOut(TEST_UID_USER)).isTrue();
+ }
+
+ @Test
+ public void testUnfadeOutUid_getsUnfaded_isFadedReturnsFalse() {
+ final ArrayList<AudioPlaybackConfiguration> apcsToUnfade =
+ new ArrayList<AudioPlaybackConfiguration>();
+ final ArrayList<AudioPlaybackConfiguration> apcsToKeepfaded =
+ new ArrayList<AudioPlaybackConfiguration>();
+ AudioPlaybackConfiguration apcMediaPiid1 = createAudioPlaybackConfiguration(
+ TEST_MEDIA_AUDIO_ATTRIBUTE, TEST_UID_USER, TEST_PIID_1, TEST_PID,
+ PLAYER_TYPE_JAM_AUDIOTRACK);
+ AudioPlaybackConfiguration apcGamePiid2 = createAudioPlaybackConfiguration(
+ TEST_GAME_AUDIO_ATTRIBUTE, TEST_UID_USER, TEST_PIID_2, TEST_PID,
+ PLAYER_TYPE_JAM_AUDIOTRACK);
+ AudioPlaybackConfiguration apcMediaPiid3 = createAudioPlaybackConfiguration(
+ TEST_MEDIA_AUDIO_ATTRIBUTE, TEST_UID_SYSTEM, TEST_PIID_3, TEST_PID,
+ PLAYER_TYPE_JAM_AUDIOTRACK);
+ HashMap<Integer, AudioPlaybackConfiguration> mapPiidToApcs = new HashMap<>();
+ apcsToUnfade.add(apcMediaPiid1);
+ apcsToUnfade.add(apcGamePiid2);
+ apcsToKeepfaded.add(apcMediaPiid3);
+ mFadeOutManager.fadeOutUid(TEST_UID_USER, apcsToUnfade);
+ mFadeOutManager.fadeOutUid(TEST_UID_SYSTEM, apcsToKeepfaded);
+ mapPiidToApcs.put(TEST_PIID_1, apcMediaPiid1);
+ mapPiidToApcs.put(TEST_PIID_2, apcGamePiid2);
+
+ mFadeOutManager.unfadeOutUid(TEST_UID_USER, mapPiidToApcs);
+
+ expect.withMessage("Fade out state after unfading for uid")
+ .that(mFadeOutManager.isUidFadedOut(TEST_UID_USER)).isFalse();
+ expect.withMessage("Fade out state for uid")
+ .that(mFadeOutManager.isUidFadedOut(TEST_UID_SYSTEM)).isTrue();
+
+ }
+
+ private FocusRequester createFocusRequester(AudioAttributes aa, String clientId,
+ String packageName, int uid, int flags) {
+ MediaFocusControl mfc = new MediaFocusControl(mContext, null);
+ return new FocusRequester(aa, AudioManager.AUDIOFOCUS_GAIN, flags, null, null, clientId,
+ null, packageName, uid, mfc, 1);
+ }
+
+ private PlayerBase.PlayerIdCard createPlayerIdCard(AudioAttributes aa, int playerType) {
+
+ Parcel p = Parcel.obtain();
+ p.writeInt(playerType);
+ aa.writeToParcel(p, 0);
+ p.writeStrongInterface(null);
+ p.writeInt(TEST_SESSION_ID);
+ p.setDataPosition(0);
+ return PlayerBase.PlayerIdCard.CREATOR.createFromParcel(p);
+ }
+
+ private AudioPlaybackConfiguration createAudioPlaybackConfiguration(AudioAttributes aa, int uid,
+ int piid, int pid, int playerType) {
+ return new AudioPlaybackConfiguration(createPlayerIdCard(aa, playerType), piid, uid, pid);
+ }
+}