/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

package android.telephony.ims;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.telecom.VideoProfile;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.emergency.EmergencyNumber.EmergencyCallRouting;
import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.util.TelephonyUtils;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;

/**
 * A Parcelable object to handle the IMS call profile, which provides the service, call type, and
 * additional information related to the call.
 * <p>
 * See the following specifications for more information about this class: GSMA IR.92/IR.94,
 * 3GPP TS 24.229/TS 26.114/TS26.111.
 * @hide
 */
@SystemApi
@TestApi
public final class ImsCallProfile implements Parcelable {
    private static final String TAG = "ImsCallProfile";

    /**
     * Service types
     */
    /**
     * It is for a special case. It helps that the application can make a call
     * without IMS connection (not registered).
     * In the moment of the call initiation, the device try to connect to the IMS network
     * and initiates the call.
     */
    public static final int SERVICE_TYPE_NONE = 0;
    /**
     * It is a default type and can be selected when the device is connected to the IMS network.
     */
    public static final int SERVICE_TYPE_NORMAL = 1;
    /**
     * It is for an emergency call.
     */
    public static final int SERVICE_TYPE_EMERGENCY = 2;

    /**
     * Call types
     */
    /**
     * IMSPhone to support IR.92 & IR.94 (voice + video upgrade/downgrade)
     */
    public static final int CALL_TYPE_VOICE_N_VIDEO = 1;
    /**
     * IR.92 (Voice only)
     */
    public static final int CALL_TYPE_VOICE = 2;
    /**
     * VT to support IR.92 & IR.94 (voice + video upgrade/downgrade)
     */
    public static final int CALL_TYPE_VIDEO_N_VOICE = 3;
    /**
     * Video Telephony (audio / video two way)
     */
    public static final int CALL_TYPE_VT = 4;
    /**
     * Video Telephony (audio two way / video TX one way)
     */
    public static final int CALL_TYPE_VT_TX = 5;
    /**
     * Video Telephony (audio two way / video RX one way)
     */
    public static final int CALL_TYPE_VT_RX = 6;
    /**
     * Video Telephony (audio two way / video inactive)
     */
    public static final int CALL_TYPE_VT_NODIR = 7;
    /**
     * VideoShare (video two way)
     */
    public static final int CALL_TYPE_VS = 8;
    /**
     * VideoShare (video TX one way)
     */
    public static final int CALL_TYPE_VS_TX = 9;
    /**
     * VideoShare (video RX one way)
     */
    public static final int CALL_TYPE_VS_RX = 10;

    /**
     * Extra properties for IMS call.
     */
    /**
     * Boolean extra properties - "true" / "false"
     *  conference : Indicates if the session is for the conference call or not.
     *  e_call : Indicates if the session is for the emergency call or not.
     *  vms : Indicates if the session is connected to the voice mail system or not.
     *  call_mode_changeable : Indicates if the session is able to upgrade/downgrade
     *      the video during voice call.
     *  conference_avail : Indicates if the session can be extended to the conference.
     */
    /**
     * @hide
     */
    public static final String EXTRA_CONFERENCE = "conference";

    /**
     * Boolean extra property set on an {@link ImsCallProfile} to indicate that this call is an
     * emergency call.  The {@link ImsService} sets this on a call to indicate that the network has
     * identified the call as an emergency call.
     */
    public static final String EXTRA_EMERGENCY_CALL = "e_call";

    /**
     * @hide
     */
    public static final String EXTRA_VMS = "vms";
    /**
     * @hide
     */
    public static final String EXTRA_CALL_MODE_CHANGEABLE = "call_mode_changeable";
    /**
     * @hide
     */
    public static final String EXTRA_CONFERENCE_AVAIL = "conference_avail";

    /**
     * Extra key used to store a Bundle containing proprietary extras to send to the ImsService.
     * Use {@link #getProprietaryCallExtras()} instead.
     * @hide
     */
    @TestApi
    public static final String EXTRA_OEM_EXTRAS = "android.telephony.ims.extra.OEM_EXTRAS";

