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

Commit 98ad7769 authored by Raj Goparaju's avatar Raj Goparaju Committed by Android (Google) Code Review
Browse files

Merge changes from topic "feature_audioCrossover" into main

* changes:
  Refactor to ecapsulate FadeOutManager configurations
  Refactor methods of FadeOutManager
parents ed867c87 eb16bd19
Loading
Loading
Loading
Loading
+180 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.audio;

import android.annotation.NonNull;
import android.media.AudioAttributes;
import android.media.AudioPlaybackConfiguration;
import android.media.VolumeShaper;
import android.util.Slog;

import java.util.List;

/**
 * Class to encapsulate configurations used for fading players
 */
public final class FadeConfigurations {
    public static final String TAG = "AS.FadeConfigurations";

    private static final boolean DEBUG = PlaybackActivityMonitor.DEBUG;


    /** duration of the fade out curve */
    private static final long DEFAULT_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
     */
    private static final long DEFAULT_DELAY_FADE_IN_OFFENDERS_MS = 2000;

    private static final List<Integer> DEFAULT_UNFADEABLE_PLAYER_TYPES = List.of(
            AudioPlaybackConfiguration.PLAYER_TYPE_AAUDIO,
            AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL
    );

    private static final List<Integer> DEFAULT_UNFADEABLE_CONTENT_TYPES = List.of(
            AudioAttributes.CONTENT_TYPE_SPEECH
    );

    private static final List<Integer> DEFAULT_FADEABLE_USAGES = List.of(
            AudioAttributes.USAGE_GAME,
            AudioAttributes.USAGE_MEDIA
    );

    private static final VolumeShaper.Configuration DEFAULT_FADEOUT_VSHAPE =
            new VolumeShaper.Configuration.Builder()
                    .setId(PlaybackActivityMonitor.VOLUME_SHAPER_SYSTEM_FADEOUT_ID)
                    .setCurve(new float[]{0.f, 0.25f, 1.0f} /* times */,
                            new float[]{1.f, 0.65f, 0.0f} /* volumes */)
                    .setOptionFlags(VolumeShaper.Configuration.OPTION_FLAG_CLOCK_TIME)
                    .setDuration(DEFAULT_FADE_OUT_DURATION_MS)
                    .build();

    private static final int INVALID_UID = -1;

    /**
     * Query {@link android.media.AudioAttributes.AttributeUsage usages} that are allowed to
     * fade
     * @return list of {@link android.media.AudioAttributes.AttributeUsage}
     */
    @NonNull
    public List<Integer> getFadeableUsages() {
        return DEFAULT_FADEABLE_USAGES;
    }

    /**
     * Query {@link android.media.AudioAttributes.AttributeContentType content types} that are
     * exempted from fade enforcement
     * @return list of {@link android.media.AudioAttributes.AttributeContentType}
     */
    @NonNull
    public List<Integer> getUnfadeableContentTypes() {
        return DEFAULT_UNFADEABLE_CONTENT_TYPES;
    }

    /**
     * Query {@link android.media.AudioPlaybackConfiguration.PlayerType player types} that are
     * exempted from fade enforcement
     * @return list of {@link android.media.AudioPlaybackConfiguration.PlayerType}
     */
    @NonNull
    public List<Integer> getUnfadeablePlayerTypes() {
        return DEFAULT_UNFADEABLE_PLAYER_TYPES;
    }

    /**
     * Get the {@link android.media.VolumeShaper.Configuration} configuration to be applied
     * for the fade-out
     * @param aa The {@link android.media.AudioAttributes}
     * @return {@link android.media.VolumeShaper.Configuration} for the
     * {@link android.media.AudioAttributes.AttributeUsage} or default volume shaper if not
     * configured
     */
    @NonNull
    public VolumeShaper.Configuration getFadeOutVolumeShaperConfig(@NonNull AudioAttributes aa) {
        return DEFAULT_FADEOUT_VSHAPE;
    }

