Ims: Add support for Adhoc Conference calls
Add support for Adhoc Conference calls
Test: Manual
Bug: 62151032
Change-Id: Ib8c0ed191503468f335e3edcea5e886362bb7909
diff --git a/src/com/android/services/telephony/CdmaConference.java b/src/com/android/services/telephony/CdmaConference.java
old mode 100755
new mode 100644
index 693fd16..32badd0
--- a/src/com/android/services/telephony/CdmaConference.java
+++ b/src/com/android/services/telephony/CdmaConference.java
@@ -73,6 +73,16 @@
}
@Override
+ public void onAnswer(int videoState) {
+ Log.e(this, new Exception(), "Answer not supported for CDMA conference call.");
+ }
+
+ @Override
+ public void onReject() {
+ Log.e(this, new Exception(), "Reject not supported for CDMA conference call.");
+ }
+
+ @Override
public void onHold() {
Log.e(this, new Exception(), "Hold not supported for CDMA conference call.");
}
diff --git a/src/com/android/services/telephony/ImsConference.java b/src/com/android/services/telephony/ImsConference.java
index 4c3f9c9..4649d23 100644
--- a/src/com/android/services/telephony/ImsConference.java
+++ b/src/com/android/services/telephony/ImsConference.java
@@ -199,6 +199,18 @@
TelephonyConnection telephonyConnection = (TelephonyConnection) c;
handleConferenceParticipantsUpdate(telephonyConnection, participants);
}
+
+ /**
+ * Handles request to play a ringback tone.
+ *
+ * @param c The connection.
+ * @param ringback Whether the ringback tone is to be played.
+ */
+ @Override
+ public void onRingbackRequested(android.telecom.Connection c, boolean ringback) {
+ Log.d(this, "onRingbackRequested ringback %s", ringback ? "Y" : "N");
+ setRingbackRequested(ringback);
+ }
};
/**
@@ -224,6 +236,11 @@
private TelecomAccountRegistry mTelecomAccountRegistry;
/**
+ * The participant with which Adhoc Conference call is getting formed.
+ */
+ private List<Uri> mParticipants;
+
+ /**
* The known conference participant connections. The HashMap is keyed by a Pair containing
* the handle and endpoint Uris.
* Access to the hashmap is protected by the {@link #mUpdateSyncRoot}.
@@ -383,6 +400,10 @@
conferenceProperties = changeBitmask(conferenceProperties,
Connection.PROPERTY_REMOTELY_HOSTED, !isConferenceHost());
+ conferenceProperties = changeBitmask(conferenceProperties,
+ Connection.PROPERTY_IS_ADHOC_CONFERENCE,
+ (properties & Connection.PROPERTY_IS_ADHOC_CONFERENCE) != 0);
+
return conferenceProperties;
}
@@ -422,6 +443,26 @@
return VideoProfile.STATE_AUDIO_ONLY;
}
+ public Connection getConferenceHost() {
+ return mConferenceHost;
+ }
+
+ /**
+ * @return The address's to which this Connection is currently communicating.
+ */
+ public final List<Uri> getParticipants() {
+ return mParticipants;
+ }
+
+ /**
+ * Sets the value of the {@link #getParticipants()}.
+ *
+ * @param address The new address's.
+ */
+ public final void setParticipants(List<Uri> address) {
+ mParticipants = address;
+ }
+
/**
* Invoked when the Conference and all its {@link Connection}s should be disconnected.
* <p>
@@ -481,6 +522,28 @@
}
/**
+ * Invoked when the conference is answered.
+ */
+ @Override
+ public void onAnswer(int videoState) {
+ if (mConferenceHost == null) {
+ return;
+ }
+ mConferenceHost.performAnswer(videoState);
+ }
+
+ /**
+ * Invoked when the conference is rejected.
+ */
+ @Override
+ public void onReject() {
+ if (mConferenceHost == null) {
+ return;
+ }
+ mConferenceHost.performReject();
+ }
+
+ /**
* Invoked when the conference should be put on hold.
*/
@Override
@@ -838,7 +901,8 @@
+ "newParticipantcount=%d", oldParticipantCount, newParticipantCount);
// If the single party call emulation fature flag is enabled, we can potentially treat
// the conference as a single party call when there is just one participant.
- if (mFeatureFlagProxy.isUsingSinglePartyCallEmulation()) {
+ if (mFeatureFlagProxy.isUsingSinglePartyCallEmulation() &&
+ !mConferenceHost.isAdhocConferenceCall()) {
if (oldParticipantCount != 1 && newParticipantCount == 1) {
// If number of participants goes to 1, emulate a single party call.
startEmulatingSinglePartyCall();
@@ -1173,11 +1237,13 @@
switch (state) {
case Connection.STATE_INITIALIZING:
case Connection.STATE_NEW:
- case Connection.STATE_RINGING:
// No-op -- not applicable.
break;
+ case Connection.STATE_RINGING:
+ setConferenceOnRinging();
+ break;
case Connection.STATE_DIALING:
- setDialing();
+ setConferenceOnDialing();
break;
case Connection.STATE_DISCONNECTED:
DisconnectCause disconnectCause;
@@ -1198,10 +1264,10 @@
destroyTelephonyConference();
break;
case Connection.STATE_ACTIVE:
- setActive();
+ setConferenceOnActive();
break;
case Connection.STATE_HOLDING:
- setOnHold();
+ setConferenceOnHold();
break;
}
}
diff --git a/src/com/android/services/telephony/ImsConferenceController.java b/src/com/android/services/telephony/ImsConferenceController.java
index 1e10055..98be86e 100644
--- a/src/com/android/services/telephony/ImsConferenceController.java
+++ b/src/com/android/services/telephony/ImsConferenceController.java
@@ -52,8 +52,19 @@
Log.v(ImsConferenceController.class, "onDestroyed: %s", conference);
}
+ if (conference instanceof ImsConference) {
+ // Ims Conference call ended, so UE may now have the ability to initiate
+ // an Adhoc Conference call. Hence, try enabling adhoc conference capability
+ mTelecomAccountRegistry.refreshAdhocConference(true);
+ }
mImsConferences.remove(conference);
}
+
+ @Override
+ public void onStateChanged(Conference conference, int oldState, int newState) {
+ Log.v(this, "onStateChanged: Conference = " + conference);
+ recalculateConferenceable();
+ }
};
private final TelephonyConnection.TelephonyConnectionListener mTelephonyConnectionListener =
@@ -101,10 +112,11 @@
private final ArrayList<TelephonyConnection> mTelephonyConnections = new ArrayList<>();
/**
- * List of known {@link ImsConference}s. Realistically there will only ever be a single
- * concurrent IMS conference.
+ * List of known {@link ImsConference}s. There can be upto maximum two Ims conference calls.
+ * One conference call can be a host conference call and another conference call formed as a
+ * result of accepting incoming conference call.
*/
- private final ArrayList<ImsConference> mImsConferences = new ArrayList<>(1);
+ private final ArrayList<ImsConference> mImsConferences = new ArrayList<>(2);
private TelecomAccountRegistry mTelecomAccountRegistry;
@@ -122,6 +134,17 @@
mFeatureFlagProxy = featureFlagProxy;
}
+ void addConference(ImsConference conference) {
+ if (mImsConferences.contains(conference)) {
+ // Adding a duplicate realistically shouldn't happen.
+ Log.w(this, "addConference - conference already tracked; conference=%s", conference);
+ return;
+ }
+ mImsConferences.add(conference);
+ conference.addTelephonyConferenceListener(mConferenceListener);
+ recalculateConferenceable();
+ }
+
/**
* Adds a new connection to the IMS conference controller.
*
@@ -250,6 +273,11 @@
continue;
}
+ // Since UE cannot host two conference calls, remove the ability to initiate
+ // another conference call as there already exists a conference call, which
+ // is hosted on this device.
+ mTelecomAccountRegistry.refreshAdhocConference(false);
+
switch (conference.getState()) {
case Connection.STATE_ACTIVE:
//fall through
@@ -360,6 +388,10 @@
Log.v(this, "Start new ImsConference - connection: %s", connection);
}
+ if (connection.isAdhocConferenceCall()) {
+ Log.w(this, "start new ImsConference - control should never come here");
+ return;
+ }
// Make a clone of the connection which will become the Ims conference host connection.
// This is necessary since the Connection Service does not support removing a connection
// from Telecom. Instead we create a new instance and remove the old one from telecom.
diff --git a/src/com/android/services/telephony/PstnIncomingCallNotifier.java b/src/com/android/services/telephony/PstnIncomingCallNotifier.java
index 2dfeaed..46f15b2 100644
--- a/src/com/android/services/telephony/PstnIncomingCallNotifier.java
+++ b/src/com/android/services/telephony/PstnIncomingCallNotifier.java
@@ -288,7 +288,11 @@
}
} else {
TelecomManager tm = mPhone.getContext().getSystemService(TelecomManager.class);
- tm.addNewIncomingCall(handle, extras);
+ if (connection.isMultiparty()) {
+ tm.addNewIncomingConference(handle, extras);
+ } else {
+ tm.addNewIncomingCall(handle, extras);
+ }
}
}
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index 5a1e6a6..69a00c5 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -49,6 +49,8 @@
import android.telephony.TelephonyManager;
import android.telephony.ims.ImsException;
import android.telephony.ims.ImsMmTelManager;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.RegistrationManager;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.text.TextUtils;
@@ -86,9 +88,11 @@
private final PstnPhoneCapabilitiesNotifier mPhoneCapabilitiesNotifier;
private boolean mIsEmergency;
private boolean mIsRttCapable;
+ private boolean mIsAdhocConfCapable;
private boolean mIsEmergencyPreferred;
private MmTelFeature.MmTelCapabilities mMmTelCapabilities;
private ImsMmTelManager.CapabilityCallback mMmtelCapabilityCallback;
+ private RegistrationManager.RegistrationCallback mImsRegistrationCallback;
private ImsMmTelManager mMmTelManager;
private final boolean mIsDummy;
private boolean mIsVideoCapable;
@@ -106,6 +110,7 @@
mPhone = phone;
mIsEmergency = isEmergency;
mIsDummy = isDummy;
+ mIsAdhocConfCapable = mPhone.isImsRegistered();
mAccount = registerPstnPhoneAccount(isEmergency, isDummy);
Log.i(this, "Registered phoneAccount: %s with handle: %s",
mAccount, mAccount.getAccountHandle());
@@ -138,15 +143,38 @@
updateRttCapability();
}
};
-
registerMmTelCapabilityCallback();
+
+ mImsRegistrationCallback = new RegistrationManager.RegistrationCallback() {
+ @Override
+ public void onRegistered(int imsRadioTech) {
+ updateAdhocConfCapability(true);
+ }
+
+ @Override
+ public void onRegistering(int imsRadioTech) {
+ updateAdhocConfCapability(false);
+ }
+
+ @Override
+ public void onUnregistered(ImsReasonInfo imsReasonInfo) {
+ updateAdhocConfCapability(false);
+ }
+ };
+ registerImsRegistrationCallback();
}
void teardown() {
mIncomingCallNotifier.teardown();
mPhoneCapabilitiesNotifier.teardown();
- if (mMmTelManager != null && mMmtelCapabilityCallback != null) {
- mMmTelManager.unregisterMmTelCapabilityCallback(mMmtelCapabilityCallback);
+ if (mMmTelManager != null) {
+ if (mMmtelCapabilityCallback != null) {
+ mMmTelManager.unregisterMmTelCapabilityCallback(mMmtelCapabilityCallback);
+ }
+
+ if (mImsRegistrationCallback != null) {
+ mMmTelManager.unregisterImsRegistrationCallback(mImsRegistrationCallback);
+ }
}
}
@@ -171,6 +199,25 @@
}
}
+ private void registerImsRegistrationCallback() {
+ if (mMmTelManager == null || mImsRegistrationCallback == null) {
+ return;
+ }
+
+ try {
+ mMmTelManager.registerImsRegistrationCallback(mContext.getMainExecutor(),
+ mImsRegistrationCallback);
+ } catch (ImsException e) {
+ Log.w(this, "registerImsRegistrationCallback: registration failed, no ImsService"
+ + " available. Exception: " + e.getMessage());
+ return;
+ } catch (IllegalArgumentException e) {
+ Log.w(this, "registerImsRegistrationCallback: registration failed, invalid"
+ + " subscription, Exception" + e.getMessage());
+ return;
+ }
+ }
+
/**
* Trigger re-registration of this account.
*/
@@ -321,6 +368,12 @@
extras.putAll(getPhoneAccountExtras());
}
+ if (mIsAdhocConfCapable && isCarrierAdhocConferenceCallSupported()) {
+ capabilities |= PhoneAccount.CAPABILITY_ADHOC_CONFERENCE_CALLING;
+ } else {
+ capabilities &= ~PhoneAccount.CAPABILITY_ADHOC_CONFERENCE_CALLING;
+ }
+
final boolean isHandoverFromSupported = mContext.getResources().getBoolean(
R.bool.config_support_handover_from);
if (isHandoverFromSupported && !isEmergency) {
@@ -511,6 +564,19 @@
}
/**
+ * Determines from carrier config whether adhoc conference calling is supported.
+ *
+ * @return {@code true} if adhoc conference calling is supported, {@code false} otherwise.
+ */
+ private boolean isCarrierAdhocConferenceCallSupported() {
+ PersistableBundle b =
+ PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
+ return b != null &&
+ b.getBoolean(CarrierConfigManager.KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL);
+ }
+
+
+ /**
* Determines from carrier config whether merging calls is supported.
*
* @return {@code true} if merging calls is supported, {@code false} otherwise.
@@ -664,6 +730,26 @@
}
}
+ public void updateAdhocConfCapability(boolean isAdhocConfCapable) {
+ synchronized (mAccountsLock) {
+ if (!mAccounts.contains(this)) {
+ // Account has already been torn down, don't try to register it again.
+ // This handles the case where teardown has already happened, and we got a Ims
+ // registartion update that lost the race for the mAccountsLock. In such a
+ // scenario by the time we get here, the original phone account could have been
+ // torn down.
+ return;
+ }
+
+ if (isAdhocConfCapable != mIsAdhocConfCapable) {
+ Log.i(this, "updateAdhocConfCapability - changed, new value: "
+ + isAdhocConfCapable);
+ mIsAdhocConfCapable = isAdhocConfCapable;
+ mAccount = registerPstnPhoneAccount(mIsEmergency, mIsDummy);
+ }
+ }
+ }
+
public void updateRttCapability() {
boolean isRttEnabled = isRttCurrentlySupported();
if (isRttEnabled != mIsRttCapable) {
@@ -1048,6 +1134,21 @@
return null;
}
+ public void refreshAdhocConference(boolean isEnableAdhocConf) {
+ synchronized (mAccountsLock) {
+ Log.v(this, "refreshAdhocConference isEnable = " + isEnableAdhocConf);
+ for (AccountEntry entry : mAccounts) {
+ boolean hasAdhocConfCapability = entry.mAccount.hasCapabilities(
+ PhoneAccount.CAPABILITY_ADHOC_CONFERENCE_CALLING);
+ if (!isEnableAdhocConf && hasAdhocConfCapability) {
+ entry.updateAdhocConfCapability(isEnableAdhocConf);
+ } else if (isEnableAdhocConf && !hasAdhocConfCapability) {
+ entry.updateAdhocConfCapability(entry.mPhone.isImsRegistered());
+ }
+ }
+ }
+ }
+
/**
* Returns whethere a the subscription associated with a {@link PhoneAccountHandle} is using a
* sim call manager.
diff --git a/src/com/android/services/telephony/TelephonyConference.java b/src/com/android/services/telephony/TelephonyConference.java
index afbe89d..d720639 100644
--- a/src/com/android/services/telephony/TelephonyConference.java
+++ b/src/com/android/services/telephony/TelephonyConference.java
@@ -93,6 +93,16 @@
}
@Override
+ public void onAnswer(int videoState) {
+ Log.e(this, new Exception(), "Answer not supported for GSM conference call.");
+ }
+
+ @Override
+ public void onReject() {
+ Log.e(this, new Exception(), "Reject not supported for GSM conference call.");
+ }
+
+ @Override
public void onMerge(Connection connection) {
try {
Phone phone = ((TelephonyConnection) connection).getPhone();
diff --git a/src/com/android/services/telephony/TelephonyConferenceBase.java b/src/com/android/services/telephony/TelephonyConferenceBase.java
index 5e7ecf6..1c81fb9 100644
--- a/src/com/android/services/telephony/TelephonyConferenceBase.java
+++ b/src/com/android/services/telephony/TelephonyConferenceBase.java
@@ -48,6 +48,14 @@
public void onConferenceMembershipChanged(Connection connection) {}
/**
+ * Listener called when there conference call state changes.
+ * @param conference The conference.
+ * @param oldState previous state of conference call.
+ * @param newState new state of conference call.
+ */
+ public void onStateChanged(Conference conference, int oldState, int newState) {}
+
+ /**
* Listener called when a conference is destroyed.
* @param conference The conference.
*/
@@ -129,6 +137,54 @@
}
/**
+ * Sets state to be on hold.
+ */
+ public final void setConferenceOnHold() {
+ int oldState = getState();
+ if (oldState == Connection.STATE_HOLDING) {
+ return;
+ }
+ setOnHold();
+ notifyStateChanged(oldState, getState());
+ }
+
+ /**
+ * Sets state to be dialing.
+ */
+ public final void setConferenceOnDialing() {
+ int oldState = getState();
+ if (oldState == Connection.STATE_DIALING) {
+ return;
+ }
+ setDialing();
+ notifyStateChanged(oldState, getState());
+ }
+
+ /**
+ * Sets state to be ringing.
+ */
+ public final void setConferenceOnRinging() {
+ int oldState = getState();
+ if (oldState == Connection.STATE_RINGING) {
+ return;
+ }
+ setRinging();
+ notifyStateChanged(oldState, getState());
+ }
+
+ /**
+ * Sets state to be active.
+ */
+ public final void setConferenceOnActive() {
+ int oldState = getState();
+ if (oldState == Connection.STATE_ACTIVE) {
+ return;
+ }
+ setActive();
+ notifyStateChanged(oldState, getState());
+ }
+
+ /**
* Updates RIL voice radio technology used for current conference after its creation.
*/
public void updateCallRadioTechAfterCreation() {
@@ -188,4 +244,12 @@
listener.onDestroyed(this);
}
}
+
+ private void notifyStateChanged(int oldState, int newState) {
+ if (oldState != newState) {
+ for (TelephonyConferenceListener listener : mListeners) {
+ listener.onStateChanged(this, oldState, newState);
+ }
+ }
+ }
}
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index 14f0cb8..edaf1de 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -114,6 +114,9 @@
private static final int MSG_ON_CONNECTION_EVENT = 19;
private static final int MSG_REDIAL_CONNECTION_CHANGED = 20;
+ private List<Uri> mParticipants;
+ private boolean mIsAdhocConferenceCall;
+
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
@@ -163,7 +166,9 @@
"not foreground connection, skipping");
return;
}
- setRingbackRequested((Boolean) ((AsyncResult) msg.obj).result);
+ boolean ringback = (Boolean) ((AsyncResult) msg.obj).result;
+ setRingbackRequested(ringback);
+ notifyRingbackRequested(ringback);
break;
case MSG_DISCONNECT:
updateState();
@@ -463,6 +468,7 @@
public void onVideoProviderChanged(android.telecom.Connection c,
Connection.VideoProvider videoProvider) {}
public void onVideoStateChanged(android.telecom.Connection c, int videoState) {}
+ public void onRingbackRequested(Connection c, boolean ringback) {}
}
private final PostDialListener mPostDialListener = new PostDialListener() {
@@ -876,14 +882,7 @@
@Override
public void onAnswer(int videoState) {
- Log.v(this, "onAnswer");
- if (isValidRingingCall() && getPhone() != null) {
- try {
- getPhone().acceptCall(videoState);
- } catch (CallStateException e) {
- Log.e(this, e, "Failed to accept call.");
- }
- }
+ performAnswer(videoState);
}
@Override
@@ -927,7 +926,11 @@
@Override
public void onReject() {
- Log.v(this, "onReject");
+ performReject();
+ }
+
+ public void performReject() {
+ Log.v(this, "performReject");
if (isValidRingingCall()) {
mHandler.obtainMessage(MSG_HANGUP, android.telephony.DisconnectCause.INCOMING_REJECTED)
.sendToTarget();
@@ -1001,6 +1004,17 @@
originalConnection.sendRttModifyResponse(textStream);
}
+ public void performAnswer(int videoState) {
+ Log.v(this, "performAnswer");
+ if (isValidRingingCall() && getPhone() != null) {
+ try {
+ getPhone().acceptCall(videoState);
+ } catch (CallStateException e) {
+ Log.e(this, e, "Failed to accept call.");
+ }
+ }
+ }
+
public void performHold() {
Log.v(this, "performHold");
// TODO: Can dialing calls be put on hold as well since they take up the
@@ -1167,6 +1181,8 @@
newProperties = changeBitmask(newProperties, PROPERTY_IS_RTT, isRtt());
newProperties = changeBitmask(newProperties, PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL,
isNetworkIdentifiedEmergencyCall());
+ newProperties = changeBitmask(newProperties, PROPERTY_IS_ADHOC_CONFERENCE,
+ isAdhocConferenceCall());
if (getConnectionProperties() != newProperties) {
setTelephonyConnectionProperties(newProperties);
@@ -1255,6 +1271,7 @@
setTelephonyVideoState(mOriginalConnection.getVideoState());
setOriginalConnectionCapabilities(mOriginalConnection.getConnectionCapabilities());
setIsNetworkIdentifiedEmergencyCall(mOriginalConnection.isNetworkIdentifiedEmergencyCall());
+ setIsAdhocConferenceCall(mOriginalConnection.isAdhocConference());
setAudioModeIsVoip(mOriginalConnection.getAudioModeIsVoip());
setTelephonyVideoProvider(mOriginalConnection.getVideoProvider());
setAudioQuality(mOriginalConnection.getAudioQuality());
@@ -1511,6 +1528,39 @@
return true;
}
+ /**
+ * @return The address's to which this Connection is currently communicating.
+ */
+ public final @Nullable List<Uri> getParticipants() {
+ return mParticipants;
+ }
+
+ /**
+ * Sets the value of the {@link #getParticipants()} property.
+ *
+ * @param address The participant address's.
+ */
+ public final void setParticipants(@Nullable List<Uri> address) {
+ mParticipants = address;
+ }
+
+ /**
+ * @return true if connection is adhocConference call else false.
+ */
+ public final boolean isAdhocConferenceCall() {
+ return mIsAdhocConferenceCall;
+ }
+
+ /**
+ * Sets the value of the {@link #isAdhocConferenceCall()} property.
+ *
+ * @param isAdhocConferenceCall represents if the call is adhoc conference call or not.
+ */
+ public void setIsAdhocConferenceCall(boolean isAdhocConferenceCall) {
+ mIsAdhocConferenceCall = isAdhocConferenceCall;
+ updateConnectionProperties();
+ }
+
private boolean canHoldImsCalls() {
PersistableBundle b = getCarrierConfig();
// Return true if the CarrierConfig is unavailable
@@ -2530,6 +2580,8 @@
}
sb.append(" confSupported:");
sb.append(mIsConferenceSupported ? "Y" : "N");
+ sb.append(" isAdhocConf:");
+ sb.append(isAdhocConferenceCall() ? "Y" : "N");
sb.append("]");
return sb.toString();
}
@@ -2855,6 +2907,16 @@
}
/**
+ * Notifies {@link TelephonyConnectionListener}s of a whether to play Ringback Tone or not.
+ * @param ringback Whether the ringback tone is to be played
+ */
+ private void notifyRingbackRequested(boolean ringback) {
+ for (TelephonyConnectionListener listener : mTelephonyListeners) {
+ listener.onRingbackRequested(this, ringback);
+ }
+ }
+
+ /**
* Notifies {@link TelephonyConnectionListener}s of changes to the video provider for a
* connection.
* @param videoProvider The new video provider.
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 40b941e..2178537 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -516,6 +516,136 @@
return super.onUnbind(intent);
}
+ private Conference placeOutgoingConference(ConnectionRequest request,
+ Connection resultConnection, Phone phone) {
+ if (resultConnection instanceof TelephonyConnection) {
+ return placeOutgoingConference((TelephonyConnection) resultConnection, phone, request);
+ }
+ return null;
+ }
+
+ private Conference placeOutgoingConference(TelephonyConnection conferenceHostConnection,
+ Phone phone, ConnectionRequest request) {
+ updatePhoneAccount(conferenceHostConnection, phone);
+ com.android.internal.telephony.Connection originalConnection = null;
+ try {
+ originalConnection = phone.startConference(
+ getParticipantsToDial(request.getParticipants()),
+ new ImsPhone.ImsDialArgs.Builder()
+ .setVideoState(request.getVideoState())
+ .setRttTextStream(conferenceHostConnection.getRttTextStream())
+ .build());
+ } catch (CallStateException e) {
+ Log.e(this, e, "placeOutgoingConference, phone.startConference exception: " + e);
+ handleCallStateException(e, conferenceHostConnection, phone);
+ return null;
+ }
+
+ if (originalConnection == null) {
+ Log.d(this, "placeOutgoingConference, phone.startConference returned null");
+ conferenceHostConnection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
+ android.telephony.DisconnectCause.OUTGOING_FAILURE,
+ "conferenceHostConnection is null",
+ phone.getPhoneId()));
+ conferenceHostConnection.clearOriginalConnection();
+ conferenceHostConnection.destroy();
+ } else {
+ conferenceHostConnection.setOriginalConnection(originalConnection);
+ }
+
+ return prepareConference(conferenceHostConnection, request.getAccountHandle());
+ }
+
+ Conference prepareConference(Connection conn, PhoneAccountHandle phoneAccountHandle) {
+ if (!(conn instanceof TelephonyConnection)) {
+ Log.w(this, "prepareConference returning NULL conference");
+ return null;
+ }
+
+ TelephonyConnection connection = (TelephonyConnection)conn;
+ ImsConference conference = new ImsConference(TelecomAccountRegistry.getInstance(this),
+ mTelephonyConnectionServiceProxy, connection,
+ phoneAccountHandle, () -> true);
+ mImsConferenceController.addConference(conference);
+ conference.setVideoState(connection,
+ connection.getVideoState());
+ conference.setVideoProvider(connection,
+ connection.getVideoProvider());
+ conference.setStatusHints(connection.getStatusHints());
+ conference.setAddress(connection.getAddress(),
+ connection.getAddressPresentation());
+ conference.setCallerDisplayName(connection.getCallerDisplayName(),
+ connection.getCallerDisplayNamePresentation());
+ conference.setParticipants(connection.getParticipants());
+ return conference;
+ }
+
+ @Override
+ public @Nullable Conference onCreateIncomingConference(
+ @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
+ @NonNull final ConnectionRequest request) {
+ Log.i(this, "onCreateIncomingConference, request: " + request);
+ Connection connection = onCreateIncomingConnection(connectionManagerPhoneAccount, request);
+ Log.d(this, "onCreateIncomingConference, connection: %s", connection);
+ if (connection == null) {
+ Log.i(this, "onCreateIncomingConference, implementation returned null connection.");
+ return Conference.createFailedConference(
+ new DisconnectCause(DisconnectCause.ERROR, "IMPL_RETURNED_NULL_CONNECTION"),
+ request.getAccountHandle());
+ }
+
+ final Phone phone = getPhoneForAccount(request.getAccountHandle(),
+ false /* isEmergencyCall*/, null /* not an emergency call */);
+ if (phone == null) {
+ Log.d(this, "onCreateIncomingConference, phone is null");
+ return Conference.createFailedConference(
+ DisconnectCauseUtil.toTelecomDisconnectCause(
+ android.telephony.DisconnectCause.OUT_OF_SERVICE,
+ "Phone is null"),
+ request.getAccountHandle());
+ }
+
+ return prepareConference(connection, request.getAccountHandle());
+ }
+
+ @Override
+ public @Nullable Conference onCreateOutgoingConference(
+ @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
+ @NonNull final ConnectionRequest request) {
+ Log.i(this, "onCreateOutgoingConference, request: " + request);
+ Connection connection = onCreateOutgoingConnection(connectionManagerPhoneAccount, request);
+ Log.d(this, "onCreateOutgoingConference, connection: %s", connection);
+ if (connection == null) {
+ Log.i(this, "onCreateOutgoingConference, implementation returned null connection.");
+ return Conference.createFailedConference(
+ new DisconnectCause(DisconnectCause.ERROR, "IMPL_RETURNED_NULL_CONNECTION"),
+ request.getAccountHandle());
+ }
+
+ final Phone phone = getPhoneForAccount(request.getAccountHandle(),
+ false /* isEmergencyCall*/, null /* not an emergency call */);
+ if (phone == null) {
+ Log.d(this, "onCreateOutgoingConference, phone is null");
+ return Conference.createFailedConference(
+ DisconnectCauseUtil.toTelecomDisconnectCause(
+ android.telephony.DisconnectCause.OUT_OF_SERVICE,
+ "Phone is null"),
+ request.getAccountHandle());
+ }
+
+ return placeOutgoingConference(request, connection, phone);
+ }
+
+ private String[] getParticipantsToDial(List<Uri> participants) {
+ String[] participantsToDial = new String[participants.size()];
+ int i = 0;
+ for (Uri participant : participants) {
+ participantsToDial[i] = participant.getSchemeSpecificPart();
+ i++;
+ }
+ return participantsToDial;
+ }
+
@Override
public Connection onCreateOutgoingConnection(
PhoneAccountHandle connectionManagerPhoneAccount,
@@ -523,7 +653,9 @@
Log.i(this, "onCreateOutgoingConnection, request: " + request);
Uri handle = request.getAddress();
- if (handle == null) {
+ boolean isAdhocConference = request.isAdhocConferenceCall();
+
+ if (!isAdhocConference && handle == null) {
Log.d(this, "onCreateOutgoingConnection, handle is null");
return Connection.createFailedConnection(
mDisconnectCauseFactory.toTelecomDisconnectCause(
@@ -626,7 +758,6 @@
}
final String numberToDial = number;
-
final boolean isAirplaneModeOn = mDeviceState.isAirplaneModeOn(this);
boolean needToTurnOnRadio = (isEmergencyNumber && (!isRadioOn() || isAirplaneModeOn))
@@ -698,7 +829,15 @@
if (!isEmergencyNumber) {
final Connection resultConnection = getTelephonyConnection(request, numberToDial,
false, handle, phone);
- return placeOutgoingConnection(request, resultConnection, phone);
+ if (isAdhocConference) {
+ if (resultConnection instanceof TelephonyConnection) {
+ TelephonyConnection conn = (TelephonyConnection)resultConnection;
+ conn.setParticipants(request.getParticipants());
+ }
+ return resultConnection;
+ } else {
+ return placeOutgoingConnection(request, resultConnection, phone);
+ }
} else {
final Connection resultConnection = getTelephonyConnection(request, numberToDial,
true, handle, phone);
@@ -981,7 +1120,7 @@
final TelephonyConnection connection =
createConnectionFor(phone, null, true /* isOutgoing */, request.getAccountHandle(),
- request.getTelecomCallId());
+ request.getTelecomCallId(), request.isAdhocConferenceCall());
if (connection == null) {
return Connection.createFailedConnection(
mDisconnectCauseFactory.toTelecomDisconnectCause(
@@ -994,6 +1133,8 @@
connection.setTelephonyVideoState(request.getVideoState());
connection.setRttTextStream(request.getRttTextStream());
connection.setTtyEnabled(isTtyModeEnabled);
+ connection.setIsAdhocConferenceCall(request.isAdhocConferenceCall());
+ connection.setParticipants(request.getParticipants());
return connection;
}
@@ -1042,7 +1183,8 @@
TelephonyConnection connection =
createConnectionFor(phone, originalConnection, false /* isOutgoing */,
- request.getAccountHandle(), request.getTelecomCallId());
+ request.getAccountHandle(), request.getTelecomCallId(),
+ request.isAdhocConferenceCall());
handleIncomingRtt(request, originalConnection);
if (connection == null) {
return Connection.createCanceledConnection();
@@ -1154,6 +1296,29 @@
connection.close();
}
+ /**
+ * Called by the {@link ConnectionService} when a newly created {@link Conference} has been
+ * added to the {@link ConnectionService} and sent to Telecom. Here it is safe to send
+ * connection events.
+ *
+ * @param conference the {@link Conference}.
+ */
+ @Override
+ public void onCreateConferenceComplete(Conference conference) {
+ if (conference instanceof ImsConference) {
+ ImsConference imsConference = (ImsConference)conference;
+ TelephonyConnection telephonyConnection =
+ (TelephonyConnection)(imsConference.getConferenceHost());
+ maybeSendInternationalCallEvent(telephonyConnection);
+ }
+ }
+
+ public void onCreateIncomingConferenceFailed(PhoneAccountHandle connectionManagerPhoneAccount,
+ ConnectionRequest request) {
+ Log.i(this, "onCreateIncomingConferenceFailed, request: " + request);
+ onCreateIncomingConnectionFailed(connectionManagerPhoneAccount, request);
+ }
+
@Override
public void triggerConferenceRecalculate() {
if (mTelephonyConferenceController.shouldRecalculate()) {
@@ -1495,34 +1660,7 @@
}
} catch (CallStateException e) {
Log.e(this, e, "placeOutgoingConnection, phone.dial exception: " + e);
- int cause = android.telephony.DisconnectCause.OUTGOING_FAILURE;
- switch (e.getError()) {
- case CallStateException.ERROR_OUT_OF_SERVICE:
- cause = android.telephony.DisconnectCause.OUT_OF_SERVICE;
- break;
- case CallStateException.ERROR_POWER_OFF:
- cause = android.telephony.DisconnectCause.POWER_OFF;
- break;
- case CallStateException.ERROR_ALREADY_DIALING:
- cause = android.telephony.DisconnectCause.ALREADY_DIALING;
- break;
- case CallStateException.ERROR_CALL_RINGING:
- cause = android.telephony.DisconnectCause.CANT_CALL_WHILE_RINGING;
- break;
- case CallStateException.ERROR_CALLING_DISABLED:
- cause = android.telephony.DisconnectCause.CALLING_DISABLED;
- break;
- case CallStateException.ERROR_TOO_MANY_CALLS:
- cause = android.telephony.DisconnectCause.TOO_MANY_ONGOING_CALLS;
- break;
- case CallStateException.ERROR_OTASP_PROVISIONING_IN_PROCESS:
- cause = android.telephony.DisconnectCause.OTASP_PROVISIONING_IN_PROCESS;
- break;
- }
- connection.setTelephonyConnectionDisconnected(
- mDisconnectCauseFactory.toTelecomDisconnectCause(cause, e.getMessage(),
- phone.getPhoneId()));
- connection.close();
+ handleCallStateException(e, connection, phone);
return;
}
@@ -1576,12 +1714,55 @@
CarrierConfigManager.KEY_ALLOW_HOLD_CALL_DURING_EMERGENCY_BOOL, true);
}
+ private void handleCallStateException(CallStateException e, TelephonyConnection connection,
+ Phone phone) {
+ int cause = android.telephony.DisconnectCause.OUTGOING_FAILURE;
+ switch (e.getError()) {
+ case CallStateException.ERROR_OUT_OF_SERVICE:
+ cause = android.telephony.DisconnectCause.OUT_OF_SERVICE;
+ break;
+ case CallStateException.ERROR_POWER_OFF:
+ cause = android.telephony.DisconnectCause.POWER_OFF;
+ break;
+ case CallStateException.ERROR_ALREADY_DIALING:
+ cause = android.telephony.DisconnectCause.ALREADY_DIALING;
+ break;
+ case CallStateException.ERROR_CALL_RINGING:
+ cause = android.telephony.DisconnectCause.CANT_CALL_WHILE_RINGING;
+ break;
+ case CallStateException.ERROR_CALLING_DISABLED:
+ cause = android.telephony.DisconnectCause.CALLING_DISABLED;
+ break;
+ case CallStateException.ERROR_TOO_MANY_CALLS:
+ cause = android.telephony.DisconnectCause.TOO_MANY_ONGOING_CALLS;
+ break;
+ case CallStateException.ERROR_OTASP_PROVISIONING_IN_PROCESS:
+ cause = android.telephony.DisconnectCause.OTASP_PROVISIONING_IN_PROCESS;
+ break;
+ }
+ connection.setTelephonyConnectionDisconnected(
+ DisconnectCauseUtil.toTelecomDisconnectCause(cause, e.getMessage(),
+ phone.getPhoneId()));
+ connection.close();
+ }
+
private TelephonyConnection createConnectionFor(
Phone phone,
com.android.internal.telephony.Connection originalConnection,
boolean isOutgoing,
PhoneAccountHandle phoneAccountHandle,
String telecomCallId) {
+ return createConnectionFor(phone, originalConnection, isOutgoing, phoneAccountHandle,
+ telecomCallId, false);
+ }
+
+ private TelephonyConnection createConnectionFor(
+ Phone phone,
+ com.android.internal.telephony.Connection originalConnection,
+ boolean isOutgoing,
+ PhoneAccountHandle phoneAccountHandle,
+ String telecomCallId,
+ boolean isAdhocConference) {
TelephonyConnection returnConnection = null;
int phoneType = phone.getPhoneType();
if (phoneType == TelephonyManager.PHONE_TYPE_GSM) {
@@ -1592,8 +1773,10 @@
allowsMute, isOutgoing, telecomCallId);
}
if (returnConnection != null) {
- // Listen to Telephony specific callbacks from the connection
- returnConnection.addTelephonyConnectionListener(mTelephonyConnectionListener);
+ if (!isAdhocConference) {
+ // Listen to Telephony specific callbacks from the connection
+ returnConnection.addTelephonyConnectionListener(mTelephonyConnectionListener);
+ }
returnConnection.setVideoPauseSupported(
TelecomAccountRegistry.getInstance(this).isVideoPauseSupported(
phoneAccountHandle));