/*
 * Copyright (C) 2016 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 com.android.server.telecom;

import static android.telecom.Call.Details.DIRECTION_INCOMING;
import static android.telecom.Call.Details.DIRECTION_OUTGOING;
import static android.telecom.Call.Details.DIRECTION_UNKNOWN;

import android.net.Uri;
import android.os.Bundle;
import android.telecom.Connection;
import android.telecom.DisconnectCause;
import android.telecom.ParcelableCall;
import android.telecom.ParcelableRttCall;
import android.telecom.TelecomManager;
import android.telephony.ims.ImsCallProfile;
import android.text.TextUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/**
 * Utilities dealing with {@link ParcelableCall}.
 */
public class ParcelableCallUtils {
    private static final int CALL_STATE_OVERRIDE_NONE = -1;

    /**
     * A list of extra keys which should be removed from a {@link ParcelableCall} when it is being
     * generated for the purpose of sending to a incallservice other than the system incallservice.
     * By convention we only pass keys namespaced with android.*, however there are some keys which
     * should not be passed to non-system incallservice apps either.
     */
    private static List<String> EXTRA_KEYS_TO_SANITIZE;
    static {
        EXTRA_KEYS_TO_SANITIZE = new ArrayList<>();
        EXTRA_KEYS_TO_SANITIZE.add(android.telecom.Connection.EXTRA_SIP_INVITE);
    }

    /**
     * A list of extra keys which should be added to {@link ParcelableCall} when it is being
     * generated for the purpose of sending to a CallScreeningService which has access to these
     * restricted keys.
     */
    private static List<String> RESTRICTED_CALL_SCREENING_EXTRA_KEYS;
    static {
        RESTRICTED_CALL_SCREENING_EXTRA_KEYS = new ArrayList<>();
        RESTRICTED_CALL_SCREENING_EXTRA_KEYS.add(android.telecom.Connection.EXTRA_SIP_INVITE);
        RESTRICTED_CALL_SCREENING_EXTRA_KEYS.add(ImsCallProfile.EXTRA_IS_BUSINESS_CALL);
        RESTRICTED_CALL_SCREENING_EXTRA_KEYS.add(ImsCallProfile.EXTRA_ASSERTED_DISPLAY_NAME);
    }

    public static class Converter {
        public ParcelableCall toParcelableCall(Call call, boolean includeVideoProvider,
                PhoneAccountRegistrar phoneAccountRegistrar) {
            return ParcelableCallUtils.toParcelableCall(
                    call, includeVideoProvider, phoneAccountRegistrar, false, false, false);
        }

        public ParcelableCall toParcelableCallForScreening(Call call,
                boolean areRestrictedExtrasIncluded) {
            return ParcelableCallUtils.toParcelableCallForScreening(call,
                    areRestrictedExtrasIncluded);
        }
    }

    /**
     * Parcels all information for a {@link Call} into a new {@link ParcelableCall} instance.
     *
     * @param call The {@link Call} to parcel.
     * @param includeVideoProvider {@code true} if the video provider should be parcelled with the
     *      {@link Call}, {@code false} otherwise.  Since the {@link ParcelableCall#getVideoCall()}
     *      method creates a {@link VideoCallImpl} instance on access it is important for the
     *      recipient of the {@link ParcelableCall} to know if the video provider changed.
     * @param phoneAccountRegistrar The {@link PhoneAccountRegistrar}.
     * @param supportsExternalCalls Indicates whether the call should be parcelled for an
     *      {@link InCallService} which supports external calls or not.
     * @param includeRttCall {@code true} if the RTT call should be included, {@code false}
     *      otherwise.
     * @param isForSystemInCallService {@code true} if this call is being parcelled for the system incallservice,
     *      {@code false} otherwise.  When parceling for the system incallservice, the entire call extras
     *      is included.  When parceling for anything other than the system incallservice, some extra key
     *      values will be stripped for privacy sake.
     */
    public static ParcelableCall toParcelableCall(
            Call call,
            boolean includeVideoProvider,
            PhoneAccountRegistrar phoneAccountRegistrar,
            boolean supportsExternalCalls,
            boolean includeRttCall,
            boolean isForSystemInCallService) {
        return toParcelableCall(call, includeVideoProvider, phoneAccountRegistrar,
                supportsExternalCalls, CALL_STATE_OVERRIDE_NONE /* overrideState */,
                includeRttCall, isForSystemInCallService);
    }