    /**
     * Get the duration to fade out a player of type usage
     * @param aa The {@link android.media.AudioAttributes}
     * @return duration in milliseconds for the
     * {@link android.media.AudioAttributes} or default duration if not configured
     */
    public long getFadeOutDuration(@NonNull AudioAttributes aa) {
        if (!isFadeable(aa, INVALID_UID, AudioPlaybackConfiguration.PLAYER_TYPE_UNKNOWN)) {
            return 0;
        }
        return DEFAULT_FADE_OUT_DURATION_MS;
    }

    /**
     * Get the delay to fade in offending players that do not stop after losing audio focus.
     * @param aa The {@link android.media.AudioAttributes}
     * @return delay in milliseconds for the
     * {@link android.media.AudioAttributes.Attribute} or default delay if not configured
     */
    public long getDelayFadeInOffenders(@NonNull AudioAttributes aa) {
        return DEFAULT_DELAY_FADE_IN_OFFENDERS_MS;
    }

    /**
     * Check if it is allowed to fade for the given {@link android.media.AudioAttributes},
     * client uid and {@link android.media.AudioPlaybackConfiguration.PlayerType} config.
     * @param aa The {@link android.media.AudioAttributes}
     * @param uid The uid of the client owning the player
     * @param playerType The {@link android.media.AudioPlaybackConfiguration.PlayerType}
     * @return {@code true} if it the config is fadeable and {@code false} otherwise
     */
    public boolean isFadeable(@NonNull AudioAttributes aa, int uid,
            @AudioPlaybackConfiguration.PlayerType int playerType) {
        if (isPlayerTypeUnfadeable(playerType)) {
            if (DEBUG) {
                Slog.i(TAG, "not fadeable: player type:" + playerType);
            }
            return false;
        }
        if (isContentTypeUnfadeable(aa.getContentType())) {
            if (DEBUG) {
                Slog.i(TAG, "not fadeable: content type:" + aa.getContentType());
            }
            return false;
        }
        if (!isUsageFadeable(aa.getUsage())) {
            if (DEBUG) {
                Slog.i(TAG, "not fadeable: usage:" + aa.getUsage());
            }
            return false;
        }
        return true;
    }

    private boolean isUsageFadeable(int usage) {
        return getFadeableUsages().contains(usage);
    }

    private boolean isContentTypeUnfadeable(int contentType) {
        return getUnfadeableContentTypes().contains(contentType);
    }

    private boolean isPlayerTypeUnfadeable(int playerType) {
        return getUnfadeablePlayerTypes().contains(playerType);
    }
}
+159 −127
Original line number Diff line number Diff line
@@ -21,66 +21,53 @@ import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.AudioPlaybackConfiguration;
import android.media.VolumeShaper;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;

import com.android.internal.util.ArrayUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.server.utils.EventLogger;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Objects;

