A few conference calling fixes:
1. b/28325313 - When an SRVCC occurs, ensure that a phone account handle is set on
the GsmConnection which is added to Telecom. Also ensure that the
PROPERTY_CONFERENCE bit is set on the connection to ensure that the
InCall UI can display the correct label.
2. b/29059073 - Handle case where a video call is downgraded to an audio
only call, and thus should be able to be merged into a conference.
Bug: 29059073
Bug: 28325313
Change-Id: I56d3f89e4f07d8ad5edc5793fe6094e5887ff786
diff --git a/src/com/android/services/telephony/ImsConference.java b/src/com/android/services/telephony/ImsConference.java
index 69d57d7..62bbfe2 100644
--- a/src/com/android/services/telephony/ImsConference.java
+++ b/src/com/android/services/telephony/ImsConference.java
@@ -808,13 +808,19 @@
}
PhoneAccountHandle phoneAccountHandle = null;
- if (mConferenceHost.getPhone() != null &&
- mConferenceHost.getPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
- Phone imsPhone = mConferenceHost.getPhone();
- // The phone account handle for an ImsPhone is based on the default phone (ie the
- // base GSM or CDMA phone, not on the ImsPhone itself).
- phoneAccountHandle =
- PhoneUtils.makePstnPhoneAccountHandle(imsPhone.getDefaultPhone());
+ if (mConferenceHost.getPhone() != null) {
+ if (mConferenceHost.getPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
+ Phone imsPhone = mConferenceHost.getPhone();
+ // The phone account handle for an ImsPhone is based on the default phone (ie the
+ // base GSM or CDMA phone, not on the ImsPhone itself).
+ phoneAccountHandle =
+ PhoneUtils.makePstnPhoneAccountHandle(imsPhone.getDefaultPhone());
+ } else {
+ // In the case of SRVCC, we still need a phone account, so use the top level phone
+ // to create a phone account.
+ phoneAccountHandle = PhoneUtils.makePstnPhoneAccountHandle(
+ mConferenceHost.getPhone());
+ }
}
if (mConferenceHost.getPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) {
diff --git a/src/com/android/services/telephony/ImsConferenceController.java b/src/com/android/services/telephony/ImsConferenceController.java
index 0f9ae5d..a874674 100644
--- a/src/com/android/services/telephony/ImsConferenceController.java
+++ b/src/com/android/services/telephony/ImsConferenceController.java
@@ -78,6 +78,12 @@
Log.v(this, "onConferenceStarted");
recalculate();
}
+
+ @Override
+ public void onConferenceSupportedChanged(Connection c, boolean isConferenceSupported) {
+ Log.v(this, "onConferenceSupportedChanged");
+ recalculate();
+ }
};
/**
@@ -172,6 +178,7 @@
// If this connection does not support being in a conference call, then it is not
// conferenceable with any other connection.
if (!connection.isConferenceSupported()) {
+ connection.setConferenceableConnections(Collections.<Connection>emptyList());
continue;
}
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index 19b1d8a..b5b23b4 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -27,7 +27,6 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.PersistableBundle;
-import android.os.ServiceManager;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
@@ -40,7 +39,6 @@
import android.telephony.TelephonyManager;
import android.text.TextUtils;
-import com.android.internal.telephony.IPhoneSubInfo;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
import com.android.phone.PhoneGlobals;
@@ -50,6 +48,7 @@
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
+import java.util.Optional;
/**
* Owns all data we have registered with Telecom including handling dynamic addition and
@@ -72,6 +71,7 @@
private boolean mIsVideoPauseSupported;
private boolean mIsMergeCallSupported;
private boolean mIsVideoConferencingSupported;
+ private boolean mIsMergeOfWifiCallsAllowedWhenVoWifiOff;
AccountEntry(Phone phone, boolean isEmergency, boolean isDummy) {
mPhone = phone;
@@ -191,6 +191,8 @@
}
mIsMergeCallSupported = isCarrierMergeCallSupported();
mIsVideoConferencingSupported = isCarrierVideoConferencingSupported();
+ mIsMergeOfWifiCallsAllowedWhenVoWifiOff =
+ isCarrierMergeOfWifiCallsAllowedWhenVoWifiOff();
if (isEmergency && mContext.getResources().getBoolean(
R.bool.config_emergency_account_emergency_calls_only)) {
@@ -312,6 +314,20 @@
}
/**
+ * Determines from carrier config whether merging of wifi calls is allowed when VoWIFI is
+ * turned off.
+ *
+ * @return {@code true} merging of wifi calls when VoWIFI is disabled should be prevented,
+ * {@code false} otherwise.
+ */
+ private boolean isCarrierMergeOfWifiCallsAllowedWhenVoWifiOff() {
+ PersistableBundle b =
+ PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
+ return b != null && b.getBoolean(
+ CarrierConfigManager.KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL);
+ }
+
+ /**
* @return The {@linke PhoneAccount} extras associated with the current subscription.
*/
private Bundle getPhoneAccountExtras() {
@@ -366,6 +382,14 @@
public boolean isVideoConferencingSupported() {
return mIsVideoConferencingSupported;
}
+
+ /**
+ * Indicate whether this account allow merging of wifi calls when VoWIFI is off.
+ * @return {@code true} if allowed, {@code false} otherwise.
+ */
+ public boolean isMergeOfWifiCallsAllowedWhenVoWifiOff() {
+ return mIsMergeOfWifiCallsAllowedWhenVoWifiOff;
+ }
}
private OnSubscriptionsChangedListener mOnSubscriptionsChangedListener =
@@ -396,6 +420,7 @@
private final TelephonyManager mTelephonyManager;
private final SubscriptionManager mSubscriptionManager;
private List<AccountEntry> mAccounts = new LinkedList<AccountEntry>();
+ private Object mAccountsLock = new Object();
private int mServiceState = ServiceState.STATE_POWER_OFF;
// TODO: Remove back-pointer from app singleton to Service, since this is not a preferred
@@ -432,9 +457,11 @@
* @return {@code True} if video pausing is supported.
*/
boolean isVideoPauseSupported(PhoneAccountHandle handle) {
- for (AccountEntry entry : mAccounts) {
- if (entry.getPhoneAccountHandle().equals(handle)) {
- return entry.isVideoPauseSupported();
+ synchronized (mAccountsLock) {
+ for (AccountEntry entry : mAccounts) {
+ if (entry.getPhoneAccountHandle().equals(handle)) {
+ return entry.isVideoPauseSupported();
+ }
}
}
return false;
@@ -448,9 +475,11 @@
* @return {@code True} if merging calls is supported.
*/
boolean isMergeCallSupported(PhoneAccountHandle handle) {
- for (AccountEntry entry : mAccounts) {
- if (entry.getPhoneAccountHandle().equals(handle)) {
- return entry.isMergeCallSupported();
+ synchronized (mAccountsLock) {
+ for (AccountEntry entry : mAccounts) {
+ if (entry.getPhoneAccountHandle().equals(handle)) {
+ return entry.isMergeCallSupported();
+ }
}
}
return false;
@@ -464,15 +493,37 @@
* @return {@code True} if video conferencing is supported.
*/
boolean isVideoConferencingSupported(PhoneAccountHandle handle) {
- for (AccountEntry entry : mAccounts) {
- if (entry.getPhoneAccountHandle().equals(handle)) {
- return entry.isVideoConferencingSupported();
+ synchronized (mAccountsLock) {
+ for (AccountEntry entry : mAccounts) {
+ if (entry.getPhoneAccountHandle().equals(handle)) {
+ return entry.isVideoConferencingSupported();
+ }
}
}
return false;
}
/**
+ * Determines if the {@link AccountEntry} associated with a {@link PhoneAccountHandle} allows
+ * merging of wifi calls when VoWIFI is disabled.
+ *
+ * @param handle The {@link PhoneAccountHandle}.
+ * @return {@code True} if merging of wifi calls is allowed when VoWIFI is disabled.
+ */
+ boolean isMergeOfWifiCallsAllowedWhenVoWifiOff(final PhoneAccountHandle handle) {
+ synchronized (mAccountsLock) {
+ Optional<AccountEntry> result = mAccounts.stream().filter(
+ entry -> entry.getPhoneAccountHandle().equals(handle)).findFirst();
+
+ if (result.isPresent()) {
+ return result.get().isMergeOfWifiCallsAllowedWhenVoWifiOff();
+ } else {
+ return false;
+ }
+ }
+ }
+
+ /**
* @return Reference to the {@code TelecomAccountRegistry}'s subscription manager.
*/
SubscriptionManager getSubscriptionManager() {
@@ -486,9 +537,11 @@
* @return The address.
*/
Uri getAddress(PhoneAccountHandle handle) {
- for (AccountEntry entry : mAccounts) {
- if (entry.getPhoneAccountHandle().equals(handle)) {
- return entry.mAccount.getAddress();
+ synchronized (mAccountsLock) {
+ for (AccountEntry entry : mAccounts) {
+ if (entry.getPhoneAccountHandle().equals(handle)) {
+ return entry.mAccount.getAddress();
+ }
}
}
return null;
@@ -521,9 +574,11 @@
* @return {@code True} if an entry exists.
*/
boolean hasAccountEntryForPhoneAccount(PhoneAccountHandle handle) {
- for (AccountEntry entry : mAccounts) {
- if (entry.getPhoneAccountHandle().equals(handle)) {
- return true;
+ synchronized (mAccountsLock) {
+ for (AccountEntry entry : mAccounts) {
+ if (entry.getPhoneAccountHandle().equals(handle)) {
+ return true;
+ }
}
}
return false;
@@ -562,28 +617,31 @@
final boolean phoneAccountsEnabled = mContext.getResources().getBoolean(
R.bool.config_pstn_phone_accounts_enabled);
- if (phoneAccountsEnabled) {
- for (Phone phone : phones) {
- int subscriptionId = phone.getSubId();
- Log.d(this, "Phone with subscription id %d", subscriptionId);
- if (subscriptionId >= 0) {
- mAccounts.add(new AccountEntry(phone, false /* emergency */,
- false /* isDummy */));
+ synchronized (mAccountsLock) {
+ if (phoneAccountsEnabled) {
+ for (Phone phone : phones) {
+ int subscriptionId = phone.getSubId();
+ Log.d(this, "Phone with subscription id %d", subscriptionId);
+ if (subscriptionId >= 0) {
+ mAccounts.add(new AccountEntry(phone, false /* emergency */,
+ false /* isDummy */));
+ }
}
}
- }
- // If we did not list ANY accounts, we need to provide a "default" SIM account
- // for emergency numbers since no actual SIM is needed for dialing emergency
- // numbers but a phone account is.
- if (mAccounts.isEmpty()) {
- mAccounts.add(new AccountEntry(PhoneFactory.getDefaultPhone(), true /* emergency */,
- false /* isDummy */));
- }
+ // If we did not list ANY accounts, we need to provide a "default" SIM account
+ // for emergency numbers since no actual SIM is needed for dialing emergency
+ // numbers but a phone account is.
+ if (mAccounts.isEmpty()) {
+ mAccounts.add(new AccountEntry(PhoneFactory.getDefaultPhone(), true /* emergency */,
+ false /* isDummy */));
+ }
- // Add a fake account entry.
- if (DBG && phones.length > 0 && "TRUE".equals(System.getProperty("dummy_sim"))) {
- mAccounts.add(new AccountEntry(phones[0], false /* emergency */, true /* isDummy */));
+ // Add a fake account entry.
+ if (DBG && phones.length > 0 && "TRUE".equals(System.getProperty("dummy_sim"))) {
+ mAccounts.add(new AccountEntry(phones[0], false /* emergency */,
+ true /* isDummy */));
+ }
}
// Clean up any PhoneAccounts that are no longer relevant
@@ -615,9 +673,11 @@
}
private void tearDownAccounts() {
- for (AccountEntry entry : mAccounts) {
- entry.teardown();
+ synchronized (mAccountsLock) {
+ for (AccountEntry entry : mAccounts) {
+ entry.teardown();
+ }
+ mAccounts.clear();
}
- mAccounts.clear();
}
}
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index c0917e5..eb05e48 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -27,8 +27,10 @@
import android.telecom.ConferenceParticipant;
import android.telecom.Connection;
import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
import android.telecom.StatusHints;
import android.telecom.TelecomManager;
+import android.telecom.VideoProfile;
import android.telephony.PhoneNumberUtils;
import android.util.Pair;
@@ -41,6 +43,8 @@
import com.android.internal.telephony.gsm.SuppServiceNotification;
import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.imsphone.ImsPhone;
+import com.android.phone.PhoneUtils;
import com.android.phone.R;
import java.lang.Override;
@@ -158,6 +162,10 @@
case MSG_SET_VIDEO_STATE:
int videoState = (int) msg.obj;
setVideoState(videoState);
+
+ // A change to the video state of the call can influence whether or not it
+ // can be part of a conference.
+ refreshConferenceSupported();
break;
case MSG_SET_VIDEO_PROVIDER:
@@ -709,6 +717,10 @@
if (PhoneNumberUtils.isEmergencyNumber(mOriginalConnection.getAddress())) {
mTreatAsEmergencyCall = true;
}
+
+ // Changing the address of the connection can change whether it is an emergency call or
+ // not, which can impact whether it can be part of a conference.
+ refreshConferenceSupported();
}
}
@@ -1420,6 +1432,62 @@
}
/**
+ * Determines whether the connection supports conference calling. A connection supports
+ * conference calling if it:
+ * 1. Is not an emergency call.
+ * 2. Carrier supports conference calls.
+ * 3. If call is a video call, carrier supports video conference calls.
+ * 4. If call is a wifi call and VoWIFI is disabled and carrier supports merging these calls.
+ */
+ private void refreshConferenceSupported() {
+ boolean isVideoCall = VideoProfile.isVideo(getVideoState());
+ Phone phone = getPhone();
+ boolean isIms = phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS;
+ boolean isVoWifiEnabled = false;
+ if (isIms) {
+ ImsPhone imsPhone = (ImsPhone) phone;
+ isVoWifiEnabled = imsPhone.isWifiCallingEnabled();
+ }
+ PhoneAccountHandle phoneAccountHandle = isIms ? PhoneUtils
+ .makePstnPhoneAccountHandle(phone.getDefaultPhone())
+ : PhoneUtils.makePstnPhoneAccountHandle(phone);
+ TelecomAccountRegistry telecomAccountRegistry = TelecomAccountRegistry
+ .getInstance(getPhone().getContext());
+ boolean isConferencingSupported = telecomAccountRegistry
+ .isMergeCallSupported(phoneAccountHandle);
+ boolean isVideoConferencingSupported = telecomAccountRegistry
+ .isVideoConferencingSupported(phoneAccountHandle);
+ boolean isMergeOfWifiCallsAllowedWhenVoWifiOff = telecomAccountRegistry
+ .isMergeOfWifiCallsAllowedWhenVoWifiOff(phoneAccountHandle);
+
+ Log.v(this, "refreshConferenceSupported : isConfSupp=%b, isVidConfSupp=%b, " +
+ "isMergeOfWifiAllowed=%b, isWifi=%b, isVoWifiEnabled=%b", isConferencingSupported,
+ isVideoConferencingSupported, isMergeOfWifiCallsAllowedWhenVoWifiOff, isWifi(),
+ isVoWifiEnabled);
+ boolean isConferenceSupported = true;
+ if (mTreatAsEmergencyCall) {
+ isConferenceSupported = false;
+ Log.d(this, "refreshConferenceSupported = false; emergency call");
+ } else if (!isConferencingSupported) {
+ isConferenceSupported = false;
+ Log.d(this, "refreshConferenceSupported = false; carrier doesn't support conf.");
+ } else if (isVideoCall && !isVideoConferencingSupported) {
+ isConferenceSupported = false;
+ Log.d(this, "refreshConferenceSupported = false; video conf not supported.");
+ } else if (!isMergeOfWifiCallsAllowedWhenVoWifiOff && isWifi() && !isVoWifiEnabled) {
+ isConferenceSupported = false;
+ Log.d(this,
+ "refreshConferenceSupported = false; can't merge wifi calls when voWifi off.");
+ } else {
+ Log.d(this, "refreshConferenceSupported = true.");
+ }
+
+ if (isConferenceSupported != isConferenceSupported()) {
+ setConferenceSupported(isConferenceSupported);
+ notifyConferenceSupportedChanged(isConferenceSupported);
+ }
+ }
+ /**
* Provides a mapping from extras keys which may be found in the
* {@link com.android.internal.telephony.Connection} to their equivalents defined in
* {@link android.telecom.Connection}.
@@ -1472,6 +1540,8 @@
} else {
sb.append("Y");
}
+ sb.append(" confSupported:");
+ sb.append(mIsConferenceSupported ? "Y" : "N");
sb.append("]");
return sb.toString();
}
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 9f252a2..31d0475 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -623,15 +623,6 @@
returnConnection.setVideoPauseSupported(
TelecomAccountRegistry.getInstance(this).isVideoPauseSupported(
phoneAccountHandle));
- boolean isEmergencyCall = (address != null && PhoneNumberUtils.isEmergencyNumber(
- address.getSchemeSpecificPart()));
- boolean isVideoCall = VideoProfile.isVideo(videoState);
- boolean isConferencingSupported = TelecomAccountRegistry.getInstance(this)
- .isMergeCallSupported(phoneAccountHandle);
- boolean isVideoConferencingSupported = TelecomAccountRegistry.getInstance(this)
- .isVideoConferencingSupported(phoneAccountHandle);
- returnConnection.setConferenceSupported(!isEmergencyCall && isConferencingSupported
- && (!isVideoCall || (isVideoCall && isVideoConferencingSupported)));
}
return returnConnection;
}