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

Commit 1e2c2270 authored by Hall Liu's avatar Hall Liu Committed by android-build-merger
Browse files

Merge "Update ringer to take BT device into account" am: 9533e0a4 am: fb7b5bf5 am: 2900263b

am: 4bd0e0e8

Change-Id: I55816a71aa1b505ac489c43e9d3110c091c830e2
parents 5fd3c66d 4bd0e0e8
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -424,7 +424,8 @@ public class CallAudioManager extends CallsManagerListenerBase {

    @VisibleForTesting
    public boolean startRinging() {
        return mRinger.startRinging(mForegroundCall);
        return mRinger.startRinging(mForegroundCall,
                mCallAudioRouteStateMachine.isHfpDeviceAvailable());
    }

    @VisibleForTesting
+4 −0
Original line number Diff line number Diff line
@@ -1323,6 +1323,10 @@ public class CallAudioRouteStateMachine extends StateMachine {
        getHandler().getLooper().dump(pw::println, "");
    }

    public boolean isHfpDeviceAvailable() {
        return mBluetoothRouteManager.isBluetoothAvailable();
    }

    /**
     * Sets whether notifications should be suppressed or not.  Used when in a call to ensure the
     * device will not vibrate due to notifications.
+1 −1
Original line number Diff line number Diff line
@@ -61,7 +61,7 @@ import java.util.Objects;
 * can send updates to the in-call app. This class is created and owned by CallsManager and retains
 * a binding to the {@link IInCallService} (implemented by the in-call app).
 */
public final class InCallController extends CallsManagerListenerBase {
public class InCallController extends CallsManagerListenerBase {

    public class InCallServiceConnection {
        /**
+34 −29
Original line number Diff line number Diff line
@@ -97,41 +97,44 @@ public class Ringer {
        mInCallController = inCallController;
    }

    public boolean startRinging(Call foregroundCall) {
        AudioManager audioManager =
                (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
        boolean isRingerAudible = audioManager.getStreamVolume(AudioManager.STREAM_RING) > 0;

        if (mSystemSettingsUtil.isTheaterModeOn(mContext)) {
            return false;
        }

    public boolean startRinging(Call foregroundCall, boolean isHfpDeviceAttached) {
        if (foregroundCall == null) {
            Log.wtf(this, "startRinging called with null foreground call.");
            return false;
        }

        if (mInCallController.doesConnectedDialerSupportRinging()) {
        AudioManager audioManager =
                (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
        boolean isVolumeOverZero = audioManager.getStreamVolume(AudioManager.STREAM_RING) > 0;
        boolean shouldRingForContact = shouldRingForContact(foregroundCall.getContactUri());
        boolean isRingtonePresent = !(mRingtoneFactory.getRingtone(foregroundCall) == null);
        boolean isSelfManaged = foregroundCall.isSelfManaged();

        boolean isRingerAudible = isVolumeOverZero && shouldRingForContact && isRingtonePresent;
        // Acquire audio focus under any of the following conditions:
        // 1. Should ring for contact and there's an HFP device attached
        // 2. Volume is over zero, we should ring for the contact, and there's a audible ringtone
        //    present.
        // 3. The call is self-managed.
        boolean shouldAcquireAudioFocus =
                isRingerAudible || (isHfpDeviceAttached && shouldRingForContact) || isSelfManaged;

        // Don't do call waiting operations or vibration unless these are false.
        boolean isTheaterModeOn = mSystemSettingsUtil.isTheaterModeOn(mContext);
        boolean letDialerHandleRinging = mInCallController.doesConnectedDialerSupportRinging();
        boolean endEarly = isTheaterModeOn || letDialerHandleRinging || isSelfManaged;

        if (endEarly) {
            if (letDialerHandleRinging) {
                Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING);
            return isRingerAudible;
            }

        if (foregroundCall.isSelfManaged()) {
            Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING, "Self-managed");
            return false;
            Log.i(this, "Ending early -- isTheaterModeOn=%s, letDialerHandleRinging=%s, " +
                    "isSelfManaged=%s", isTheaterModeOn, letDialerHandleRinging, isSelfManaged);
            return shouldAcquireAudioFocus;
        }

        stopCallWaiting();

        if (!shouldRingForContact(foregroundCall.getContactUri())) {
            return false;
        }

        // Don't ring/acquire focus if there is no ringtone
        if (mRingtoneFactory.getRingtone(foregroundCall) == null) {
            isRingerAudible = false;
        }

        if (isRingerAudible) {
            mRingingCall = foregroundCall;
            Log.addEvent(foregroundCall, LogUtils.Events.START_RINGER);
@@ -141,10 +144,12 @@ public class Ringer {
            // request the custom ringtone from the call and expect it to be current.
            mRingtonePlayer.play(mRingtoneFactory, foregroundCall);
        } else {
            Log.i(this, "startRingingOrCallWaiting, skipping because volume is 0");
            Log.i(this, "startRinging: skipping because ringer would not be audible. " +
                    "isVolumeOverZero=%s, shouldRingForContact=%s, isRingtonePresent=%s",
                    isVolumeOverZero, shouldRingForContact, isRingtonePresent);
        }

        if (shouldVibrate(mContext, foregroundCall) && !mIsVibrating) {
        if (shouldVibrate(mContext, foregroundCall) && !mIsVibrating && shouldRingForContact) {
            mVibratingCall = foregroundCall;
            mVibrator.vibrate(VIBRATION_PATTERN, VIBRATION_PATTERN_REPEAT,
                    VIBRATION_ATTRIBUTES);
@@ -153,7 +158,7 @@ public class Ringer {
            Log.addEvent(foregroundCall, LogUtils.Events.SKIP_VIBRATION, "already vibrating");
        }

        return isRingerAudible;
        return shouldAcquireAudioFocus;
    }

    public void startCallWaiting(Call call) {
+224 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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.telecom.tests;

import android.app.NotificationManager;
import android.content.Context;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.Ringtone;
import android.os.Bundle;
import android.os.Vibrator;
import android.test.suitebuilder.annotation.SmallTest;

import com.android.server.telecom.AsyncRingtonePlayer;
import com.android.server.telecom.Call;
import com.android.server.telecom.InCallController;
import com.android.server.telecom.InCallTonePlayer;
import com.android.server.telecom.Ringer;
import com.android.server.telecom.RingtoneFactory;
import com.android.server.telecom.SystemSettingsUtil;

import org.mockito.Mock;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class RingerTest extends TelecomTestCase {
    @Mock InCallTonePlayer.Factory mockPlayerFactory;
    @Mock SystemSettingsUtil mockSystemSettingsUtil;
    @Mock AsyncRingtonePlayer mockRingtonePlayer;
    @Mock RingtoneFactory mockRingtoneFactory;
    @Mock Vibrator mockVibrator;
    @Mock InCallController mockInCallController;

    @Mock InCallTonePlayer mockTonePlayer;
    @Mock Call mockCall1;
    @Mock Call mockCall2;

    Ringer mRingerUnderTest;
    AudioManager mockAudioManager;
    public void setUp() throws Exception {
        super.setUp();
        mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
        mRingerUnderTest = new Ringer(mockPlayerFactory, mContext, mockSystemSettingsUtil,
                mockRingtonePlayer, mockRingtoneFactory, mockVibrator, mockInCallController);
        when(mockPlayerFactory.createPlayer(anyInt())).thenReturn(mockTonePlayer);
        mockAudioManager =
                (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
        when(mockAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
        NotificationManager notificationManager =
                (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
        when(notificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(true);
    }

    @SmallTest
    public void testNoActionInTheaterMode() {
        // Start call waiting to make sure that it doesn't stop when we start ringing
        mRingerUnderTest.startCallWaiting(mockCall1);
        when(mockSystemSettingsUtil.isTheaterModeOn(any(Context.class))).thenReturn(true);
        assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
        verify(mockTonePlayer, never()).stopTone();
        verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class));
        verify(mockVibrator, never()).vibrate(
                any(long[].class), anyInt(), any(AudioAttributes.class));
    }

    @SmallTest
    public void testNoActionWhenDialerRings() {
        // Start call waiting to make sure that it doesn't stop when we start ringing
        mRingerUnderTest.startCallWaiting(mockCall1);
        when(mockInCallController.doesConnectedDialerSupportRinging()).thenReturn(true);
        assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
        verify(mockTonePlayer, never()).stopTone();
        verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class));
        verify(mockVibrator, never()).vibrate(
                any(long[].class), anyInt(), any(AudioAttributes.class));
    }

    @SmallTest
    public void testAudioFocusStillAcquiredWhenDialerRings() {
        // Start call waiting to make sure that it doesn't stop when we start ringing
        mRingerUnderTest.startCallWaiting(mockCall1);
        when(mockInCallController.doesConnectedDialerSupportRinging()).thenReturn(true);
        ensureRingerIsAudible();
        assertTrue(mRingerUnderTest.startRinging(mockCall2, false));
        verify(mockTonePlayer, never()).stopTone();
        verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class));
        verify(mockVibrator, never()).vibrate(
                any(long[].class), anyInt(), any(AudioAttributes.class));
    }

    @SmallTest
    public void testNoActionWhenCallIsSelfManaged() {
        // Start call waiting to make sure that it doesn't stop when we start ringing
        mRingerUnderTest.startCallWaiting(mockCall1);
        when(mockCall2.isSelfManaged()).thenReturn(true);
        // We do want to acquire audio focus when self-managed
        assertTrue(mRingerUnderTest.startRinging(mockCall2, true));
        verify(mockTonePlayer, never()).stopTone();
        verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class));
        verify(mockVibrator, never()).vibrate(
                any(long[].class), anyInt(), any(AudioAttributes.class));
    }

    @SmallTest
    public void testCallWaitingButNoRingForSpecificContacts() {
        NotificationManager notificationManager =
                (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
        when(notificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(false);
        // Start call waiting to make sure that it does stop when we start ringing
        mRingerUnderTest.startCallWaiting(mockCall1);
        verify(mockTonePlayer).startTone();

        assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
        verify(mockTonePlayer).stopTone();
        verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class));
        verify(mockVibrator, never()).vibrate(
                any(long[].class), anyInt(), any(AudioAttributes.class));
    }

    @SmallTest
    public void testVibrateButNoRingForNullRingtone() {
        mRingerUnderTest.startCallWaiting(mockCall1);
        when(mockRingtoneFactory.getRingtone(any(Call.class))).thenReturn(null);
        when(mockAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
        enableVibrationWhenRinging();
        assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
        verify(mockTonePlayer).stopTone();
        verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class));
        verify(mockVibrator).vibrate(
                any(long[].class), anyInt(), any(AudioAttributes.class));
    }

    @SmallTest
    public void testVibrateButNoRingForSilentRingtone() {
        mRingerUnderTest.startCallWaiting(mockCall1);
        Ringtone mockRingtone = mock(Ringtone.class);
        when(mockRingtoneFactory.getRingtone(any(Call.class))).thenReturn(mockRingtone);
        when(mockAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
        when(mockAudioManager.getStreamVolume(AudioManager.STREAM_RING)).thenReturn(0);
        enableVibrationWhenRinging();
        assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
        verify(mockTonePlayer).stopTone();
        verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class));
        verify(mockVibrator).vibrate(
                any(long[].class), anyInt(), any(AudioAttributes.class));
    }

    @SmallTest
    public void testRingAndNoVibrate() {
        mRingerUnderTest.startCallWaiting(mockCall1);
        ensureRingerIsAudible();
        enableVibrationOnlyWhenNotRinging();
        assertTrue(mRingerUnderTest.startRinging(mockCall2, false));
        verify(mockTonePlayer).stopTone();
        verify(mockRingtonePlayer).play(any(RingtoneFactory.class), any(Call.class));
        verify(mockVibrator, never()).vibrate(
                any(long[].class), anyInt(), any(AudioAttributes.class));
    }

    @SmallTest
    public void testSilentRingWithHfpStillAcquiresFocus1() {
        mRingerUnderTest.startCallWaiting(mockCall1);
        Ringtone mockRingtone = mock(Ringtone.class);
        when(mockRingtoneFactory.getRingtone(any(Call.class))).thenReturn(mockRingtone);
        when(mockAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
        when(mockAudioManager.getStreamVolume(AudioManager.STREAM_RING)).thenReturn(0);
        enableVibrationOnlyWhenNotRinging();
        assertTrue(mRingerUnderTest.startRinging(mockCall2, true));
        verify(mockTonePlayer).stopTone();
        verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class));
        verify(mockVibrator, never()).vibrate(
                any(long[].class), anyInt(), any(AudioAttributes.class));
    }

    @SmallTest
    public void testSilentRingWithHfpStillAcquiresFocus2() {
        mRingerUnderTest.startCallWaiting(mockCall1);
        when(mockRingtoneFactory.getRingtone(any(Call.class))).thenReturn(null);
        when(mockAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
        when(mockAudioManager.getStreamVolume(AudioManager.STREAM_RING)).thenReturn(0);
        enableVibrationOnlyWhenNotRinging();
        assertTrue(mRingerUnderTest.startRinging(mockCall2, true));
        verify(mockTonePlayer).stopTone();
        verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class));
        verify(mockVibrator, never()).vibrate(
                any(long[].class), anyInt(), any(AudioAttributes.class));
    }

    private void ensureRingerIsAudible() {
        Ringtone mockRingtone = mock(Ringtone.class);
        when(mockRingtoneFactory.getRingtone(any(Call.class))).thenReturn(mockRingtone);
        when(mockAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
        when(mockAudioManager.getStreamVolume(AudioManager.STREAM_RING)).thenReturn(100);
    }

    private void enableVibrationWhenRinging() {
        when(mockVibrator.hasVibrator()).thenReturn(true);
        when(mockSystemSettingsUtil.canVibrateWhenRinging(any(Context.class))).thenReturn(true);
    }

    private void enableVibrationOnlyWhenNotRinging() {
        when(mockVibrator.hasVibrator()).thenReturn(true);
        when(mockSystemSettingsUtil.canVibrateWhenRinging(any(Context.class))).thenReturn(false);
    }
}