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

Commit d84ba275 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes Ic8ead19e,I3b952997,Id19f2d62 into main

* changes:
  Remove static memoization from AudioPlayerStateMonitor.getInstance
  Remove cooldown for sessionless foreground vol key routing
  Exclude notifications from 'recently played audio' vol key routing
parents 65388286 40d6a46b
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
@@ -28,3 +28,26 @@ flag {
  description: "Deprecate platform MediaSession2 APIs"
  bug: "436849846"
}

flag {
  name: "filter_session_audio_playback_by_usage"
  namespace: "media_solutions"
  description: "Filter audio playback uids used for session tracking by audio usage type (to e.g. ignore notification audio)."
  bug: "432003816"
}

flag {
  name: "sessionless_vol_key_zero_cooldown"
  namespace: "media_solutions"
  description: "Decrease the 'cooldown' (time since last audio playback) for routing volume "
               "keys to a foreground session-less app (instead of an active remote session) "
               "from infinity to zero."
  bug: "432003816"
}

flag {
  name: "remove_audioplayerstatemonitor_static_memoization"
  namespace: "media_solutions"
  description: "Remove the static memoization from AudioPlayerStateMonitor.getInstance(Context)."
  bug: "438917725"
}
+53 −3
Original line number Diff line number Diff line
@@ -29,6 +29,8 @@ import android.util.ArraySet;
import android.util.Log;

import com.android.internal.annotations.GuardedBy;
import com.android.media.mediasession.flags.Flags;
import com.android.server.SystemService;

import java.io.PrintWriter;
import java.util.ArrayList;
@@ -89,6 +91,10 @@ class AudioPlayerStateMonitor {
    }

    private final Object mLock = new Object();

    @GuardedBy("mLock")
    private final Set<Integer> mIgnoredAudioUsageTypes;

    @GuardedBy("mLock")
    private final Map<OnAudioPlayerActiveStateChangedListener, MessageHandler> mListenerMap =
            new ArrayMap<>();
