DSDA: Handle call resume failure
Handles the call resume failure across subscriptions. We will attempt to
auto-unhold the foreground call that was previously held as part of
the original operation to swap the foreground and background calls as it
is historically more likely to succeed.
Bug: 390116261
Flag: com.android.server.telecom.flags.enable_call_sequencing
Test: atest CallTest
Test: atest CallSequencingBasicCallTest
Change-Id: I72417cfa89d57fbfa9c384b6df939bf1dd700749
diff --git a/flags/telecom_call_flags.aconfig b/flags/telecom_call_flags.aconfig
index 8e15910..0000f32 100644
--- a/flags/telecom_call_flags.aconfig
+++ b/flags/telecom_call_flags.aconfig
@@ -77,3 +77,11 @@
purpose: PURPOSE_BUGFIX
}
}
+
+# OWNER=pmadapurmath TARGET=25Q4
+flag {
+ name: "call_sequencing_call_resume_failed"
+ namespace: "telecom"
+ description: "Connection event received when a call resume fails"
+ bug: "390116261"
+}
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index 40f3aa7..a54a3b6 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -205,6 +205,7 @@
default void onHoldToneRequested(Call call) {};
default void onCallHoldFailed(Call call) {};
default void onCallSwitchFailed(Call call) {};
+ default void onCallResumeFailed(Call call) {};
default void onConnectionEvent(Call call, String event, Bundle extras) {};
default void onCallStreamingStateChanged(Call call, boolean isStreaming) {}
default void onExternalCallChanged(Call call, boolean isExternalCall) {};
@@ -295,6 +296,8 @@
@Override
public void onCallSwitchFailed(Call call) {}
@Override
+ public void onCallResumeFailed(Call call) {}
+ @Override
public void onConnectionEvent(Call call, String event, Bundle extras) {}
@Override
public void onCallStreamingStateChanged(Call call, boolean isStreaming) {}
@@ -4545,6 +4548,10 @@
for (Listener l : mListeners) {
l.onCallSwitchFailed(this);
}
+ } else if (Connection.EVENT_CALL_RESUME_FAILED.equals(event)) {
+ for (Listener l : mListeners) {
+ l.onCallResumeFailed(this);
+ }
} else if (Connection.EVENT_DEVICE_TO_DEVICE_MESSAGE.equals(event)
&& extras != null && extras.containsKey(
Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_TYPE)
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 247fd0b..4d59fc8 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -1477,6 +1477,14 @@
markAllAnsweredCallAsRinging(call, "switch");
}
+ @Override
+ public void onCallResumeFailed(Call call) {
+ Call heldCall = getFirstCallWithState(call, true /* skipSelfManaged */, CallState.ON_HOLD);
+ if (heldCall != null) {
+ mCallSequencingAdapter.handleCallResumeFailed(call, heldCall);
+ }
+ }
+
private void markAllAnsweredCallAsRinging(Call call, String actionName) {
// Normally, we don't care whether a call hold or switch has failed.
// However, if a call was held or switched in order to answer an incoming call, that
diff --git a/src/com/android/server/telecom/callsequencing/CallsManagerCallSequencingAdapter.java b/src/com/android/server/telecom/callsequencing/CallsManagerCallSequencingAdapter.java
index f9ee8d7..611bb9e 100644
--- a/src/com/android/server/telecom/callsequencing/CallsManagerCallSequencingAdapter.java
+++ b/src/com/android/server/telecom/callsequencing/CallsManagerCallSequencingAdapter.java
@@ -364,6 +364,20 @@
});
}
+ /**
+ * Upon a call resume failure, we will auto-unhold the foreground call that was held. Note that
+ * this should only apply for calls across phone accounts as the ImsPhoneCallTracker handles
+ * this for a single phone.
+ * @param callResumeFailed The call that failed to resume.
+ * @param callToUnhold The fg call that was held.
+ */
+ public void handleCallResumeFailed(Call callResumeFailed, Call callToUnhold) {
+ if (mIsCallSequencingEnabled && !mSequencingController.arePhoneAccountsSame(
+ callResumeFailed, callToUnhold)) {
+ unholdCall(callToUnhold);
+ }
+ }
+
public Handler getHandler() {
return mHandler;
}
diff --git a/src/com/android/server/telecom/callsequencing/VerifyCallStateChangeTransaction.java b/src/com/android/server/telecom/callsequencing/VerifyCallStateChangeTransaction.java
index 7bebb55..b7e4f04 100644
--- a/src/com/android/server/telecom/callsequencing/VerifyCallStateChangeTransaction.java
+++ b/src/com/android/server/telecom/callsequencing/VerifyCallStateChangeTransaction.java
@@ -18,8 +18,10 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.telecom.Call;
+import com.android.server.telecom.CallState;
import com.android.server.telecom.TelecomSystem;
+import android.telecom.CallException;
import android.telecom.Log;
import java.util.Set;
@@ -56,6 +58,26 @@
}
};
+ private final Call.ListenerBase mCallListenerImpl = new Call.ListenerBase() {
+ @Override
+ public void onCallHoldFailed(Call call) {
+ if (call.equals(mCall) && mTargetCallStates.contains(CallState.ON_HOLD)) {
+ // Fail the transaction if a call hold failure is received.
+ mTransactionResult.complete(new CallTransactionResult(
+ CallException.CODE_CANNOT_HOLD_CURRENT_ACTIVE_CALL, "error holding call"));
+ }
+ }
+ @Override
+ public void onCallResumeFailed(Call call) {
+ if (call.equals(mCall) && mTargetCallStates.contains(CallState.ACTIVE)) {
+ // Fail the transaction if a call resume failure is received (this means that the
+ // current call could not be unheld).
+ mTransactionResult.complete(new CallTransactionResult(
+ CallException.CODE_CALL_CANNOT_BE_SET_TO_ACTIVE, "error unholding call"));
+ }
+ }
+ };
+
public VerifyCallStateChangeTransaction(TelecomSystem.SyncRoot lock, Call call,
int... targetCallStates) {
super(lock, CALL_STATE_TIMEOUT_MILLISECONDS);
@@ -73,12 +95,14 @@
return mTransactionResult;
}
mCall.addCallStateListener(mCallStateListenerImpl);
+ mCall.addListener(mCallListenerImpl);
return mTransactionResult;
}
@Override
public void finishTransaction() {
mCall.removeCallStateListener(mCallStateListenerImpl);
+ mCall.removeListener(mCallListenerImpl);
}
private boolean isNewCallStateTargetCallState() {
diff --git a/tests/src/com/android/server/telecom/tests/CallTest.java b/tests/src/com/android/server/telecom/tests/CallTest.java
index 3a7a822..b2cdd7d 100644
--- a/tests/src/com/android/server/telecom/tests/CallTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallTest.java
@@ -999,6 +999,7 @@
@Test
@SmallTest
public void testOnConnectionEventNotifiesListener() {
+ when(mFeatureFlags.enableCallSequencing()).thenReturn(true);
Call.Listener listener = mock(Call.Listener.class);
Call call = createCall("1");
call.addListener(listener);
@@ -1017,6 +1018,9 @@
call.onConnectionEvent(Connection.EVENT_CALL_SWITCH_FAILED, null);
verify(listener).onCallSwitchFailed(call);
+ call.onConnectionEvent(Connection.EVENT_CALL_RESUME_FAILED, null);
+ verify(listener).onCallResumeFailed(call);
+
final int d2dType = 1;
final int d2dValue = 2;
final Bundle d2dExtras = new Bundle();