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

Commit 87c0eaff authored by Tyler Gunn's avatar Tyler Gunn
Browse files

Add support for using resource files as incall tones.

Adding support for using an audio file as an incall tone.
Replacing the default Telecom hangup tone with a fancy end call tone
audio file.

Test: Manual
Bug: 78540877
Change-Id: Ifb0506f3d564c92e41f0d40b94ed3aa29a7fdd9b
Merged-In: Ifb0506f3d564c92e41f0d40b94ed3aa29a7fdd9b
parent 9cb2041a
Loading
Loading
Loading
Loading

res/raw/endcall.ogg

0 → 100644
+19.2 KiB

File added.

No diff preview for this file type.

+7 −3
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.content.IntentFilter;
import android.media.AudioManager;
import android.media.AudioSystem;
import android.media.ToneGenerator;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -408,8 +409,12 @@ public class CallsManager extends Call.ListenerBase
                        wiredHeadsetManager,
                        mDockManager);

        AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
        InCallTonePlayer.MediaPlayerFactory mediaPlayerFactory =
                (resourceId, attributes) -> MediaPlayer.create(mContext, resourceId, attributes,
                        audioManager.generateAudioSessionId());
        InCallTonePlayer.Factory playerFactory = new InCallTonePlayer.Factory(
                callAudioRoutePeripheralAdapter, lock, toneGeneratorFactory);
                callAudioRoutePeripheralAdapter, lock, toneGeneratorFactory, mediaPlayerFactory);

        SystemSettingsUtil systemSettingsUtil = new SystemSettingsUtil();
        RingtoneFactory ringtoneFactory = new RingtoneFactory(this, context);