    /**
     * Rule for originating identity (number) presentation, MO/MT.
     *      {@link ImsCallProfile#OIR_DEFAULT}
     *      {@link ImsCallProfile#OIR_PRESENTATION_RESTRICTED}
     *      {@link ImsCallProfile#OIR_PRESENTATION_NOT_RESTRICTED}
     */
    public static final String EXTRA_OIR = "oir";
    /**
     * Rule for calling name presentation
     *      {@link ImsCallProfile#OIR_DEFAULT}
     *      {@link ImsCallProfile#OIR_PRESENTATION_RESTRICTED}
     *      {@link ImsCallProfile#OIR_PRESENTATION_NOT_RESTRICTED}
     */
    public static final String EXTRA_CNAP = "cnap";
    /**
     * To identify the Ims call type, MO
     *      {@link ImsCallProfile#DIALSTRING_NORMAL}
     *      {@link ImsCallProfile#DIALSTRING_SS_CONF}
     *      {@link ImsCallProfile#DIALSTRING_USSD}
     */
    public static final String EXTRA_DIALSTRING = "dialstring";
    /**
     * This extra holds call fail cause because of which redial is attempted.
     * see {@link android.telephony.ims.ImsReasonInfo} {@code CODE_*}
     * for possible values this extra can hold.
     *
     * @hide
     */
    public static final String EXTRA_RETRY_CALL_FAIL_REASON =
            "android.telephony.ims.extra.RETRY_CALL_FAIL_REASON";
    /**
     * This extra holds call network type on which lower layers
     * may try attempting redial.
     * See {@link TelephonyManager} {@code NETWORK_TYPE_*}
     * for possible values this extra can hold.
     *
     * @hide
     */
    public static final String EXTRA_RETRY_CALL_FAIL_NETWORKTYPE =
            "android.telephony.ims.extra.RETRY_CALL_FAIL_NETWORKTYPE";

    /**
     * Values for EXTRA_OIR / EXTRA_CNAP
     */
    /**
     * Default presentation for Originating Identity.
     */
    public static final int OIR_DEFAULT = 0;    // "user subscription default value"
    /**
     * Restricted presentation for Originating Identity.
     */
    public static final int OIR_PRESENTATION_RESTRICTED = 1;
    /**
     * Not restricted presentation for Originating Identity.
     */
    public static final int OIR_PRESENTATION_NOT_RESTRICTED = 2;
    /**
     * Presentation unknown for Originating Identity.
     */
    public static final int OIR_PRESENTATION_UNKNOWN = 3;
    /**
     * Payphone presentation for Originating Identity.
     */
    public static final int OIR_PRESENTATION_PAYPHONE = 4;

    //Values for EXTRA_DIALSTRING
    /**
     * A default or normal normal call.
     */
    public static final int DIALSTRING_NORMAL = 0;
    /**
     * Call for SIP-based user configuration
     */
    public static final int DIALSTRING_SS_CONF = 1;
    /**
     * Call for USSD message
     */
    public static final int DIALSTRING_USSD = 2;

    /**
     * Call is not restricted on peer side and High Definition media is supported
     */
    public static final int CALL_RESTRICT_CAUSE_NONE = 0;

    /**
     * High Definition media is not supported on the peer side due to the Radio Access Technology
     * (RAT) it is are connected to.
     */
    public static final int CALL_RESTRICT_CAUSE_RAT = 1;

    /**
     * The service has been disabled on the peer side.
     */
    public static final int CALL_RESTRICT_CAUSE_DISABLED = 2;

    /**
     * High definition media is not currently supported.
     */
    public static final int CALL_RESTRICT_CAUSE_HD = 3;

    /**@hide*/
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(prefix = "CALL_RESTRICT_CAUSE_", value = {
            CALL_RESTRICT_CAUSE_NONE,
            CALL_RESTRICT_CAUSE_RAT,
            CALL_RESTRICT_CAUSE_DISABLED,
            CALL_RESTRICT_CAUSE_HD
    })
    public @interface CallRestrictCause {}

    /**
     * String extra properties
     *  oi : Originating identity (number), MT only
     *  cna : Calling name
     *  ussd : For network-initiated USSD, MT only
     *  remote_uri : Connected user identity (it can be used for the conference)
     *  ChildNum: Child number info.
     *  Codec: Codec info.
     *  DisplayText: Display text for the call.
     *  AdditionalCallInfo: Additional call info.
     *  CallPull: Boolean value specifying if the call is a pulled call.
     */
    public static final String EXTRA_OI = "oi";
    public static final String EXTRA_CNA = "cna";
    public static final String EXTRA_USSD = "ussd";
    public static final String EXTRA_REMOTE_URI = "remote_uri";
    public static final String EXTRA_CHILD_NUMBER = "ChildNum";
    public static final String EXTRA_CODEC = "Codec";
    public static final String EXTRA_DISPLAY_TEXT = "DisplayText";
    public static final String EXTRA_ADDITIONAL_CALL_INFO = "AdditionalCallInfo";
    public static final String EXTRA_IS_CALL_PULL = "CallPull";

    /**
     * String extra property
     *  Containing fields from the SIP INVITE message for an IMS call
     */
    public static final String EXTRA_ADDITIONAL_SIP_INVITE_FIELDS =
                                  "android.telephony.ims.extra.ADDITIONAL_SIP_INVITE_FIELDS";