    /**
     * Parcels all information for a {@link Call} into a new {@link ParcelableCall} instance.
     *
     * @param call The {@link Call} to parcel.
     * @param includeVideoProvider {@code true} if the video provider should be parcelled with the
     *      {@link Call}, {@code false} otherwise.  Since the {@link ParcelableCall#getVideoCall()}
     *      method creates a {@link VideoCallImpl} instance on access it is important for the
     *      recipient of the {@link ParcelableCall} to know if the video provider changed.
     * @param phoneAccountRegistrar The {@link PhoneAccountRegistrar}.
     * @param supportsExternalCalls Indicates whether the call should be parcelled for an
     *      {@link InCallService} which supports external calls or not.
     * @param overrideState When not {@link #CALL_STATE_OVERRIDE_NONE}, use the provided state as an
     *      override to whatever is defined in the call.
     * @param isForSystemInCallService {@code true} if this call is being parcelled for the system incallservice,
     *      {@code false} otherwise.  When parceling for the system incallservice, the entire call extras
     *      is included.  When parceling for anything other than the system incallservice, some extra key
     *      values will be stripped for privacy sake.
     * @return The {@link ParcelableCall} containing all call information from the {@link Call}.
     */
    public static ParcelableCall toParcelableCall(
            Call call,
            boolean includeVideoProvider,
            PhoneAccountRegistrar phoneAccountRegistrar,
            boolean supportsExternalCalls,
            int overrideState,
            boolean includeRttCall,
            boolean isForSystemInCallService) {
        int state;
        if (overrideState == CALL_STATE_OVERRIDE_NONE) {
            state = getParcelableState(call, supportsExternalCalls);
        } else {
            state = overrideState;
        }
        int capabilities = convertConnectionToCallCapabilities(call.getConnectionCapabilities());
        int properties = convertConnectionToCallProperties(call.getConnectionProperties());
        int supportedAudioRoutes = call.getSupportedAudioRoutes();

        if (call.isConference()) {
            properties |= android.telecom.Call.Details.PROPERTY_CONFERENCE;
        }

        if (call.isWorkCall()) {
            properties |= android.telecom.Call.Details.PROPERTY_ENTERPRISE_CALL;
        }

        if (call.getIsVoipAudioMode()) {
            properties |= android.telecom.Call.Details.PROPERTY_VOIP_AUDIO_MODE;
        }

        if (call.isTransactionalCall()) {
            properties |= android.telecom.Call.Details.PROPERTY_IS_TRANSACTIONAL;
        }

        // If this is a single-SIM device, the "default SIM" will always be the only SIM.
        boolean isDefaultSmsAccount = phoneAccountRegistrar != null &&
                phoneAccountRegistrar.isUserSelectedSmsPhoneAccount(call.getTargetPhoneAccount());
        if (call.isRespondViaSmsCapable() && isDefaultSmsAccount) {
            capabilities |= android.telecom.Call.Details.CAPABILITY_RESPOND_VIA_TEXT;
        }

        if (call.isEmergencyCall()) {
            capabilities = removeCapability(
                    capabilities, android.telecom.Call.Details.CAPABILITY_MUTE);
        }

        if (state == android.telecom.Call.STATE_DIALING) {
            capabilities = removeCapability(capabilities,
                    android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL);
            capabilities = removeCapability(capabilities,
                    android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL);
        }

        String parentCallId = null;
        Call parentCall = call.getParentCall();
        if (parentCall != null) {
            parentCallId = parentCall.getId();
        }

        List<Call> childCalls = call.getChildCalls();
        List<String> childCallIds = new ArrayList<>();
        if (!childCalls.isEmpty()) {
            for (Call child : childCalls) {
                childCallIds.add(child.getId());
            }
        }

        Uri handle = call.getHandlePresentation() == TelecomManager.PRESENTATION_ALLOWED ?
                call.getHandle() : null;
        String callerDisplayName = call.getCallerDisplayNamePresentation() ==
                TelecomManager.PRESENTATION_ALLOWED ?  call.getCallerDisplayName() : null;

        Uri contactPhotoUri = call.getContactPhotoUri();

        List<Call> conferenceableCalls = call.getConferenceableCalls();
        List<String> conferenceableCallIds = new ArrayList<String>(conferenceableCalls.size());
        for (Call otherCall : conferenceableCalls) {
            conferenceableCallIds.add(otherCall.getId());
        }

        ParcelableRttCall rttCall = includeRttCall ? getParcelableRttCall(call) : null;
        int callDirection;
        if (call.isIncoming()) {
            callDirection = DIRECTION_INCOMING;
        } else if (call.isUnknown()) {
            callDirection = DIRECTION_UNKNOWN;
        } else {
            callDirection = DIRECTION_OUTGOING;
        }

        String activeChildCallId = null;
        if (call.getConferenceLevelActiveCall() != null) {
            activeChildCallId = call.getConferenceLevelActiveCall().getId();
        }

        Bundle extras;
        if (isForSystemInCallService) {
            extras = call.getExtras();
        } else {
            extras = sanitizeExtras(call.getExtras());
        }

        return new ParcelableCall.ParcelableCallBuilder()
                .setId(call.getId())
                .setState(state)
                .setDisconnectCause(call.getDisconnectCause())
                .setCannedSmsResponses(call.getCannedSmsResponses())
                .setCapabilities(capabilities)
                .setProperties(properties)
                .setSupportedAudioRoutes(supportedAudioRoutes)
                .setConnectTimeMillis(call.getConnectTimeMillis())
                .setHandle(handle)
                .setHandlePresentation(call.getHandlePresentation())
                .setCallerDisplayName(callerDisplayName)
                .setCallerDisplayNamePresentation(call.getCallerDisplayNamePresentation())
                .setGatewayInfo(call.getGatewayInfo())
                .setAccountHandle(call.getTargetPhoneAccount())
                .setIsVideoCallProviderChanged(includeVideoProvider)
                .setVideoCallProvider(includeVideoProvider ? call.getVideoProvider() : null)
                .setIsRttCallChanged(includeRttCall)
                .setRttCall(rttCall)
                .setParentCallId(parentCallId)
                .setChildCallIds(childCallIds)
                .setStatusHints(call.getStatusHints())
                .setVideoState(call.getVideoState())
                .setConferenceableCallIds(conferenceableCallIds)
                .setIntentExtras(call.getIntentExtras())
                .setExtras(extras)
                .setCreationTimeMillis(call.getCreationTimeMillis())
                .setCallDirection(callDirection)
                .setCallerNumberVerificationStatus(call.getCallerNumberVerificationStatus())
                .setContactDisplayName(call.getName())
                .setActiveChildCallId(activeChildCallId)
                .setContactPhotoUri(contactPhotoUri)
                .createParcelableCall();
    }

