Merge "Add volume to logs when call audio route changes."
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index c17c447..35ba93a 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -92,6 +92,9 @@
private static final int RTT_PIPE_WRITE_SIDE_INDEX = 1;
private static final int INVALID_RTT_REQUEST_ID = -1;
+
+ private static final char NO_DTMF_TONE = '\0';
+
/**
* Listener for events on the call.
*/
@@ -403,6 +406,7 @@
private final String mId;
private String mConnectionId;
private Analytics.CallInfo mAnalytics;
+ private char mPlayingDtmfTone;
private boolean mWasConferencePreviouslyMerged = false;
private boolean mWasHighDefAudio = false;
@@ -1571,7 +1575,8 @@
/**
* Plays the specified DTMF tone.
*/
- void playDtmfTone(char digit) {
+ @VisibleForTesting
+ public void playDtmfTone(char digit) {
if (mConnectionService == null) {
Log.w(this, "playDtmfTone() request on a call without a connection service.");
} else {
@@ -1579,12 +1584,14 @@
mConnectionService.playDtmfTone(this, digit);
Log.addEvent(this, LogUtils.Events.START_DTMF, Log.pii(digit));
}
+ mPlayingDtmfTone = digit;
}
/**
* Stops playing any currently playing DTMF tone.
*/
- void stopDtmfTone() {
+ @VisibleForTesting
+ public void stopDtmfTone() {
if (mConnectionService == null) {
Log.w(this, "stopDtmfTone() request on a call without a connection service.");
} else {
@@ -1592,6 +1599,15 @@
Log.addEvent(this, LogUtils.Events.STOP_DTMF);
mConnectionService.stopDtmfTone(this);
}
+ mPlayingDtmfTone = NO_DTMF_TONE;
+ }
+
+ /**
+ * @return {@code true} if a DTMF tone has been started via {@link #playDtmfTone(char)} but has
+ * not been stopped via {@link #stopDtmfTone()}, {@code false} otherwise.
+ */
+ boolean isDtmfTonePlaying() {
+ return mPlayingDtmfTone != NO_DTMF_TONE;
}
/**
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 7485841..34f299d 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -1229,6 +1229,9 @@
if (targetPhoneAccountHandle != null) {
if (!accounts.contains(targetPhoneAccountHandle)) {
targetPhoneAccountHandle = null;
+ } else {
+ // The target phone account is valid and was found.
+ return Arrays.asList(targetPhoneAccountHandle);
}
}
@@ -1489,8 +1492,12 @@
if (!mCalls.contains(call)) {
Log.i(this, "Request to play DTMF in a non-existent call %s", call);
} else {
- call.playDtmfTone(digit);
- mDtmfLocalTonePlayer.playTone(call, digit);
+ if (call.getState() != CallState.ON_HOLD) {
+ call.playDtmfTone(digit);
+ mDtmfLocalTonePlayer.playTone(call, digit);
+ } else {
+ Log.i(this, "Request to play DTMF tone for held call %s", call.getId());
+ }
}
}
@@ -1788,7 +1795,8 @@
maybeMoveToSpeakerPhone(call);
}
- void markCallAsOnHold(Call call) {
+ @VisibleForTesting
+ public void markCallAsOnHold(Call call) {
setCallState(call, CallState.ON_HOLD, "on-hold set explicitly");
}
@@ -2259,6 +2267,12 @@
Log.i(this, "setCallState %s -> %s, call: %s", CallState.toString(oldState),
CallState.toString(newState), call);
if (newState != oldState) {
+ // If the call switches to held state while a DTMF tone is playing, stop the tone to
+ // ensure that the tone generator stops playing the tone.
+ if (newState == CallState.ON_HOLD && call.isDtmfTonePlaying()) {
+ stopDtmfTone(call);
+ }
+
// Unfortunately, in the telephony world the radio is king. So if the call notifies
// us that the call is in a particular state, we allow it even if it doesn't make
// sense (e.g., STATE_ACTIVE -> STATE_RINGING).
diff --git a/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java b/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java
index fda68a8..a7e21ad 100644
--- a/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java
+++ b/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java
@@ -30,6 +30,7 @@
import android.telecom.GatewayInfo;
import android.telecom.Log;
import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telecom.VideoProfile;
import android.telephony.DisconnectCause;
@@ -262,6 +263,21 @@
return DisconnectCause.INVALID_NUMBER;
}
+ // True for all managed calls, false for self-managed calls.
+ boolean sendNewOutgoingCallBroadcast = true;
+ PhoneAccountHandle targetPhoneAccount = mIntent.getParcelableExtra(
+ TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
+ if (targetPhoneAccount != null) {
+ PhoneAccount phoneAccount =
+ mCallsManager.getPhoneAccountRegistrar().getPhoneAccountUnchecked(
+ targetPhoneAccount);
+ if (phoneAccount != null && phoneAccount.isSelfManaged()) {
+ callImmediately = true;
+ sendNewOutgoingCallBroadcast = false;
+ Log.i(this, "Skipping NewOutgoingCallBroadcast for self-managed call.");
+ }
+ }
+
if (callImmediately) {
String scheme = isUriNumber ? PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL;
boolean speakerphoneOn = mIntent.getBooleanExtra(
@@ -278,9 +294,11 @@
// initiate the call again because of the presence of the EXTRA_ALREADY_CALLED extra.
}
- UserHandle targetUser = mCall.getInitiatingUser();
- Log.i(this, "Sending NewOutgoingCallBroadcast for %s to %s", mCall, targetUser);
- broadcastIntent(intent, number, !callImmediately, targetUser);
+ if (sendNewOutgoingCallBroadcast) {
+ UserHandle targetUser = mCall.getInitiatingUser();
+ Log.i(this, "Sending NewOutgoingCallBroadcast for %s to %s", mCall, targetUser);
+ broadcastIntent(intent, number, !callImmediately, targetUser);
+ }
return DisconnectCause.NOT_DISCONNECTED;
}
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java b/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java
index fed3233..4aaa4e6 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java
@@ -55,6 +55,7 @@
import static org.mockito.Matchers.same;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.timeout;
@@ -1145,7 +1146,8 @@
private void resetMocks() {
reset(mockAudioManager, mockBluetoothRouteManager, mockCallsManager,
- mockConnectionServiceWrapper, fakeCall);
+ mockConnectionServiceWrapper);
+ fakeCall = mock(Call.class);
when(mockCallsManager.getForegroundCall()).thenReturn(fakeCall);
when(fakeCall.getConnectionService()).thenReturn(mockConnectionServiceWrapper);
when(fakeCall.isAlive()).thenReturn(true);
diff --git a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
index 5e7eb50..714261a 100644
--- a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
@@ -18,8 +18,11 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyChar;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.ComponentName;
@@ -37,6 +40,7 @@
import com.android.server.telecom.CallerInfoAsyncQueryFactory;
import com.android.server.telecom.CallsManager;
import com.android.server.telecom.ClockProxy;
+import com.android.server.telecom.ConnectionServiceWrapper;
import com.android.server.telecom.ContactsAsyncHelper;
import com.android.server.telecom.DefaultDialerCache;
import com.android.server.telecom.EmergencyCallHelper;
@@ -59,6 +63,7 @@
import com.android.server.telecom.bluetooth.BluetoothRouteManager;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
@@ -315,6 +320,84 @@
assertTrue(accounts.contains(SIM_1_HANDLE));
}
+ /**
+ * Tests that we will use the provided target phone account if it exists.
+ * @throws Exception
+ */
+ @MediumTest
+ public void testUseSpecifiedAccount() throws Exception {
+ when(mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(any(), any())).thenReturn(
+ null);
+ when(mPhoneAccountRegistrar.getCallCapablePhoneAccounts(any(), anyBoolean(),
+ any(), anyInt())).thenReturn(
+ new ArrayList<>(Arrays.asList(SIM_1_HANDLE, SIM_2_HANDLE)));
+
+ List<PhoneAccountHandle> accounts = mCallsManager.findOutgoingCallPhoneAccount(
+ SIM_2_HANDLE, TEST_ADDRESS, false /* isVideo */, null /* userHandle */);
+
+ assertEquals(1, accounts.size());
+ assertTrue(accounts.contains(SIM_2_HANDLE));
+ }
+
+ /**
+ * Verifies that an active call will result in playing a DTMF tone when requested.
+ * @throws Exception
+ */
+ @MediumTest
+ public void testPlayDtmfWhenActive() throws Exception {
+ Call callSpy = addSpyCall();
+ mCallsManager.playDtmfTone(callSpy, '1');
+ verify(callSpy).playDtmfTone(anyChar());
+ }
+
+ /**
+ * Verifies that DTMF requests are suppressed when a call is held.
+ * @throws Exception
+ */
+ @MediumTest
+ public void testSuppessDtmfWhenHeld() throws Exception {
+ Call callSpy = addSpyCall();
+ callSpy.setState(CallState.ON_HOLD, "test");
+
+ mCallsManager.playDtmfTone(callSpy, '1');
+ verify(callSpy, never()).playDtmfTone(anyChar());
+ }
+
+ /**
+ * Verifies that DTMF requests are suppressed when a call is held.
+ * @throws Exception
+ */
+ @MediumTest
+ public void testCancelDtmfWhenHeld() throws Exception {
+ Call callSpy = addSpyCall();
+ mCallsManager.playDtmfTone(callSpy, '1');
+ mCallsManager.markCallAsOnHold(callSpy);
+ verify(callSpy).stopDtmfTone();
+ }
+
+ private Call addSpyCall() {
+ Call ongoingCall = new Call("1", /* callId */
+ mComponentContextFixture.getTestDouble(),
+ mCallsManager,
+ mLock, /* ConnectionServiceRepository */
+ null,
+ mContactsAsyncHelper,
+ mCallerInfoAsyncQueryFactory,
+ mPhoneNumberUtilsAdapter,
+ TEST_ADDRESS,
+ null /* GatewayInfo */,
+ null /* connectionManagerPhoneAccountHandle */,
+ SIM_2_HANDLE,
+ Call.CALL_DIRECTION_INCOMING,
+ false /* shouldAttachToExistingConnection*/,
+ false /* isConference */,
+ mClockProxy);
+ ongoingCall.setState(CallState.ACTIVE, "just cuz");
+ Call callSpy = Mockito.spy(ongoingCall);
+ mCallsManager.addCall(callSpy);
+ return callSpy;
+ }
+
private void setupMsimAccounts() {
TelephonyManager mockTelephonyManager = mComponentContextFixture.getTelephonyManager();
when(mockTelephonyManager.getMultiSimConfiguration()).thenReturn(
diff --git a/tests/src/com/android/server/telecom/tests/InCallServiceFixture.java b/tests/src/com/android/server/telecom/tests/InCallServiceFixture.java
index 7635427..f851162 100644
--- a/tests/src/com/android/server/telecom/tests/InCallServiceFixture.java
+++ b/tests/src/com/android/server/telecom/tests/InCallServiceFixture.java
@@ -25,7 +25,6 @@
import android.os.IBinder;
import android.os.IInterface;
import android.os.RemoteException;
-import android.telecom.AudioState;
import android.telecom.CallAudioState;
import android.telecom.ParcelableCall;
@@ -49,7 +48,8 @@
public boolean mShowDialpad;
public boolean mCanAddCall;
public boolean mSilenceRinger;
- public CountDownLatch mLock = new CountDownLatch(1);
+ public CountDownLatch mUpdateCallLock = new CountDownLatch(1);
+ public CountDownLatch mAddCallLock = new CountDownLatch(1);
public class FakeInCallService extends IInCallService.Stub {
@Override
@@ -68,8 +68,9 @@
if (mCallById.containsKey(call.getId())) {
throw new RuntimeException("Call " + call.getId() + " already added");
}
- mCallById.put(call.getId(), call);
mLatestCallId = call.getId();
+ mCallById.put(call.getId(), call);
+ mAddCallLock.countDown();
}
@Override
@@ -77,9 +78,9 @@
if (!mCallById.containsKey(call.getId())) {
throw new RuntimeException("Call " + call.getId() + " not added yet");
}
- mCallById.put(call.getId(), call);
mLatestCallId = call.getId();
- mLock.countDown();
+ mCallById.put(call.getId(), call);
+ mUpdateCallLock.countDown();
}
@Override
@@ -159,10 +160,23 @@
public void waitForUpdate() {
try {
- mLock.await(5000, TimeUnit.MILLISECONDS);
+ mUpdateCallLock.await(5000, TimeUnit.MILLISECONDS);
} catch (InterruptedException ie) {
return;
}
- mLock = new CountDownLatch(1);
+ mUpdateCallLock = new CountDownLatch(1);
+ }
+
+ public void waitUntilNumCalls(int numCalls) {
+ if (mCallById.size() == numCalls) {
+ return;
+ }
+ mAddCallLock = new CountDownLatch(1);
+
+ try {
+ mAddCallLock.await(5000, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException ie) {
+ return;
+ }
}
}
diff --git a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
index 475b550..b84f9d5 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
@@ -578,13 +578,8 @@
int startingNumConnections = connectionServiceFixture.mConnectionById.size();
int startingNumCalls = mInCallServiceFixtureX.mCallById.size();
- String callId = startOutgoingPhoneCallPendingCreateConnection(number, phoneAccountHandle,
+ startOutgoingPhoneCallPendingCreateConnection(number, phoneAccountHandle,
connectionServiceFixture, initiatingUser, videoState);
- // Set the phone account if there is one specified
- if (phoneAccountHandle != null) {
- mInCallServiceFixtureX.getInCallAdapter().phoneAccountSelected(
- callId, phoneAccountHandle, false);
- }
return outgoingCallCreateConnectionComplete(startingNumConnections, startingNumCalls,
phoneAccountHandle, connectionServiceFixture);
@@ -847,34 +842,35 @@
// is added, future interactions as triggered by the ConnectionService, through the various
// test fixtures, will be synchronous.
- if (!hasInCallAdapter
- && phoneAccountHandle != mPhoneAccountSelfManaged.getAccountHandle()) {
- verify(mInCallServiceFixtureX.getTestDouble(), timeout(TEST_TIMEOUT))
- .setInCallAdapter(any(IInCallAdapter.class));
- verify(mInCallServiceFixtureY.getTestDouble(), timeout(TEST_TIMEOUT))
- .setInCallAdapter(any(IInCallAdapter.class));
+ if (phoneAccountHandle != mPhoneAccountSelfManaged.getAccountHandle()) {
+ if (!hasInCallAdapter) {
+ verify(mInCallServiceFixtureX.getTestDouble(), timeout(TEST_TIMEOUT))
+ .setInCallAdapter(any(IInCallAdapter.class));
+ verify(mInCallServiceFixtureY.getTestDouble(), timeout(TEST_TIMEOUT))
+ .setInCallAdapter(any(IInCallAdapter.class));
- // Give the InCallService time to respond
- assertTrueWithTimeout(new Predicate<Void>() {
- @Override
- public boolean apply(Void v) {
- return mInCallServiceFixtureX.mInCallAdapter != null;
- }
- });
+ // Give the InCallService time to respond
+ assertTrueWithTimeout(new Predicate<Void>() {
+ @Override
+ public boolean apply(Void v) {
+ return mInCallServiceFixtureX.mInCallAdapter != null;
+ }
+ });
- assertTrueWithTimeout(new Predicate<Void>() {
- @Override
- public boolean apply(Void v) {
- return mInCallServiceFixtureY.mInCallAdapter != null;
- }
- });
+ assertTrueWithTimeout(new Predicate<Void>() {
+ @Override
+ public boolean apply(Void v) {
+ return mInCallServiceFixtureY.mInCallAdapter != null;
+ }
+ });
- verify(mInCallServiceFixtureX.getTestDouble(), timeout(TEST_TIMEOUT))
- .addCall(any(ParcelableCall.class));
- verify(mInCallServiceFixtureY.getTestDouble(), timeout(TEST_TIMEOUT))
- .addCall(any(ParcelableCall.class));
+ verify(mInCallServiceFixtureX.getTestDouble(), timeout(TEST_TIMEOUT))
+ .addCall(any(ParcelableCall.class));
+ verify(mInCallServiceFixtureY.getTestDouble(), timeout(TEST_TIMEOUT))
+ .addCall(any(ParcelableCall.class));
- // Give the InCallService time to respond
+ // Give the InCallService time to respond
+ }
assertTrueWithTimeout(new Predicate<Void>() {
@Override
@@ -883,18 +879,11 @@
connectionServiceFixture.mConnectionById.size();
}
});
- assertTrueWithTimeout(new Predicate<Void>() {
- @Override
- public boolean apply(Void v) {
- return startingNumCalls + 1 == mInCallServiceFixtureX.mCallById.size();
- }
- });
- assertTrueWithTimeout(new Predicate<Void>() {
- @Override
- public boolean apply(Void v) {
- return startingNumCalls + 1 == mInCallServiceFixtureY.mCallById.size();
- }
- });
+
+ mInCallServiceFixtureX.waitUntilNumCalls(startingNumCalls + 1);
+ mInCallServiceFixtureY.waitUntilNumCalls(startingNumCalls + 1);
+ assertEquals(startingNumCalls + 1, mInCallServiceFixtureX.mCallById.size());
+ assertEquals(startingNumCalls + 1, mInCallServiceFixtureY.mCallById.size());
assertEquals(mInCallServiceFixtureX.mLatestCallId,
mInCallServiceFixtureY.mLatestCallId);