    /**
     * CallDisconnectCause: Specify call disconnect cause. This extra should be a code
     * corresponding to ImsReasonInfo and should only be populated in the case that the
     * call has already been missed
     */
    public static final String EXTRA_CALL_DISCONNECT_CAUSE =
                                 "android.telephony.ims.extra.CALL_DISCONNECT_CAUSE";

    /**
     * Extra key which the RIL can use to indicate the radio technology used for a call.
     * Valid values are:
     * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE},
     * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_IWLAN}, and the other defined
     * {@code RIL_RADIO_TECHNOLOGY_*} constants.
     * Note: Despite the fact the {@link android.telephony.ServiceState} values are integer
     * constants, the values passed for the {@link #EXTRA_CALL_RAT_TYPE} should be strings (e.g.
     * "14" vs (int) 14).
     * Note: This is used by {@link com.android.internal.telephony.imsphone.ImsPhoneConnection#
     *      updateImsCallRatFromExtras(Bundle)} to determine whether to set the
     * {@link android.telecom.TelecomManager#EXTRA_CALL_NETWORK_TYPE} extra value and
     * {@link android.telecom.Connection#PROPERTY_WIFI} property on a connection.
     * @deprecated the constants associated with this extra are hidden, instead use
     * {@link #EXTRA_CALL_NETWORK_TYPE}.
     */
    @Deprecated
    public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech";

    /**
     * Extra key with an {@code int} value which can be set in {@link #setCallExtraInt(String, int)}
     * to indicate the network type used for a call.
     * <p>
     * Valid values are defined by {@code TelephonyManager.NETWORK_TYPE_*} constants. An example may
     * be {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE}.
     */
    public static final String EXTRA_CALL_NETWORK_TYPE =
            "android.telephony.ims.extra.CALL_NETWORK_TYPE";

    /**
     * Similar to {@link #EXTRA_CALL_RAT_TYPE}, except with a lowercase 'c'.  Used to ensure
     * compatibility with modems that are non-compliant with the {@link #EXTRA_CALL_RAT_TYPE}
     * extra key.  Should be removed when the non-compliant modems are fixed.
     * @hide
     * @deprecated Use {@link #EXTRA_CALL_NETWORK_TYPE} instead.
     */
    @Deprecated
    public static final String EXTRA_CALL_RAT_TYPE_ALT = "callRadioTech";

    /**
     * String extra property containing forwarded numbers associated with the current connection
     * for an IMS call. The value is string array, and it can include multiple numbers, and
     * the array values are expected E164 (e.g. +1 (650) 253-0000) format.
     */
    public static final String EXTRA_FORWARDED_NUMBER =
            "android.telephony.ims.extra.FORWARDED_NUMBER";

    /** @hide */
    public int mServiceType;
    /** @hide */
    @UnsupportedAppUsage
    public int mCallType;
    /** @hide */
    @UnsupportedAppUsage
    public @CallRestrictCause int mRestrictCause = CALL_RESTRICT_CAUSE_NONE;

    /**
     * The VERSTAT for an incoming call's phone number.
     */
    private @VerificationStatus int mCallerNumberVerificationStatus;

    /**
     * Indicates that the network could not perform verification.
     */
    public static final int VERIFICATION_STATUS_NOT_VERIFIED = 0;

    /**
     * Indicates that verification by the network passed.  This indicates there is a high likelihood
     * that the call originated from a valid source.
     */
    public static final int VERIFICATION_STATUS_PASSED = 1;

    /**
     * Indicates that verification by the network failed.  This indicates there is a high likelihood
     * that the call did not originate from a valid source.
     */
    public static final int VERIFICATION_STATUS_FAILED = 2;

    /**@hide*/
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(prefix = "VERIFICATION_STATUS_", value = {
            VERIFICATION_STATUS_NOT_VERIFIED,
            VERIFICATION_STATUS_PASSED,
            VERIFICATION_STATUS_FAILED
    })
    public @interface VerificationStatus {}

    /**
     * The emergency service categories, only valid if {@link #getServiceType} returns
     * {@link #SERVICE_TYPE_EMERGENCY}
     *
     * If valid, the value is the bitwise-OR combination of the following constants:
     * <ol>
     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED} </li>
     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_POLICE} </li>
     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AMBULANCE} </li>
     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE} </li>
     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD} </li>
     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE} </li>
     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MIEC} </li>
     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AIEC} </li>
     * </ol>
     *
     * Reference: 3gpp 23.167, Section 6 - Functional description;
     *            3gpp 22.101, Section 10 - Emergency Calls.
     */
    private @EmergencyServiceCategories int mEmergencyServiceCategories =
            EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED;

    /**
     * The emergency Uniform Resource Names (URN), only valid if {@link #getServiceType} returns
     * {@link #SERVICE_TYPE_EMERGENCY}.
     *
     * Reference: 3gpp 24.503, Section 5.1.6.8.1 - General;
     *            3gpp 22.101, Section 10 - Emergency Calls.
     */
    private List<String> mEmergencyUrns = new ArrayList<>();

