diff options
author | 2019-12-02 11:57:37 +0530 | |
---|---|---|
committer | 2020-01-22 03:22:58 +0000 | |
commit | 80aa2140e352ab561a8ac50aad62f4531bf96d90 (patch) | |
tree | a8efbc81f6fc2826e43564578e80fd245e09733b | |
parent | ff266fb0648310558d354e3c7a8ee99aab259831 (diff) |
Ims: Add support for Adhoc Conference calls
Add support for Adhoc Conference calls
Test: Manual
Bug: 62151032
Change-Id: Id50d235595d2133f867848ffdebdfe11e2f1c896
17 files changed, 817 insertions, 19 deletions
diff --git a/api/current.txt b/api/current.txt index 6d5d228d295e..7ae64b4eee1e 100644 --- a/api/current.txt +++ b/api/current.txt @@ -43379,6 +43379,7 @@ package android.telecom { field public static final int PROPERTY_GENERIC_CONFERENCE = 2; // 0x2 field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 128; // 0x80 field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10 + field public static final int PROPERTY_IS_ADHOC_CONFERENCE = 8192; // 0x2000 field public static final int PROPERTY_IS_EXTERNAL_CALL = 64; // 0x40 field public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 2048; // 0x800 field public static final int PROPERTY_RTT = 1024; // 0x400 @@ -43456,6 +43457,7 @@ package android.telecom { public abstract class Conference extends android.telecom.Conferenceable { ctor public Conference(android.telecom.PhoneAccountHandle); method public final boolean addConnection(android.telecom.Connection); + method @NonNull public static android.telecom.Conference createFailedConference(@NonNull android.telecom.DisconnectCause, @NonNull android.telecom.PhoneAccountHandle); method public final void destroy(); method public final android.telecom.CallAudioState getCallAudioState(); method public final java.util.List<android.telecom.Connection> getConferenceableConnections(); @@ -43470,6 +43472,8 @@ package android.telecom { method public final android.telecom.StatusHints getStatusHints(); method public android.telecom.Connection.VideoProvider getVideoProvider(); method public int getVideoState(); + method public final boolean isRingbackRequested(); + method public void onAnswer(int); method public void onCallAudioStateChanged(android.telecom.CallAudioState); method public void onConnectionAdded(android.telecom.Connection); method public void onDisconnect(); @@ -43478,6 +43482,7 @@ package android.telecom { method public void onMerge(android.telecom.Connection); method public void onMerge(); method public void onPlayDtmfTone(char); + method public void onReject(); method public void onSeparate(android.telecom.Connection); method public void onStopDtmfTone(); method public void onSwap(); @@ -43497,6 +43502,8 @@ package android.telecom { method public final void setDisconnected(android.telecom.DisconnectCause); method public final void setExtras(@Nullable android.os.Bundle); method public final void setOnHold(); + method public final void setRingbackRequested(boolean); + method public final void setRinging(); method public final void setStatusHints(android.telecom.StatusHints); method public final void setVideoProvider(android.telecom.Connection, android.telecom.Connection.VideoProvider); method public final void setVideoState(android.telecom.Connection, int); @@ -43654,6 +43661,7 @@ package android.telecom { field public static final int PROPERTY_ASSISTED_DIALING_USED = 512; // 0x200 field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20 field public static final int PROPERTY_HIGH_DEF_AUDIO = 4; // 0x4 + field public static final int PROPERTY_IS_ADHOC_CONFERENCE = 4096; // 0x1000 field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10 field public static final int PROPERTY_IS_RTT = 256; // 0x100 field public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 1024; // 0x400 @@ -43726,8 +43734,10 @@ package android.telecom { method public android.telecom.PhoneAccountHandle getAccountHandle(); method public android.net.Uri getAddress(); method public android.os.Bundle getExtras(); + method @Nullable public java.util.List<android.net.Uri> getParticipants(); method public android.telecom.Connection.RttTextStream getRttTextStream(); method public int getVideoState(); + method public boolean isAdhocConferenceCall(); method public boolean isRequestingRtt(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.telecom.ConnectionRequest> CREATOR; @@ -43747,9 +43757,13 @@ package android.telecom { method public void onConference(android.telecom.Connection, android.telecom.Connection); method public void onConnectionServiceFocusGained(); method public void onConnectionServiceFocusLost(); + method @Nullable public android.telecom.Conference onCreateIncomingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest); + method public void onCreateIncomingConferenceFailed(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest); method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest); method public void onCreateIncomingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest); method public android.telecom.Connection onCreateIncomingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest); + method @Nullable public android.telecom.Conference onCreateOutgoingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest); + method public void onCreateOutgoingConferenceFailed(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest); method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest); method public void onCreateOutgoingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest); method public android.telecom.Connection onCreateOutgoingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest); @@ -43864,6 +43878,7 @@ package android.telecom { method public boolean supportsUriScheme(String); method public android.telecom.PhoneAccount.Builder toBuilder(); method public void writeToParcel(android.os.Parcel, int); + field public static final int CAPABILITY_ADHOC_CONFERENCE_CALLING = 16384; // 0x4000 field public static final int CAPABILITY_CALL_PROVIDER = 2; // 0x2 field public static final int CAPABILITY_CALL_SUBJECT = 64; // 0x40 field public static final int CAPABILITY_CONNECTION_MANAGER = 1; // 0x1 @@ -44059,6 +44074,7 @@ package android.telecom { method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ANSWER_PHONE_CALLS, android.Manifest.permission.MODIFY_PHONE_STATE}) public void acceptRingingCall(); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ANSWER_PHONE_CALLS, android.Manifest.permission.MODIFY_PHONE_STATE}) public void acceptRingingCall(int); method public void addNewIncomingCall(android.telecom.PhoneAccountHandle, android.os.Bundle); + method public void addNewIncomingConference(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.os.Bundle); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void cancelMissedCallsNotification(); method public android.content.Intent createManageBlockedNumbersIntent(); method @Deprecated @RequiresPermission(android.Manifest.permission.ANSWER_PHONE_CALLS) public boolean endCall(); @@ -44086,6 +44102,7 @@ package android.telecom { method public void registerPhoneAccount(android.telecom.PhoneAccount); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void showInCallScreen(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void silenceRinger(); + method @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void startConference(@NonNull java.util.List<android.net.Uri>, @NonNull android.os.Bundle); method public void unregisterPhoneAccount(android.telecom.PhoneAccountHandle); field public static final String ACTION_CHANGE_DEFAULT_DIALER = "android.telecom.action.CHANGE_DEFAULT_DIALER"; field public static final String ACTION_CHANGE_PHONE_ACCOUNTS = "android.telecom.action.CHANGE_PHONE_ACCOUNTS"; @@ -44520,6 +44537,7 @@ package android.telephony { field public static final String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool"; field public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool"; field public static final String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL = "support_3gpp_call_forwarding_while_roaming_bool"; + field public static final String KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL = "support_adhoc_conference_calls_bool"; field public static final String KEY_SUPPORT_CLIR_NETWORK_DEFAULT_BOOL = "support_clir_network_default_bool"; field public static final String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool"; field public static final String KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL = "support_emergency_sms_over_ims_bool"; diff --git a/api/test-current.txt b/api/test-current.txt index 22ac3abcefd9..68da72f2204c 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -2845,6 +2845,8 @@ package android.telecom { method @NonNull public android.telecom.ConnectionRequest.Builder setAccountHandle(@NonNull android.telecom.PhoneAccountHandle); method @NonNull public android.telecom.ConnectionRequest.Builder setAddress(@NonNull android.net.Uri); method @NonNull public android.telecom.ConnectionRequest.Builder setExtras(@NonNull android.os.Bundle); + method @NonNull public android.telecom.ConnectionRequest.Builder setIsAdhocConferenceCall(boolean); + method @NonNull public android.telecom.ConnectionRequest.Builder setParticipants(@Nullable java.util.List<android.net.Uri>); method @NonNull public android.telecom.ConnectionRequest.Builder setRttPipeFromInCall(@NonNull android.os.ParcelFileDescriptor); method @NonNull public android.telecom.ConnectionRequest.Builder setRttPipeToInCall(@NonNull android.os.ParcelFileDescriptor); method @NonNull public android.telecom.ConnectionRequest.Builder setShouldShowIncomingCallUi(boolean); diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index 86ad795b9ea2..0ebf71fac814 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -548,8 +548,14 @@ public final class Call { */ public static final int PROPERTY_VOIP_AUDIO_MODE = 0x00001000; + /** + * Indicates that the call is an adhoc conference call. This property can be set for both + * incoming and outgoing calls. + */ + public static final int PROPERTY_IS_ADHOC_CONFERENCE = 0x00002000; + //****************************************************************************************** - // Next PROPERTY value: 0x00002000 + // Next PROPERTY value: 0x00004000 //****************************************************************************************** private final String mTelecomCallId; @@ -726,6 +732,9 @@ public final class Call { if (hasProperty(properties, PROPERTY_VOIP_AUDIO_MODE)) { builder.append(" PROPERTY_VOIP_AUDIO_MODE"); } + if (hasProperty(properties, PROPERTY_IS_ADHOC_CONFERENCE)) { + builder.append(" PROPERTY_IS_ADHOC_CONFERENCE"); + } builder.append("]"); return builder.toString(); } diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java index 456290cd772a..6b0845f5d12b 100644 --- a/telecomm/java/android/telecom/Conference.java +++ b/telecomm/java/android/telecom/Conference.java @@ -69,6 +69,7 @@ public abstract class Conference extends Conferenceable { public void onConnectionEvent(Conference c, String event, Bundle extras) {} public void onCallerDisplayNameChanged( Conference c, String callerDisplayName, int presentation) {} + public void onRingbackRequested(Conference c, boolean ringback) {} } private final Set<Listener> mListeners = new CopyOnWriteArraySet<>(); @@ -97,6 +98,7 @@ public abstract class Conference extends Conferenceable { private int mAddressPresentation; private String mCallerDisplayName; private int mCallerDisplayNamePresentation; + private boolean mRingbackRequested = false; private final Connection.Listener mConnectionDeathListener = new Connection.Listener() { @Override @@ -170,6 +172,14 @@ public abstract class Conference extends Conferenceable { } /** + * Returns whether this conference is requesting that the system play a ringback tone + * on its behalf. + */ + public final boolean isRingbackRequested() { + return mRingbackRequested; + } + + /** * Returns the capabilities of the conference. See {@code CAPABILITY_*} constants in class * {@link Connection} for valid values. * @@ -308,6 +318,35 @@ public abstract class Conference extends Conferenceable { public void onConnectionAdded(Connection connection) {} /** + * Notifies this Conference, which is in {@code STATE_RINGING}, of + * a request to accept. + * For managed {@link ConnectionService}s, this will be called when the user answers a call via + * the default dialer's {@link InCallService}. + * + * @param videoState The video state in which to answer the connection. + */ + public void onAnswer(int videoState) {} + + /** + * Notifies this Conference, which is in {@code STATE_RINGING}, of + * a request to accept. + * For managed {@link ConnectionService}s, this will be called when the user answers a call via + * the default dialer's {@link InCallService}. + * @hide + */ + public final void onAnswer() { + onAnswer(VideoProfile.STATE_AUDIO_ONLY); + } + + /** + * Notifies this Conference, which is in {@code STATE_RINGING}, of + * a request to reject. + * For managed {@link ConnectionService}s, this will be called when the user rejects a call via + * the default dialer's {@link InCallService}. + */ + public void onReject() {} + + /** * Sets state to be on hold. */ public final void setOnHold() { @@ -322,9 +361,17 @@ public abstract class Conference extends Conferenceable { } /** + * Sets state to be ringing. + */ + public final void setRinging() { + setState(Connection.STATE_RINGING); + } + + /** * Sets state to be active. */ public final void setActive() { + setRingbackRequested(false); setState(Connection.STATE_ACTIVE); } @@ -436,6 +483,21 @@ public abstract class Conference extends Conferenceable { } /** + * Requests that the framework play a ringback tone. This is to be invoked by implementations + * that do not play a ringback tone themselves in the conference's audio stream. + * + * @param ringback Whether the ringback tone is to be played. + */ + public final void setRingbackRequested(boolean ringback) { + if (mRingbackRequested != ringback) { + mRingbackRequested = ringback; + for (Listener l : mListeners) { + l.onRingbackRequested(this, ringback); + } + } + } + + /** * Set the video state for the conference. * Valid values: {@link VideoProfile#STATE_AUDIO_ONLY}, * {@link VideoProfile#STATE_BIDIRECTIONAL}, @@ -640,14 +702,6 @@ public abstract class Conference extends Conferenceable { } private void setState(int newState) { - if (newState != Connection.STATE_ACTIVE && - newState != Connection.STATE_HOLDING && - newState != Connection.STATE_DISCONNECTED) { - Log.w(this, "Unsupported state transition for Conference call.", - Connection.stateToString(newState)); - return; - } - if (mState != newState) { int oldState = mState; mState = newState; @@ -657,6 +711,37 @@ public abstract class Conference extends Conferenceable { } } + private static class FailureSignalingConference extends Conference { + private boolean mImmutable = false; + public FailureSignalingConference(DisconnectCause disconnectCause, + PhoneAccountHandle phoneAccount) { + super(phoneAccount); + setDisconnected(disconnectCause); + mImmutable = true; + } + public void checkImmutable() { + if (mImmutable) { + throw new UnsupportedOperationException("Conference is immutable"); + } + } + } + + /** + * Return a {@code Conference} which represents a failed conference attempt. The returned + * {@code Conference} will have a {@link android.telecom.DisconnectCause} and as specified, + * and a {@link #getState()} of {@code STATE_DISCONNECTED}. + * <p> + * The returned {@code Conference} can be assumed to {@link #destroy()} itself when appropriate, + * so users of this method need not maintain a reference to its return value to destroy it. + * + * @param disconnectCause The disconnect cause, ({@see android.telecomm.DisconnectCause}). + * @return A {@code Conference} which indicates failure. + */ + public @NonNull static Conference createFailedConference( + @NonNull DisconnectCause disconnectCause, @NonNull PhoneAccountHandle phoneAccount) { + return new FailureSignalingConference(disconnectCause, phoneAccount); + } + private final void clearConferenceableList() { for (Connection c : mConferenceableConnections) { c.removeConnectionListener(mConnectionDeathListener); @@ -667,11 +752,13 @@ public abstract class Conference extends Conferenceable { @Override public String toString() { return String.format(Locale.US, - "[State: %s,Capabilites: %s, VideoState: %s, VideoProvider: %s, ThisObject %s]", + "[State: %s,Capabilites: %s, VideoState: %s, VideoProvider: %s," + + "isRingbackRequested: %s, ThisObject %s]", Connection.stateToString(mState), Call.Details.capabilitiesToString(mConnectionCapabilities), getVideoState(), getVideoProvider(), + isRingbackRequested() ? "Y" : "N", super.toString()); } diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 0becaf237a73..a6f79fc71cc3 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -497,8 +497,17 @@ public abstract class Connection extends Conferenceable { @TestApi public static final int PROPERTY_REMOTELY_HOSTED = 1 << 11; + /** + * Set by the framework to indicate that it is an adhoc conference call. + * <p> + * This is used for Outgoing and incoming conference calls. + * + */ + public static final int PROPERTY_IS_ADHOC_CONFERENCE = 1 << 12; + + //********************************************************************************************** - // Next PROPERTY value: 1<<12 + // Next PROPERTY value: 1<<13 //********************************************************************************************** /** @@ -953,6 +962,10 @@ public abstract class Connection extends Conferenceable { builder.append(isLong ? " PROPERTY_REMOTELY_HOSTED" : " remote_hst"); } + if ((properties & PROPERTY_IS_ADHOC_CONFERENCE) == PROPERTY_IS_ADHOC_CONFERENCE) { + builder.append(isLong ? " PROPERTY_IS_ADHOC_CONFERENCE" : " adhoc_conf"); + } + builder.append("]"); return builder.toString(); } diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java index 221f8f129744..6d7ceca0a2cd 100644 --- a/telecomm/java/android/telecom/ConnectionRequest.java +++ b/telecomm/java/android/telecom/ConnectionRequest.java @@ -26,6 +26,9 @@ import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; +import java.util.ArrayList; +import java.util.List; + /** * Simple data container encapsulating a request to some entity to * create a new {@link Connection}. @@ -46,6 +49,8 @@ public final class ConnectionRequest implements Parcelable { private boolean mShouldShowIncomingCallUi = false; private ParcelFileDescriptor mRttPipeToInCall; private ParcelFileDescriptor mRttPipeFromInCall; + private List<Uri> mParticipants; + private boolean mIsAdhocConference = false; public Builder() { } @@ -59,6 +64,15 @@ public final class ConnectionRequest implements Parcelable { } /** + * Sets the participants for the resulting {@link ConnectionRequest} + * @param participants The participants to which the {@link Connection} is to connect. + */ + public @NonNull Builder setParticipants(@Nullable List<Uri> participants) { + this.mParticipants = participants; + return this; + } + + /** * Sets the address for the resulting {@link ConnectionRequest} * @param address The address(e.g., phone number) to which the {@link Connection} is to * connect. @@ -108,6 +122,16 @@ public final class ConnectionRequest implements Parcelable { } /** + * Sets isAdhocConference for the resulting {@link ConnectionRequest} + * @param isAdhocConference {@code true} if it is a adhoc conference call + * {@code false}, if not a adhoc conference call + */ + public @NonNull Builder setIsAdhocConferenceCall(boolean isAdhocConference) { + this.mIsAdhocConference = isAdhocConference; + return this; + } + + /** * Sets the RTT pipe for transferring text into the {@link ConnectionService} for the * resulting {@link ConnectionRequest} * @param rttPipeFromInCall The data pipe to read from. @@ -141,7 +165,9 @@ public final class ConnectionRequest implements Parcelable { mTelecomCallId, mShouldShowIncomingCallUi, mRttPipeFromInCall, - mRttPipeToInCall); + mRttPipeToInCall, + mParticipants, + mIsAdhocConference); } } @@ -155,6 +181,8 @@ public final class ConnectionRequest implements Parcelable { private final ParcelFileDescriptor mRttPipeFromInCall; // Cached return value of getRttTextStream -- we don't want to wrap it more than once. private Connection.RttTextStream mRttTextStream; + private List<Uri> mParticipants; + private final boolean mIsAdhocConference; /** * @param accountHandle The accountHandle which should be used to place the call. @@ -214,6 +242,21 @@ public final class ConnectionRequest implements Parcelable { boolean shouldShowIncomingCallUi, ParcelFileDescriptor rttPipeFromInCall, ParcelFileDescriptor rttPipeToInCall) { + this(accountHandle, handle, extras, videoState, telecomCallId, + shouldShowIncomingCallUi, rttPipeFromInCall, rttPipeToInCall, null, false); + } + + private ConnectionRequest( + PhoneAccountHandle accountHandle, + Uri handle, + Bundle extras, + int videoState, + String telecomCallId, + boolean shouldShowIncomingCallUi, + ParcelFileDescriptor rttPipeFromInCall, + ParcelFileDescriptor rttPipeToInCall, + List<Uri> participants, + boolean isAdhocConference) { mAccountHandle = accountHandle; mAddress = handle; mExtras = extras; @@ -222,6 +265,8 @@ public final class ConnectionRequest implements Parcelable { mShouldShowIncomingCallUi = shouldShowIncomingCallUi; mRttPipeFromInCall = rttPipeFromInCall; mRttPipeToInCall = rttPipeToInCall; + mParticipants = participants; + mIsAdhocConference = isAdhocConference; } private ConnectionRequest(Parcel in) { @@ -233,6 +278,11 @@ public final class ConnectionRequest implements Parcelable { mShouldShowIncomingCallUi = in.readInt() == 1; mRttPipeFromInCall = in.readParcelable(getClass().getClassLoader()); mRttPipeToInCall = in.readParcelable(getClass().getClassLoader()); + + mParticipants = new ArrayList<Uri>(); + in.readList(mParticipants, getClass().getClassLoader()); + + mIsAdhocConference = in.readInt() == 1; } /** @@ -246,6 +296,11 @@ public final class ConnectionRequest implements Parcelable { public Uri getAddress() { return mAddress; } /** + * The participants to which the {@link Connection} is to connect. + */ + public @Nullable List<Uri> getParticipants() { return mParticipants; } + + /** * Application-specific extra data. Used for passing back information from an incoming * call {@code Intent}, and for any proprietary extensions arranged between a client * and servant {@code ConnectionService} which agree on a vocabulary for such data. @@ -290,6 +345,13 @@ public final class ConnectionRequest implements Parcelable { } /** + * @return {@code true} if the call is a adhoc conference call else @return {@code false} + */ + public boolean isAdhocConferenceCall() { + return mIsAdhocConference; + } + + /** * Gets the {@link ParcelFileDescriptor} that is used to send RTT text from the connection * service to the in-call UI. In order to obtain an * {@link java.io.InputStream} from this {@link ParcelFileDescriptor}, use @@ -345,11 +407,12 @@ public final class ConnectionRequest implements Parcelable { @Override public String toString() { - return String.format("ConnectionRequest %s %s", + return String.format("ConnectionRequest %s %s isAdhocConf: %s", mAddress == null ? Uri.EMPTY : Connection.toLogSafePhoneNumber(mAddress.toString()), - bundleToString(mExtras)); + bundleToString(mExtras), + isAdhocConferenceCall() ? "Y" : "N"); } private static String bundleToString(Bundle extras){ @@ -406,5 +469,7 @@ public final class ConnectionRequest implements Parcelable { destination.writeInt(mShouldShowIncomingCallUi ? 1 : 0); destination.writeParcelable(mRttPipeFromInCall, 0); destination.writeParcelable(mRttPipeToInCall, 0); + destination.writeList(mParticipants); + destination.writeInt(mIsAdhocConference ? 1 : 0); } } diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java index 3a0494e17db9..440f044fdcf7 100644 --- a/telecomm/java/android/telecom/ConnectionService.java +++ b/telecomm/java/android/telecom/ConnectionService.java @@ -154,6 +154,9 @@ public abstract class ConnectionService extends Service { private static final String SESSION_CONNECTION_SERVICE_FOCUS_LOST = "CS.cSFL"; private static final String SESSION_CONNECTION_SERVICE_FOCUS_GAINED = "CS.cSFG"; private static final String SESSION_HANDOVER_FAILED = "CS.haF"; + private static final String SESSION_CREATE_CONF = "CS.crConf"; + private static final String SESSION_CREATE_CONF_COMPLETE = "CS.crConfC"; + private static final String SESSION_CREATE_CONF_FAILED = "CS.crConfF"; private static final int MSG_ADD_CONNECTION_SERVICE_ADAPTER = 1; private static final int MSG_CREATE_CONNECTION = 2; @@ -188,6 +191,9 @@ public abstract class ConnectionService extends Service { private static final int MSG_HANDOVER_FAILED = 32; private static final int MSG_HANDOVER_COMPLETE = 33; private static final int MSG_DEFLECT = 34; + private static final int MSG_CREATE_CONFERENCE = 35; + private static final int MSG_CREATE_CONFERENCE_COMPLETE = 36; + private static final int MSG_CREATE_CONFERENCE_FAILED = 37; private static Connection sNullConnection; @@ -291,6 +297,63 @@ public abstract class ConnectionService extends Service { } @Override + public void createConference( + PhoneAccountHandle connectionManagerPhoneAccount, + String id, + ConnectionRequest request, + boolean isIncoming, + boolean isUnknown, + Session.Info sessionInfo) { + Log.startSession(sessionInfo, SESSION_CREATE_CONF); + try { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = connectionManagerPhoneAccount; + args.arg2 = id; + args.arg3 = request; + args.arg4 = Log.createSubsession(); + args.argi1 = isIncoming ? 1 : 0; + args.argi2 = isUnknown ? 1 : 0; + mHandler.obtainMessage(MSG_CREATE_CONFERENCE, args).sendToTarget(); + } finally { + Log.endSession(); + } + } + + @Override + public void createConferenceComplete(String id, Session.Info sessionInfo) { + Log.startSession(sessionInfo, SESSION_CREATE_CONF_COMPLETE); + try { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = id; + args.arg2 = Log.createSubsession(); + mHandler.obtainMessage(MSG_CREATE_CONFERENCE_COMPLETE, args).sendToTarget(); + } finally { + Log.endSession(); + } + } + + @Override + public void createConferenceFailed( + PhoneAccountHandle connectionManagerPhoneAccount, + String callId, + ConnectionRequest request, + boolean isIncoming, + Session.Info sessionInfo) { + Log.startSession(sessionInfo, SESSION_CREATE_CONF_FAILED); + try { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = callId; + args.arg2 = request; + args.arg3 = Log.createSubsession(); + args.arg4 = connectionManagerPhoneAccount; + args.argi1 = isIncoming ? 1 : 0; + mHandler.obtainMessage(MSG_CREATE_CONFERENCE_FAILED, args).sendToTarget(); + } finally { + Log.endSession(); + } + } + + @Override public void handoverFailed(String callId, ConnectionRequest request, int reason, Session.Info sessionInfo) { Log.startSession(sessionInfo, SESSION_HANDOVER_FAILED); @@ -802,6 +865,106 @@ public abstract class ConnectionService extends Service { } break; } + case MSG_CREATE_CONFERENCE: { + SomeArgs args = (SomeArgs) msg.obj; + Log.continueSession((Session) args.arg4, SESSION_HANDLER + SESSION_CREATE_CONN); + try { + final PhoneAccountHandle connectionManagerPhoneAccount = + (PhoneAccountHandle) args.arg1; + final String id = (String) args.arg2; + final ConnectionRequest request = (ConnectionRequest) args.arg3; + final boolean isIncoming = args.argi1 == 1; + final boolean isUnknown = args.argi2 == 1; + if (!mAreAccountsInitialized) { + Log.d(this, "Enqueueing pre-initconference request %s", id); + mPreInitializationConnectionRequests.add( + new android.telecom.Logging.Runnable( + SESSION_HANDLER + SESSION_CREATE_CONF + ".pIConfR", + null /*lock*/) { + @Override + public void loggedRun() { + createConference(connectionManagerPhoneAccount, + id, + request, + isIncoming, + isUnknown); + } + }.prepare()); + } else { + createConference(connectionManagerPhoneAccount, + id, + request, + isIncoming, + isUnknown); + } + } finally { + args.recycle(); + Log.endSession(); + } + break; + } + case MSG_CREATE_CONFERENCE_COMPLETE: { + SomeArgs args = (SomeArgs) msg.obj; + Log.continueSession((Session) args.arg2, + SESSION_HANDLER + SESSION_CREATE_CONN_COMPLETE); + try { + final String id = (String) args.arg1; + if (!mAreAccountsInitialized) { + Log.d(this, "Enqueueing pre-init conference request %s", id); + mPreInitializationConnectionRequests.add( + new android.telecom.Logging.Runnable( + SESSION_HANDLER + SESSION_CREATE_CONF_COMPLETE + + ".pIConfR", + null /*lock*/) { + @Override + public void loggedRun() { + notifyCreateConferenceComplete(id); + } + }.prepare()); + } else { + notifyCreateConferenceComplete(id); + } + } finally { + args.recycle(); + Log.endSession(); + } + break; + } + case MSG_CREATE_CONFERENCE_FAILED: { + SomeArgs args = (SomeArgs) msg.obj; + Log.continueSession((Session) args.arg3, SESSION_HANDLER + + SESSION_CREATE_CONN_FAILED); + try { + final String id = (String) args.arg1; + final ConnectionRequest request = (ConnectionRequest) args.arg2; + final boolean isIncoming = args.argi1 == 1; + final PhoneAccountHandle connectionMgrPhoneAccount = + (PhoneAccountHandle) args.arg4; + if (!mAreAccountsInitialized) { + Log.d(this, "Enqueueing pre-init conference request %s", id); + mPreInitializationConnectionRequests.add( + new android.telecom.Logging.Runnable( + SESSION_HANDLER + SESSION_CREATE_CONF_FAILED + + ".pIConfR", + null /*lock*/) { + @Override + public void loggedRun() { + createConferenceFailed(connectionMgrPhoneAccount, id, + request, isIncoming); + } + }.prepare()); + } else { + Log.i(this, "createConferenceFailed %s", id); + createConferenceFailed(connectionMgrPhoneAccount, id, request, + isIncoming); + } + } finally { + args.recycle(); + Log.endSession(); + } + break; + } + case MSG_HANDOVER_FAILED: { SomeArgs args = (SomeArgs) msg.obj; Log.continueSession((Session) args.arg3, SESSION_HANDLER + @@ -1162,6 +1325,12 @@ public abstract class ConnectionService extends Service { public void onStateChanged(Conference conference, int oldState, int newState) { String id = mIdByConference.get(conference); switch (newState) { + case Connection.STATE_RINGING: + mAdapter.setRinging(id); + break; + case Connection.STATE_DIALING: + mAdapter.setDialing(id); + break; case Connection.STATE_ACTIVE: mAdapter.setActive(id); break; @@ -1292,6 +1461,13 @@ public abstract class ConnectionService extends Service { mAdapter.onConnectionEvent(id, event, extras); } } + + @Override + public void onRingbackRequested(Conference c, boolean ringback) { + String id = mIdByConference.get(c); + Log.d(this, "Adapter conference onRingback %b", ringback); + mAdapter.setRingbackRequested(id, ringback); + } }; private final Connection.Listener mConnectionListener = new Connection.Listener() { @@ -1534,6 +1710,70 @@ public abstract class ConnectionService extends Service { return super.onUnbind(intent); } + + /** + * This can be used by telecom to either create a new outgoing conference call or attach + * to an existing incoming conference call. In either case, telecom will cycle through a + * set of services and call createConference until a connection service cancels the process + * or completes it successfully. + */ + private void createConference( + final PhoneAccountHandle callManagerAccount, + final String callId, + final ConnectionRequest request, + boolean isIncoming, + boolean isUnknown) { + + Conference conference = null; + conference = isIncoming ? onCreateIncomingConference(callManagerAccount, request) + : onCreateOutgoingConference(callManagerAccount, request); + + Log.d(this, "createConference, conference: %s", conference); + if (conference == null) { + Log.i(this, "createConference, implementation returned null conference."); + conference = Conference.createFailedConference( + new DisconnectCause(DisconnectCause.ERROR, "IMPL_RETURNED_NULL_CONFERENCE"), + request.getAccountHandle()); + } + if (conference.getExtras() != null) { + conference.getExtras().putString(Connection.EXTRA_ORIGINAL_CONNECTION_ID, callId); + } + mConferenceById.put(callId, conference); + mIdByConference.put(conference, callId); + conference.addListener(mConferenceListener); + ParcelableConference parcelableConference = new ParcelableConference( + request.getAccountHandle(), + conference.getState(), + conference.getConnectionCapabilities(), + conference.getConnectionProperties(), + Collections.<String>emptyList(), //connectionIds + conference.getVideoProvider() == null ? + null : conference.getVideoProvider().getInterface(), + conference.getVideoState(), + conference.getConnectTimeMillis(), + conference.getConnectionStartElapsedRealTime(), + conference.getStatusHints(), + conference.getExtras(), + conference.getAddress(), + conference.getAddressPresentation(), + conference.getCallerDisplayName(), + conference.getCallerDisplayNamePresentation(), + conference.getDisconnectCause(), + conference.isRingbackRequested()); + if (conference.getState() != Connection.STATE_DISCONNECTED) { + conference.setTelecomCallId(callId); + mAdapter.setVideoProvider(callId, conference.getVideoProvider()); + mAdapter.setVideoState(callId, conference.getVideoState()); + onConferenceAdded(conference); + } + + Log.d(this, "createConference, calling handleCreateConferenceSuccessful %s", callId); + mAdapter.handleCreateConferenceComplete( + callId, + request, + parcelableConference); + } + /** * This can be used by telecom to either create a new outgoing call or attach to an existing * incoming call. In either case, telecom will cycle through a set of services and call @@ -1645,6 +1885,18 @@ public abstract class ConnectionService extends Service { } } + private void createConferenceFailed(final PhoneAccountHandle callManagerAccount, + final String callId, final ConnectionRequest request, + boolean isIncoming) { + + Log.i(this, "createConferenceFailed %s", callId); + if (isIncoming) { + onCreateIncomingConferenceFailed(callManagerAccount, request); + } else { + onCreateOutgoingConferenceFailed(callManagerAccount, request); + } + } + private void handoverFailed(final String callId, final ConnectionRequest request, int reason) { @@ -1669,6 +1921,24 @@ public abstract class ConnectionService extends Service { "notifyCreateConnectionComplete")); } + /** + * Called by Telecom when the creation of a new Conference has completed and it is now added + * to Telecom. + * @param callId The ID of the connection. + */ + private void notifyCreateConferenceComplete(final String callId) { + Log.i(this, "notifyCreateConferenceComplete %s", callId); + if (callId == null) { + // This could happen if the conference fails quickly and is removed from the + // ConnectionService before Telecom sends the create conference complete callback. + Log.w(this, "notifyCreateConferenceComplete: callId is null."); + return; + } + onCreateConferenceComplete(findConferenceForAction(callId, + "notifyCreateConferenceComplete")); + } + + private void abort(String callId) { Log.d(this, "abort %s", callId); findConnectionForAction(callId, "abort").onAbort(); @@ -1676,12 +1946,20 @@ public abstract class ConnectionService extends Service { private void answerVideo(String callId, int videoState) { Log.d(this, "answerVideo %s", callId); - findConnectionForAction(callId, "answer").onAnswer(videoState); + if (mConnectionById.containsKey(callId)) { + findConnectionForAction(callId, "answer").onAnswer(videoState); + } else { + findConferenceForAction(callId, "answer").onAnswer(videoState); + } } private void answer(String callId) { Log.d(this, "answer %s", callId); - findConnectionForAction(callId, "answer").onAnswer(); + if (mConnectionById.containsKey(callId)) { + findConnectionForAction(callId, "answer").onAnswer(); + } else { + findConferenceForAction(callId, "answer").onAnswer(); + } } private void deflect(String callId, Uri address) { @@ -1691,7 +1969,11 @@ public abstract class ConnectionService extends Service { private void reject(String callId) { Log.d(this, "reject %s", callId); - findConnectionForAction(callId, "reject").onReject(); + if (mConnectionById.containsKey(callId)) { + findConnectionForAction(callId, "reject").onReject(); + } else { + findConferenceForAction(callId, "reject").onReject(); + } } private void reject(String callId, String rejectWithMessage) { @@ -2198,6 +2480,21 @@ public abstract class ConnectionService extends Service { ConnectionRequest request) { return null; } + /** + * Create a {@code Connection} given an incoming request. This is used to attach to existing + * incoming conference call. + * + * @param connectionManagerPhoneAccount See description at + * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. + * @param request Details about the incoming call. + * @return The {@code Connection} object to satisfy this call, or {@code null} to + * not handle the call. + */ + public @Nullable Conference onCreateIncomingConference( + @Nullable PhoneAccountHandle connectionManagerPhoneAccount, + @Nullable ConnectionRequest request) { + return null; + } /** * Called after the {@link Connection} returned by @@ -2212,6 +2509,19 @@ public abstract class ConnectionService extends Service { } /** + * Called after the {@link Conference} returned by + * {@link #onCreateIncomingConference(PhoneAccountHandle, ConnectionRequest)} + * or {@link #onCreateOutgoingConference(PhoneAccountHandle, ConnectionRequest)} has been + * added to the {@link ConnectionService} and sent to Telecom. + * + * @param conference the {@link Conference}. + * @hide + */ + public void onCreateConferenceComplete(Conference conference) { + } + + + /** * Called by Telecom to inform the {@link ConnectionService} that its request to create a new * incoming {@link Connection} was denied. * <p> @@ -2250,6 +2560,47 @@ public abstract class ConnectionService extends Service { } /** + * Called by Telecom to inform the {@link ConnectionService} that its request to create a new + * incoming {@link Conference} was denied. + * <p> + * Used when a self-managed {@link ConnectionService} attempts to create a new incoming + * {@link Conference}, but Telecom has determined that the call cannot be allowed at this time. + * The {@link ConnectionService} is responsible for silently rejecting the new incoming + * {@link Conference}. + * <p> + * See {@link TelecomManager#isIncomingCallPermitted(PhoneAccountHandle)} for more information. + * + * @param connectionManagerPhoneAccount See description at + * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. + * @param request The incoming connection request. + */ + public void onCreateIncomingConferenceFailed( + @Nullable PhoneAccountHandle connectionManagerPhoneAccount, + @Nullable ConnectionRequest request) { + } + + /** + * Called by Telecom to inform the {@link ConnectionService} that its request to create a new + * outgoing {@link Conference} was denied. + * <p> + * Used when a self-managed {@link ConnectionService} attempts to create a new outgoing + * {@link Conference}, but Telecom has determined that the call cannot be placed at this time. + * The {@link ConnectionService} is responisible for informing the user that the + * {@link Conference} cannot be made at this time. + * <p> + * See {@link TelecomManager#isOutgoingCallPermitted(PhoneAccountHandle)} for more information. + * + * @param connectionManagerPhoneAccount See description at + * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. + * @param request The outgoing connection request. + */ + public void onCreateOutgoingConferenceFailed( + @Nullable PhoneAccountHandle connectionManagerPhoneAccount, + @Nullable ConnectionRequest request) { + } + + + /** * Trigger recalculate functinality for conference calls. This is used when a Telephony * Connection is part of a conference controller but is not yet added to Connection * Service and hence cannot be added to the conference call. @@ -2289,6 +2640,36 @@ public abstract class ConnectionService extends Service { } /** + * Create a {@code Conference} given an outgoing request. This is used to initiate new + * outgoing conference call. + * + * @param connectionManagerPhoneAccount The connection manager account to use for managing + * this call. + * <p> + * If this parameter is not {@code null}, it means that this {@code ConnectionService} + * has registered one or more {@code PhoneAccount}s having + * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}. This parameter will contain + * one of these {@code PhoneAccount}s, while the {@code request} will contain another + * (usually but not always distinct) {@code PhoneAccount} to be used for actually + * making the connection. + * <p> + * If this parameter is {@code null}, it means that this {@code ConnectionService} is + * being asked to make a direct connection. The + * {@link ConnectionRequest#getAccountHandle()} of parameter {@code request} will be + * a {@code PhoneAccount} registered by this {@code ConnectionService} to use for + * making the connection. + * @param request Details about the outgoing call. + * @return The {@code Conference} object to satisfy this call, or the result of an invocation + * of {@link Connection#createFailedConnection(DisconnectCause)} to not handle the call. + */ + public @Nullable Conference onCreateOutgoingConference( + @Nullable PhoneAccountHandle connectionManagerPhoneAccount, + @Nullable ConnectionRequest request) { + return null; + } + + + /** * Called by Telecom to request that a {@link ConnectionService} creates an instance of an * outgoing handover {@link Connection}. * <p> diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java index 04e930ccd954..8f273233044e 100644 --- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java +++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java @@ -100,6 +100,19 @@ final class ConnectionServiceAdapter implements DeathRecipient { } } + void handleCreateConferenceComplete( + String id, + ConnectionRequest request, + ParcelableConference conference) { + for (IConnectionServiceAdapter adapter : mAdapters) { + try { + adapter.handleCreateConferenceComplete(id, request, conference, + Log.getExternalSession()); + } catch (RemoteException e) { + } + } + } + /** * Sets a call's state to active (e.g., an ongoing call where two parties can actively * communicate). diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java index 60b2172fdeca..79ad51b92b81 100644 --- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java +++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java @@ -75,6 +75,7 @@ final class ConnectionServiceAdapterServant { private static final int MSG_SET_PHONE_ACCOUNT_CHANGED = 34; private static final int MSG_CONNECTION_SERVICE_FOCUS_RELEASED = 35; private static final int MSG_SET_CONFERENCE_STATE = 36; + private static final int MSG_HANDLE_CREATE_CONFERENCE_COMPLETE = 37; private final IConnectionServiceAdapter mDelegate; @@ -103,6 +104,19 @@ final class ConnectionServiceAdapterServant { } break; } + case MSG_HANDLE_CREATE_CONFERENCE_COMPLETE: { + SomeArgs args = (SomeArgs) msg.obj; + try { + mDelegate.handleCreateConferenceComplete( + (String) args.arg1, + (ConnectionRequest) args.arg2, + (ParcelableConference) args.arg3, + null /*Session.Info*/); + } finally { + args.recycle(); + } + break; + } case MSG_SET_ACTIVE: mDelegate.setActive((String) msg.obj, null /*Session.Info*/); break; @@ -366,6 +380,20 @@ final class ConnectionServiceAdapterServant { } @Override + public void handleCreateConferenceComplete( + String id, + ConnectionRequest request, + ParcelableConference conference, + Session.Info sessionInfo) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = id; + args.arg2 = request; + args.arg3 = conference; + mHandler.obtainMessage(MSG_HANDLE_CREATE_CONFERENCE_COMPLETE, args).sendToTarget(); + } + + + @Override public void setActive(String connectionId, Session.Info sessionInfo) { mHandler.obtainMessage(MSG_SET_ACTIVE, connectionId).sendToTarget(); } diff --git a/telecomm/java/android/telecom/ParcelableConference.java b/telecomm/java/android/telecom/ParcelableConference.java index ede05943772e..90b69a338c7e 100644 --- a/telecomm/java/android/telecom/ParcelableConference.java +++ b/telecomm/java/android/telecom/ParcelableConference.java @@ -47,6 +47,34 @@ public final class ParcelableConference implements Parcelable { private final int mAddressPresentation; private final String mCallerDisplayName; private final int mCallerDisplayNamePresentation; + private DisconnectCause mDisconnectCause; + private boolean mRingbackRequested; + + public ParcelableConference( + PhoneAccountHandle phoneAccount, + int state, + int connectionCapabilities, + int connectionProperties, + List<String> connectionIds, + IVideoProvider videoProvider, + int videoState, + long connectTimeMillis, + long connectElapsedTimeMillis, + StatusHints statusHints, + Bundle extras, + Uri address, + int addressPresentation, + String callerDisplayName, + int callerDisplayNamePresentation, + DisconnectCause disconnectCause, + boolean ringbackRequested) { + this(phoneAccount, state, connectionCapabilities, connectionProperties, connectionIds, + videoProvider, videoState, connectTimeMillis, connectElapsedTimeMillis, + statusHints, extras, address, addressPresentation, callerDisplayName, + callerDisplayNamePresentation); + mDisconnectCause = disconnectCause; + mRingbackRequested = ringbackRequested; + } public ParcelableConference( PhoneAccountHandle phoneAccount, @@ -79,6 +107,8 @@ public final class ParcelableConference implements Parcelable { mAddressPresentation = addressPresentation; mCallerDisplayName = callerDisplayName; mCallerDisplayNamePresentation = callerDisplayNamePresentation; + mDisconnectCause = null; + mRingbackRequested = false; } @Override @@ -100,6 +130,10 @@ public final class ParcelableConference implements Parcelable { .append(mVideoState) .append(", VideoProvider: ") .append(mVideoProvider) + .append(", isRingbackRequested: ") + .append(mRingbackRequested) + .append(", disconnectCause: ") + .append(mDisconnectCause) .toString(); } @@ -151,6 +185,13 @@ public final class ParcelableConference implements Parcelable { return mAddress; } + public final DisconnectCause getDisconnectCause() { + return mDisconnectCause; + } + + public boolean isRingbackRequested() { + return mRingbackRequested; + } public int getHandlePresentation() { return mAddressPresentation; } @@ -177,11 +218,14 @@ public final class ParcelableConference implements Parcelable { int addressPresentation = source.readInt(); String callerDisplayName = source.readString(); int callerDisplayNamePresentation = source.readInt(); + DisconnectCause disconnectCause = source.readParcelable(classLoader); + boolean isRingbackRequested = source.readInt() == 1; return new ParcelableConference(phoneAccount, state, capabilities, properties, connectionIds, videoCallProvider, videoState, connectTimeMillis, connectElapsedTimeMillis, statusHints, extras, address, addressPresentation, - callerDisplayName, callerDisplayNamePresentation); + callerDisplayName, callerDisplayNamePresentation, disconnectCause, + isRingbackRequested); } @Override @@ -215,5 +259,7 @@ public final class ParcelableConference implements Parcelable { destination.writeInt(mAddressPresentation); destination.writeString(mCallerDisplayName); destination.writeInt(mCallerDisplayNamePresentation); + destination.writeParcelable(mDisconnectCause, 0); + destination.writeInt(mRingbackRequested ? 1 : 0); } } diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java index bb858cb0761b..abb210f13376 100644 --- a/telecomm/java/android/telecom/PhoneAccount.java +++ b/telecomm/java/android/telecom/PhoneAccount.java @@ -331,7 +331,17 @@ public final class PhoneAccount implements Parcelable { */ public static final int CAPABILITY_EMERGENCY_PREFERRED = 0x2000; - /* NEXT CAPABILITY: 0x4000 */ + /** + * An adhoc conference call is established by providing a list of addresses to + * {@code TelecomManager#startConference(List<Uri>, int videoState)} where the + * {@link ConnectionService} is responsible for connecting all indicated participants + * to a conference simultaneously. + * This is in contrast to conferences formed by merging calls together (e.g. using + * {@link android.telecom.Call#mergeConference()}). + */ + public static final int CAPABILITY_ADHOC_CONFERENCE_CALLING = 0x4000; + + /* NEXT CAPABILITY: 0x8000 */ /** * URI scheme for telephone number URIs. @@ -1054,6 +1064,9 @@ public final class PhoneAccount implements Parcelable { if (hasCapabilities(CAPABILITY_RTT)) { sb.append("Rtt"); } + if (hasCapabilities(CAPABILITY_ADHOC_CONFERENCE_CALLING)) { + sb.append("AdhocConf"); + } return sb.toString(); } diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java index 1e73bd61d68e..76640e036eeb 100644 --- a/telecomm/java/android/telecom/RemoteConnectionService.java +++ b/telecomm/java/android/telecom/RemoteConnectionService.java @@ -101,6 +101,14 @@ final class RemoteConnectionService { } @Override + public void handleCreateConferenceComplete( + String id, + ConnectionRequest request, + ParcelableConference parcel, + Session.Info info) { + } + + @Override public void setActive(String callId, Session.Info sessionInfo) { if (mConnectionById.containsKey(callId)) { findConnectionForAction(callId, "setActive") diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index af3c55abf00c..68f5bf637ce2 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -1792,6 +1792,45 @@ public class TelecomManager { } /** + * Registers a new incoming conference. A {@link ConnectionService} should invoke this method + * when it has an incoming conference. For managed {@link ConnectionService}s, the specified + * {@link PhoneAccountHandle} must have been registered with {@link #registerPhoneAccount} and + * the user must have enabled the corresponding {@link PhoneAccount}. This can be checked using + * {@link #getPhoneAccount}. Self-managed {@link ConnectionService}s must have + * {@link android.Manifest.permission#MANAGE_OWN_CALLS} to add a new incoming call. + * <p> + * The incoming conference you are adding is assumed to have a video state of + * {@link VideoProfile#STATE_AUDIO_ONLY}, unless the extra value + * {@link #EXTRA_INCOMING_VIDEO_STATE} is specified. + * <p> + * Once invoked, this method will cause the system to bind to the {@link ConnectionService} + * associated with the {@link PhoneAccountHandle} and request additional information about the + * call (See {@link ConnectionService#onCreateIncomingConference}) before starting the incoming + * call UI. + * <p> + * For a managed {@link ConnectionService}, a {@link SecurityException} will be thrown if either + * the {@link PhoneAccountHandle} does not correspond to a registered {@link PhoneAccount} or + * the associated {@link PhoneAccount} is not currently enabled by the user. + * + * @param phoneAccount A {@link PhoneAccountHandle} registered with + * {@link #registerPhoneAccount}. + * @param extras A bundle that will be passed through to + * {@link ConnectionService#onCreateIncomingConference}. + */ + + public void addNewIncomingConference(@NonNull PhoneAccountHandle phoneAccount, + @NonNull Bundle extras) { + try { + if (isServiceConnected()) { + getTelecomService().addNewIncomingConference( + phoneAccount, extras == null ? new Bundle() : extras); + } + } catch (RemoteException e) { + Log.e(TAG, "RemoteException adding a new incoming conference: " + phoneAccount, e); + } + } + + /** * Registers a new unknown call with Telecom. This can only be called by the system Telephony * service. This is invoked when Telephony detects a new unknown connection that was neither * a new incoming call, nor an user-initiated outgoing call. @@ -1995,6 +2034,42 @@ public class TelecomManager { } } + + /** + * Place a new conference call with the provided participants using the system telecom service + * This method doesn't support placing of emergency calls. + * + * An adhoc conference call is established by providing a list of addresses to + * {@code TelecomManager#startConference(List<Uri>, int videoState)} where the + * {@link ConnectionService} is responsible for connecting all indicated participants + * to a conference simultaneously. + * This is in contrast to conferences formed by merging calls together (e.g. using + * {@link android.telecom.Call#mergeConference()}). + * + * The following keys are supported in the supplied extras. + * <ul> + * <li>{@link #EXTRA_PHONE_ACCOUNT_HANDLE}</li> + * <li>{@link #EXTRA_START_CALL_WITH_SPEAKERPHONE}</li> + * <li>{@link #EXTRA_START_CALL_WITH_VIDEO_STATE}</li> + * </ul> + * + * @param participants List of participants to start conference with + * @param extras Bundle of extras to use with the call + */ + @RequiresPermission(android.Manifest.permission.CALL_PHONE) + public void startConference(@NonNull List<Uri> participants, + @NonNull Bundle extras) { + ITelecomService service = getTelecomService(); + if (service != null) { + try { + service.startConference(participants, extras, + mContext.getOpPackageName()); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelecomService#placeCall", e); + } + } + } + /** * Enables and disables specified phone account. * diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl index e35093c9656a..96f2483f32f9 100644 --- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl +++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl @@ -53,6 +53,20 @@ oneway interface IConnectionService { void createConnectionFailed(in PhoneAccountHandle connectionManagerPhoneAccount, String callId, in ConnectionRequest request, boolean isIncoming, in Session.Info sessionInfo); + void createConference( + in PhoneAccountHandle connectionManagerPhoneAccount, + String callId, + in ConnectionRequest request, + boolean isIncoming, + boolean isUnknown, + in Session.Info sessionInfo); + + void createConferenceComplete(String callId, in Session.Info sessionInfo); + + void createConferenceFailed(in PhoneAccountHandle connectionManagerPhoneAccount, String callId, + in ConnectionRequest request, boolean isIncoming, in Session.Info sessionInfo); + + void abort(String callId, in Session.Info sessionInfo); void answerVideo(String callId, int videoState, in Session.Info sessionInfo); diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl index 9cf098c75177..4f63e08abce6 100644 --- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl +++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl @@ -44,6 +44,12 @@ oneway interface IConnectionServiceAdapter { in ParcelableConnection connection, in Session.Info sessionInfo); + void handleCreateConferenceComplete( + String callId, + in ConnectionRequest request, + in ParcelableConference connection, + in Session.Info sessionInfo); + void setActive(String callId, in Session.Info sessionInfo); void setRinging(String callId, in Session.Info sessionInfo); diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl index 204c37e9aa38..9a47ae15e64a 100644 --- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl +++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl @@ -242,11 +242,22 @@ interface ITelecomService { void addNewIncomingCall(in PhoneAccountHandle phoneAccount, in Bundle extras); /** + * @see TelecomServiceImpl#addNewIncomingConference + */ + void addNewIncomingConference(in PhoneAccountHandle phoneAccount, in Bundle extras); + + /** * @see TelecomServiceImpl#addNewUnknownCall */ void addNewUnknownCall(in PhoneAccountHandle phoneAccount, in Bundle extras); /** + * @see TelecomServiceImpl#startConference + */ + void startConference(in List<Uri> participants, in Bundle extras, + String callingPackage); + + /** * @see TelecomServiceImpl#placeCall */ void placeCall(in Uri handle, in Bundle extras, String callingPackage); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 57a80183c4c2..77994420d83a 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1081,6 +1081,14 @@ public class CarrierConfigManager { public static final String KEY_IGNORE_RTT_MODE_SETTING_BOOL = "ignore_rtt_mode_setting_bool"; + + /** + * Determines whether adhoc conference calls are supported by a carrier. When {@code true}, + * adhoc conference calling is supported, {@code false otherwise}. + */ + public static final String KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL = + "support_adhoc_conference_calls_bool"; + /** * Determines whether conference calls are supported by a carrier. When {@code true}, * conference calling is supported, {@code false otherwise}. @@ -3558,6 +3566,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_CALL_FORWARDING_MAP_NON_NUMBER_TO_VOICEMAIL_BOOL, false); sDefaults.putBoolean(KEY_IGNORE_RTT_MODE_SETTING_BOOL, true); sDefaults.putInt(KEY_CDMA_3WAYCALL_FLASH_DELAY_INT , 0); + sDefaults.putBoolean(KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL, false); sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true); sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL, true); sDefaults.putBoolean(KEY_SUPPORT_MANAGE_IMS_CONFERENCE_CALL_BOOL, true); |