summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Jean-Michel Trivi <jmtrivi@google.com> 2021-09-26 18:52:44 -0700
committer Jean-Michel Trivi <jmtrivi@google.com> 2021-09-27 15:20:29 +0000
commit52441f8285b129c2b7546958b5bca6d4ecece704 (patch)
tree67d4f1d0eb852e8eedbf5cc32f37b09433ae3e5f
parent609258f83de63b9a4ba4ff2d64b8ba7fd445c8ac (diff)
Audio focus: make media fade out temporary
This change only affect media/game apps that request and lose focus with GAIN and LOSS (i.e. no temporary gain/loss). The fade out mechanism for media apps losing audio focus intentionally left players in a muted state at the end of the fade out ramp. If they were starting again after a focus request, they were unmuted. But some applications don't respect audio focus, e.g. they request focus with GAIN when they start playing, but keep playing even after they lose focus with LOSS. Such "offending" apps were left muted as they were not expected to keep playing. This change is a mitigation of the behavior of offending apps: 2s after an app has been notified it lost focus, if it still had players, they will be unmuted. - for apps that followed the audio focus guidelines, their player was paused by then, so no change expected - for offending apps, their audio will be heard again, shortly after the new app (the new focus owner) is likely to have started playing. Bug: 196186950 Test: atest AudioFocusTest Change-Id: I873fd1371ae499e50f5e6a60456ce4b0139f2d34
-rw-r--r--services/core/java/com/android/server/audio/FadeOutManager.java20
-rw-r--r--services/core/java/com/android/server/audio/MediaFocusControl.java57
-rw-r--r--services/core/java/com/android/server/audio/PlaybackActivityMonitor.java6
3 files changed, 75 insertions, 8 deletions
diff --git a/services/core/java/com/android/server/audio/FadeOutManager.java b/services/core/java/com/android/server/audio/FadeOutManager.java
index bb627e5a21fb..00cb280236d7 100644
--- a/services/core/java/com/android/server/audio/FadeOutManager.java
+++ b/services/core/java/com/android/server/audio/FadeOutManager.java
@@ -36,7 +36,16 @@ 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;
+ /**
+ * 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
+ * when their app lost focus.
+ * 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 boolean DEBUG = PlaybackActivityMonitor.DEBUG;
@@ -148,6 +157,11 @@ public final class FadeOutManager {
}
}
+ /**
+ * Remove the app for the given UID from the list of faded out apps, unfade out its players
+ * @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);
@@ -157,12 +171,6 @@ public final class FadeOutManager {
fa.removeUnfadeAll(players);
}
- synchronized void forgetUid(int uid) {
- //Log.v(TAG, "forget() uid:" + uid);
- //mFadedApps.remove(uid);
- // TODO unfade all players later in case they are reused or the app continued to play
- }
-
// pre-condition: apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED
// see {@link PlaybackActivityMonitor#playerEvent}
synchronized void checkFade(@NonNull AudioPlaybackConfiguration apc) {
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index e6c4abfa2086..9548ada14b8e 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -131,6 +131,11 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
@Override
public void restoreVShapedPlayers(@NonNull FocusRequester winner) {
mFocusEnforcer.restoreVShapedPlayers(winner);
+ // remove scheduled events to unfade out offending players (if any) corresponding to
+ // this uid, as we're removing any effects of muting/ducking/fade out now
+ mFocusHandler.removeEqualMessages(MSL_L_FORGET_UID,
+ new ForgetFadeUidInfo(winner.getClientUid()));
+
}
@Override
@@ -1182,6 +1187,13 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
mFocusHandler.obtainMessage(MSG_L_FOCUS_LOSS_AFTER_FADE, focusLoser),
FadeOutManager.FADE_OUT_DURATION_MS);
}
+
+ private void postForgetUidLater(int uid) {
+ mFocusHandler.sendMessageDelayed(
+ mFocusHandler.obtainMessage(MSL_L_FORGET_UID, new ForgetFadeUidInfo(uid)),
+ FadeOutManager.DELAY_FADE_IN_OFFENDERS_MS);
+ }
+
//=================================================================
// Message handling
private Handler mFocusHandler;
@@ -1196,6 +1208,8 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
*/
private static final int MSG_L_FOCUS_LOSS_AFTER_FADE = 1;
+ private static final int MSL_L_FORGET_UID = 2;
+
private void initFocusThreading() {
mFocusThread = new HandlerThread(TAG);
mFocusThread.start();
@@ -1213,15 +1227,56 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
if (loser.isInFocusLossLimbo()) {
loser.dispatchFocusChange(AudioManager.AUDIOFOCUS_LOSS);
loser.release();
- mFocusEnforcer.forgetUid(loser.getClientUid());
+ postForgetUidLater(loser.getClientUid());
}
}
break;
+
+ case MSL_L_FORGET_UID:
+ final int uid = ((ForgetFadeUidInfo) msg.obj).mUid;
+ if (DEBUG) {
+ Log.d(TAG, "MSL_L_FORGET_UID uid=" + uid);
+ }
+ mFocusEnforcer.forgetUid(uid);
+ break;
default:
break;
}
}
};
+ }
+ /**
+ * Class to associate a UID with a scheduled event to "forget" a UID for the fade out behavior.
+ * Having a class with an equals() override allows using Handler.removeEqualsMessage() to
+ * unschedule events when needed. Here we need to unschedule the "unfading out" == "forget uid"
+ * whenever a new, more recent, focus related event happens before this one is handled.
+ */
+ private static final class ForgetFadeUidInfo {
+ private final int mUid;
+
+ ForgetFadeUidInfo(int uid) {
+ mUid = uid;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final ForgetFadeUidInfo f = (ForgetFadeUidInfo) o;
+ if (f.mUid != mUid) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return mUid;
+ }
}
}
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index a13b2eb344d9..b94cea4d5d40 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -747,7 +747,11 @@ public final class PlaybackActivityMonitor
@Override
public void forgetUid(int uid) {
- mFadingManager.forgetUid(uid);
+ final HashMap<Integer, AudioPlaybackConfiguration> players;
+ synchronized (mPlayerLock) {
+ players = (HashMap<Integer, AudioPlaybackConfiguration>) mPlayers.clone();
+ }
+ mFadingManager.unfadeOutUid(uid, players);
}
//=================================================================