    /**
     * The emergency call routing, only valid if {@link #getServiceType} returns
     * {@link #SERVICE_TYPE_EMERGENCY}
     *
     * If valid, the value is any of the following constants:
     * <ol>
     * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_UNKNOWN} </li>
     * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_NORMAL} </li>
     * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_EMERGENCY} </li>
     * </ol>
     */
    private @EmergencyCallRouting int mEmergencyCallRouting =
            EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN;

    /** Indicates if the call is for testing purpose */
    private boolean mEmergencyCallTesting = false;

    /** Indicates if we have known the intent of the user for the call is emergency */
    private boolean mHasKnownUserIntentEmergency = false;

    /**
     * Extras associated with this {@link ImsCallProfile}.
     * <p>
     * Valid data types include:
     * <ul>
     *     <li>{@link Integer} (and int)</li>
     *     <li>{@link Long} (and long)</li>
     *     <li>{@link Double} (and double)</li>
     *     <li>{@link String}</li>
     *     <li>{@code int[]}</li>
     *     <li>{@code long[]}</li>
     *     <li>{@code double[]}</li>
     *     <li>{@code String[]}</li>
     *     <li>{@link android.os.PersistableBundle}</li>
     *     <li>{@link Boolean} (and boolean)</li>
     *     <li>{@code boolean[]}</li>
     *     <li>Other {@link Parcelable} classes in the {@code android.*} namespace.</li>
     * </ul>
     * <p>
     * Invalid types will be removed when the {@link ImsCallProfile} is parceled for transmit across
     * a {@link android.os.Binder}.
     */
    /** @hide */
    @UnsupportedAppUsage
    public Bundle mCallExtras;
    /** @hide */
    @UnsupportedAppUsage
    public ImsStreamMediaProfile mMediaProfile;

    /** @hide */
    public ImsCallProfile(Parcel in) {
        readFromParcel(in);
    }

    /**
     * Default Constructor that initializes the call profile with service type
     * {@link #SERVICE_TYPE_NORMAL} and call type {@link #CALL_TYPE_VIDEO_N_VOICE}
     */
    public ImsCallProfile() {
        mServiceType = SERVICE_TYPE_NORMAL;
        mCallType = CALL_TYPE_VOICE_N_VIDEO;
        mCallExtras = new Bundle();
        mMediaProfile = new ImsStreamMediaProfile();
    }

    /**
     * Constructor.
     *
     * @param serviceType the service type for the call. Can be one of the following:
     *                    {@link #SERVICE_TYPE_NONE},
     *                    {@link #SERVICE_TYPE_NORMAL},
     *                    {@link #SERVICE_TYPE_EMERGENCY}
     * @param callType the call type. Can be one of the following:
     *                 {@link #CALL_TYPE_VOICE_N_VIDEO},
     *                 {@link #CALL_TYPE_VOICE},
     *                 {@link #CALL_TYPE_VIDEO_N_VOICE},
     *                 {@link #CALL_TYPE_VT},
     *                 {@link #CALL_TYPE_VT_TX},
     *                 {@link #CALL_TYPE_VT_RX},
     *                 {@link #CALL_TYPE_VT_NODIR},
     *                 {@link #CALL_TYPE_VS},
     *                 {@link #CALL_TYPE_VS_TX},
     *                 {@link #CALL_TYPE_VS_RX}
     */
    public ImsCallProfile(int serviceType, int callType) {
        mServiceType = serviceType;
        mCallType = callType;
        mCallExtras = new Bundle();
        mMediaProfile = new ImsStreamMediaProfile();
    }

    /**
     * Constructor.
     *
     * @param serviceType the service type for the call. Can be one of the following:
     *                    {@link #SERVICE_TYPE_NONE},
     *                    {@link #SERVICE_TYPE_NORMAL},
     *                    {@link #SERVICE_TYPE_EMERGENCY}
     * @param callType the call type. Can be one of the following:
     *                 {@link #CALL_TYPE_VOICE_N_VIDEO},
     *                 {@link #CALL_TYPE_VOICE},
     *                 {@link #CALL_TYPE_VIDEO_N_VOICE},
     *                 {@link #CALL_TYPE_VT},
     *                 {@link #CALL_TYPE_VT_TX},
     *                 {@link #CALL_TYPE_VT_RX},
     *                 {@link #CALL_TYPE_VT_NODIR},
     *                 {@link #CALL_TYPE_VS},
     *                 {@link #CALL_TYPE_VS_TX},
     *                 {@link #CALL_TYPE_VS_RX}
     * @param callExtras A bundle with the call extras.
     * @param mediaProfile The IMS stream media profile.
     */
    public ImsCallProfile(int serviceType, int callType, Bundle callExtras,
            ImsStreamMediaProfile mediaProfile) {
        mServiceType = serviceType;
        mCallType = callType;
        mCallExtras = callExtras;
        mMediaProfile = mediaProfile;
    }

