1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package android.telephony.ims;
18 
19 import android.annotation.NonNull;
20 import android.annotation.SystemApi;
21 import android.annotation.TestApi;
22 import android.os.Bundle;
23 import android.os.Parcel;
24 import android.os.Parcelable;
25 import android.telecom.Call;
26 import android.telecom.Connection;
27 
28 import com.android.telephony.Rlog;
29 
30 import java.util.HashMap;
31 import java.util.Iterator;
32 import java.util.Map.Entry;
33 import java.util.Set;
34 
35 /**
36  * Provides the conference information (defined in RFC 4575) for IMS conference call.
37  *
38  * @hide
39  */
40 @SystemApi
41 @TestApi
42 public final class ImsConferenceState implements Parcelable {
43     private static final String TAG = "ImsConferenceState";
44     /**
45      * conference-info : user
46      */
47     // user (String) : Tel or SIP URI
48     public static final String USER = "user";
49     // user > display text (String)
50     public static final String DISPLAY_TEXT = "display-text";
51     // user > endpoint (String) : URI or GRUU or Phone number
52     public static final String ENDPOINT = "endpoint";
53     // user > endpoint > status
54     public static final String STATUS = "status";
55 
56     /**
57      * status-type (String) :
58      * "pending" : Endpoint is not yet in the session, but it is anticipated that he/she will
59      *      join in the near future.
60      * "dialing-out" : Focus has dialed out to connect the endpoint to the conference,
61      *      but the endpoint is not yet in the roster (probably being authenticated).
62      * "dialing-in" : Endpoint is dialing into the conference, not yet in the roster
63      *      (probably being authenticated).
64      * "alerting" : PSTN alerting or SIP 180 Ringing was returned for the outbound call;
65      *      endpoint is being alerted.
66      * "on-hold" : Active signaling dialog exists between an endpoint and a focus,
67      *      but endpoint is "on-hold" for this conference, i.e., he/she is neither "hearing"
68      *      the conference mix nor is his/her media being mixed in the conference.
69      * "connected" : Endpoint is a participant in the conference. Depending on the media policies,
70      *      he/she can send and receive media to and from other participants.
71      * "disconnecting" : Focus is in the process of disconnecting the endpoint
72      *      (e.g. in SIP a DISCONNECT or BYE was sent to the endpoint).
73      * "disconnected" : Endpoint is not a participant in the conference, and no active dialog
74      *      exists between the endpoint and the focus.
75      * "muted-via-focus" : Active signaling dialog exists beween an endpoint and a focus and
76      *      the endpoint can "listen" to the conference, but the endpoint's media is not being
77      *      mixed into the conference.
78      * "connect-fail" : Endpoint fails to join the conference by rejecting the conference call.
79      */
80     public static final String STATUS_PENDING = "pending";
81     public static final String STATUS_DIALING_OUT = "dialing-out";
82     public static final String STATUS_DIALING_IN = "dialing-in";
83     public static final String STATUS_ALERTING = "alerting";
84     public static final String STATUS_ON_HOLD = "on-hold";
85     public static final String STATUS_CONNECTED = "connected";
86     public static final String STATUS_DISCONNECTING = "disconnecting";
87     public static final String STATUS_DISCONNECTED = "disconnected";
88     public static final String STATUS_MUTED_VIA_FOCUS = "muted-via-focus";
89     public static final String STATUS_CONNECT_FAIL = "connect-fail";
90     public static final String STATUS_SEND_ONLY = "sendonly";
91     public static final String STATUS_SEND_RECV = "sendrecv";
92 
93     /**
94      * conference-info : SIP status code (integer)
95      */
96     public static final String SIP_STATUS_CODE = "sipstatuscode";
97 
98     public final HashMap<String, Bundle> mParticipants = new HashMap<String, Bundle>();
99 
100     /** @hide */
ImsConferenceState()101     public ImsConferenceState() {
102     }
103 
ImsConferenceState(Parcel in)104     private ImsConferenceState(Parcel in) {
105         readFromParcel(in);
106     }
107 
108     @Override
describeContents()109     public int describeContents() {
110         return 0;
111     }
112 
113     @Override
writeToParcel(Parcel out, int flags)114     public void writeToParcel(Parcel out, int flags) {
115         out.writeInt(mParticipants.size());
116 
117         if (mParticipants.size() > 0) {
118             Set<Entry<String, Bundle>> entries = mParticipants.entrySet();
119 
120             if (entries != null) {
121                 Iterator<Entry<String, Bundle>> iterator = entries.iterator();
122 
123                 while (iterator.hasNext()) {
124                     Entry<String, Bundle> entry = iterator.next();
125 
126                     out.writeString(entry.getKey());
127                     out.writeParcelable(entry.getValue(), 0);
128                 }
129             }
130         }
131     }
132 
readFromParcel(Parcel in)133     private void readFromParcel(Parcel in) {
134         int size = in.readInt();
135 
136         for (int i = 0; i < size; ++i) {
137             String user = in.readString();
138             Bundle state = in.readParcelable(null);
139             mParticipants.put(user, state);
140         }
141     }
142 
143     public static final @android.annotation.NonNull Creator<ImsConferenceState> CREATOR =
144             new Creator<ImsConferenceState>() {
145         @Override
146         public ImsConferenceState createFromParcel(Parcel in) {
147             return new ImsConferenceState(in);
148         }
149 
150         @Override
151         public ImsConferenceState[] newArray(int size) {
152             return new ImsConferenceState[size];
153         }
154     };
155 
156     /**
157      * Translates an {@code ImsConferenceState} status type to a telecom connection state.
158      *
159      * @param status The status type.
160      * @return The corresponding {@link android.telecom.Connection} state.
161      */
getConnectionStateForStatus(String status)162     public static int getConnectionStateForStatus(String status) {
163         if (status.equals(STATUS_PENDING)) {
164             return Connection.STATE_INITIALIZING;
165         } else if (status.equals(STATUS_DIALING_IN)) {
166             return Connection.STATE_RINGING;
167         } else if (status.equals(STATUS_ALERTING) ||
168                 status.equals(STATUS_DIALING_OUT)) {
169             return Connection.STATE_DIALING;
170         } else if (status.equals(STATUS_ON_HOLD) ||
171                 status.equals(STATUS_SEND_ONLY)) {
172             return Connection.STATE_HOLDING;
173         } else if (status.equals(STATUS_CONNECTED) ||
174                 status.equals(STATUS_MUTED_VIA_FOCUS) ||
175                 status.equals(STATUS_DISCONNECTING) ||
176                 status.equals(STATUS_SEND_RECV)) {
177             return Connection.STATE_ACTIVE;
178         } else if (status.equals(STATUS_DISCONNECTED)) {
179             return Connection.STATE_DISCONNECTED;
180         }
181         return Call.STATE_ACTIVE;
182     }
183 
184     @NonNull
185     @Override
toString()186     public String toString() {
187         StringBuilder sb = new StringBuilder();
188         sb.append("[");
189         sb.append(ImsConferenceState.class.getSimpleName());
190         sb.append(" ");
191         if (mParticipants.size() > 0) {
192             Set<Entry<String, Bundle>> entries = mParticipants.entrySet();
193 
194             if (entries != null) {
195                 Iterator<Entry<String, Bundle>> iterator = entries.iterator();
196                 sb.append("<");
197                 while (iterator.hasNext()) {
198                     Entry<String, Bundle> entry = iterator.next();
199                     sb.append(Rlog.pii(TAG, entry.getKey()));
200                     sb.append(": ");
201                     Bundle participantData = entry.getValue();
202 
203                     for (String key : participantData.keySet()) {
204                         sb.append(key);
205                         sb.append("=");
206                         if (ENDPOINT.equals(key) || USER.equals(key)) {
207                             sb.append(Rlog.pii(TAG, participantData.get(key)));
208                         } else {
209                             sb.append(participantData.get(key));
210                         }
211                         sb.append(", ");
212                     }
213                 }
214                 sb.append(">");
215             }
216         }
217         sb.append("]");
218         return sb.toString();
219     }
220 }
221