@@ -419,8 +424,7 @@ public class CallsManager extends Call.ListenerBase
                emergencyCallHelper);
        mRinger = new Ringer(playerFactory, context, systemSettingsUtil, asyncRingtonePlayer,
                ringtoneFactory, systemVibrator, mInCallController);
        mCallRecordingTonePlayer = new CallRecordingTonePlayer(mContext,
                (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE), mLock);
        mCallRecordingTonePlayer = new CallRecordingTonePlayer(mContext, audioManager, mLock);
        mCallAudioManager = new CallAudioManager(callAudioRouteStateMachine,
                this, callAudioModeStateMachineFactory.create((AudioManager)
                        mContext.getSystemService(Context.AUDIO_SERVICE)),
+104 −14
Original line number Diff line number Diff line
@@ -16,7 +16,9 @@

package com.android.server.telecom;

import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.ToneGenerator;
import android.os.Handler;
import android.os.Looper;
@@ -27,9 +29,11 @@ import android.telecom.Logging.Session;
import com.android.internal.annotations.VisibleForTesting;

/**
 * Play a call-related tone (ringback, busy signal, etc.) through ToneGenerator. To use, create an
 * instance using InCallTonePlayer.Factory (passing in the TONE_* constant for the tone you want)
 * and start() it. Implemented on top of {@link Thread} so that the tone plays in its own thread.
 * Play a call-related tone (ringback, busy signal, etc.) either through ToneGenerator, or using a
 * media resource file.
 * To use, create an instance using InCallTonePlayer.Factory (passing in the TONE_* constant for
 * the tone you want) and start() it. Implemented on top of {@link Thread} so that the tone plays in
 * its own thread.
 */
public class InCallTonePlayer extends Thread {

@@ -41,12 +45,15 @@ public class InCallTonePlayer extends Thread {
        private final CallAudioRoutePeripheralAdapter mCallAudioRoutePeripheralAdapter;
        private final TelecomSystem.SyncRoot mLock;
        private final ToneGeneratorFactory mToneGeneratorFactory;
        private final MediaPlayerFactory mMediaPlayerFactory;

        Factory(CallAudioRoutePeripheralAdapter callAudioRoutePeripheralAdapter,
                TelecomSystem.SyncRoot lock, ToneGeneratorFactory toneGeneratorFactory) {
                TelecomSystem.SyncRoot lock, ToneGeneratorFactory toneGeneratorFactory,
                MediaPlayerFactory mediaPlayerFactory) {
            mCallAudioRoutePeripheralAdapter = callAudioRoutePeripheralAdapter;
            mLock = lock;
            mToneGeneratorFactory = toneGeneratorFactory;
            mMediaPlayerFactory = mediaPlayerFactory;
        }

        public void setCallAudioManager(CallAudioManager callAudioManager) {
@@ -55,7 +62,8 @@ public class InCallTonePlayer extends Thread {

        public InCallTonePlayer createPlayer(int tone) {
            return new InCallTonePlayer(tone, mCallAudioManager,
                    mCallAudioRoutePeripheralAdapter, mLock, mToneGeneratorFactory);
                    mCallAudioRoutePeripheralAdapter, mLock, mToneGeneratorFactory,
                    mMediaPlayerFactory);
        }
    }

@@ -63,6 +71,10 @@ public class InCallTonePlayer extends Thread {
        ToneGenerator get (int streamType, int volume);
    }

    public interface MediaPlayerFactory {
        MediaPlayer get (int resourceId, AudioAttributes attributes);
    }

    // The possible tones that we can play.
    public static final int TONE_INVALID = 0;
    public static final int TONE_BUSY = 1;
@@ -80,9 +92,12 @@ public class InCallTonePlayer extends Thread {
    public static final int TONE_VOICE_PRIVACY = 13;
    public static final int TONE_VIDEO_UPGRADE = 14;

    private static final int TONE_RESOURCE_ID_UNDEFINED = -1;

    private static final int RELATIVE_VOLUME_EMERGENCY = 100;
    private static final int RELATIVE_VOLUME_HIPRI = 80;
    private static final int RELATIVE_VOLUME_LOPRI = 50;
    private static final int RELATIVE_VOLUME_UNDEFINED = -1;

    // Buffer time (in msec) to add on to the tone timeout value. Needed mainly when the timeout
    // value for a tone is exact duration of the tone itself.
@@ -111,6 +126,9 @@ public class InCallTonePlayer extends Thread {
    /** Current state of the tone player. */
    private int mState;

    /** For tones which are not generated using ToneGenerator. */
    private MediaPlayer mToneMediaPlayer = null;

    /** Telecom lock object. */
    private final TelecomSystem.SyncRoot mLock;

@@ -118,6 +136,7 @@ public class InCallTonePlayer extends Thread {
    private final Object mSessionLock = new Object();

    private final ToneGeneratorFactory mToneGenerator;
    private final MediaPlayerFactory mMediaPlayerFactory;

    /**
     * Initializes the tone player. Private; use the {@link Factory} to create tone players.
@@ -129,19 +148,20 @@ public class InCallTonePlayer extends Thread {
            CallAudioManager callAudioManager,
            CallAudioRoutePeripheralAdapter callAudioRoutePeripheralAdapter,
            TelecomSystem.SyncRoot lock,
            ToneGeneratorFactory toneGeneratorFactory) {
            ToneGeneratorFactory toneGeneratorFactory,
            MediaPlayerFactory mediaPlayerFactor) {
        mState = STATE_OFF;
        mToneId = toneId;
        mCallAudioManager = callAudioManager;
        mCallAudioRoutePeripheralAdapter = callAudioRoutePeripheralAdapter;
        mLock = lock;
        mToneGenerator = toneGeneratorFactory;
        mMediaPlayerFactory = mediaPlayerFactor;
    }

    /** {@inheritDoc} */
    @Override
    public void run() {
        ToneGenerator toneGenerator = null;
        try {
            synchronized (mSessionLock) {
                if (mSession != null) {
@@ -154,6 +174,8 @@ public class InCallTonePlayer extends Thread {
            final int toneType;  // Passed to ToneGenerator.startTone.
            final int toneVolume;  // Passed to the ToneGenerator constructor.
            final int toneLengthMillis;
            final int mediaResourceId; // The resourceId of the tone to play.  Used for media-based
                                      // tones.

            switch (mToneId) {
                case TONE_BUSY:
@@ -161,11 +183,16 @@ public class InCallTonePlayer extends Thread {
                    toneType = ToneGenerator.TONE_SUP_BUSY;
                    toneVolume = RELATIVE_VOLUME_HIPRI;
                    toneLengthMillis = 4000;
                    mediaResourceId = TONE_RESOURCE_ID_UNDEFINED;
                    break;
                case TONE_CALL_ENDED:
                    toneType = ToneGenerator.TONE_PROP_PROMPT;
                    toneVolume = RELATIVE_VOLUME_HIPRI;
                    toneLengthMillis = 200;
                    // Don't use tone generator
                    toneType = ToneGenerator.TONE_UNKNOWN;
                    toneVolume = RELATIVE_VOLUME_UNDEFINED;
                    toneLengthMillis = 0;

                    // Use a tone resource file for a more rich, full-bodied tone experience.
                    mediaResourceId = R.raw.endcall;
                    break;
                case TONE_OTA_CALL_ENDED:
                    // TODO: fill in
@@ -174,46 +201,55 @@ public class InCallTonePlayer extends Thread {
                    toneType = ToneGenerator.TONE_SUP_CALL_WAITING;
                    toneVolume = RELATIVE_VOLUME_HIPRI;
                    toneLengthMillis = Integer.MAX_VALUE - TIMEOUT_BUFFER_MILLIS;
                    mediaResourceId = TONE_RESOURCE_ID_UNDEFINED;
                    break;
                case TONE_CDMA_DROP:
                    toneType = ToneGenerator.TONE_CDMA_CALLDROP_LITE;
                    toneVolume = RELATIVE_VOLUME_LOPRI;
                    toneLengthMillis = 375;
                    mediaResourceId = TONE_RESOURCE_ID_UNDEFINED;
                    break;
                case TONE_CONGESTION:
                    toneType = ToneGenerator.TONE_SUP_CONGESTION;
                    toneVolume = RELATIVE_VOLUME_HIPRI;
                    toneLengthMillis = 4000;
                    mediaResourceId = TONE_RESOURCE_ID_UNDEFINED;
                    break;
                case TONE_INTERCEPT:
                    toneType = ToneGenerator.TONE_CDMA_ABBR_INTERCEPT;
                    toneVolume = RELATIVE_VOLUME_LOPRI;
                    toneLengthMillis = 500;
                    mediaResourceId = TONE_RESOURCE_ID_UNDEFINED;
                    break;
                case TONE_OUT_OF_SERVICE:
                    toneType = ToneGenerator.TONE_CDMA_CALLDROP_LITE;
                    toneVolume = RELATIVE_VOLUME_LOPRI;
                    toneLengthMillis = 375;
                    mediaResourceId = TONE_RESOURCE_ID_UNDEFINED;
                    break;
                case TONE_REDIAL:
                    toneType = ToneGenerator.TONE_CDMA_ALERT_AUTOREDIAL_LITE;
                    toneVolume = RELATIVE_VOLUME_LOPRI;
                    toneLengthMillis = 5000;
                    mediaResourceId = TONE_RESOURCE_ID_UNDEFINED;
                    break;
                case TONE_REORDER:
                    toneType = ToneGenerator.TONE_CDMA_REORDER;
                    toneVolume = RELATIVE_VOLUME_HIPRI;
                    toneLengthMillis = 4000;
                    mediaResourceId = TONE_RESOURCE_ID_UNDEFINED;
                    break;
                case TONE_RING_BACK:
                    toneType = ToneGenerator.TONE_SUP_RINGTONE;
                    toneVolume = RELATIVE_VOLUME_HIPRI;
                    toneLengthMillis = Integer.MAX_VALUE - TIMEOUT_BUFFER_MILLIS;
                    mediaResourceId = TONE_RESOURCE_ID_UNDEFINED;
                    break;
                case TONE_UNOBTAINABLE_NUMBER:
                    toneType = ToneGenerator.TONE_SUP_ERROR;
                    toneVolume = RELATIVE_VOLUME_HIPRI;
                    toneLengthMillis = 4000;
                    mediaResourceId = TONE_RESOURCE_ID_UNDEFINED;
                    break;
                case TONE_VOICE_PRIVACY:
                    // TODO: fill in.
@@ -223,6 +259,7 @@ public class InCallTonePlayer extends Thread {
                    toneType = ToneGenerator.TONE_SUP_CALL_WAITING;
                    toneVolume = RELATIVE_VOLUME_HIPRI;
                    toneLengthMillis = 4000;
                    mediaResourceId = TONE_RESOURCE_ID_UNDEFINED;
                    break;
                default:
                    throw new IllegalStateException("Bad toneId: " + mToneId);
@@ -233,16 +270,38 @@ public class InCallTonePlayer extends Thread {
                stream = AudioManager.STREAM_BLUETOOTH_SCO;
            }

            if (toneType != ToneGenerator.TONE_UNKNOWN) {
                playToneGeneratorTone(stream, toneVolume, toneType, toneLengthMillis);
            } else if (mediaResourceId != TONE_RESOURCE_ID_UNDEFINED) {
                playMediaTone(stream, mediaResourceId);
            }
        } finally {
            cleanUpTonePlayer();
            Log.endSession();
        }
    }

    /**
     * Play a tone generated by the {@link ToneGenerator}.
     * @param stream The stream on which the tone will be played.
     * @param toneVolume The volume of the tone.
     * @param toneType The type of tone to play.
     * @param toneLengthMillis How long to play the tone.
     */
    private void playToneGeneratorTone(int stream, int toneVolume, int toneType,
            int toneLengthMillis) {
        ToneGenerator toneGenerator = null;
        try {
            // If the ToneGenerator creation fails, just continue without it. It is a local audio
            // signal, and is not as important.
            try {
                Log.v(this, "Creating generator");
                toneGenerator = mToneGenerator.get(stream, toneVolume);
            } catch (RuntimeException e) {
                Log.w(this, "Failed to create ToneGenerator.", e);
                return;
            }

            Log.i(this, "playToneGeneratorTone: toneType=%d", toneType);
            // TODO: Certain CDMA tones need to check the ringer-volume state before
            // playing. See CallNotifier.InCallTonePlayer.

@@ -267,11 +326,42 @@ public class InCallTonePlayer extends Thread {
            if (toneGenerator != null) {
                toneGenerator.release();
            }
            cleanUpTonePlayer();
            Log.endSession();
        }
    }

    /**
     * Plays an audio-file based media tone.
     * @param stream The audio stream on which to play the tone.
     * @param toneResourceId The resource ID of the tone to play.
     */
    private void playMediaTone(int stream, int toneResourceId) {
        synchronized (this) {
            if (mState != STATE_STOPPED) {
                mState = STATE_ON;
            }
            Log.i(this, "playMediaTone: toneResourceId=%d", toneResourceId);
            AudioAttributes attributes = new AudioAttributes.Builder()
                    .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
                    .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
                    .setLegacyStreamType(stream)
                    .build();
            mToneMediaPlayer = mMediaPlayerFactory.get(toneResourceId, attributes);
            mToneMediaPlayer.setLooping(false);
            mToneMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
                @Override
                public void onCompletion(MediaPlayer mp) {
                    synchronized (this) {
                        mState = STATE_OFF;
                    }
                    mToneMediaPlayer.release();
                    mToneMediaPlayer = null;
                }
            });
            mToneMediaPlayer.start();
        }

    }

    @VisibleForTesting
    public void startTone() {
        sTonesPlaying++;