    /**
     * Creates a ParcelableCall with the bare minimum properties required for a
     * {@link android.telecom.CallScreeningService}.  We ONLY expose the following:
     * <ul>
     *     <li>Call Id (not exposed to public, but needed to associated calls)</li>
     *     <li>Call directoin</li>
     *     <li>Creation time</li>
     *     <li>Connection time</li>
     *     <li>Handle (phone number)</li>
     *     <li>Handle (phone number) presentation</li>
     *     <li>Caller number verification status (verstat)</li>
     * </ul>
     * All other fields are nulled or set to 0 values.
     * Where the call screening service is part of the system incallservice, the
     * {@link Connection#EXTRA_SIP_INVITE} header information is also sent to the call screening
     * service (since the system incallservice has access to this anyways).
     * @param call The telecom call to send to a call screening service.
     * @param areRestrictedExtrasIncluded {@code true} if the set of restricted extras defined in
     *                                    {@link #RESTRICTED_CALL_SCREENING_EXTRA_KEYS} are to
     *                                    be included in the parceled call, {@code false} otherwise.
     * @return Minimal {@link ParcelableCall} to send to the call screening service.
     */
    public static ParcelableCall toParcelableCallForScreening(Call call,
            boolean areRestrictedExtrasIncluded) {
        Uri handle = call.getHandlePresentation() == TelecomManager.PRESENTATION_ALLOWED ?
                call.getHandle() : null;
        int callDirection;
        if (call.isIncoming()) {
            callDirection = DIRECTION_INCOMING;
        } else if (call.isUnknown()) {
            callDirection = DIRECTION_UNKNOWN;
        } else {
            callDirection = DIRECTION_OUTGOING;
        }
        Bundle callExtras;
        if (areRestrictedExtrasIncluded) {
            callExtras = sanitizeRestrictedCallExtras(call.getExtras());
        } else {
            callExtras = new Bundle();
        }

        return new ParcelableCall.ParcelableCallBuilder()
                .setId(call.getId())
                .setState(getParcelableState(call, false /* supportsExternalCalls */))
                .setDisconnectCause(new DisconnectCause(DisconnectCause.UNKNOWN))
                .setCannedSmsResponses(null)
                .setCapabilities(0)
                .setProperties(0)
                .setSupportedAudioRoutes(0)
                .setConnectTimeMillis(call.getConnectTimeMillis())
                .setHandle(handle)
                .setHandlePresentation(call.getHandlePresentation())
                .setCallerDisplayName(null)
                .setCallerDisplayNamePresentation(0)
                .setGatewayInfo(null)
                .setAccountHandle(null)
                .setIsVideoCallProviderChanged(false)
                .setVideoCallProvider(null)
                .setIsRttCallChanged(false)
                .setRttCall(null)
                .setParentCallId(null)
                .setChildCallIds(null)
                .setStatusHints(null)
                .setVideoState(0)
                .setConferenceableCallIds(Collections.emptyList())
                .setIntentExtras(null)
                .setExtras(callExtras)
                .setCreationTimeMillis(call.getCreationTimeMillis())
                .setCallDirection(callDirection)
                .setCallerNumberVerificationStatus(call.getCallerNumberVerificationStatus())
                .setContactDisplayName(null)
                .setActiveChildCallId(null)
                .createParcelableCall();
    }