    public String getCallExtra(String name) {
        return getCallExtra(name, "");
    }

    public String getCallExtra(String name, String defaultValue) {
        if (mCallExtras == null) {
            return defaultValue;
        }

        return mCallExtras.getString(name, defaultValue);
    }

    public boolean getCallExtraBoolean(String name) {
        return getCallExtraBoolean(name, false);
    }

    public boolean getCallExtraBoolean(String name, boolean defaultValue) {
        if (mCallExtras == null) {
            return defaultValue;
        }

        return mCallExtras.getBoolean(name, defaultValue);
    }

    public int getCallExtraInt(String name) {
        return getCallExtraInt(name, -1);
    }

    public int getCallExtraInt(String name, int defaultValue) {
        if (mCallExtras == null) {
            return defaultValue;
        }

        return mCallExtras.getInt(name, defaultValue);
    }

    public void setCallExtra(String name, String value) {
        if (mCallExtras != null) {
            mCallExtras.putString(name, value);
        }
    }

    public void setCallExtraBoolean(String name, boolean value) {
        if (mCallExtras != null) {
            mCallExtras.putBoolean(name, value);
        }
    }

    public void setCallExtraInt(String name, int value) {
        if (mCallExtras != null) {
            mCallExtras.putInt(name, value);
        }
    }

    /**
     * Set the call restrict cause, which provides the reason why a call has been restricted from
     * using High Definition media.
     */
    public void setCallRestrictCause(@CallRestrictCause int cause) {
        mRestrictCause = cause;
    }

    public void updateCallType(ImsCallProfile profile) {
        mCallType = profile.mCallType;
    }

    public void updateCallExtras(ImsCallProfile profile) {
        mCallExtras.clear();
        mCallExtras = (Bundle) profile.mCallExtras.clone();
    }

    /**
     * Updates the media profile for the call.
     *
     * @param profile Call profile with new media profile.
     */
    public void updateMediaProfile(ImsCallProfile profile) {
        mMediaProfile = profile.mMediaProfile;
    }

    /**
     * Sets the verification status for the phone number of an incoming call as identified in
     * ATIS-1000082.
     * <p>
     * The ImsService should parse the verstat information from the SIP INVITE headers for the call
     * to determine this information.  It is typically found in the P-Asserted-Identity OR From
     * header fields.
     * @param callerNumberVerificationStatus the new verification status.
     */
    public void setCallerNumberVerificationStatus(
            @VerificationStatus int callerNumberVerificationStatus) {
        mCallerNumberVerificationStatus = callerNumberVerificationStatus;
    }

    /**
     * Gets the verification status for the phone number of an incoming call as identified in
     * ATIS-1000082.
     * @return the verification status.
     */
    public @VerificationStatus int getCallerNumberVerificationStatus() {
        return mCallerNumberVerificationStatus;
    }

    @NonNull
    @Override
    public String toString() {
        return "{ serviceType=" + mServiceType
                + ", callType=" + mCallType
                + ", restrictCause=" + mRestrictCause
                + ", mediaProfile=" + (mMediaProfile != null ? mMediaProfile.toString() : "null")
                + ", emergencyServiceCategories=" + mEmergencyServiceCategories
                + ", emergencyUrns=" + mEmergencyUrns
                + ", emergencyCallRouting=" + mEmergencyCallRouting
                + ", emergencyCallTesting=" + mEmergencyCallTesting
                + ", hasKnownUserIntentEmergency=" + mHasKnownUserIntentEmergency
                + ", mRestrictCause=" + mRestrictCause
                + ", mCallerNumberVerstat= " + mCallerNumberVerificationStatus + " }";
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel out, int flags) {
        Bundle filteredExtras = maybeCleanseExtras(mCallExtras);
        out.writeInt(mServiceType);
        out.writeInt(mCallType);
        out.writeBundle(filteredExtras);
        out.writeParcelable(mMediaProfile, 0);
        out.writeInt(mEmergencyServiceCategories);
        out.writeStringList(mEmergencyUrns);
        out.writeInt(mEmergencyCallRouting);
        out.writeBoolean(mEmergencyCallTesting);
        out.writeBoolean(mHasKnownUserIntentEmergency);
        out.writeInt(mRestrictCause);
        out.writeInt(mCallerNumberVerificationStatus);
    }

