IMS RCS API Improvements-SipDelegate am: 56980ed57e am: 92c4160245
Original change: https://android-review.googlesource.com/c/platform/packages/services/Telephony/+/1899050
Change-Id: Ib07cd3136e2e2c3d5e8611a312dcff699f736e5b
diff --git a/OWNERS b/OWNERS
index c394f15..b4ef543 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,3 +1,4 @@
+amitmahajan@google.com
breadley@google.com
fionaxu@google.com
jackyu@google.com
diff --git a/src/com/android/services/telephony/ImsConference.java b/src/com/android/services/telephony/ImsConference.java
index 0be927a..7f088f7 100644
--- a/src/com/android/services/telephony/ImsConference.java
+++ b/src/com/android/services/telephony/ImsConference.java
@@ -257,7 +257,7 @@
@Override
public void onConnectionPropertiesChanged(Connection c, int connectionProperties) {
- Log.d(this, "onConnectionPropertiesChanged: Connection: %s,"
+ Log.i(ImsConference.this, "onConnectionPropertiesChanged: Connection: %s,"
+ " connectionProperties: %s", c, connectionProperties);
updateConnectionProperties(connectionProperties);
}
@@ -383,6 +383,11 @@
private boolean mIsUsingSimCallManager = false;
/**
+ * See {@link #isRemotelyHosted()} for details.
+ */
+ private boolean mWasRemotelyHosted = false;
+
+ /**
* Where {@link #isMultiparty()} is {@code false}, contains the
* {@link ConferenceParticipantConnection#getUserEntity()} and
* {@link ConferenceParticipantConnection#getEndpoint()} of the single participant which this
@@ -522,11 +527,12 @@
(properties & Connection.PROPERTY_IS_EXTERNAL_CALL) != 0);
conferenceProperties = changeBitmask(conferenceProperties,
- Connection.PROPERTY_REMOTELY_HOSTED, !isConferenceHost());
+ Connection.PROPERTY_REMOTELY_HOSTED, isRemotelyHosted());
conferenceProperties = changeBitmask(conferenceProperties,
Connection.PROPERTY_IS_ADHOC_CONFERENCE,
(properties & Connection.PROPERTY_IS_ADHOC_CONFERENCE) != 0);
+ Log.i(this, "applyHostProperties: confProp=%s", conferenceProperties);
return conferenceProperties;
}
@@ -774,6 +780,26 @@
}
/**
+ * Returns whether the conference is remotely hosted or not.
+ * This method will cache the current remotely hosted state when the conference host or
+ * original connection becomes null. This is important for scenarios where the conference host
+ * or original connection changes midway through a conference such as in an SRVCC scenario.
+ * @return {@code true} if the conference was remotely hosted based on the conference host and
+ * its original connection, or based on the last known remotely hosted state. {@code false}
+ * otherwise.
+ */
+ public boolean isRemotelyHosted() {
+ if (mConferenceHost == null || mConferenceHost.getOriginalConnection() == null) {
+ return mWasRemotelyHosted;
+ }
+ com.android.internal.telephony.Connection originalConnection =
+ mConferenceHost.getOriginalConnection();
+ mWasRemotelyHosted = originalConnection.isMultiparty()
+ && !originalConnection.isConferenceHost();
+ return mWasRemotelyHosted;
+ }
+
+ /**
* Determines if this conference is hosted on the current device or the peer device.
*
* @return {@code true} if this conference is hosted on the current device, {@code false} if it
@@ -1209,6 +1235,7 @@
ConferenceParticipantConnection connection = new ConferenceParticipantConnection(
parent.getOriginalConnection(), participant,
!isConferenceHost() /* isRemotelyHosted */);
+
if (participant.getConnectTime() == 0) {
connection.setConnectTimeMillis(parent.getConnectTimeMillis());
connection.setConnectionStartElapsedRealtimeMillis(
diff --git a/src/com/android/services/telephony/TelephonyConferenceController.java b/src/com/android/services/telephony/TelephonyConferenceController.java
index 228541a..9aa3dbe 100644
--- a/src/com/android/services/telephony/TelephonyConferenceController.java
+++ b/src/com/android/services/telephony/TelephonyConferenceController.java
@@ -40,6 +40,7 @@
*/
final class TelephonyConferenceController {
private static final int TELEPHONY_CONFERENCE_MAX_SIZE = 5;
+ private static final String RIL_REPORTED_CONFERENCE_CALL_STRING = "Conference Call";
private final TelephonyConnection.TelephonyConnectionListener mTelephonyConnectionListener =
new TelephonyConnection.TelephonyConnectionListener() {
@@ -179,7 +180,6 @@
private void recalculateConference() {
Set<TelephonyConnection> conferencedConnections = new HashSet<>();
int numGsmConnections = 0;
-
for (TelephonyConnection connection : mTelephonyConnections) {
com.android.internal.telephony.Connection radioConnection =
connection.getOriginalConnection();
@@ -271,11 +271,19 @@
// Remove all instances of PROPERTY_IS_DOWNGRADED_CONFERENCE. This
// property should only be set on the parent call (i.e. the newly
// created TelephonyConference.
- Log.d(this, "Removing PROPERTY_IS_DOWNGRADED_CONFERENCE from connection"
- + " %s", connection);
- int newProperties = connection.getConnectionProperties()
- & ~Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE;
- connection.setTelephonyConnectionProperties(newProperties);
+ // This doesn't apply to a connection whose address is "Conference
+ // Call", which may be updated by some modem to create a connection
+ // to represent a merged conference connection in SRVCC.
+ if (connection.getAddress() == null
+ || !connection.getAddress().getSchemeSpecificPart()
+ .equalsIgnoreCase(
+ RIL_REPORTED_CONFERENCE_CALL_STRING)) {
+ Log.d(this, "Removing PROPERTY_IS_DOWNGRADED_CONFERENCE"
+ + " from connection %s", connection);
+ int newProperties = connection.getConnectionProperties()
+ & ~Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE;
+ connection.setTelephonyConnectionProperties(newProperties);
+ }
isDowngradedConference = true;
}
mTelephonyConference.addTelephonyConnection(connection);
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index 18a40cf..ed07726 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -3814,4 +3814,13 @@
mCommunicator.sendMessages(set);
}
}
+
+ /**
+ * Returns the current telephony connection listeners for test purposes.
+ * @return list of telephony connection listeners.
+ */
+ @VisibleForTesting
+ public List<TelephonyConnectionListener> getTelephonyConnectionListeners() {
+ return new ArrayList<>(mTelephonyListeners);
+ }
}
diff --git a/tests/src/com/android/services/telephony/ImsConferenceTest.java b/tests/src/com/android/services/telephony/ImsConferenceTest.java
index 3bc5ee8..9d2f5ac 100644
--- a/tests/src/com/android/services/telephony/ImsConferenceTest.java
+++ b/tests/src/com/android/services/telephony/ImsConferenceTest.java
@@ -622,6 +622,46 @@
}
/**
+ * Tests a scenario where a handover connection arrives via
+ * {@link TelephonyConnection#onOriginalConnectionRedialed(
+ * com.android.internal.telephony.Connection)}. During this process, the conference properties
+ * get updated. Since the original connection is null at this point, we need to verify that
+ * the remotely hosted property is retained from before the original connection was nulled.
+ */
+ @Test
+ public void testIsConferenceRemotelyHostedCachingOnSRVCC() {
+ mConferenceHost.setIsImsConnection(true);
+ when(mConferenceHost.getMockImsPhoneConnection().isMultiparty()).thenReturn(true);
+ when(mConferenceHost.getMockImsPhoneConnection().isConferenceHost()).thenReturn(true);
+
+ // Start out with a valid conference host.
+ ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
+ mMockTelephonyConnectionServiceProxy, mConferenceHost,
+ null /* phoneAccountHandle */, () -> false /* featureFlagProxy */,
+ new ImsConference.CarrierConfiguration.Builder().build());
+
+ // By default it is not remotely hosted.
+ assertFalse(imsConference.isRemotelyHosted());
+ assertEquals(0,
+ imsConference.getConnectionProperties() & Connection.PROPERTY_REMOTELY_HOSTED);
+
+ // Simulate a change to the original connection due to srvcc
+ com.android.internal.telephony.Connection previousOriginalConnection =
+ mConferenceHost.getMockImsPhoneConnection();
+ mConferenceHost.setMockImsPhoneConnection(null);
+
+ // Trigger the property update which takes place when the original connection changes.
+ mConferenceHost.getTelephonyConnectionListeners().forEach(
+ l -> l.onConnectionPropertiesChanged(mConferenceHost,
+ mConferenceHost.getConnectionProperties()));
+
+ // Should still NOT be remotely hosted based on cached value.
+ assertFalse(imsConference.isRemotelyHosted());
+ assertEquals(0,
+ imsConference.getConnectionProperties() & Connection.PROPERTY_REMOTELY_HOSTED);
+ }
+
+ /**
* Verifies that an ImsConference can handle SIP and TEL URIs for both the P-Associated-Uri and
* conference event package identities.
*/
diff --git a/tests/src/com/android/services/telephony/TelephonyConferenceControllerTest.java b/tests/src/com/android/services/telephony/TelephonyConferenceControllerTest.java
index cfdc2fd..b7fe988 100644
--- a/tests/src/com/android/services/telephony/TelephonyConferenceControllerTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConferenceControllerTest.java
@@ -23,9 +23,11 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.net.Uri;
import android.os.Looper;
import android.telecom.Conference;
import android.telecom.Connection;
+import android.telecom.PhoneAccount;
import android.test.suitebuilder.annotation.SmallTest;
import org.junit.Before;
@@ -35,7 +37,9 @@
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.List;
/**
* Tests the functionality in TelephonyConferenceController.java
@@ -111,6 +115,46 @@
}
/**
+ * Verify the connection with "Conference Call" with PROPERTY_IS_DOWNGRADED_CONFERENCE
+ * during SRVCC
+ */
+ @Test
+ @SmallTest
+ public void testSrvccConferenceConnection() {
+ when(mTestTelephonyConnectionA.mMockRadioConnection.getCall()
+ .isMultiparty()).thenReturn(true);
+ when(mTestTelephonyConnectionB.mMockRadioConnection.getCall()
+ .isMultiparty()).thenReturn(true);
+
+ List<Connection> listConnections = Arrays.asList(
+ mTestTelephonyConnectionA, mTestTelephonyConnectionB);
+ when(mMockTelephonyConnectionServiceProxy.getAllConnections()).thenReturn(listConnections);
+
+ mTestTelephonyConnectionA.setAddress(
+ Uri.fromParts(PhoneAccount.SCHEME_TEL, "Conference Call", null), 0);
+ mTestTelephonyConnectionA.setTelephonyConnectionProperties(
+ Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE);
+ mTestTelephonyConnectionB.setAddress(
+ Uri.fromParts(PhoneAccount.SCHEME_TEL, "5551213", null), 0);
+ mTestTelephonyConnectionB.setTelephonyConnectionProperties(
+ Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE);
+
+ // add telephony connection B
+ mControllerTest.add(mTestTelephonyConnectionB);
+
+ // add telephony connection A
+ mControllerTest.add(mTestTelephonyConnectionA);
+
+ // verify the connection with "Conference Call" has PROPERTY_IS_DOWNGRADED_CONFERENCE
+ assertTrue((mTestTelephonyConnectionA.getConnectionProperties()
+ & Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE) != 0);
+
+ // verify the connection with "5551213" hasn't PROPERTY_IS_DOWNGRADED_CONFERENCE
+ assertFalse((mTestTelephonyConnectionB.getConnectionProperties()
+ & Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE) != 0);
+ }
+
+ /**
* Behavior: add telephony connection B and A to conference controller,
* set status for connections and merged calls, remove one call
* Assumption: after performing the behaviours, the status of Connection A is STATE_ACTIVE;
diff --git a/tests/src/com/android/services/telephony/TestTelephonyConnection.java b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
index 817e9a7..e149d3b 100644
--- a/tests/src/com/android/services/telephony/TestTelephonyConnection.java
+++ b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
@@ -305,4 +305,12 @@
public PersistableBundle getCarrierConfigBundle() {
return mCarrierConfig;
}
+
+ public ImsPhoneConnection getMockImsPhoneConnection() {
+ return mImsPhoneConnection;
+ }
+
+ public void setMockImsPhoneConnection(ImsPhoneConnection connection) {
+ mImsPhoneConnection = connection;
+ }
}