/**
 * Class to handle fading out players
 */
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;
    public static final String TAG = "AS.FadeOutManager";

    private static final boolean DEBUG = PlaybackActivityMonitor.DEBUG;

    private static final VolumeShaper.Configuration FADEOUT_VSHAPE =
            new VolumeShaper.Configuration.Builder()
                    .setId(PlaybackActivityMonitor.VOLUME_SHAPER_SYSTEM_FADEOUT_ID)
                    .setCurve(new float[]{0.f, 0.25f, 1.0f} /* times */,
                            new float[]{1.f, 0.65f, 0.0f} /* volumes */)
                    .setOptionFlags(VolumeShaper.Configuration.OPTION_FLAG_CLOCK_TIME)
                    .setDuration(FADE_OUT_DURATION_MS)
                    .build();
    private static final VolumeShaper.Operation PLAY_CREATE_IF_NEEDED =
            new VolumeShaper.Operation.Builder(VolumeShaper.Operation.PLAY)
                    .createIfNeeded()
                    .build();

    private static final int[] UNFADEABLE_PLAYER_TYPES = {
            AudioPlaybackConfiguration.PLAYER_TYPE_AAUDIO,
            AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL,
    };

    private static final int[] UNFADEABLE_CONTENT_TYPES = {
            AudioAttributes.CONTENT_TYPE_SPEECH,
    };

    private static final int[] FADEABLE_USAGES = {
            AudioAttributes.USAGE_GAME,
            AudioAttributes.USAGE_MEDIA,
    };

    // like a PLAY_CREATE_IF_NEEDED operation but with a skip to the end of the ramp
    private static final VolumeShaper.Operation PLAY_SKIP_RAMP =
            new VolumeShaper.Operation.Builder(PLAY_CREATE_IF_NEEDED).setXOffset(1.0f).build();

    private final Object mLock = new Object();

    /**
     * Map of uid (key) to faded out apps (value)
     */
    @GuardedBy("mLock")
    private final SparseArray<FadedOutApp> mUidToFadedAppsMap = new SparseArray<>();

    @GuardedBy("mLock")
    private FadeConfigurations mFadeConfigurations;

    public FadeOutManager() {
        mFadeConfigurations = new FadeConfigurations();
    }

    public FadeOutManager(FadeConfigurations fadeConfigurations) {
        mFadeConfigurations = Objects.requireNonNull(fadeConfigurations,
                "Fade configurations can not be null");
    }

    // TODO explore whether a shorter fade out would be a better UX instead of not fading out at all
    //      (legacy behavior)
