Merge "AudioService: mute media/game players during phone calls"

This commit is contained in:
Jean-Michel Trivi
2017-02-08 22:28:42 +00:00
committed by Android (Google) Code Review
3 changed files with 139 additions and 2 deletions

View File

@@ -49,11 +49,18 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
* that they lost focus.
*/
static final boolean ENFORCE_DUCKING = false;
/**
* set to true so the framework enforces muting media/game itself when the device is ringing
* or in a call.
*/
static final boolean ENFORCE_MUTING_FOR_RING_OR_CALL = true;
private final Context mContext;
private final AppOpsManager mAppOps;
private PlayerFocusEnforcer mFocusEnforcer; // never null
private boolean mRingOrCallActive = false;
protected MediaFocusControl(Context cntxt, PlayerFocusEnforcer pfe) {
mContext = cntxt;
mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE);
@@ -78,6 +85,16 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
mFocusEnforcer.unduckPlayers(winner);
}
@Override
public void mutePlayersForCall(int[] usagesToMute) {
mFocusEnforcer.mutePlayersForCall(usagesToMute);
}
@Override
public void unmutePlayersForCall() {
mFocusEnforcer.unmutePlayersForCall();
}
//==========================================================================================
// AudioFocus
//==========================================================================================
@@ -139,7 +156,9 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
stackIterator.next().dump(pw);
}
}
pw.println("\n Notify on duck: " + mNotifyFocusOwnerOnDuck +"\n");
pw.println("\n");
pw.println(" Notify on duck: " + mNotifyFocusOwnerOnDuck + "\n");
pw.println(" In ring or call: " + mRingOrCallActive + "\n");
}
/**
@@ -400,6 +419,18 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
}
}
/**
* Delay after entering ringing or call mode after which the framework will mute streams
* that are still playing.
*/
private static final int RING_CALL_MUTING_ENFORCEMENT_DELAY_MS = 100;
/**
* Usages to mute when the device rings or is in a call
*/
private final static int[] USAGES_TO_MUTE_IN_RING_OR_CALL =
{ AudioAttributes.USAGE_MEDIA, AudioAttributes.USAGE_GAME };
/**
* Return the volume ramp time expected before playback with the given AudioAttributes would
* start after gaining audio focus.
@@ -452,6 +483,10 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
}
synchronized(mAudioFocusLock) {
boolean enteringRingOrCall = !mRingOrCallActive
& (AudioSystem.IN_VOICE_COMM_FOCUS_ID.compareTo(clientId) == 0);
if (enteringRingOrCall) { mRingOrCallActive = true; }
boolean focusGrantDelayed = false;
if (!canReassignAudioFocus()) {
if ((flags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK) == 0) {
@@ -523,6 +558,9 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(),
AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
if (ENFORCE_MUTING_FOR_RING_OR_CALL & enteringRingOrCall) {
runAudioCheckerForRingOrCallAsync(true/*enteringRingOrCall*/);
}
}//synchronized(mAudioFocusLock)
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
@@ -539,7 +577,15 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
try {
// this will take care of notifying the new focus owner if needed
synchronized(mAudioFocusLock) {
boolean exitingRingOrCall = mRingOrCallActive
& (AudioSystem.IN_VOICE_COMM_FOCUS_ID.compareTo(clientId) == 0);
if (exitingRingOrCall) { mRingOrCallActive = false; }
removeFocusStackEntry(clientId, true /*signal*/, true /*notifyFocusFollowers*/);
if (ENFORCE_MUTING_FOR_RING_OR_CALL & exitingRingOrCall) {
runAudioCheckerForRingOrCallAsync(false/*enteringRingOrCall*/);
}
}
} catch (java.util.ConcurrentModificationException cme) {
// Catching this exception here is temporary. It is here just to prevent
@@ -559,4 +605,26 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
}
}
private void runAudioCheckerForRingOrCallAsync(final boolean enteringRingOrCall) {
new Thread() {
public void run() {
if (enteringRingOrCall) {
try {
Thread.sleep(RING_CALL_MUTING_ENFORCEMENT_DELAY_MS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (mAudioFocusLock) {
// since the new thread starting running the state could have changed, so
// we need to check again mRingOrCallActive, not enteringRingOrCall
if (mRingOrCallActive) {
mFocusEnforcer.mutePlayersForCall(USAGES_TO_MUTE_IN_RING_OR_CALL);
} else {
mFocusEnforcer.unmutePlayersForCall();
}
}
}
}.start();
}
}

View File

@@ -147,6 +147,11 @@ public final class PlaybackActivityMonitor
for (int piid : mDuckedPlayers) {
pw.println(" " + piid);
}
// players muted due to the device ringing or being in a call
pw.println("\n muted player piids:");
for (int piid : mMutedPlayers) {
pw.println(" " + piid);
}
}
}
@@ -231,6 +236,7 @@ public final class PlaybackActivityMonitor
//=================================================================
// PlayerFocusEnforcer implementation
private final ArrayList<Integer> mDuckedPlayers = new ArrayList<Integer>();
private final ArrayList<Integer> mMutedPlayers = new ArrayList<Integer>();
@Override
public boolean duckPlayers(FocusRequester winner, FocusRequester loser) {
@@ -290,9 +296,9 @@ public final class PlaybackActivityMonitor
&& winner.hasSameUid(apc.getClientUid())) {
try {
if (DEBUG) { Log.v(TAG, "unducking player" + piid); }
mDuckedPlayers.remove(new Integer(piid));
//FIXME just a test before we have VolumeShape
apc.getPlayerProxy().setPan(0.0f);
mDuckedPlayers.remove(new Integer(piid));
} catch (Exception e) {
Log.e(TAG, "Error unducking player " + piid, e);
}
@@ -303,6 +309,65 @@ public final class PlaybackActivityMonitor
}
}
@Override
public void mutePlayersForCall(int[] usagesToMute) {
if (DEBUG) {
String log = new String("mutePlayersForCall: usages=");
for (int usage : usagesToMute) { log += " " + usage; }
Log.v(TAG, log);
}
synchronized (mPlayerLock) {
final Set<Integer> piidSet = mPlayers.keySet();
final Iterator<Integer> piidIterator = piidSet.iterator();
// find which players to mute
while (piidIterator.hasNext()) {
final Integer piid = piidIterator.next();
final AudioPlaybackConfiguration apc = mPlayers.get(piid);
final int playerUsage = apc.getAudioAttributes().getUsage();
boolean mute = false;
for (int usageToMute : usagesToMute) {
if (playerUsage == usageToMute) {
mute = true;
break;
}
}
if (mute) {
try {
if (DEBUG) { Log.v(TAG, "muting player" + piid); }
apc.getPlayerProxy().setVolume(0.0f);
mMutedPlayers.add(piid);
} catch (Exception e) {
Log.e(TAG, "Error muting player " + piid, e);
}
}
}
}
}
@Override
public void unmutePlayersForCall() {
if (DEBUG) {
Log.v(TAG, "unmutePlayersForCall()");
}
synchronized (mPlayerLock) {
if (mMutedPlayers.isEmpty()) {
return;
}
for (int piid : mMutedPlayers) {
final AudioPlaybackConfiguration apc = mPlayers.get(piid);
if (apc != null) {
try {
if (DEBUG) { Log.v(TAG, "unmuting player" + piid); }
apc.getPlayerProxy().setVolume(1.0f);
mMutedPlayers.remove(new Integer(piid));
} catch (Exception e) {
Log.e(TAG, "Error unmuting player " + piid, e);
}
}
}
}
}
//=================================================================
// Track playback activity listeners

View File

@@ -28,4 +28,8 @@ public interface PlayerFocusEnforcer {
public boolean duckPlayers(FocusRequester winner, FocusRequester loser);
public void unduckPlayers(FocusRequester winner);
public void mutePlayersForCall(int[] usagesToMute);
public void unmutePlayersForCall();
}