    private void readFromParcel(Parcel in) {
        mServiceType = in.readInt();
        mCallType = in.readInt();
        mCallExtras = in.readBundle();
        mMediaProfile = in.readParcelable(ImsStreamMediaProfile.class.getClassLoader());
        mEmergencyServiceCategories = in.readInt();
        mEmergencyUrns = in.createStringArrayList();
        mEmergencyCallRouting = in.readInt();
        mEmergencyCallTesting = in.readBoolean();
        mHasKnownUserIntentEmergency = in.readBoolean();
        mRestrictCause = in.readInt();
        mCallerNumberVerificationStatus = in.readInt();
    }

    public static final @android.annotation.NonNull Creator<ImsCallProfile> CREATOR = new Creator<ImsCallProfile>() {
        @Override
        public ImsCallProfile createFromParcel(Parcel in) {
            return new ImsCallProfile(in);
        }

        @Override
        public ImsCallProfile[] newArray(int size) {
            return new ImsCallProfile[size];
        }
    };

    public int getServiceType() {
        return mServiceType;
    }

    public int getCallType() {
        return mCallType;
    }

    /**
     * @return The call restrict cause, which provides the reason why a call has been restricted
     * from using High Definition media.
     */
    public @CallRestrictCause int getRestrictCause() {
        return mRestrictCause;
    }

    public Bundle getCallExtras() {
        return mCallExtras;
    }

    /**
     * Get the proprietary extras set for this ImsCallProfile.
     * @return A {@link Bundle} containing proprietary call extras that were not set by the
     * platform.
     */
    public @NonNull Bundle getProprietaryCallExtras() {
        if (mCallExtras == null) {
            return new Bundle();
        }
        Bundle proprietaryExtras = mCallExtras.getBundle(EXTRA_OEM_EXTRAS);
        if (proprietaryExtras == null) {
            return new Bundle();
        }
        // Make a copy so users do not accidentally change this copy of the extras.
        return new Bundle(proprietaryExtras);
    }

    public ImsStreamMediaProfile getMediaProfile() {
        return mMediaProfile;
    }

    /**
     * Converts from the call types defined in {@link ImsCallProfile} to the
     * video state values defined in {@link VideoProfile}.
     *
     * @param callProfile The call profile.
     * @return The video state.
     */
    public static int getVideoStateFromImsCallProfile(ImsCallProfile callProfile) {
        int videostate = getVideoStateFromCallType(callProfile.mCallType);
        if (callProfile.isVideoPaused() && !VideoProfile.isAudioOnly(videostate)) {
            videostate |= VideoProfile.STATE_PAUSED;
        } else {
            videostate &= ~VideoProfile.STATE_PAUSED;
        }
        return videostate;
    }

    /**
     * Translates a {@link ImsCallProfile} {@code CALL_TYPE_*} constant into a video state.
     * @param callType The call type.
     * @return The video state.
     */
    public static int getVideoStateFromCallType(int callType) {
        int videostate = VideoProfile.STATE_AUDIO_ONLY;
        switch (callType) {
            case CALL_TYPE_VT_TX:
                videostate = VideoProfile.STATE_TX_ENABLED;
                break;
            case CALL_TYPE_VT_RX:
                videostate = VideoProfile.STATE_RX_ENABLED;
                break;
            case CALL_TYPE_VT:
                videostate = VideoProfile.STATE_BIDIRECTIONAL;
                break;
            case CALL_TYPE_VOICE:
                videostate = VideoProfile.STATE_AUDIO_ONLY;
                break;
            default:
                videostate = VideoProfile.STATE_AUDIO_ONLY;
                break;
        }
        return videostate;
    }

    /**
     * Converts from the video state values defined in {@link VideoProfile}
     * to the call types defined in {@link ImsCallProfile}.
     *
     * @param videoState The video state.
     * @return The call type.
     */
    public static int getCallTypeFromVideoState(int videoState) {
        boolean videoTx = isVideoStateSet(videoState, VideoProfile.STATE_TX_ENABLED);
        boolean videoRx = isVideoStateSet(videoState, VideoProfile.STATE_RX_ENABLED);
        boolean isPaused = isVideoStateSet(videoState, VideoProfile.STATE_PAUSED);
        if (isPaused) {
            return ImsCallProfile.CALL_TYPE_VT_NODIR;
        } else if (videoTx && !videoRx) {
            return ImsCallProfile.CALL_TYPE_VT_TX;
        } else if (!videoTx && videoRx) {
            return ImsCallProfile.CALL_TYPE_VT_RX;
        } else if (videoTx && videoRx) {
            return ImsCallProfile.CALL_TYPE_VT;
        }
        return ImsCallProfile.CALL_TYPE_VOICE;
    }