@@ -88,73 +75,71 @@ public final class FadeOutManager {
     * Determine whether the focus request would trigger a fade out, given the parameters of the
     * requester and those of the focus loser
     * @param requester the parameters for the focus request
     * @return true if there can be a fade out over the requester starting to play
     * @return {@code true} if there can be a fade out over the requester starting to play
     */
    static boolean canCauseFadeOut(@NonNull FocusRequester requester,
            @NonNull FocusRequester loser) {
    boolean canCauseFadeOut(@NonNull FocusRequester requester, @NonNull FocusRequester loser) {
        if (requester.getAudioAttributes().getContentType() == AudioAttributes.CONTENT_TYPE_SPEECH)
        {
            if (DEBUG) { Log.i(TAG, "not fading out: new focus is for speech"); }
            if (DEBUG) {
                Slog.i(TAG, "not fading out: new focus is for speech");
            }
            return false;
        }
        if ((loser.getGrantFlags() & AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS) != 0) {
            if (DEBUG) { Log.i(TAG, "not fading out: loser has PAUSES_ON_DUCKABLE_LOSS"); }
            if (DEBUG) {
                Slog.i(TAG, "not fading out: loser has PAUSES_ON_DUCKABLE_LOSS");
            }
            return false;
        }

        return true;
    }

    /**
     * Evaluates whether the player associated with this configuration can and should be faded out
     * @param apc the configuration of the player
     * @return true if player type and AudioAttributes are compatible with fade out
     * @return {@code true} if player type and AudioAttributes are compatible with fade out
     */
    static boolean canBeFadedOut(@NonNull AudioPlaybackConfiguration apc) {
        if (ArrayUtils.contains(UNFADEABLE_PLAYER_TYPES, apc.getPlayerType())) {
            if (DEBUG) { Log.i(TAG, "not fading: player type:" + apc.getPlayerType()); }
            return false;
        }
        if (ArrayUtils.contains(UNFADEABLE_CONTENT_TYPES,
                apc.getAudioAttributes().getContentType())) {
            if (DEBUG) {
                Log.i(TAG, "not fading: content type:"
                        + apc.getAudioAttributes().getContentType());
            }
            return false;
        }
        if (!ArrayUtils.contains(FADEABLE_USAGES, apc.getAudioAttributes().getUsage())) {
            if (DEBUG) {
                Log.i(TAG, "not fading: usage:" + apc.getAudioAttributes().getUsage());
            }
            return false;
    boolean canBeFadedOut(@NonNull AudioPlaybackConfiguration apc) {
        synchronized (mLock) {
            return mFadeConfigurations.isFadeable(apc.getAudioAttributes(), apc.getClientUid(),
                    apc.getPlayerType());
        }
        return true;
    }

    static long getFadeOutDurationOnFocusLossMillis(AudioAttributes aa) {
        if (ArrayUtils.contains(UNFADEABLE_CONTENT_TYPES, aa.getContentType())) {
            return 0;
        }
        if (!ArrayUtils.contains(FADEABLE_USAGES, aa.getUsage())) {
            return 0;
    /**
     * Get the duration to fade-out after losing audio focus
     * @param aa The {@link android.media.AudioAttributes} of the player
     * @return duration in milliseconds
     */
    long getFadeOutDurationOnFocusLossMillis(@NonNull AudioAttributes aa) {
        synchronized (mLock) {
            return mFadeConfigurations.getFadeOutDuration(aa);
        }
        return FADE_OUT_DURATION_MS;
    }

    /**
     * Map of uid (key) to faded out apps (value)
     * Get the delay to fade-in the offending players that do not stop after losing audio focus
     * @param aa The {@link android.media.AudioAttributes}
     * @return duration in milliseconds
     */
    private final HashMap<Integer, FadedOutApp> mFadedApps = new HashMap<Integer, FadedOutApp>();
    long getFadeInDelayForOffendersMillis(@NonNull AudioAttributes aa) {
        synchronized (mLock) {
            return mFadeConfigurations.getDelayFadeInOffenders(aa);
        }
    }

    synchronized void fadeOutUid(int uid, ArrayList<AudioPlaybackConfiguration> players) {
        Log.i(TAG, "fadeOutUid() uid:" + uid);
        if (!mFadedApps.containsKey(uid)) {
            mFadedApps.put(uid, new FadedOutApp(uid));
    void fadeOutUid(int uid, ArrayList<AudioPlaybackConfiguration> players) {
        Slog.i(TAG, "fadeOutUid() uid:" + uid);
        synchronized (mLock) {
            if (!mUidToFadedAppsMap.contains(uid)) {
                mUidToFadedAppsMap.put(uid, new FadedOutApp(uid));
            }
        final FadedOutApp fa = mFadedApps.get(uid);
            final FadedOutApp fa = mUidToFadedAppsMap.get(uid);
            for (AudioPlaybackConfiguration apc : players) {
            fa.addFade(apc, false /*skipRamp*/);
                final VolumeShaper.Configuration volShaper =
                        mFadeConfigurations.getFadeOutVolumeShaperConfig(apc.getAudioAttributes());
                fa.addFade(apc, /* skipRamp= */ false, volShaper);
            }
        }
    }

@@ -163,49 +148,82 @@ public final class FadeOutManager {
     * @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);
    void unfadeOutUid(int uid, HashMap<Integer, AudioPlaybackConfiguration> players) {
        Slog.i(TAG, "unfadeOutUid() uid:" + uid);
        synchronized (mLock) {
            final FadedOutApp fa = mUidToFadedAppsMap.get(uid);
            if (fa == null) {
                return;
            }
            mUidToFadedAppsMap.remove(uid);
            fa.removeUnfadeAll(players);
        }
    }

    // pre-condition: apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED
    //   see {@link PlaybackActivityMonitor#playerEvent}
    synchronized void checkFade(@NonNull AudioPlaybackConfiguration apc) {
    void checkFade(@NonNull AudioPlaybackConfiguration apc) {
        if (DEBUG) {
            Log.v(TAG, "checkFade() player piid:"
            Slog.v(TAG, "checkFade() player piid:"
                    + apc.getPlayerInterfaceId() + " uid:" + apc.getClientUid());
        }
        final FadedOutApp fa = mFadedApps.get(apc.getClientUid());

        synchronized (mLock) {
            final VolumeShaper.Configuration volShaper =
                    mFadeConfigurations.getFadeOutVolumeShaperConfig(apc.getAudioAttributes());
            final FadedOutApp fa = mUidToFadedAppsMap.get(apc.getClientUid());
            if (fa == null) {
                return;
            }
        fa.addFade(apc, true);
            fa.addFade(apc, /* skipRamp= */ true, volShaper);
        }
    }

    /**
     * Remove the player from the list of faded out players because it has been released
     * @param apc the released player
     */
    synchronized void removeReleased(@NonNull AudioPlaybackConfiguration apc) {
    void removeReleased(@NonNull AudioPlaybackConfiguration apc) {
        final int uid = apc.getClientUid();
        if (DEBUG) {
            Log.v(TAG, "removedReleased() player piid: "
            Slog.v(TAG, "removedReleased() player piid: "
                    + apc.getPlayerInterfaceId() + " uid:" + uid);
        }
        final FadedOutApp fa = mFadedApps.get(uid);
        synchronized (mLock) {
            final FadedOutApp fa = mUidToFadedAppsMap.get(uid);
            if (fa == null) {
                return;
            }
            fa.removeReleased(apc);
        }
    }

    synchronized void dump(PrintWriter pw) {
        for (FadedOutApp da : mFadedApps.values()) {
            da.dump(pw);
    /**
     * Check if uid is currently faded out
     * @param uid Client id
     * @return {@code true} if uid is currently faded out. Othwerwise, {@code false}.
     */
    boolean isUidFadedOut(int uid) {
        synchronized (mLock) {
            return mUidToFadedAppsMap.contains(uid);
        }
    }

    /**
     * Update fade configurations used for player fade operations
     * @param fadeConfigurations set of configs that define fade properties
     */
    void setFadeConfigurations(@NonNull FadeConfigurations fadeConfigurations) {
        synchronized (mLock) {
            mFadeConfigurations = fadeConfigurations;
        }
    }

    void dump(PrintWriter pw) {
        synchronized (mLock) {
            for (int index = 0; index < mUidToFadedAppsMap.size(); index++) {
                mUidToFadedAppsMap.valueAt(index).dump(pw);
            }
        }
    }

@@ -215,7 +233,8 @@ public final class FadeOutManager {
     */
    private static final class FadedOutApp {
        private final int mUid;
        private final ArrayList<Integer> mFadedPlayers = new ArrayList<Integer>();
        // key -> piid; value -> volume shaper config applied
        private final SparseArray<VolumeShaper.Configuration> mFadedPlayers = new SparseArray<>();

        FadedOutApp(int uid) {
            mUid = uid;
@@ -223,8 +242,9 @@ public final class FadeOutManager {

        void dump(PrintWriter pw) {
            pw.print("\t uid:" + mUid + " piids:");
            for (int piid : mFadedPlayers) {
                pw.print(" " + piid);
            for (int index = 0; index < mFadedPlayers.size(); index++) {
                pw.print("piid: " + mFadedPlayers.keyAt(index) + " Volume shaper: "
                        + mFadedPlayers.valueAt(index));
            }
            pw.println("");
        }
@@ -233,48 +253,60 @@ public final class FadeOutManager {
         * Add this player to the list of faded out players and apply the fade
         * @param apc a config that satisfies
         *      apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED
         * @param skipRamp true if the player should be directly into the end of ramp state.
         *      This value would for instance be false when adding players at the start of a fade.
         * @param skipRamp {@code true} if the player should be directly into the end of ramp state.
         *      This value would for instance be {@code false} when adding players at the start
         *      of a fade.
         */
        void addFade(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) {
            final int piid = new Integer(apc.getPlayerInterfaceId());
            if (mFadedPlayers.contains(piid)) {
        void addFade(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp,
                @NonNull VolumeShaper.Configuration volShaper) {
            final int piid = Integer.valueOf(apc.getPlayerInterfaceId());

            // positive index return implies player is already faded
            if (mFadedPlayers.indexOfKey(piid) >= 0) {
                if (DEBUG) {
                    Log.v(TAG, "player piid:" + piid + " already faded out");
                    Slog.v(TAG, "player piid:" + piid + " already faded out");
                }
                return;
            }
            if (apc.getPlayerProxy() != null) {
                try {
                    PlaybackActivityMonitor.sEventLogger.enqueue(
                        (new PlaybackActivityMonitor.FadeOutEvent(apc, skipRamp)).printLog(TAG));
                apc.getPlayerProxy().applyVolumeShaper(
                        FADEOUT_VSHAPE,
                            (new PlaybackActivityMonitor.FadeOutEvent(apc, skipRamp)).printLog(
                                    TAG));
                    apc.getPlayerProxy().applyVolumeShaper(volShaper,
                            skipRamp ? PLAY_SKIP_RAMP : PLAY_CREATE_IF_NEEDED);
                mFadedPlayers.add(piid);
                    mFadedPlayers.put(piid, volShaper);
                } catch (Exception e) {
                Log.e(TAG, "Error fading out player piid:" + piid
                    Slog.e(TAG, "Error fading out player piid:" + piid
                            + " uid:" + apc.getClientUid(), e);
                }
            } else {
                if (DEBUG) {
                    Slog.v(TAG, "Error fading out player piid:" + piid
                            + ", player not found for uid " + mUid);
                }
            }
        }

        void removeUnfadeAll(HashMap<Integer, AudioPlaybackConfiguration> players) {
            for (int piid : mFadedPlayers) {
            for (int index = 0; index < mFadedPlayers.size(); index++) {
                int piid = mFadedPlayers.keyAt(index);
                final AudioPlaybackConfiguration apc = players.get(piid);
                if (apc != null) {
                if ((apc != null) && (apc.getPlayerProxy() != null)) {
                    final VolumeShaper.Configuration volShaper = mFadedPlayers.valueAt(index);
                    try {
                        PlaybackActivityMonitor.sEventLogger.enqueue(
                                (new EventLogger.StringEvent("unfading out piid:"
                                        + piid)).printLog(TAG));
                        apc.getPlayerProxy().applyVolumeShaper(
                                FADEOUT_VSHAPE,
                        apc.getPlayerProxy().applyVolumeShaper(volShaper,
                                VolumeShaper.Operation.REVERSE);
                    } catch (Exception e) {
                        Log.e(TAG, "Error unfading out player piid:" + piid + " uid:" + mUid, e);
                        Slog.e(TAG, "Error unfading out player piid:" + piid + " uid:" + mUid, e);
                    }
                } else {
                    // this piid was in the list of faded players, but wasn't found
                    if (DEBUG) {
                        Log.v(TAG, "Error unfading out player piid:" + piid
                        Slog.v(TAG, "Error unfading out player piid:" + piid
                                + ", player not found for uid " + mUid);
                    }
                }
@@ -283,7 +315,7 @@ public final class FadeOutManager {
        }

        void removeReleased(@NonNull AudioPlaybackConfiguration apc) {
            mFadedPlayers.remove(new Integer(apc.getPlayerInterfaceId()));
            mFadedPlayers.delete(Integer.valueOf(apc.getPlayerInterfaceId()));
        }
    }
}
+2 −1
Original line number Diff line number Diff line
@@ -495,7 +495,8 @@ public class FocusRequester {
                // will be dispatched later, it is now in limbo mode
                mFocusLossFadeLimbo = true;
                mFocusController.postDelayedLossAfterFade(this,
                        FadeOutManager.FADE_OUT_DURATION_MS);
                        mFocusController.getFadeOutDurationOnFocusLossMillis(
                                this.getAudioAttributes()));
                return true;
            }
        }
+27 −10

File changed.

Preview size limit exceeded, changes collapsed.

+21 −10

File changed.

Preview size limit exceeded, changes collapsed.

Loading