Home | History | Annotate | Download | only in telecom
      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 (at) 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      */
     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
    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
    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 (at) 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 (at) 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 (at) 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
    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
    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      */
    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     }
    254 
    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     /**
    267      * The connect elapsed time of the participant to the conference.
    268      */
    269     public long getConnectElapsedTime() {
    270         return mConnectElapsedTime;
    271     }
    272 
    273     public void setConnectElapsedTime(long connectElapsedTime) {
    274         mConnectElapsedTime = connectElapsedTime;
    275     }
    276 
    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 (at) 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 (at) 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