    /**
     * Badly named old method, kept for compatibility.
     * See {@link #presentationToOir(int)}.
     * @hide
     */
    @UnsupportedAppUsage
    public static int presentationToOIR(int presentation) {
        switch (presentation) {
            case PhoneConstants.PRESENTATION_RESTRICTED:
                return ImsCallProfile.OIR_PRESENTATION_RESTRICTED;
            case PhoneConstants.PRESENTATION_ALLOWED:
                return ImsCallProfile.OIR_PRESENTATION_NOT_RESTRICTED;
            case PhoneConstants.PRESENTATION_PAYPHONE:
                return ImsCallProfile.OIR_PRESENTATION_PAYPHONE;
            case PhoneConstants.PRESENTATION_UNKNOWN:
                return ImsCallProfile.OIR_PRESENTATION_UNKNOWN;
            default:
                return ImsCallProfile.OIR_DEFAULT;
        }
    }

    /**
     * Translate presentation value to OIR value
     * @param presentation
     * @return OIR values
     */
    public static int presentationToOir(int presentation) {
        return presentationToOIR(presentation);
    }

    /**
     * Translate OIR value to presentation value
     * @param oir value
     * @return presentation value
     * @hide
     */
    public static int OIRToPresentation(int oir) {
        switch(oir) {
            case ImsCallProfile.OIR_PRESENTATION_RESTRICTED:
                return PhoneConstants.PRESENTATION_RESTRICTED;
            case ImsCallProfile.OIR_PRESENTATION_NOT_RESTRICTED:
                return PhoneConstants.PRESENTATION_ALLOWED;
            case ImsCallProfile.OIR_PRESENTATION_PAYPHONE:
                return PhoneConstants.PRESENTATION_PAYPHONE;
            case ImsCallProfile.OIR_PRESENTATION_UNKNOWN:
                return PhoneConstants.PRESENTATION_UNKNOWN;
            default:
                return PhoneConstants.PRESENTATION_UNKNOWN;
        }
    }

    /**
     * Checks if video call is paused
     * @return true if call is video paused
     */
    public boolean isVideoPaused() {
        return mMediaProfile.mVideoDirection == ImsStreamMediaProfile.DIRECTION_INACTIVE;
    }

    /**
     * Determines if the {@link ImsCallProfile} represents a video call.
     *
     * @return {@code true} if the profile is for a video call, {@code false} otherwise.
     */
    public boolean isVideoCall() {
        return VideoProfile.isVideo(getVideoStateFromCallType(mCallType));
    }

    /**
     * Cleanses a {@link Bundle} to ensure that it contains only data of type:
     * 1. Primitive data types (e.g. int, bool, and other values determined by
     * {@link android.os.PersistableBundle#isValidType(Object)}).
     * 2. Other Bundles.
     * 3. {@link Parcelable} objects in the {@code android.*} namespace.
     * @param extras the source {@link Bundle}
     * @return where all elements are valid types the source {@link Bundle} is returned unmodified,
     *      otherwise a copy of the {@link Bundle} with the invalid elements is returned.
     */
    private Bundle maybeCleanseExtras(Bundle extras) {
        if (extras == null) {
            return null;
        }

        int startSize = extras.size();
        Bundle filtered = TelephonyUtils.filterValues(extras);
        int endSize = filtered.size();
        if (startSize != endSize) {
            Log.i(TAG, "maybeCleanseExtras: " + (startSize - endSize) + " extra values were "
                    + "removed - only primitive types and system parcelables are permitted.");
        }
        return filtered;
    }

    /**
     * Determines if a video state is set in a video state bit-mask.
     *
     * @param videoState The video state bit mask.
     * @param videoStateToCheck The particular video state to check.
     * @return True if the video state is set in the bit-mask.
     */
    private static boolean isVideoStateSet(int videoState, int videoStateToCheck) {
        return (videoState & videoStateToCheck) == videoStateToCheck;
    }

    /**
     * Set the emergency number information. The set value is valid
     * only if {@link #getServiceType} returns {@link #SERVICE_TYPE_EMERGENCY}
     *
     * Reference: 3gpp 23.167, Section 6 - Functional description;
     *            3gpp 24.503, Section 5.1.6.8.1 - General;
     *            3gpp 22.101, Section 10 - Emergency Calls.
     *
     * @hide
     */
    public void setEmergencyCallInfo(EmergencyNumber num, boolean hasKnownUserIntentEmergency) {
        setEmergencyServiceCategories(num.getEmergencyServiceCategoryBitmaskInternalDial());
        setEmergencyUrns(num.getEmergencyUrns());
        setEmergencyCallRouting(num.getEmergencyCallRouting());
        setEmergencyCallTesting(num.getEmergencyNumberSourceBitmask()
                == EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST);
        setHasKnownUserIntentEmergency(hasKnownUserIntentEmergency);
    }

