summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Raj Goparaju <rajgoparaju@google.com> 2023-09-28 11:30:37 -0700
committer Raj Goparaju <rajgoparaju@google.com> 2023-11-07 14:43:00 -0800
commitf17c31468c7751fc5e712eabd72655d4d67dda6f (patch)
treed9f00af5d5822b5b2a185c7060c05f7dd01b2da2
parent10614f6487059a631cda474e820a6e6ff33e208d (diff)
Refactor methods of FadeOutManager
The target is to eventually make the FadeOutManager properties configurable. As a first step: 1. Update the interface to include common method calls 2. Use the interface instead of static methods 3. Convert synchronized methods to using lock object 4. Add new unit test for the class Bug: 186905459 Bug: 302870089 Test: atest -c --rerun-until-failure 1000 AudioFocusTest FadeOutManagerTest Change-Id: I00f7986ccfeeac690035d390e9aa1a1c87db61e7
-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);
+ }
+}