Merge "AudioService: mute media/game players during phone calls"
This commit is contained in:
committed by
Android (Google) Code Review
commit
fd4e9e0f8d
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
Reference in New Issue
Block a user