• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package android.telecom;
18 
19 import android.net.Uri;
20 import android.os.Parcel;
21 import android.os.Parcelable;
22 import android.telephony.PhoneNumberUtils;
23 import android.text.TextUtils;
24 
25 import com.android.internal.annotations.VisibleForTesting;
26 import com.android.internal.telephony.PhoneConstants;
27 
28 /**
29  * Parcelable representation of a participant's state in a conference call.
30  * @hide
31  */
32 public class ConferenceParticipant implements Parcelable {
33 
34     /**
35      * 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     /**
40      * 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     /**
63      * 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     /**
73      * 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     /**
80      * 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.
86      * @param callDirection The direction of the call (incoming/outgoing).
87      */
ConferenceParticipant(Uri handle, String displayName, Uri endpoint, int state, int callDirection)88     public ConferenceParticipant(Uri handle, String displayName, Uri endpoint, int state,
89             int callDirection) {
90         mHandle = handle;
91         mDisplayName = displayName;
92         mEndpoint = endpoint;
93         mState = state;
94         mCallDirection = callDirection;
95     }
96 
97     /**
98      * Responsible for creating {@code ConferenceParticipant} objects for deserialized Parcels.
99      */
100     public static final @android.annotation.NonNull Parcelable.Creator<ConferenceParticipant> CREATOR =
101             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();
110                     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;
120                 }
121 
122                 @Override
123                 public ConferenceParticipant[] newArray(int size) {
124                     return new ConferenceParticipant[size];
125                 }
126             };
127 
128     @Override
describeContents()129     public int describeContents() {
130         return 0;
131     }
132 
133     /**
134      * 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
getParticipantPresentation()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     /**
182      * 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
writeToParcel(Parcel dest, int flags)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);
193         dest.writeLong(mConnectTime);
194         dest.writeLong(mConnectElapsedTime);
195         dest.writeInt(mCallDirection);
196     }
197 
198     /**
199      * Builds a string representation of this instance.
200      *
201      * @return String representing the conference participant.
202      */
203     @Override
toString()204     public String toString() {
205         StringBuilder sb = new StringBuilder();
206         sb.append("[ConferenceParticipant Handle: ");
207         sb.append(Log.pii(mHandle));
208         sb.append(" DisplayName: ");
209         sb.append(Log.pii(mDisplayName));
210         sb.append(" Endpoint: ");
211         sb.append(Log.pii(mEndpoint));
212         sb.append(" State: ");
213         sb.append(Connection.stateToString(mState));
214         sb.append(" ConnectTime: ");
215         sb.append(getConnectTime());
216         sb.append(" ConnectElapsedTime: ");
217         sb.append(getConnectElapsedTime());
218         sb.append(" Direction: ");
219         sb.append(getCallDirection() == Call.Details.DIRECTION_INCOMING ? "Incoming" : "Outgoing");
220         sb.append("]");
221         return sb.toString();
222     }
223 
224     /**
225      * The conference participant's handle (e.g., phone number).
226      */
getHandle()227     public Uri getHandle() {
228         return mHandle;
229     }
230 
231     /**
232      * The display name for the participant.
233      */
getDisplayName()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      */
getEndpoint()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      */
getState()251     public int getState() {
252         return mState;
253     }
254 
255     /**
256      * The connect time of the participant to the conference.
257      */
getConnectTime()258     public long getConnectTime() {
259         return mConnectTime;
260     }
261 
setConnectTime(long connectTime)262     public void setConnectTime(long connectTime) {
263         this.mConnectTime = connectTime;
264     }
265 
266     /**
267      * The connect elapsed time of the participant to the conference.
268      */
getConnectElapsedTime()269     public long getConnectElapsedTime() {
270         return mConnectElapsedTime;
271     }
272 
setConnectElapsedTime(long connectElapsedTime)273     public void setConnectElapsedTime(long connectElapsedTime) {
274         mConnectElapsedTime = connectElapsedTime;
275     }
276 
277     /**
278      * @return The direction of the call (incoming/outgoing).
279      */
getCallDirection()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      */
setCallDirection(@all.Details.CallDirection int callDirection)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
getParticipantAddress(Uri address, String countryIso)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     }
348 }
349