    /**
     * Set the emergency service categories. The set value is valid only if
     * {@link #getServiceType} returns {@link #SERVICE_TYPE_EMERGENCY}
     *
     * If valid, the value is the bitwise-OR combination of the following constants:
     * <ol>
     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED} </li>
     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_POLICE} </li>
     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AMBULANCE} </li>
     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE} </li>
     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD} </li>
     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE} </li>
     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MIEC} </li>
     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AIEC} </li>
     * </ol>
     *
     * Reference: 3gpp 23.167, Section 6 - Functional description;
     *            3gpp 22.101, Section 10 - Emergency Calls.
     */
    @VisibleForTesting
    public void setEmergencyServiceCategories(
            @EmergencyServiceCategories int emergencyServiceCategories) {
        mEmergencyServiceCategories = emergencyServiceCategories;
    }

    /**
     * Set the emergency Uniform Resource Names (URN), only valid if {@link #getServiceType}
     * returns {@link #SERVICE_TYPE_EMERGENCY}.
     *
     * Reference: 3gpp 24.503, Section 5.1.6.8.1 - General;
     *            3gpp 22.101, Section 10 - Emergency Calls.
     */
    @VisibleForTesting
    public void setEmergencyUrns(@NonNull List<String> emergencyUrns) {
        mEmergencyUrns = emergencyUrns;
    }

    /**
     * Set the emergency call routing, only valid if {@link #getServiceType} returns
     * {@link #SERVICE_TYPE_EMERGENCY}
     *
     * If valid, the value is any of the following constants:
     * <ol>
     * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_UNKNOWN} </li>
     * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_NORMAL} </li>
     * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_EMERGENCY} </li>
     * </ol>
     */
    @VisibleForTesting
    public void setEmergencyCallRouting(@EmergencyCallRouting int emergencyCallRouting) {
        mEmergencyCallRouting = emergencyCallRouting;
    }

    /**
     * Set if this is for testing emergency call, only valid if {@link #getServiceType} returns
     * {@link #SERVICE_TYPE_EMERGENCY}.
     */
    @VisibleForTesting
    public void setEmergencyCallTesting(boolean isTesting) {
        mEmergencyCallTesting = isTesting;
    }

    /**
     * Set if we have known the user intent of the call is emergency.
     *
     * This is only used to specify when the dialed number is ambiguous when it can be identified
     * as both emergency number and any other non-emergency number; e.g. in some situation, 611
     * could be both an emergency number in a country and a non-emergency number of a carrier's
     * customer service hotline.
     */
    @VisibleForTesting
    public void setHasKnownUserIntentEmergency(boolean hasKnownUserIntentEmergency) {
        mHasKnownUserIntentEmergency = hasKnownUserIntentEmergency;
    }

    /**
     * Get the emergency service categories, only valid if {@link #getServiceType} returns
     * {@link #SERVICE_TYPE_EMERGENCY}
     *
     * @return the emergency service categories,
     *
     * If valid, the value is the bitwise-OR combination of the following constants:
     * <ol>
     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED} </li>
     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_POLICE} </li>
     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AMBULANCE} </li>
     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE} </li>
     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD} </li>
     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE} </li>
     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MIEC} </li>
     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AIEC} </li>
     * </ol>
     *
     * Reference: 3gpp 23.167, Section 6 - Functional description;
     *            3gpp 22.101, Section 10 - Emergency Calls.
     */
    public @EmergencyServiceCategories int getEmergencyServiceCategories() {
        return mEmergencyServiceCategories;
    }

    /**
     * Get the emergency Uniform Resource Names (URN), only valid if {@link #getServiceType}
     * returns {@link #SERVICE_TYPE_EMERGENCY}.
     *
     * Reference: 3gpp 24.503, Section 5.1.6.8.1 - General;
     *            3gpp 22.101, Section 10 - Emergency Calls.
     */
    public @NonNull List<String> getEmergencyUrns() {
        return mEmergencyUrns;
    }

    /**
     * Get the emergency call routing, only valid if {@link #getServiceType} returns
     * {@link #SERVICE_TYPE_EMERGENCY}
     *
     * If valid, the value is any of the following constants:
     * <ol>
     * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_UNKNOWN} </li>
     * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_NORMAL} </li>
     * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_EMERGENCY} </li>
     * </ol>
     */
    public @EmergencyCallRouting int getEmergencyCallRouting() {
        return mEmergencyCallRouting;
    }

    /**
     * Get if the emergency call is for testing purpose.
     */
    public boolean isEmergencyCallTesting() {
        return mEmergencyCallTesting;
    }

    /**
     * Checks if we have known the user intent of the call is emergency.
     *
     * This is only used to specify when the dialed number is ambiguous when it can be identified
     * as both emergency number and any other non-emergency number; e.g. in some situation, 611
     * could be both an emergency number in a country and a non-emergency number of a carrier's
     * customer service hotline.
     */
    public boolean hasKnownUserIntentEmergency() {
        return mHasKnownUserIntentEmergency;
    }
}
