Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 62b8634f authored by Jean-Michel Trivi's avatar Jean-Michel Trivi
Browse files

AudioService: mute media/game players during phone calls

When the device is ringing or in a call, mute all media/game audio
  players.

Test: run a MediaPlayer, receive call
Bug 30258418

Change-Id: Idb3bd8d33f89aea6ac3293d21801b4b3387d969a
parent f4872e41
Loading
Loading
Loading
Loading
+69 −1
Original line number Diff line number Diff line
@@ -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();
    }
}
+66 −1
Original line number Diff line number Diff line
@@ -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

+4 −0
Original line number Diff line number Diff line
@@ -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();
}
 No newline at end of file