Merge "[RCS UCE] The creation of UceController by TelephonyRcsService."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index a222705..2b2df11 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -24,7 +24,6 @@
<original-package android:name="com.android.phone" />
- <protected-broadcast android:name="android.telecom.action.TTY_PREFERRED_MODE_CHANGED" />
<protected-broadcast android:name="android.telecom.action.CURRENT_TTY_MODE_CHANGED" />
<protected-broadcast android:name="android.intent.action.SERVICE_STATE" />
<protected-broadcast android:name="android.intent.action.RADIO_TECHNOLOGY" />
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index a08893b..4ee4141 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -289,6 +289,10 @@
private static final int EVENT_GET_CALL_WAITING_DONE = 88;
private static final int CMD_SET_CALL_WAITING = 89;
private static final int EVENT_SET_CALL_WAITING_DONE = 90;
+ private static final int CMD_ENABLE_NR_DUAL_CONNECTIVITY = 91;
+ private static final int EVENT_ENABLE_NR_DUAL_CONNECTIVITY_DONE = 92;
+ private static final int CMD_IS_NR_DUAL_CONNECTIVITY_ENABLED = 93;
+ private static final int EVENT_IS_NR_DUAL_CONNECTIVITY_ENABLED_DONE = 94;
// Parameters of select command.
private static final int SELECT_COMMAND = 0xA4;
@@ -751,6 +755,90 @@
handleNullReturnEvent(msg, "resetModemConfig");
break;
+ case CMD_IS_NR_DUAL_CONNECTIVITY_ENABLED: {
+ request = (MainThreadRequest) msg.obj;
+ onCompleted = obtainMessage(EVENT_IS_NR_DUAL_CONNECTIVITY_ENABLED_DONE,
+ request);
+ Phone phone = getPhoneFromRequest(request);
+ if (phone != null) {
+ phone.isNrDualConnectivityEnabled(onCompleted, request.workSource);
+ } else {
+ loge("isNRDualConnectivityEnabled: No phone object");
+ request.result = false;
+ notifyRequester(request);
+ }
+ break;
+ }
+
+ case EVENT_IS_NR_DUAL_CONNECTIVITY_ENABLED_DONE:
+ ar = (AsyncResult) msg.obj;
+ request = (MainThreadRequest) ar.userObj;
+ if (ar.exception == null && ar.result != null) {
+ request.result = ar.result;
+ } else {
+ // request.result must be set to something non-null
+ // for the calling thread to unblock
+ if (request.result != null) {
+ request.result = ar.result;
+ } else {
+ request.result = false;
+ }
+ if (ar.result == null) {
+ loge("isNRDualConnectivityEnabled: Empty response");
+ } else if (ar.exception instanceof CommandException) {
+ loge("isNRDualConnectivityEnabled: CommandException: "
+ + ar.exception);
+ } else {
+ loge("isNRDualConnectivityEnabled: Unknown exception");
+ }
+ }
+ notifyRequester(request);
+ break;
+
+ case CMD_ENABLE_NR_DUAL_CONNECTIVITY: {
+ request = (MainThreadRequest) msg.obj;
+ onCompleted = obtainMessage(EVENT_ENABLE_NR_DUAL_CONNECTIVITY_DONE, request);
+ Phone phone = getPhoneFromRequest(request);
+ if (phone != null) {
+ phone.setNrDualConnectivityState((int) request.argument, onCompleted,
+ request.workSource);
+ } else {
+ loge("enableNrDualConnectivity: No phone object");
+ request.result =
+ TelephonyManager.ENABLE_NR_DUAL_CONNECTIVITY_RADIO_NOT_AVAILABLE;
+ notifyRequester(request);
+ }
+ break;
+ }
+
+ case EVENT_ENABLE_NR_DUAL_CONNECTIVITY_DONE: {
+ ar = (AsyncResult) msg.obj;
+ request = (MainThreadRequest) ar.userObj;
+ if (ar.exception == null) {
+ request.result =
+ TelephonyManager.ENABLE_NR_DUAL_CONNECTIVITY_SUCCESS;
+ } else {
+ request.result =
+ TelephonyManager
+ .ENABLE_NR_DUAL_CONNECTIVITY_RADIO_ERROR;
+ if (ar.exception instanceof CommandException) {
+ CommandException.Error error =
+ ((CommandException) (ar.exception)).getCommandError();
+ if (error == CommandException.Error.RADIO_NOT_AVAILABLE) {
+ request.result =
+ TelephonyManager
+ .ENABLE_NR_DUAL_CONNECTIVITY_RADIO_NOT_AVAILABLE;
+ }
+ loge("enableNrDualConnectivity" + ": CommandException: "
+ + ar.exception);
+ } else {
+ loge("enableNrDualConnectivity" + ": Unknown exception");
+ }
+ }
+ notifyRequester(request);
+ break;
+ }
+
case CMD_GET_PREFERRED_NETWORK_TYPE:
request = (MainThreadRequest) msg.obj;
onCompleted = obtainMessage(EVENT_GET_PREFERRED_NETWORK_TYPE_DONE, request);
@@ -5733,6 +5821,58 @@
}
/**
+ * Enable/Disable E-UTRA-NR Dual Connectivity
+ * @param subId subscription id of the sim card
+ * @param nrDualConnectivityState expected NR dual connectivity state
+ * This can be passed following states
+ * <ol>
+ * <li>Enable NR dual connectivity {@link TelephonyManager#NR_DUAL_CONNECTIVITY_ENABLE}
+ * <li>Disable NR dual connectivity {@link TelephonyManager#NR_DUAL_CONNECTIVITY_DISABLE}
+ * <li>Disable NR dual connectivity and force secondary cell to be released
+ * {@link TelephonyManager#NR_DUAL_CONNECTIVITY_DISABLE_IMMEDIATE}
+ * </ol>
+ * @return operation result.
+ */
+ @Override
+ public int setNrDualConnectivityState(int subId,
+ @TelephonyManager.NrDualConnectivityState int nrDualConnectivityState) {
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mApp, subId, "enableNRDualConnectivity");
+ WorkSource workSource = getWorkSource(Binder.getCallingUid());
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ int result = (int) sendRequest(CMD_ENABLE_NR_DUAL_CONNECTIVITY,
+ nrDualConnectivityState, subId,
+ workSource);
+ if (DBG) log("enableNRDualConnectivity result: " + result);
+ return result;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Is E-UTRA-NR Dual Connectivity enabled
+ * @return true if dual connectivity is enabled else false
+ */
+ @Override
+ public boolean isNrDualConnectivityEnabled(int subId) {
+ TelephonyPermissions
+ .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+ mApp, subId, "isNRDualConnectivityEnabled");
+ WorkSource workSource = getWorkSource(Binder.getCallingUid());
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ boolean isEnabled = (boolean) sendRequest(CMD_IS_NR_DUAL_CONNECTIVITY_ENABLED,
+ null, subId, workSource);
+ if (DBG) log("isNRDualConnectivityEnabled: " + isEnabled);
+ return isEnabled;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
* Get the effective allowed network types on the device.
* This API will return an intersection of allowed network types for all reasons,
* including the configuration done through setAllowedNetworkTypes
diff --git a/src/com/android/services/telephony/ImsConference.java b/src/com/android/services/telephony/ImsConference.java
index 4826ecb..c9f762b 100644
--- a/src/com/android/services/telephony/ImsConference.java
+++ b/src/com/android/services/telephony/ImsConference.java
@@ -16,6 +16,7 @@
package com.android.services.telephony;
+import android.annotation.NonNull;
import android.content.Context;
import android.graphics.drawable.Icon;
import android.net.Uri;
@@ -28,6 +29,7 @@
import android.telecom.TelecomManager;
import android.telecom.VideoProfile;
import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
import android.util.Pair;
import com.android.ims.internal.ConferenceParticipant;
@@ -1269,58 +1271,79 @@
}
/**
+ * Extracts a phone number from a {@link Uri}.
+ * <p>
+ * Phone numbers can be represented either as a TEL URI or a SIP URI.
+ * For conference event packages, RFC3261 specifies how participants can be identified using a
+ * SIP URI.
+ * A valid SIP uri has the format: sip:user:password@host:port;uri-parameters?headers
+ * Per RFC3261, the "user" can be a telephone number.
+ * For example: sip:1650555121;phone-context=blah.com@host.com
+ * In this case, the phone number is in the user field of the URI, and the parameters can be
+ * ignored.
+ *
+ * A SIP URI can also specify a phone number in a format similar to:
+ * sip:+1-212-555-1212@something.com;user=phone
+ * In this case, the phone number is again in user field and the parameters can be ignored.
+ * We can get the user field in these instances by splitting the string on the @, ;, or :
+ * and looking at the first found item.
+ * @param handle The URI containing a SIP or TEL formatted phone number.
+ * @return extracted phone number.
+ */
+ private static @NonNull String extractPhoneNumber(@NonNull Uri handle) {
+ // Number is always in the scheme specific part, regardless of whether this is a TEL or SIP
+ // URI.
+ String number = handle.getSchemeSpecificPart();
+ // Get anything before the @ for the SIP case.
+ String[] numberParts = number.split("[@;:]");
+
+ if (numberParts.length == 0) {
+ Log.v(LOG_TAG, "extractPhoneNumber(N) : no number in handle");
+ return "";
+ }
+ return numberParts[0];
+ }
+
+ /**
* Determines if the passed in participant handle is the same as the conference host's handle.
* Starts with a simple equality check. However, the handles from a conference event package
* will be a SIP uri, so we need to pull that apart to look for the participant's phone number.
*
- * @param hostHandles The handle(s) of the connection hosting the conference.
+ * @param hostHandles The handle(s) of the connection hosting the conference, typically obtained
+ * from P-Associated-Uri entries.
* @param handle The handle of the conference participant.
* @return {@code true} if the host's handle matches the participant's handle, {@code false}
* otherwise.
*/
- private boolean isParticipantHost(Uri[] hostHandles, Uri handle) {
+ @VisibleForTesting
+ public static boolean isParticipantHost(Uri[] hostHandles, Uri handle) {
// If there is no host handle or no participant handle, bail early.
if (hostHandles == null || hostHandles.length == 0 || handle == null) {
- Log.v(this, "isParticipantHost(N) : host or participant uri null");
+ Log.v(LOG_TAG, "isParticipantHost(N) : host or participant uri null");
return false;
}
- // Conference event package participants are identified using SIP URIs (see RFC3261).
- // A valid SIP uri has the format: sip:user:password@host:port;uri-parameters?headers
- // Per RFC3261, the "user" can be a telephone number.
- // For example: sip:1650555121;phone-context=blah.com@host.com
- // In this case, the phone number is in the user field of the URI, and the parameters can be
- // ignored.
- //
- // A SIP URI can also specify a phone number in a format similar to:
- // sip:+1-212-555-1212@something.com;user=phone
- // In this case, the phone number is again in user field and the parameters can be ignored.
- // We can get the user field in these instances by splitting the string on the @, ;, or :
- // and looking at the first found item.
-
- String number = handle.getSchemeSpecificPart();
- String numberParts[] = number.split("[@;:]");
-
- if (numberParts.length == 0) {
- Log.v(this, "isParticipantHost(N) : no number in participant handle");
+ String number = extractPhoneNumber(handle);
+ // If we couldn't extract the participant's number, then we can't determine if it is the
+ // host or not.
+ if (TextUtils.isEmpty(number)) {
return false;
}
- number = numberParts[0];
for (Uri hostHandle : hostHandles) {
if (hostHandle == null) {
continue;
}
- // The host number will be a tel: uri. Per RFC3966, the part after tel: is the phone
- // number.
- String hostNumber = hostHandle.getSchemeSpecificPart();
+ // Similar to the CEP participant data, the host identity in the P-Associated-Uri could
+ // be a SIP URI or a TEL URI.
+ String hostNumber = extractPhoneNumber(hostHandle);
// Use a loose comparison of the phone numbers. This ensures that numbers that differ
// by special characters are counted as equal.
// E.g. +16505551212 would be the same as 16505551212
boolean isHost = PhoneNumberUtils.compare(hostNumber, number);
- Log.v(this, "isParticipantHost(%s) : host: %s, participant %s", (isHost ? "Y" : "N"),
+ Log.v(LOG_TAG, "isParticipantHost(%s) : host: %s, participant %s", (isHost ? "Y" : "N"),
Rlog.pii(LOG_TAG, hostNumber), Rlog.pii(LOG_TAG, number));
if (isHost) {
diff --git a/tests/src/com/android/services/telephony/ImsConferenceTest.java b/tests/src/com/android/services/telephony/ImsConferenceTest.java
index f13d709..7e6488d 100644
--- a/tests/src/com/android/services/telephony/ImsConferenceTest.java
+++ b/tests/src/com/android/services/telephony/ImsConferenceTest.java
@@ -16,6 +16,8 @@
package com.android.services.telephony;
+import static junit.framework.Assert.assertTrue;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -573,4 +575,40 @@
assertEquals(0, imsConference.getNumberOfParticipants());
verify(mConferenceHost.mMockCall).hangup();
}
+
+ /**
+ * Verifies that an ImsConference can handle SIP and TEL URIs for both the P-Associated-Uri and
+ * conference event package identities.
+ */
+ @Test
+ public void testIsParticipantHost() {
+ // Simplest case, assume P-Associated-Uri is a tel URI and that the CEP participant is also
+ // a tel URI.
+ assertTrue(ImsConference.isParticipantHost(new Uri[] {
+ Uri.parse("tel:+8616505551234")},
+ Uri.parse("tel:+8616505551234")));
+
+ // Assume P-Associated-Uri is a tel URI and the CEP participant is a sip URI.
+ assertTrue(ImsConference.isParticipantHost(new Uri[] {
+ Uri.parse("tel:+8616505551234")},
+ Uri.parse("sip:+8616505551234@bj.ims.mnc011.mcc460.3gppnetwork.org")));
+
+ // Assume P-Associated-Uri is a sip URI and the CEP participant is a tel URI.
+ assertTrue(ImsConference.isParticipantHost(new Uri[] {
+ Uri.parse("sip:+8616505551234@bj.ims.mnc011.mcc460.3gppnetwork.org")},
+ Uri.parse("tel:+8616505551234")));
+
+ // Assume both P-Associated-Uri and the CEP participant are SIP URIs.
+ assertTrue(ImsConference.isParticipantHost(new Uri[] {
+ Uri.parse("sip:+8616505551234@bj.ims.mnc011.mcc460.3gppnetwork.org")},
+ Uri.parse("sip:+8616505551234@bj.ims.mnc011.mcc460.3gppnetwork.org")));
+
+ // Corner cases
+ assertFalse(ImsConference.isParticipantHost(new Uri[] {
+ Uri.parse("tel:+8616505551234")}, Uri.fromParts("", "", "")));
+ assertFalse(ImsConference.isParticipantHost(new Uri[] {
+ Uri.parse("tel:+8616505551234")}, null));
+ assertFalse(ImsConference.isParticipantHost(null, null));
+ assertFalse(ImsConference.isParticipantHost(new Uri[0], null));
+ }
}