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

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

Merge "Reuse ringtone instead of creating multiple when playing"

parents 625a34c7 a2e55eef
Loading
Loading
Loading
Loading
+11 −100
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.media.AudioAttributes;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.media.VolumeShaper;
import android.net.Uri;
import android.os.Handler;
@@ -27,13 +28,10 @@ import android.os.HandlerThread;
import android.os.Message;
import android.telecom.Log;
import android.telecom.Logging.Session;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.Preconditions;

import java.util.concurrent.CompletableFuture;

/**
 * Plays the default ringtone. Uses {@link Ringtone} in a separate thread so that this class can be
 * used from the main thread.
@@ -50,12 +48,6 @@ public class AsyncRingtonePlayer {
    /** The current ringtone. Only used by the ringtone thread. */
    private Ringtone mRingtone;

    /**
     * CompletableFuture which signals a caller when we know whether a ringtone will play haptics
     * or not.
     */
    private CompletableFuture<Boolean> mHapticsFuture = null;

    public AsyncRingtonePlayer() {
        // Empty
    }
@@ -65,35 +57,14 @@ public class AsyncRingtonePlayer {
     * If {@link VolumeShaper.Configuration} is specified, it is applied to the ringtone to change
     * the volume of the ringtone as it plays.
     *
     * @param factory The {@link RingtoneFactory}.
     * @param incomingCall The ringing {@link Call}.
     * @param volumeShaperConfig An optional {@link VolumeShaper.Configuration} which is applied to
     *                           the ringtone to change its volume while it rings.
     * @param isVibrationEnabled {@code true} if the settings and DND configuration of the device
     *                           is such that the vibrator should be used, {@code false} otherwise.
     * @return A {@link CompletableFuture} which on completion indicates whether or not the ringtone
     *         has a haptic track.  {@code True} indicates that a haptic track is present on the
     *         ringtone; in this case the default vibration in {@link Ringer} should not be played.
     *         {@code False} indicates that a haptic track is NOT present on the ringtone;
     *         in this case the default vibration in {@link Ringer} should be trigger if needed.
     * @param ringtone The {@link Ringtone}.
     */
    public @NonNull
    CompletableFuture<Boolean> play(RingtoneFactory factory, Call incomingCall,
            @Nullable VolumeShaper.Configuration volumeShaperConfig, boolean isRingerAudible,
            boolean isVibrationEnabled) {
    public void play(@NonNull Ringtone ringtone) {
        Log.d(this, "Posting play.");
        if (mHapticsFuture == null) {
            mHapticsFuture = new CompletableFuture<>();
        }
        SomeArgs args = SomeArgs.obtain();
        args.arg1 = factory;
        args.arg2 = incomingCall;
        args.arg3 = volumeShaperConfig;
        args.arg4 = isVibrationEnabled;
        args.arg5 = isRingerAudible;
        args.arg6 = Log.createSubsession();
        args.arg1 = ringtone;
        args.arg2 = Log.createSubsession();
        postMessage(EVENT_PLAY, true /* shouldCreateHandler */, args);
        return mHapticsFuture;
    }

    /** Stops playing the ringtone. */
@@ -151,76 +122,23 @@ public class AsyncRingtonePlayer {
     * Starts the actual playback of the ringtone. Executes on ringtone-thread.
     */
    private void handlePlay(SomeArgs args) {
        RingtoneFactory factory = (RingtoneFactory) args.arg1;
        Call incomingCall = (Call) args.arg2;
        VolumeShaper.Configuration volumeShaperConfig = (VolumeShaper.Configuration) args.arg3;
        boolean isVibrationEnabled = (boolean) args.arg4;
        boolean isRingerAudible = (boolean) args.arg5;
        Session session = (Session) args.arg6;
        Ringtone ringtone = (Ringtone) args.arg1;
        Session session = (Session) args.arg2;
        args.recycle();

        Log.continueSession(session, "ARP.hP");
        try {
            // don't bother with any of this if there is an EVENT_STOP waiting.
            if (mHandler.hasMessages(EVENT_STOP)) {
                completeHapticFuture(false /* ringtoneHasHaptics */);
                return;
            }

            // If the Ringtone Uri is EMPTY, then the "None" Ringtone has been selected.
            // If ringer is not audible for this call, then the phone is in "Vibrate" mode.
            // Use haptic-only ringtone or do not play anything.
            if (!isRingerAudible || Uri.EMPTY.equals(incomingCall.getRingtone())) {
                if (isVibrationEnabled) {
                    setRingtone(factory.getHapticOnlyRingtone());
                    if (mRingtone == null) {
                        completeHapticFuture(false /* ringtoneHasHaptics */);
                        return;
                    }
                } else {
                    setRingtone(null);
                    completeHapticFuture(false /* ringtoneHasHaptics */);
                return;
            }
            }

            ThreadUtil.checkNotOnMainThread();
            Log.i(this, "handlePlay: Play ringtone.");

            if (mRingtone == null) {
                setRingtone(factory.getRingtone(incomingCall, volumeShaperConfig));
                if (mRingtone == null) {
                    Uri ringtoneUri = incomingCall.getRingtone();
                    String ringtoneUriString = (ringtoneUri == null) ? "null" :
                            ringtoneUri.toSafeString();
                    Log.addEvent(null, LogUtils.Events.ERROR_LOG, "Failed to get ringtone from " +
                            "factory. Skipping ringing. Uri was: " + ringtoneUriString);
                    completeHapticFuture(false /* ringtoneHasHaptics */);
                    return;
                }
            }

            // With the ringtone to play now known, we can determine if it has haptic channels or
            // not; we will complete the haptics future so the default vibration code in Ringer can
            // know whether to trigger the vibrator.
            if (mHapticsFuture != null && !mHapticsFuture.isDone()) {
                boolean hasHaptics = factory.hasHapticChannels(mRingtone);
                Log.i(this, "handlePlay: hasHaptics=%b, isVibrationEnabled=%b", hasHaptics,
                        isVibrationEnabled);
                SystemSettingsUtil systemSettingsUtil = new SystemSettingsUtil();
                if (hasHaptics && (volumeShaperConfig == null
                        || systemSettingsUtil.isAudioCoupledVibrationForRampingRingerEnabled())) {
                    AudioAttributes attributes = mRingtone.getAudioAttributes();
                    Log.d(this, "handlePlay: %s haptic channel",
                            (isVibrationEnabled ? "unmuting" : "muting"));
                    mRingtone.setAudioAttributes(
                            new AudioAttributes.Builder(attributes)
                                    .setHapticChannelsMuted(!isVibrationEnabled)
                                    .build());
                }
                completeHapticFuture(hasHaptics);
            }

            setRingtone(ringtone);
            Uri uri = mRingtone.getUri();
            String uriString = (uri != null ? uri.toSafeString() : "");
            Log.i(this, "handlePlay: Play ringtone. Uri: " + uriString);
            mRingtone.setLooping(true);
            if (mRingtone.isPlaying()) {
                Log.d(this, "Ringtone already playing.");
@@ -268,11 +186,4 @@ public class AsyncRingtonePlayer {
        }
        mRingtone = ringtone;
    }

    private void completeHapticFuture(boolean ringtoneHasHaptics) {
        if (mHapticsFuture != null) {
            mHapticsFuture.complete(ringtoneHasHaptics);
            mHapticsFuture = null;
        }
    }
}
+26 −19
Original line number Diff line number Diff line
@@ -19,12 +19,12 @@ package com.android.server.telecom;
import android.media.AudioManager;
import android.os.Looper;
import android.os.Message;
import android.os.Trace;
import android.telecom.Log;
import android.telecom.Logging.Runnable;
import android.telecom.Logging.Session;
import android.util.LocalLog;
import android.util.SparseArray;

import com.android.internal.util.IState;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.State;
@@ -370,17 +370,20 @@ public class CallAudioModeStateMachine extends StateMachine {
        private boolean mHasFocus = false;

        private void tryStartRinging() {
            Trace.traceBegin(Trace.TRACE_TAG_AUDIO, "CallAudioMode.tryStartRinging");
            try {
                if (mHasFocus && mCallAudioManager.isRingtonePlaying()) {
                Log.i(LOG_TAG, "RingingFocusState#tryStartRinging -- audio focus previously"
                    Log.i(LOG_TAG,
                        "RingingFocusState#tryStartRinging -- audio focus previously"
                            + " acquired and ringtone already playing -- skipping.");
                    return;
                }

                if (mCallAudioManager.startRinging()) {
                mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_RING,
                        AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
                // Do not set MODE_RINGTONE if we were previously in the CALL_SCREENING mode -- this
                // trips up the audio system.
                    mAudioManager.requestAudioFocusForCall(
                        AudioManager.STREAM_RING, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
                    // Do not set MODE_RINGTONE if we were previously in the CALL_SCREENING mode --
                    // this trips up the audio system.
                    if (mAudioManager.getMode() != AudioManager.MODE_CALL_SCREENING) {
                        mAudioManager.setMode(AudioManager.MODE_RINGTONE);
                        mLocalLog.log("Mode MODE_RINGTONE");
@@ -389,7 +392,11 @@ public class CallAudioModeStateMachine extends StateMachine {
                        CallAudioRouteStateMachine.RINGING_FOCUS);
                    mHasFocus = true;
                } else {
                Log.i(LOG_TAG, "RINGING state, try start ringing but not acquiring audio focus");
                    Log.i(
                        LOG_TAG, "RINGING state, try start ringing but not acquiring audio focus");
                }
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_AUDIO);
            }
        }

+139 −101

File changed.

Preview size limit exceeded, changes collapsed.

+11 −10
Original line number Diff line number Diff line
@@ -65,7 +65,9 @@ public class RingtoneFactory {
    }

    public Ringtone getRingtone(Call incomingCall,
            @Nullable VolumeShaper.Configuration volumeShaperConfig) {
        @Nullable VolumeShaper.Configuration volumeShaperConfig, boolean hapticChannelsMuted) {
        AudioAttributes audioAttrs = getDefaultRingtoneAudioAttributes(hapticChannelsMuted);

        // Use the default ringtone of the work profile if the contact is a work profile contact.
        Context userContext = isWorkContact(incomingCall) ?
                getWorkProfileContextForUser(mCallsManager.getCurrentUserHandle()) :
@@ -73,8 +75,6 @@ public class RingtoneFactory {
        Uri ringtoneUri = incomingCall.getRingtone();
        Ringtone ringtone = null;

        AudioAttributes audioAttrs = getRingtoneAudioAttributes();

        if(ringtoneUri != null && userContext != null) {
            // Ringtone URI is explicitly specified. First, try to create a Ringtone with that.
            try {
@@ -101,9 +101,11 @@ public class RingtoneFactory {
                    Log.i(this, "getRingtone: Settings.System.DEFAULT_RINGTONE_URI is null.");
                }
            }

            if (defaultRingtoneUri == null) {
                return null;
            }

            try {
                ringtone = RingtoneManager.getRingtone(
                    contextToUse, defaultRingtoneUri, volumeShaperConfig, audioAttrs);
@@ -114,24 +116,23 @@ public class RingtoneFactory {
        return ringtone;
    }

    public AudioAttributes getRingtoneAudioAttributes() {
    public AudioAttributes getDefaultRingtoneAudioAttributes(boolean hapticChannelsMuted) {
        return new AudioAttributes.Builder()
            .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
            .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
            .setHapticChannelsMuted(hapticChannelsMuted)
            .build();
    }

    public Ringtone getRingtone(Call incomingCall) {
        return getRingtone(incomingCall, null);
    }

    /** Returns a ringtone to be used when ringer is not audible for the incoming call. */
    @Nullable
    public Ringtone getHapticOnlyRingtone() {
        Uri ringtoneUri = Uri.parse("file://" + mContext.getString(
                com.android.internal.R.string.config_defaultRingtoneVibrationSound));
        AudioAttributes audioAttrs = getRingtoneAudioAttributes();
        Ringtone ringtone = RingtoneManager.getRingtone(mContext, ringtoneUri, null, audioAttrs);
        AudioAttributes audioAttrs = getDefaultRingtoneAudioAttributes(
            /* hapticChannelsMuted */ false);
        Ringtone ringtone = RingtoneManager.getRingtone(
            mContext, ringtoneUri, /* volumeShaperConfig */ null, audioAttrs);
        if (ringtone != null) {
            // Make sure the sound is muted.
            ringtone.setVolume(0);
+112 −92

File changed.

Preview size limit exceeded, changes collapsed.