    /**
     * Sanitize the extras bundle passed in, removing keys which should not be sent to non-system
     * incallservice apps.
     * @param oldExtras Extras bundle to sanitize.
     * @return The sanitized extras bundle.
     */
    private static Bundle sanitizeExtras(Bundle oldExtras) {
        if (oldExtras == null) {
            return new Bundle();
        }
        Bundle extras = new Bundle(oldExtras);
        for (String key : EXTRA_KEYS_TO_SANITIZE) {
            extras.remove(key);
        }

        // As a catch-all remove any that don't start with android namespace.
        Iterator<String> toCheck = extras.keySet().iterator();
        while (toCheck.hasNext()) {
            String extraKey = toCheck.next();
            if (TextUtils.isEmpty(extraKey) || !extraKey.startsWith("android.")) {
                toCheck.remove();
            }
        }
        return extras;
    }

    /**
     * Sanitize the extras bundle passed in, removing keys which should not be sent to call
     * screening services which have access to the restricted extras.
     * @param oldExtras Extras bundle to sanitize.
     * @return The sanitized extras bundle.
     */
    private static Bundle sanitizeRestrictedCallExtras(Bundle oldExtras) {
        if (oldExtras == null) {
            return new Bundle();
        }
        Bundle extras = new Bundle(oldExtras);
        Iterator<String> toCheck = extras.keySet().iterator();
        while (toCheck.hasNext()) {
            String extraKey = toCheck.next();
            if (TextUtils.isEmpty(extraKey)
                    || !RESTRICTED_CALL_SCREENING_EXTRA_KEYS.contains(extraKey)) {
                toCheck.remove();
            }
        }
        return extras;
    }

