/** * Copyright (C) 2010-2012 Regis Montoya (aka r3gis - www.r3gis.fr) * This file is part of CSipSimple. * * CSipSimple is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * If you own a pjsip commercial license you can also redistribute it * and/or modify it under the terms of the GNU Lesser General Public License * as an android library. * * CSipSimple is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with CSipSimple. If not, see <http://www.gnu.org/licenses/>. * * This file and this file only is also released under Apache license as an API file */ package com.csipsimple.api; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; /** * Represents state of a call session<br/> * This class helps to serialize/deserialize the state of the media layer <br/> * <b>Changing these fields has no effect on the sip call session </b>: it's * only a structured holder for datas <br/> */ public class SipCallSession implements Parcelable { /** * Describe the control state of a call <br/> * <a target="_blank" href= * "http://www.pjsip.org/pjsip/docs/html/group__PJSIP__INV.htm#ga083ffd9c75c406c41f113479cc1ebc1c" * >Pjsip documentation</a> */ public static class InvState { /** * The call is in an invalid state not syncrhonized with sip stack */ public static final int INVALID = -1; /** * Before INVITE is sent or received */ public static final int NULL = 0; /** * After INVITE is sent */ public static final int CALLING = 1; /** * After INVITE is received. */ public static final int INCOMING = 2; /** * After response with To tag. */ public static final int EARLY = 3; /** * After 2xx is sent/received. */ public static final int CONNECTING = 4; /** * After ACK is sent/received. */ public static final int CONFIRMED = 5; /** * Session is terminated. */ public static final int DISCONNECTED = 6; // Should not be constructed, just an older for int values // Not an enum because easier to pass to Parcelable private InvState() { } } /** * Option key to flag video use for the call. <br/> * The value must be a boolean. * * @see Boolean */ public static final String OPT_CALL_VIDEO = "opt_call_video"; /** * Option key to add custom headers (with X- prefix). <br/> * The value must be a bundle with key representing header name, and value representing header value. * * @see Bundle */ public static final String OPT_CALL_EXTRA_HEADERS = "opt_call_extra_headers"; /** * Describe the media state of the call <br/> * <a target="_blank" href= * "http://www.pjsip.org/pjsip/docs/html/group__PJSUA__LIB__CALL.htm#ga0608027241a5462d9f2736e3a6b8e3f4" * >Pjsip documentation</a> */ public static class MediaState { /** * Call currently has no media */ public static final int NONE = 0; /** * The media is active */ public static final int ACTIVE = 1; /** * The media is currently put on hold by local endpoint */ public static final int LOCAL_HOLD = 2; /** * The media is currently put on hold by remote endpoint */ public static final int REMOTE_HOLD = 3; /** * The media has reported error (e.g. ICE negotiation) */ public static final int ERROR = 4; // Should not be constructed, just an older for int values // Not an enum because easier to pass to Parcelable private MediaState() { } } /** * Status code of the sip call dialog Actually just shortcuts to SIP codes<br/> * <a target="_blank" href= * "http://www.pjsip.org/pjsip/docs/html/group__PJSIP__MSG__LINE.htm#gaf6d60351ee68ca0c87358db2e59b9376" * >Pjsip documentation</a> */ public static class StatusCode { public static final int TRYING = 100; public static final int RINGING = 180; public static final int CALL_BEING_FORWARDED = 181; public static final int QUEUED = 182; public static final int PROGRESS = 183; public static final int OK = 200; public static final int ACCEPTED = 202; public static final int MULTIPLE_CHOICES = 300; public static final int MOVED_PERMANENTLY = 301; public static final int MOVED_TEMPORARILY = 302; public static final int USE_PROXY = 305; public static final int ALTERNATIVE_SERVICE = 380; public static final int BAD_REQUEST = 400; public static final int UNAUTHORIZED = 401; public static final int PAYMENT_REQUIRED = 402; public static final int FORBIDDEN = 403; public static final int NOT_FOUND = 404; public static final int METHOD_NOT_ALLOWED = 405; public static final int NOT_ACCEPTABLE = 406; public static final int INTERVAL_TOO_BRIEF = 423; public static final int BUSY_HERE = 486; public static final int INTERNAL_SERVER_ERROR = 500; public static final int DECLINE = 603; /* * PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED = 407, * PJSIP_SC_REQUEST_TIMEOUT = 408, PJSIP_SC_GONE = 410, * PJSIP_SC_REQUEST_ENTITY_TOO_LARGE = 413, * PJSIP_SC_REQUEST_URI_TOO_LONG = 414, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE * = 415, PJSIP_SC_UNSUPPORTED_URI_SCHEME = 416, PJSIP_SC_BAD_EXTENSION * = 420, PJSIP_SC_EXTENSION_REQUIRED = 421, * PJSIP_SC_SESSION_TIMER_TOO_SMALL = 422, * PJSIP_SC_TEMPORARILY_UNAVAILABLE = 480, * PJSIP_SC_CALL_TSX_DOES_NOT_EXIST = 481, PJSIP_SC_LOOP_DETECTED = 482, * PJSIP_SC_TOO_MANY_HOPS = 483, PJSIP_SC_ADDRESS_INCOMPLETE = 484, * PJSIP_AC_AMBIGUOUS = 485, PJSIP_SC_BUSY_HERE = 486, * PJSIP_SC_REQUEST_TERMINATED = 487, PJSIP_SC_NOT_ACCEPTABLE_HERE = * 488, PJSIP_SC_BAD_EVENT = 489, PJSIP_SC_REQUEST_UPDATED = 490, * PJSIP_SC_REQUEST_PENDING = 491, PJSIP_SC_UNDECIPHERABLE = 493, * PJSIP_SC_INTERNAL_SERVER_ERROR = 500, PJSIP_SC_NOT_IMPLEMENTED = 501, * PJSIP_SC_BAD_GATEWAY = 502, PJSIP_SC_SERVICE_UNAVAILABLE = 503, * PJSIP_SC_SERVER_TIMEOUT = 504, PJSIP_SC_VERSION_NOT_SUPPORTED = 505, * PJSIP_SC_MESSAGE_TOO_LARGE = 513, PJSIP_SC_PRECONDITION_FAILURE = * 580, PJSIP_SC_BUSY_EVERYWHERE = 600, PJSIP_SC_DOES_NOT_EXIST_ANYWHERE * = 604, PJSIP_SC_NOT_ACCEPTABLE_ANYWHERE = 606, */ } /** * The call signaling is not secure */ public static int TRANSPORT_SECURE_NONE = 0; /** * The call signaling is secure until it arrives on server. After, nothing ensures how it goes. */ public static int TRANSPORT_SECURE_TO_SERVER = 1; /** * The call signaling is supposed to be secured end to end. */ public static int TRANSPORT_SECURE_FULL = 2; /** * Id of an invalid or not existant call */ public static final int INVALID_CALL_ID = -1; /** * Primary key for the parcelable object */ public int primaryKey = -1; /** * The starting time of the call */ protected long callStart = 0; protected int callId = INVALID_CALL_ID; protected int callState = InvState.INVALID; protected String remoteContact; protected boolean isIncoming; protected int confPort = -1; protected long accId = SipProfile.INVALID_ID; protected int mediaStatus = MediaState.NONE; protected boolean mediaSecure = false; protected int transportSecure = 0; protected boolean mediaHasVideoStream = false; protected long connectStart = 0; protected int lastStatusCode = 0; protected String lastStatusComment = ""; protected int lastReasonCode = 0; protected String mediaSecureInfo = ""; protected boolean canRecord = false; protected boolean isRecording = false; protected boolean zrtpSASVerified = false; protected boolean hasZrtp = false; /** * Construct from parcelable <br/> * Only used by {@link #CREATOR} * * @param in parcelable to build from */ private SipCallSession(Parcel in) { initFromParcel(in); } /** * Constructor for a sip call session state object <br/> * It will contains default values for all flags This class as no * setter/getter for members flags <br/> * It's aim is to allow to serialize/deserialize easily the state of a sip * call, <n>not to modify it</b> */ public SipCallSession() { // Nothing to do in default constructor } /** * Constructor by copy * @param callInfo */ public SipCallSession(SipCallSession callInfo) { Parcel p = Parcel.obtain(); callInfo.writeToParcel(p, 0); p.setDataPosition(0); initFromParcel(p); p.recycle(); } private void initFromParcel(Parcel in) { primaryKey = in.readInt(); callId = in.readInt(); callState = in.readInt(); mediaStatus = in.readInt(); remoteContact = in.readString(); isIncoming = (in.readInt() == 1); confPort = in.readInt(); accId = in.readInt(); lastStatusCode = in.readInt(); mediaSecureInfo = in.readString(); connectStart = in.readLong(); mediaSecure = (in.readInt() == 1); lastStatusComment = in.readString(); mediaHasVideoStream = (in.readInt() == 1); canRecord = (in.readInt() == 1); isRecording = (in.readInt() == 1); hasZrtp = (in.readInt() == 1); zrtpSASVerified = (in.readInt() == 1); transportSecure = (in.readInt()); lastReasonCode = in.readInt(); } /** * @see Parcelable#describeContents() */ @Override public int describeContents() { return 0; } /** * @see Parcelable#writeToParcel(Parcel, int) */ @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(primaryKey); dest.writeInt(callId); dest.writeInt(callState); dest.writeInt(mediaStatus); dest.writeString(remoteContact); dest.writeInt(isIncoming() ? 1 : 0); dest.writeInt(confPort); dest.writeInt((int) accId); dest.writeInt(lastStatusCode); dest.writeString(mediaSecureInfo); dest.writeLong(connectStart); dest.writeInt(mediaSecure ? 1 : 0); dest.writeString(getLastStatusComment()); dest.writeInt(mediaHasVideo() ? 1 : 0); dest.writeInt(canRecord ? 1 : 0); dest.writeInt(isRecording ? 1 : 0); dest.writeInt(hasZrtp ? 1 : 0); dest.writeInt(zrtpSASVerified ? 1 : 0); dest.writeInt(transportSecure); dest.writeInt(lastReasonCode); } /** * Parcelable creator. So that it can be passed as an argument of the aidl * interface */ public static final Parcelable.Creator<SipCallSession> CREATOR = new Parcelable.Creator<SipCallSession>() { public SipCallSession createFromParcel(Parcel in) { return new SipCallSession(in); } public SipCallSession[] newArray(int size) { return new SipCallSession[size]; } }; /** * A sip call session is equal to another if both means the same callId */ @Override public boolean equals(Object o) { if (o == this) { return true; } if (!(o instanceof SipCallSession)) { return false; } SipCallSession ci = (SipCallSession) o; if (ci.getCallId() == callId) { return true; } return false; } // Getters / Setters /** * Get the call id of this call info * * @return id of this call */ public int getCallId() { return callId; } /** * Get the call state of this call info * * @return the invitation state * @see InvState */ public int getCallState() { return callState; } public int getMediaStatus() { return mediaStatus; } /** * Get the remote Contact for this call info * * @return string representing the remote contact */ public String getRemoteContact() { return remoteContact; } /** * Get the call way * * @return true if the remote party was the caller */ public boolean isIncoming() { return isIncoming; } /** * Get the start time of the connection of the call * * @return duration in milliseconds * @see SystemClock#elapsedRealtime() */ public long getConnectStart() { return connectStart; } /** * Check if the call state indicates that it is an active call in * progress. * This is equivalent to state incoming or early or calling or confirmed or connecting * * @return true if the call can be considered as in progress/active */ public boolean isActive() { return (callState == InvState.INCOMING || callState == InvState.EARLY || callState == InvState.CALLING || callState == InvState.CONFIRMED || callState == InvState.CONNECTING); } /** * Chef if the call state indicates that it's an ongoing call. * This is equivalent to state confirmed. * @return true if the call can be considered as ongoing. */ public boolean isOngoing() { return callState == InvState.CONFIRMED; } /** * Get the sounds conference board port <br/> * <a target="_blank" href= * "http://www.pjsip.org/pjsip/docs/html/group__PJSUA__LIB__BASE.htm#gaf5d44947e4e62dc31dfde88884534385" * >Pjsip documentation</a> * * @return the conf port of the audio media of this call */ public int getConfPort() { return confPort; } /** * Get the identifier of the account corresponding to this call <br/> * This identifier is the one you have in {@link SipProfile#id} <br/> * It may return {@link SipProfile#INVALID_ID} if no account detected for * this call. <i>Example, case of peer to peer call</i> * * @return The {@link SipProfile#id} of the account use for this call */ public long getAccId() { return accId; } /** * Get the secure level of the signaling of the call. * * @return one of {@link #TRANSPORT_SECURE_NONE}, {@link #TRANSPORT_SECURE_TO_SERVER}, {@link #TRANSPORT_SECURE_FULL} */ public int getTransportSecureLevel() { return transportSecure; } /** * Get the secure level of the media of the call * * @return true if the call has a <b>media</b> encrypted */ public boolean isMediaSecure() { return mediaSecure; } /** * Get the information about the <b>media</b> security of this call * * @return the information about the <b>media</b> security */ public String getMediaSecureInfo() { return mediaSecureInfo; } /** * Get the information about local held state of this call * * @return the information about local held state of media */ public boolean isLocalHeld() { return mediaStatus == SipCallSession.MediaState.LOCAL_HOLD; } /** * Get the information about remote held state of this call * * @return the information about remote held state of media */ public boolean isRemoteHeld() { return (mediaStatus == SipCallSession.MediaState.NONE && isActive() && !isBeforeConfirmed()); } /** * Check if the specific call info indicates that it is a call that has not yet been confirmed by both ends.<br/> * In other worlds if the call is in state, calling, incoming early or connecting. * * @return true if the call can be considered not yet been confirmed */ public boolean isBeforeConfirmed() { return (callState == InvState.CALLING || callState == InvState.INCOMING || callState == InvState.EARLY || callState == InvState.CONNECTING); } /** * Check if the specific call info indicates that it is a call that has been ended<br/> * In other worlds if the call is in state, disconnected, invalid or null * * @return true if the call can be considered as already ended */ public boolean isAfterEnded() { return (callState == InvState.DISCONNECTED || callState == InvState.INVALID || callState == InvState.NULL); } /** * Get the latest status code of the sip dialog corresponding to this call * call * * @return the status code * @see SipCallSession.StatusCode */ public int getLastStatusCode() { return lastStatusCode; } /** * Get the last status comment of the sip dialog corresponding to this call * * @return the last status comment string from server */ public String getLastStatusComment() { return lastStatusComment; } /** * Get the latest SIP reason code if any. * For now only supports 200 (if SIP reason is set to 200) or 0 in other cases (no SIP reason / sip reason set to something different). * * @return the status code */ public int getLastReasonCode() { return lastReasonCode; } /** * Get whether the call has a video media stream connected * * @return true if the call has a video media stream */ public boolean mediaHasVideo() { return mediaHasVideoStream; } /** * Get the current call recording status for this call. * * @return true if we are currently recording this call to a file */ public boolean isRecording() { return isRecording; } /** * Get the capability to record the call to a file. * * @return true if it should be possible to record the call to a file */ public boolean canRecord() { return canRecord; } /** * @return the zrtpSASVerified */ public boolean isZrtpSASVerified() { return zrtpSASVerified; } /** * @return whether call has Zrtp encryption active */ public boolean getHasZrtp() { return hasZrtp; } /** * Get the start time of the call. * @return the callStart start time of the call. */ public long getCallStart() { return callStart; } }