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

Commit 895ecb17 authored by Yiwen Chen's avatar Yiwen Chen
Browse files

Make Telephony support ramping ringer.

Bug: 120789399
Test: When finish the whole change, flash into a dev phone to test first.
Change-Id: I477b619cb612b61ed10bc9878309b129c51b314f
parent 4cc4d6ef
Loading
Loading
Loading
Loading
+13 −4
Original line number Diff line number Diff line
@@ -16,7 +16,9 @@

package com.android.server.telecom;

import android.annotation.Nullable;
import android.media.Ringtone;
import android.media.VolumeShaper;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
@@ -67,15 +69,22 @@ public class AsyncRingtonePlayer {
        mShouldPauseBetweenRepeat = shouldPauseBetweenRepeat;
    }

    /** Plays the ringtone. */
    public void play(RingtoneFactory factory, Call incomingCall) {
    /** Plays the ringtone with ramping ringer if required. */
    public void play(RingtoneFactory factory, Call incomingCall,
            @Nullable VolumeShaper.Configuration volumeShaperConfig) {
        Log.d(this, "Posting play.");
        SomeArgs args = SomeArgs.obtain();
        args.arg1 = factory;
        args.arg2 = incomingCall;
        args.arg3 = volumeShaperConfig;
        postMessage(EVENT_PLAY, true /* shouldCreateHandler */, args);
    }

    /** Plays the ringtone. */
    public void play(RingtoneFactory factory, Call incomingCall) {
        play(factory, incomingCall, null);
    }

    /** Stops playing the ringtone. */
    public void stop() {
        Log.d(this, "Posting stop.");
@@ -136,6 +145,7 @@ public class AsyncRingtonePlayer {
    private void handlePlay(SomeArgs args) {
        RingtoneFactory factory = (RingtoneFactory) args.arg1;
        Call incomingCall = (Call) args.arg2;
        VolumeShaper.Configuration volumeShaperConfig = (VolumeShaper.Configuration) args.arg3;
        args.recycle();
        // don't bother with any of this if there is an EVENT_STOP waiting.
        if (mHandler.hasMessages(EVENT_STOP)) {
@@ -153,7 +163,7 @@ public class AsyncRingtonePlayer {
        Log.i(this, "Play ringtone.");

        if (mRingtone == null) {
            mRingtone = factory.getRingtone(incomingCall);
            mRingtone = factory.getRingtone(incomingCall, volumeShaperConfig);
            if (mRingtone == null) {
                Uri ringtoneUri = incomingCall.getRingtone();
                String ringtoneUriString = (ringtoneUri == null) ? "null" :
@@ -183,7 +193,6 @@ public class AsyncRingtonePlayer {
        if (mRingtone == null) {
            return;
        }

        if (mRingtone.isPlaying()) {
            Log.d(this, "Ringtone already playing.");
        } else {
+27 −1
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.telecom.TelecomManager;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.Ringtone;
import android.media.VolumeShaper;
import android.net.Uri;
import android.os.Bundle;
import android.os.Vibrator;
@@ -82,11 +83,17 @@ public class Ringer {

    private static final int REPEAT_SIMPLE_VIBRATION_AT = 1;

    private static final int DEFAULT_RAMPING_RINGER_DURATION = 15000;  // 15 seconds

    private static int rampingRingerDuration = -1;

    private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
            .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
            .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
            .build();

    private static VolumeShaper.Configuration volumeShaperConfig;

    /**
     * Used to keep ordering of unanswered incoming calls. There can easily exist multiple incoming
     * calls and explicit ordering is useful for maintaining the proper state of the ringer.
@@ -197,7 +204,26 @@ public class Ringer {
            // call (for the purposes of direct-to-voicemail), the information about custom
            // ringtones should be available by the time this code executes. We can safely
            // request the custom ringtone from the call and expect it to be current.
            mRingtonePlayer.play(mRingtoneFactory, foregroundCall);
            if (mSystemSettingsUtil.applyRampingRinger(mContext)
                || mSystemSettingsUtil.enableRampingRingerFromDeviceConfig()) {
                Log.i(this, "start ramping ringer.");
                int previousRampingRingerDuration = rampingRingerDuration;
                rampingRingerDuration =
                    mSystemSettingsUtil.getRampingRingerDuration() > 0
                        ? mSystemSettingsUtil.getRampingRingerDuration()
                        : DEFAULT_RAMPING_RINGER_DURATION;
                if ((rampingRingerDuration != previousRampingRingerDuration)
                    || volumeShaperConfig == null) {
                    volumeShaperConfig = new VolumeShaper.Configuration.Builder()
                        .setDuration(rampingRingerDuration)
                        .setCurve(new float[] {0.f, 1.f}, new float[] {0.f, 1.f})
                        .setInterpolatorType(VolumeShaper.Configuration.INTERPOLATOR_TYPE_LINEAR)
                        .build();
                }
                mRingtonePlayer.play(mRingtoneFactory, foregroundCall, volumeShaperConfig);
            } else {
                mRingtonePlayer.play(mRingtoneFactory, foregroundCall, null);
            }
            effect = getVibrationEffectForCall(mRingtoneFactory, foregroundCall);
        } else {
            String reason = String.format(
+11 −3
Original line number Diff line number Diff line
@@ -16,12 +16,14 @@

package com.android.server.telecom;

import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.media.AudioManager;
import android.media.RingtoneManager;
import android.media.Ringtone;
import android.media.VolumeShaper;
import android.net.Uri;
import android.os.UserHandle;
import android.os.UserManager;
@@ -52,7 +54,8 @@ public class RingtoneFactory {
        mCallsManager = callsManager;
    }

    public Ringtone getRingtone(Call incomingCall) {
    public Ringtone getRingtone(Call incomingCall,
            @Nullable VolumeShaper.Configuration volumeShaperConfig) {
        // Use the default ringtone of the work profile if the contact is a work profile contact.
        Context userContext = isWorkContact(incomingCall) ?
                getWorkProfileContextForUser(mCallsManager.getCurrentUserHandle()) :
@@ -62,7 +65,7 @@ public class RingtoneFactory {

        if(ringtoneUri != null && userContext != null) {
            // Ringtone URI is explicitly specified. First, try to create a Ringtone with that.
            ringtone = RingtoneManager.getRingtone(userContext, ringtoneUri);
            ringtone = RingtoneManager.getRingtone(userContext, ringtoneUri, volumeShaperConfig);
        }
        if(ringtone == null) {
            // Contact didn't specify ringtone or custom Ringtone creation failed. Get default
@@ -78,7 +81,8 @@ public class RingtoneFactory {
            if (defaultRingtoneUri == null) {
                return null;
            }
            ringtone = RingtoneManager.getRingtone(contextToUse, defaultRingtoneUri);
            ringtone = RingtoneManager.getRingtone(
                contextToUse, defaultRingtoneUri, volumeShaperConfig);
        }
        if (ringtone != null) {
            ringtone.setStreamType(AudioManager.STREAM_RING);
@@ -86,6 +90,10 @@ public class RingtoneFactory {
        return ringtone;
    }

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

    private Context getWorkProfileContextForUser(UserHandle userHandle) {
        // UserManager.getEnabledProfiles returns the enabled profiles along with the user's handle
        // itself (so we must filter out the user).
+42 −0
Original line number Diff line number Diff line
@@ -17,7 +17,9 @@
package com.android.server.telecom;

import android.content.Context;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.telecom.Log;

import com.android.internal.annotations.VisibleForTesting;

@@ -46,4 +48,44 @@ public class SystemSettingsUtil {
        return Settings.System.putInt(context.getContentResolver(),
                Settings.System.DEBUG_ENABLE_ENHANCED_CALL_BLOCKING, enabled ? 1 : 0);
    }

    public boolean applyRampingRinger(Context context) {
        return Settings.Global.getInt(context.getContentResolver(),
            Settings.Global.APPLY_RAMPING_RINGER, 0) == 1;
    }

    public boolean enableRampingRingerFromDeviceConfig() {
        String enableRampingRinger = DeviceConfig.getProperty(
            DeviceConfig.Telephony.NAMESPACE,
            DeviceConfig.Telephony.PROPERTY_ENABLE_RAMPING_RINGER);
        if (enableRampingRinger == null) {
            Log.i(this, "DeviceConfig.Telephony.PROPERTY_ENABLE_RAMPING_RINGER is null");
            return false;
        }
        try {
            return Boolean.valueOf(enableRampingRinger);
        } catch (Exception e) {
            Log.wtf(this,
                "Error paring DeviceConfig.Telephony.PROPERTY_ENABLE_RAMPING_RINGER: " + e);
            return false;
        }
    }

    public int getRampingRingerDuration() {
        String rampingRingerDuration = DeviceConfig.getProperty(
            DeviceConfig.Telephony.NAMESPACE,
            DeviceConfig.Telephony.PROPERTY_RAMPING_RINGER_DURATION);
        if (rampingRingerDuration == null) {
            Log.i(this, "DeviceConfig.Telephony.PROPERTY_RAMPING_RINGER_DURATION is null");
            return -1;
        }
        try {
            return Integer.parseInt(rampingRingerDuration);
        } catch (Exception e) {
            Log.wtf(this,
                "Error paring DeviceConfig.Telephony.PROPERTY_RAMPING_RINGER_DURATION: " + e);
            return -1;
        }
    }
}
+19 −2
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.content.Context;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.Ringtone;
import android.media.VolumeShaper;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
@@ -257,7 +258,7 @@ public class RingerTest extends TelecomTestCase {
        enableVibrationWhenRinging();
        assertTrue(mRingerUnderTest.startRinging(mockCall2, false));
        verify(mockTonePlayer).stopTone();
        verify(mockRingtonePlayer).play(any(RingtoneFactory.class), any(Call.class));
        verify(mockRingtonePlayer).play(any(RingtoneFactory.class), any(Call.class), eq(null));
        verify(mockVibrator).vibrate(eq(spyVibrationEffectProxy.get(FAKE_RINGTONE_URI, mContext)),
                any(AudioAttributes.class));
    }
@@ -270,11 +271,23 @@ public class RingerTest extends TelecomTestCase {
        enableVibrationOnlyWhenNotRinging();
        assertTrue(mRingerUnderTest.startRinging(mockCall2, false));
        verify(mockTonePlayer).stopTone();
        verify(mockRingtonePlayer).play(any(RingtoneFactory.class), any(Call.class));
        verify(mockRingtonePlayer).play(any(RingtoneFactory.class), any(Call.class), eq(null));
        verify(mockVibrator, never())
                .vibrate(any(VibrationEffect.class), any(AudioAttributes.class));
    }

    @SmallTest
    @Test
    public void testRingWithRampingRinger() {
        mRingerUnderTest.startCallWaiting(mockCall1);
        ensureRingerIsAudible();
        enableRampingRinger();
        assertTrue(mRingerUnderTest.startRinging(mockCall2, false));
        verify(mockTonePlayer).stopTone();
        verify(mockRingtonePlayer).play(
            any(RingtoneFactory.class), any(Call.class), any(VolumeShaper.Configuration.class));
    }

    @SmallTest
    @Test
    public void testSilentRingWithHfpStillAcquiresFocus1() {
@@ -322,4 +335,8 @@ public class RingerTest extends TelecomTestCase {
        when(mockVibrator.hasVibrator()).thenReturn(true);
        when(mockSystemSettingsUtil.canVibrateWhenRinging(any(Context.class))).thenReturn(false);
    }

    private void enableRampingRinger() {
        when(mockSystemSettingsUtil.applyRampingRinger(any(Context.class))).thenReturn(true);
    }
}