    private static int getParcelableState(Call call, boolean supportsExternalCalls) {
        int state = CallState.NEW;
        switch (call.getParcelableCallState()) {
            case CallState.ABORTED:
            case CallState.DISCONNECTED:
                state = android.telecom.Call.STATE_DISCONNECTED;
                break;
            case CallState.ACTIVE:
                state = android.telecom.Call.STATE_ACTIVE;
                break;
            case CallState.CONNECTING:
                state = android.telecom.Call.STATE_CONNECTING;
                break;
            case CallState.DIALING:
                state = android.telecom.Call.STATE_DIALING;
                break;
            case CallState.PULLING:
                if (supportsExternalCalls) {
                    // The InCallService supports external calls, so it must handle
                    // STATE_PULLING_CALL.
                    state = android.telecom.Call.STATE_PULLING_CALL;
                } else {
                    // The InCallService does NOT support external calls, so remap
                    // STATE_PULLING_CALL to STATE_DIALING.  In essence, pulling a call can be seen
                    // as a form of dialing, so it is appropriate for InCallServices which do not
                    // handle external calls.
                    state = android.telecom.Call.STATE_DIALING;
                }
                break;
            case CallState.DISCONNECTING:
                state = android.telecom.Call.STATE_DISCONNECTING;
                break;
            case CallState.NEW:
                state = android.telecom.Call.STATE_NEW;
                break;
            case CallState.ON_HOLD:
                state = android.telecom.Call.STATE_HOLDING;
                break;
            case CallState.RINGING:
            case CallState.ANSWERED:
                // TODO: does in-call UI need to see ANSWERED?
                state = android.telecom.Call.STATE_RINGING;
                break;
            case CallState.SELECT_PHONE_ACCOUNT:
                state = android.telecom.Call.STATE_SELECT_PHONE_ACCOUNT;
                break;
            case CallState.AUDIO_PROCESSING:
                state = android.telecom.Call.STATE_AUDIO_PROCESSING;
                break;
            case CallState.SIMULATED_RINGING:
                state = android.telecom.Call.STATE_SIMULATED_RINGING;
                break;
        }

        return state;
    }

    private static final int[] CONNECTION_TO_CALL_CAPABILITY = new int[] {
        Connection.CAPABILITY_HOLD,
        android.telecom.Call.Details.CAPABILITY_HOLD,

        Connection.CAPABILITY_SUPPORT_HOLD,
        android.telecom.Call.Details.CAPABILITY_SUPPORT_HOLD,

        Connection.CAPABILITY_MERGE_CONFERENCE,
        android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE,

        Connection.CAPABILITY_SWAP_CONFERENCE,
        android.telecom.Call.Details.CAPABILITY_SWAP_CONFERENCE,

        Connection.CAPABILITY_RESPOND_VIA_TEXT,
        android.telecom.Call.Details.CAPABILITY_RESPOND_VIA_TEXT,

        Connection.CAPABILITY_MUTE,
        android.telecom.Call.Details.CAPABILITY_MUTE,

        Connection.CAPABILITY_MANAGE_CONFERENCE,
        android.telecom.Call.Details.CAPABILITY_MANAGE_CONFERENCE,

        Connection.CAPABILITY_SUPPORTS_VT_LOCAL_RX,
        android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_RX,

        Connection.CAPABILITY_SUPPORTS_VT_LOCAL_TX,
        android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_TX,

        Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL,
        android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL,

        Connection.CAPABILITY_SUPPORTS_VT_REMOTE_RX,
        android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_RX,

        Connection.CAPABILITY_SUPPORTS_VT_REMOTE_TX,
        android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_TX,

        Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL,
        android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL,

        Connection.CAPABILITY_SEPARATE_FROM_CONFERENCE,
        android.telecom.Call.Details.CAPABILITY_SEPARATE_FROM_CONFERENCE,

        Connection.CAPABILITY_DISCONNECT_FROM_CONFERENCE,
        android.telecom.Call.Details.CAPABILITY_DISCONNECT_FROM_CONFERENCE,

        Connection.CAPABILITY_CAN_UPGRADE_TO_VIDEO,
        android.telecom.Call.Details.CAPABILITY_CAN_UPGRADE_TO_VIDEO,

        Connection.CAPABILITY_CAN_PAUSE_VIDEO,
        android.telecom.Call.Details.CAPABILITY_CAN_PAUSE_VIDEO,

        Connection.CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION,
        android.telecom.Call.Details.CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION,

        Connection.CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO,
        android.telecom.Call.Details.CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO,

        Connection.CAPABILITY_CAN_PULL_CALL,
        android.telecom.Call.Details.CAPABILITY_CAN_PULL_CALL,

        Connection.CAPABILITY_SUPPORT_DEFLECT,
        android.telecom.Call.Details.CAPABILITY_SUPPORT_DEFLECT,

        Connection.CAPABILITY_ADD_PARTICIPANT,
        android.telecom.Call.Details.CAPABILITY_ADD_PARTICIPANT,

        Connection.CAPABILITY_TRANSFER,
        android.telecom.Call.Details.CAPABILITY_TRANSFER,

        Connection.CAPABILITY_TRANSFER_CONSULTATIVE,
        android.telecom.Call.Details.CAPABILITY_TRANSFER_CONSULTATIVE,

        Connection.CAPABILITY_REMOTE_PARTY_SUPPORTS_RTT,
        android.telecom.Call.Details.CAPABILITY_REMOTE_PARTY_SUPPORTS_RTT

    };