@@ -106,16 +112,49 @@ class AudioPlayerStateMonitor {
    @SuppressWarnings("WeakerAccess") /* synthetic access */
    final List<Integer> mSortedAudioPlaybackClientUids = new ArrayList<>();

    /**
     * Static factory method.
     *
     * <p>If {@link Flags#removeAudioplayerstatemonitorStaticMemoization()} is true then this method
     * creates a new instance (and registers a new listener on {@link AudioManager}) each time it's
     * called, so should only be called once per {@link SystemService} lifecycle.
     *
     * @param context A context.
     * @return The constructed instance.
     */
    static AudioPlayerStateMonitor getInstance(Context context) {
        if (Flags.removeAudioplayerstatemonitorStaticMemoization()) {
            return getInstance(context, Set.of());
        }
        synchronized (AudioPlayerStateMonitor.class) {
            if (sInstance == null) {
                sInstance = new AudioPlayerStateMonitor(context);
                sInstance = new AudioPlayerStateMonitor(context, Set.of());
            }
            return sInstance;
        }
    }

    private AudioPlayerStateMonitor(Context context) {
    /**
     * Static factory method.
     *
     * <p>This method creates a new instance (and registers a new listener on {@link AudioManager})
     * each time it's called, so should only be called once per {@link SystemService} lifecycle.
     *
     * @param context A context.
     * @param ignoredAudioUsageTypes A set of {@code AudioAttributes.USAGE_} values that should be
     *     ignored when calculating the results of 'recent/active audio' methods like {@link
     *     #isPlaybackActive(int)}. This doesn't filter calls to {@link
     *     OnAudioPlayerActiveStateChangedListener#onAudioPlayerActiveStateChanged(
     *     AudioPlaybackConfiguration, boolean)}.
     * @return The constructed instance.
     */
    static AudioPlayerStateMonitor getInstance(
            Context context, Set<Integer> ignoredAudioUsageTypes) {
        return new AudioPlayerStateMonitor(context, ignoredAudioUsageTypes);
    }

    private AudioPlayerStateMonitor(Context context, Set<Integer> ignoredAudioUsageTypes) {
        mIgnoredAudioUsageTypes = Set.copyOf(ignoredAudioUsageTypes);
        AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        am.registerAudioPlaybackCallback(new AudioManagerPlaybackListener(), null);
    }
@@ -144,6 +183,9 @@ class AudioPlayerStateMonitor {
     * Returns the sorted list of UIDs that have had active audio playback. (i.e. playing an
     * audio/video) The UID whose audio is currently playing comes first, then the UID whose audio
     * playback becomes active at the last comes next.
     *
     * <p>Audio playbacks with usage types in the {@code Set<Integer> ignoredAudioUsageTypes}
     * {@linkplain #getInstance(Context, Set) factory} parameter are ignored.
     */
    public List<Integer> getSortedAudioPlaybackClientUids() {
        List<Integer> sortedAudioPlaybackClientUids = new ArrayList();
@@ -160,6 +202,9 @@ class AudioPlayerStateMonitor {
     * a media session. Then, volume events should affect the local music stream rather than other
     * media sessions.
     *
     * <p>Audio playbacks with usage types in the {@code Set<Integer> ignoredAudioUsageTypes}
     * {@linkplain #getInstance(Context, Set) factory} parameter are ignored.
     *
     * @return {@code true} if the given uid corresponds to the last process to audio or
     * {@code false} otherwise.
     */
@@ -172,6 +217,9 @@ class AudioPlayerStateMonitor {

    /**
     * Returns if the audio playback is active for the uid.
     *
     * <p>Audio playbacks with usage types in the {@code Set<Integer> ignoredAudioUsageTypes}
     * {@linkplain #getInstance(Context, Set) factory} parameter are ignored.
     */
    public boolean isPlaybackActive(int uid) {
        synchronized (mLock) {
@@ -245,7 +293,9 @@ class AudioPlayerStateMonitor {
                ArrayMap<Integer, AudioPlaybackConfiguration> activeAudioPlaybackConfigs =
                        new ArrayMap<>();
                for (AudioPlaybackConfiguration config : configs) {
                    if (config.isActive()) {
                    if (config.isActive()
                            && !mIgnoredAudioUsageTypes.contains(
                                    config.getAudioAttributes().getUsage())) {
                        mActiveAudioUids.add(config.getClientUid());
                        activeAudioPlaybackConfigs.put(config.getPlayerInterfaceId(), config);
                    }
+25 −6
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.AudioPlaybackConfiguration;
import android.media.AudioSystem;
@@ -143,6 +144,18 @@ public class MediaSessionService extends SystemService implements Monitor {
    private static final String USAGE_STATS_ACTION_STOP = "stop";
    private static final String USAGE_STATS_CATEGORY = "android.media";

    /**
     * {@code AudioAttributes.USAGE_} types that are ignored by the 'recent audio playback' tracking
     * in {@link AudioPlayerStateMonitor} if {@link
     * com.android.media.mediasession.flags.Flags#filterSessionAudioPlaybackByUsage()} is true.
     */
    private static final Set<Integer> IGNORED_AUDIO_USAGE_TYPES_FOR_PLAYBACK_TRACKING =
            Set.of(
                    AudioAttributes.USAGE_NOTIFICATION,
                    AudioAttributes.USAGE_NOTIFICATION_RINGTONE,
                    AudioAttributes.USAGE_ALARM,
                    AudioAttributes.USAGE_NOTIFICATION_EVENT);

    private final Context mContext;
    private final SessionManagerImpl mSessionManagerImpl;
    private final MessageHandler mHandler = new MessageHandler();
@@ -257,7 +270,11 @@ public class MediaSessionService extends SystemService implements Monitor {
        publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl);
        Watchdog.getInstance().addMonitor(this);
        mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
        mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance(mContext);
        mAudioPlayerStateMonitor =
                com.android.media.mediasession.flags.Flags.filterSessionAudioPlaybackByUsage()
                        ? AudioPlayerStateMonitor.getInstance(
                                mContext, IGNORED_AUDIO_USAGE_TYPES_FOR_PLAYBACK_TRACKING)
                        : AudioPlayerStateMonitor.getInstance(mContext);
        mAudioPlayerStateMonitor.registerListener(
                (config, isRemoved) -> {
                    if (DEBUG) {
@@ -2555,11 +2572,13 @@ public class MediaSessionService extends SystemService implements Monitor {
                    isValidLocalStreamType(suggestedStream)
                            && AudioSystem.isStreamActive(suggestedStream, 0);

            if (session != null && session.getUid() != uid
                    && mAudioPlayerStateMonitor.hasUidPlayedAudioLast(uid)) {
                // The app in the foreground has been the last app to play media locally.
                // Therefore, we ignore the chosen session so that volume events affect the local
                // music stream instead. See b/275185436 for details.
            if (session != null
                    && session.getUid() != uid
                    && (com.android.media.mediasession.flags.Flags.sessionlessVolKeyZeroCooldown()
                            ? mAudioPlayerStateMonitor.isPlaybackActive(uid)
                            : mAudioPlayerStateMonitor.hasUidPlayedAudioLast(uid))) {
                // We ignore the chosen session so that volume events affect the local music stream
                // instead. See b/275185436 and b/432003816 for details.
                Log.d(TAG, "Ignoring session=" + session + " and adjusting suggestedStream="
                        + suggestedStream + " instead");
                session = null;