blob: 5e4818a67b31ccb52bcf08767e7896341c9f37d3 [file] [log] [blame]
Tyler Gunn3bffcf72014-10-28 13:51:27 -07001/*
2 * Copyright (C) 2014 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
17package android.telecom;
18
19import android.net.Uri;
20import android.os.Parcel;
21import android.os.Parcelable;
Tyler Gunn6986a632019-06-25 13:45:32 -070022import android.telephony.PhoneNumberUtils;
Youming Yed6de26e2019-01-30 11:20:35 -080023import android.text.TextUtils;
24
25import com.android.internal.annotations.VisibleForTesting;
26import com.android.internal.telephony.PhoneConstants;
Tyler Gunn3bffcf72014-10-28 13:51:27 -070027
28/**
29 * Parcelable representation of a participant's state in a conference call.
30 * @hide
31 */
32public class ConferenceParticipant implements Parcelable {
33
34 /**
Youming Yed6de26e2019-01-30 11:20:35 -080035 * RFC5767 states that a SIP URI with an unknown number should use an address of
36 * {@code anonymous@anonymous.invalid}. E.g. the host name is anonymous.invalid.
37 */
38 private static final String ANONYMOUS_INVALID_HOST = "anonymous.invalid";
39 /**
Tyler Gunn3bffcf72014-10-28 13:51:27 -070040 * The conference participant's handle (e.g., phone number).
41 */
42 private final Uri mHandle;
43
44 /**
45 * The display name for the participant.
46 */
47 private final String mDisplayName;
48
49 /**
50 * The endpoint Uri which uniquely identifies this conference participant. E.g. for an IMS
51 * conference call, this is the endpoint URI for the participant on the IMS conference server.
52 */
53 private final Uri mEndpoint;
54
55 /**
56 * The state of the participant in the conference.
57 *
58 * @see android.telecom.Connection
59 */
60 private final int mState;
61
62 /**
Youming Yed6de26e2019-01-30 11:20:35 -080063 * The connect time of the participant.
64 */
65 private long mConnectTime;
66
67 /**
68 * The connect elapsed time of the participant.
69 */
70 private long mConnectElapsedTime;
71
72 /**
Tyler Gunn6986a632019-06-25 13:45:32 -070073 * The direction of the call;
74 * {@link Call.Details#DIRECTION_INCOMING} for incoming calls, or
75 * {@link Call.Details#DIRECTION_OUTGOING} for outgoing calls.
76 */
77 private int mCallDirection;
78
79 /**
Tyler Gunn3bffcf72014-10-28 13:51:27 -070080 * Creates an instance of {@code ConferenceParticipant}.
81 *
82 * @param handle The conference participant's handle (e.g., phone number).
83 * @param displayName The display name for the participant.
84 * @param endpoint The enpoint Uri which uniquely identifies this conference participant.
85 * @param state The state of the participant in the conference.
Tyler Gunn6986a632019-06-25 13:45:32 -070086 * @param callDirection The direction of the call (incoming/outgoing).
Tyler Gunn3bffcf72014-10-28 13:51:27 -070087 */
Tyler Gunn6986a632019-06-25 13:45:32 -070088 public ConferenceParticipant(Uri handle, String displayName, Uri endpoint, int state,
89 int callDirection) {
Tyler Gunn3bffcf72014-10-28 13:51:27 -070090 mHandle = handle;
91 mDisplayName = displayName;
92 mEndpoint = endpoint;
93 mState = state;
Tyler Gunn6986a632019-06-25 13:45:32 -070094 mCallDirection = callDirection;
Tyler Gunn3bffcf72014-10-28 13:51:27 -070095 }
96
97 /**
98 * Responsible for creating {@code ConferenceParticipant} objects for deserialized Parcels.
99 */
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700100 public static final @android.annotation.NonNull Parcelable.Creator<ConferenceParticipant> CREATOR =
Tyler Gunn3bffcf72014-10-28 13:51:27 -0700101 new Parcelable.Creator<ConferenceParticipant>() {
102
103 @Override
104 public ConferenceParticipant createFromParcel(Parcel source) {
105 ClassLoader classLoader = ParcelableCall.class.getClassLoader();
106 Uri handle = source.readParcelable(classLoader);
107 String displayName = source.readString();
108 Uri endpoint = source.readParcelable(classLoader);
109 int state = source.readInt();
Tyler Gunn6986a632019-06-25 13:45:32 -0700110 long connectTime = source.readLong();
111 long elapsedRealTime = source.readLong();
112 int callDirection = source.readInt();
113 ConferenceParticipant participant =
114 new ConferenceParticipant(handle, displayName, endpoint, state,
115 callDirection);
116 participant.setConnectTime(connectTime);
117 participant.setConnectElapsedTime(elapsedRealTime);
118 participant.setCallDirection(callDirection);
119 return participant;
Tyler Gunn3bffcf72014-10-28 13:51:27 -0700120 }
121
122 @Override
123 public ConferenceParticipant[] newArray(int size) {
124 return new ConferenceParticipant[size];
125 }
126 };
127
128 @Override
129 public int describeContents() {
130 return 0;
131 }
132
133 /**
Youming Yed6de26e2019-01-30 11:20:35 -0800134 * Determines the number presentation for a conference participant. Per RFC5767, if the host
135 * name contains {@code anonymous.invalid} we can assume that there is no valid caller ID
136 * information for the caller, otherwise we'll assume that the URI can be shown.
137 *
138 * @return The number presentation.
139 */
140 @VisibleForTesting
141 public int getParticipantPresentation() {
142 Uri address = getHandle();
143 if (address == null) {
144 return PhoneConstants.PRESENTATION_RESTRICTED;
145 }
146
147 String number = address.getSchemeSpecificPart();
148 // If no number, bail early and set restricted presentation.
149 if (TextUtils.isEmpty(number)) {
150 return PhoneConstants.PRESENTATION_RESTRICTED;
151 }
152 // Per RFC3261, the host name portion can also potentially include extra information:
153 // E.g. sip:anonymous1@anonymous.invalid;legid=1
154 // In this case, hostName will be anonymous.invalid and there is an extra parameter for
155 // legid=1.
156 // Parameters are optional, and the address (e.g. test@test.com) will always be the first
157 // part, with any parameters coming afterwards.
158 String [] hostParts = number.split("[;]");
159 String addressPart = hostParts[0];
160
161 // Get the number portion from the address part.
162 // This will typically be formatted similar to: 6505551212@test.com
163 String [] numberParts = addressPart.split("[@]");
164
165 // If we can't parse the host name out of the URI, then there is probably other data
166 // present, and is likely a valid SIP URI.
167 if (numberParts.length != 2) {
168 return PhoneConstants.PRESENTATION_ALLOWED;
169 }
170 String hostName = numberParts[1];
171
172 // If the hostname portion of the SIP URI is the invalid host string, presentation is
173 // restricted.
174 if (hostName.equals(ANONYMOUS_INVALID_HOST)) {
175 return PhoneConstants.PRESENTATION_RESTRICTED;
176 }
177
178 return PhoneConstants.PRESENTATION_ALLOWED;
179 }
180
181 /**
Tyler Gunn3bffcf72014-10-28 13:51:27 -0700182 * Writes the {@code ConferenceParticipant} to a parcel.
183 *
184 * @param dest The Parcel in which the object should be written.
185 * @param flags Additional flags about how the object should be written.
186 */
187 @Override
188 public void writeToParcel(Parcel dest, int flags) {
189 dest.writeParcelable(mHandle, 0);
190 dest.writeString(mDisplayName);
191 dest.writeParcelable(mEndpoint, 0);
192 dest.writeInt(mState);
Tyler Gunn6986a632019-06-25 13:45:32 -0700193 dest.writeLong(mConnectTime);
194 dest.writeLong(mConnectElapsedTime);
195 dest.writeInt(mCallDirection);
Tyler Gunn3bffcf72014-10-28 13:51:27 -0700196 }
197
198 /**
199 * Builds a string representation of this instance.
200 *
201 * @return String representing the conference participant.
202 */
203 @Override
204 public String toString() {
205 StringBuilder sb = new StringBuilder();
206 sb.append("[ConferenceParticipant Handle: ");
Tyler Gunn5de68862016-07-12 08:28:54 -0700207 sb.append(Log.pii(mHandle));
Tyler Gunn3bffcf72014-10-28 13:51:27 -0700208 sb.append(" DisplayName: ");
Tyler Gunn5de68862016-07-12 08:28:54 -0700209 sb.append(Log.pii(mDisplayName));
Tyler Gunn3bffcf72014-10-28 13:51:27 -0700210 sb.append(" Endpoint: ");
Tyler Gunn5de68862016-07-12 08:28:54 -0700211 sb.append(Log.pii(mEndpoint));
Tyler Gunn3bffcf72014-10-28 13:51:27 -0700212 sb.append(" State: ");
Tyler Gunn5de68862016-07-12 08:28:54 -0700213 sb.append(Connection.stateToString(mState));
Youming Yed6de26e2019-01-30 11:20:35 -0800214 sb.append(" ConnectTime: ");
215 sb.append(getConnectTime());
216 sb.append(" ConnectElapsedTime: ");
217 sb.append(getConnectElapsedTime());
Tyler Gunn6986a632019-06-25 13:45:32 -0700218 sb.append(" Direction: ");
219 sb.append(getCallDirection() == Call.Details.DIRECTION_INCOMING ? "Incoming" : "Outgoing");
Tyler Gunn3bffcf72014-10-28 13:51:27 -0700220 sb.append("]");
221 return sb.toString();
222 }
223
224 /**
225 * The conference participant's handle (e.g., phone number).
226 */
227 public Uri getHandle() {
228 return mHandle;
229 }
230
231 /**
232 * The display name for the participant.
233 */
234 public String getDisplayName() {
235 return mDisplayName;
236 }
237
238 /**
239 * The enpoint Uri which uniquely identifies this conference participant. E.g. for an IMS
240 * conference call, this is the endpoint URI for the participant on the IMS conference server.
241 */
242 public Uri getEndpoint() {
243 return mEndpoint;
244 }
245
246 /**
247 * The state of the participant in the conference.
248 *
249 * @see android.telecom.Connection
250 */
251 public int getState() {
252 return mState;
253 }
Youming Yed6de26e2019-01-30 11:20:35 -0800254
255 /**
256 * The connect time of the participant to the conference.
257 */
258 public long getConnectTime() {
259 return mConnectTime;
260 }
261
262 public void setConnectTime(long connectTime) {
263 this.mConnectTime = connectTime;
264 }
265
266 /**
Tyler Gunn6986a632019-06-25 13:45:32 -0700267 * The connect elapsed time of the participant to the conference.
Youming Yed6de26e2019-01-30 11:20:35 -0800268 */
269 public long getConnectElapsedTime() {
270 return mConnectElapsedTime;
271 }
272
273 public void setConnectElapsedTime(long connectElapsedTime) {
274 mConnectElapsedTime = connectElapsedTime;
275 }
Tyler Gunn6986a632019-06-25 13:45:32 -0700276
277 /**
278 * @return The direction of the call (incoming/outgoing).
279 */
280 public @Call.Details.CallDirection int getCallDirection() {
281 return mCallDirection;
282 }
283
284 /**
285 * Sets the direction of the call.
286 * @param callDirection Whether the call is incoming or outgoing.
287 */
288 public void setCallDirection(@Call.Details.CallDirection int callDirection) {
289 mCallDirection = callDirection;
290 }
291
292 /**
293 * Attempts to build a tel: style URI from a conference participant.
294 * Conference event package data contains SIP URIs, so we try to extract the phone number and
295 * format into a typical tel: style URI.
296 *
297 * @param address The conference participant's address.
298 * @param countryIso The country ISO of the current subscription; used when formatting the
299 * participant phone number to E.164 format.
300 * @return The participant's address URI.
301 * @hide
302 */
303 @VisibleForTesting
304 public static Uri getParticipantAddress(Uri address, String countryIso) {
305 if (address == null) {
306 return address;
307 }
308 // Even if address is already in tel: format, still parse it and rebuild.
309 // This is to recognize tel URIs such as:
310 // tel:6505551212;phone-context=ims.mnc012.mcc034.3gppnetwork.org
311
312 // Conference event package participants are identified using SIP URIs (see RFC3261).
313 // A valid SIP uri has the format: sip:user:password@host:port;uri-parameters?headers
314 // Per RFC3261, the "user" can be a telephone number.
315 // For example: sip:1650555121;phone-context=blah.com@host.com
316 // In this case, the phone number is in the user field of the URI, and the parameters can be
317 // ignored.
318 //
319 // A SIP URI can also specify a phone number in a format similar to:
320 // sip:+1-212-555-1212@something.com;user=phone
321 // In this case, the phone number is again in user field and the parameters can be ignored.
322 // We can get the user field in these instances by splitting the string on the @, ;, or :
323 // and looking at the first found item.
324 String number = address.getSchemeSpecificPart();
325 if (TextUtils.isEmpty(number)) {
326 return address;
327 }
328
329 String numberParts[] = number.split("[@;:]");
330 if (numberParts.length == 0) {
331 return address;
332 }
333 number = numberParts[0];
334
335 // Attempt to format the number in E.164 format and use that as part of the TEL URI.
336 // RFC2806 recommends to format telephone numbers using E.164 since it is independent of
337 // how the dialing of said numbers takes place.
338 // If conversion to E.164 fails, the returned value is null. In that case, fallback to the
339 // number which was in the CEP data.
340 String formattedNumber = null;
341 if (!TextUtils.isEmpty(countryIso)) {
342 formattedNumber = PhoneNumberUtils.formatNumberToE164(number, countryIso);
343 }
344
345 return Uri.fromParts(PhoneAccount.SCHEME_TEL,
346 formattedNumber != null ? formattedNumber : number, null);
347 }
Tyler Gunn3bffcf72014-10-28 13:51:27 -0700348}