    private static int convertConnectionToCallCapabilities(int connectionCapabilities) {
        int callCapabilities = 0;
        for (int i = 0; i < CONNECTION_TO_CALL_CAPABILITY.length; i += 2) {
            if ((CONNECTION_TO_CALL_CAPABILITY[i] & connectionCapabilities) ==
                    CONNECTION_TO_CALL_CAPABILITY[i]) {

                callCapabilities |= CONNECTION_TO_CALL_CAPABILITY[i + 1];
            }
        }
        return callCapabilities;
    }

    private static final int[] CONNECTION_TO_CALL_PROPERTIES = new int[] {
        Connection.PROPERTY_HIGH_DEF_AUDIO,
        android.telecom.Call.Details.PROPERTY_HIGH_DEF_AUDIO,

        Connection.PROPERTY_WIFI,
        android.telecom.Call.Details.PROPERTY_WIFI,

        Connection.PROPERTY_GENERIC_CONFERENCE,
        android.telecom.Call.Details.PROPERTY_GENERIC_CONFERENCE,

        Connection.PROPERTY_EMERGENCY_CALLBACK_MODE,
        android.telecom.Call.Details.PROPERTY_EMERGENCY_CALLBACK_MODE,

        Connection.PROPERTY_IS_EXTERNAL_CALL,
        android.telecom.Call.Details.PROPERTY_IS_EXTERNAL_CALL,

        Connection.PROPERTY_HAS_CDMA_VOICE_PRIVACY,
        android.telecom.Call.Details.PROPERTY_HAS_CDMA_VOICE_PRIVACY,

        Connection.PROPERTY_SELF_MANAGED,
        android.telecom.Call.Details.PROPERTY_SELF_MANAGED,

        Connection.PROPERTY_ASSISTED_DIALING,
        android.telecom.Call.Details.PROPERTY_ASSISTED_DIALING,

        Connection.PROPERTY_IS_RTT,
        android.telecom.Call.Details.PROPERTY_RTT,

        Connection.PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL,
        android.telecom.Call.Details.PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL,

        Connection.PROPERTY_IS_ADHOC_CONFERENCE,
        android.telecom.Call.Details.PROPERTY_IS_ADHOC_CONFERENCE,

        Connection.PROPERTY_CROSS_SIM,
        android.telecom.Call.Details.PROPERTY_CROSS_SIM
    };

    private static int convertConnectionToCallProperties(int connectionProperties) {
        int callProperties = 0;
        for (int i = 0; i < CONNECTION_TO_CALL_PROPERTIES.length; i += 2) {
            if ((CONNECTION_TO_CALL_PROPERTIES[i] & connectionProperties) ==
                    CONNECTION_TO_CALL_PROPERTIES[i]) {

                callProperties |= CONNECTION_TO_CALL_PROPERTIES[i + 1];
            }
        }
        return callProperties;
    }

    /**
     * Removes the specified capability from the set of capabilities bits and returns the new set.
     */
    private static int removeCapability(int capabilities, int capability) {
        return capabilities & ~capability;
    }

    private static ParcelableRttCall getParcelableRttCall(Call call) {
        if (!call.isRttCall()) {
            return null;
        }
        return new ParcelableRttCall(call.getRttMode(), call.getInCallToCsRttPipeForInCall(),
                call.getCsToInCallRttPipeForInCall());
    }

    private ParcelableCallUtils() {}
}
