Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/234.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android 如何在其他呼叫运行时以编程方式合并呼叫(会议呼叫)_Android_Api_Merge - Fatal编程技术网

Android 如何在其他呼叫运行时以编程方式合并呼叫(会议呼叫)

Android 如何在其他呼叫运行时以编程方式合并呼叫(会议呼叫),android,api,merge,Android,Api,Merge,我的要求是这样的:假设我当时正在拨打一个号码,我想通过编程方式拨打另一个号码。到目前为止,我所做的是:当某个电话已经接通时,我能够拨打某个特定的号码。例如,假设我正在呼叫号码123,1分钟后(通过使用报警管理器),我触发一个事件来呼叫另一个号码456,这就完成了 Intent intent = new Intent(Intent.ACTION_CALL); intent.setData(Uri.parse("tel:456")); startActivity(intent); 我正在使用这种意

我的要求是这样的:假设我当时正在拨打一个号码,我想通过编程方式拨打另一个号码。到目前为止,我所做的是:当某个电话已经接通时,我能够拨打某个特定的号码。例如,假设我正在呼叫号码123,1分钟后(通过使用
报警管理器
),我触发一个事件来呼叫另一个号码456,这就完成了

Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:456"));
startActivity(intent);
我正在使用这种意图通话,现在我可以看到手机屏幕上的一个按钮来合并通话:


在此图像中,您可以看到一个合并调用的按钮。现在,当用户单击“合并”时,它将合并所有3个调用。我希望以编程方式进行合并,而不是使用用户界面。

Android API不支持调用合并功能。您可以看到此线程。 $20call/android开发者/6OXDEe0tCks/8cuKdW1J9b8J
但是,您可以使用aidl打开手机的呼叫板屏幕,用户可以从中添加另一个呼叫或合并呼叫。

您无法使用智能手机管理会议。您需要一个可以为您实现这一点的中间服务。您可以使用

Voxeo为CCXML实现提供了一个良好的托管平台,您可以查看他们关于如何设置会议的介绍。在“学习CCXML 1.0\CCXML 1.0中的多方会议”中有一些示例

你可以在Voxeo上免费开发和测试,只有在你投入生产后,他们才会开始向你收费。另一个选择是Twillio

这里有一个链接


检查链接,您将获得有用的信息。#礼貌-因此

Afaik,SDK中没有以编程方式进行合并调用的API

对于android用于电话呼叫的呼叫会议,您必须使用
RIL(无线接口层)

Android的无线电接口层(RIL)在Android电话服务(Android.telephony)和无线电硬件之间提供了一个抽象层。RIL与无线电无关,包括对基于全球移动通信系统(GSM)的无线电的支持

请看这里:

更新


因此,您必须在套接字中写入AT调制解调器命令,然后调用对供应商库的回调,然后供应商库依次委托无线电固件。

您的问题似乎很有趣,因此我开始挖掘Android源代码。以下是我发现的:

您发布的图片中的活动称为

当您开始环顾四周时,您会发现第463行中有:

final boolean canMerge = activeCall.can(Capabilities.MERGE_CALLS);
然后在472:

CallCommandClient.getInstance().merge();
当您在中检查merge()方法时,您会发现它使用的接口,我认为这就是您要查找的接口:)

CallCommandClient的初始化大约在第193行

希望这有帮助&祝你好运


我列出的API大部分都是Android内部的东西。您可能需要使用反射来调用它,或者根本不可能,因为它没有标记为系统应用,所以对您的应用程序来说可能无法访问。

android中没有用于获取会议呼叫的api,您可能希望使用根系统来完成工作

官方称,android没有为电话会议提供任何api。您可以在这里学习更多有关根访问的内容


经过大量搜索,我成功地合并了呼叫,在此我想与您分享我的发现。 作为参考,我用了这个

  • 在项目中使用CallList.java
  • 最后调用函数来合并调用

  • 我可以打电话给第三方,问题是如何合并,当我们建立第三个电话,而此时已经有两个人在通话,合并选项出现在电话屏幕上,我不希望用户点击合并按钮并建立电话会议,我想通过编程将其合并我不太确定,但你可以通过performClick()方法实际调用事件,但你必须在上面搜索ID这是我能做的事情,正如我在信中提到的question@Williams谢谢你的批评。我真诚地接受你的想法,但我只是说它在Android API中不可用,不适用于所有的东西。我也想知道哪个应用程序的名称也一样。这是一个系统应用程序吗?很酷,但需要检查它是否适用于Android手机,如果您发现任何解决方案、任何文章或其他与此相关的内容,请与我们分享。您所在国家的运营商是否支持GSM电话会议?@user755当然可以。这里是yescheck。。嘿,伙计,你怎么打第二个电话的?如果我从AlarmManager尝试startActivity,它将等待呼叫断开,然后它将拨打。。。你能和我们分享你的代码吗?我是按照你的代码来做的,但是我得到了ClassNotFound
    com.android.incallui.CallCommandClient
    @MeTTeO。。。。我制作了一个定制的android.jar,在其中我可以访问所有内部和隐藏的API方法,但不能访问
    InCallUI
    CallCommandClient
    。此外,我无法使用反射访问它们。请解释一下你上面的建议。你不能直接调用CallCommandClient,因为这个类在InCallUI应用程序中,它是完全独立的apk包-不是Android SDK的一部分,而是一个独立的应用程序。相反,您应该看看CallHandlerService是如何访问ICallCommandService的。@MeTTeO,我已经介绍了我可以访问的概念,但作为您的方法,不可能在第三方应用程序中访问这些方法。所以我认为上述解释没有任何意义。那么你能告诉我怎么做吗?@MeTTeO我知道你建议的API也不能通过它访问,同一个马克说-“我制作了一个定制的android.jar,在其中我可以访问所有内部和隐藏API的方法,但无法访问InCallUI、CallCommandClient。此外,我无法使用REFLECT访问它们
    package com.example.confrencecalldemo;
    
    import android.os.Handler;
    import android.os.Message;
    import android.os.Trace;
    import android.telecom.DisconnectCause;
    import android.telecom.PhoneAccount;
    
    import com.google.common.base.Preconditions;
    import com.google.common.collect.Maps;
    
    import java.util.Collections;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.List;
    import java.util.Set;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.CopyOnWriteArrayList;
    
    
    public class CallList {
    
        private static final int DISCONNECTED_CALL_SHORT_TIMEOUT_MS = 200;
        private static final int DISCONNECTED_CALL_MEDIUM_TIMEOUT_MS = 2000;
        private static final int DISCONNECTED_CALL_LONG_TIMEOUT_MS = 5000;
    
        private static final int EVENT_DISCONNECTED_TIMEOUT = 1;
    
        private static CallList sInstance = new CallList();
    
        private final HashMap<String, CallHelper> mCallById = new HashMap<>();
        private final HashMap<android.telecom.Call, CallHelper> mCallByTelecommCall = new HashMap<>();
        private final HashMap<String, List<String>> mCallTextReponsesMap = Maps.newHashMap();
        /**
         * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
         * load factor before resizing, 1 means we only expect a single thread to
         * access the map so make only a single shard
         */
        private final Set<Listener> mListeners = Collections.newSetFromMap(
                new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
        private final HashMap<String, List<CallUpdateListener>> mCallUpdateListenerMap = Maps
                .newHashMap();
        private final Set<CallHelper> mPendingDisconnectCalls = Collections.newSetFromMap(
                new ConcurrentHashMap<CallHelper, Boolean>(8, 0.9f, 1));
    
        /**
         * Static singleton accessor method.
         */
        public static CallList getInstance() {
            return sInstance;
        }
    
        /**
         * USED ONLY FOR TESTING
         * Testing-only constructor.  Instance should only be acquired through getInstance().
         */
        CallList() {
        }
    
        public void onCallAdded(android.telecom.Call telecommCall) {
            Trace.beginSection("onCallAdded");
            CallHelper call = new CallHelper(telecommCall);
    //        Log.d(this, "onCallAdded: callState=" + call.getState());
            if (call.getState() == CallHelper.State.INCOMING ||
                    call.getState() == CallHelper.State.CALL_WAITING) {
                onIncoming(call, call.getCannedSmsResponses());
            } else {
                onUpdate(call);
            }
            Trace.endSection();
        }
    
        public void onCallRemoved(android.telecom.Call telecommCall) {
            if (mCallByTelecommCall.containsKey(telecommCall)) {
                CallHelper call = mCallByTelecommCall.get(telecommCall);
                if (updateCallInMap(call)) {
    //                Log.w(this, "Removing call not previously disconnected " + call.getId());
                }
                updateCallTextMap(call, null);
            }
        }
    
        /**
         * Called when a single call disconnects.
         */
        public void onDisconnect(CallHelper call) {
            if (updateCallInMap(call)) {
    //            Log.i(this, "onDisconnect: " + call);
                // notify those listening for changes on this specific change
                notifyCallUpdateListeners(call);
                // notify those listening for all disconnects
                notifyListenersOfDisconnect(call);
            }
        }
    
        /**
         * Called when a single call has changed.
         */
        public void onIncoming(CallHelper call, List<String> textMessages) {
            if (updateCallInMap(call)) {
    //            Log.i(this, "onIncoming - " + call);
            }
            updateCallTextMap(call, textMessages);
    
            for (Listener listener : mListeners) {
                listener.onIncomingCall(call);
            }
        }
    
        public void onUpgradeToVideo(CallHelper call){
    //        Log.d(this, "onUpgradeToVideo call=" + call);
            for (Listener listener : mListeners) {
                listener.onUpgradeToVideo(call);
            }
        }
        /**
         * Called when a single call has changed.
         */
        public void onUpdate(CallHelper call) {
            Trace.beginSection("onUpdate");
            onUpdateCall(call);
            notifyGenericListeners();
            Trace.endSection();
        }
    
        /**
         * Called when a single call has changed session modification state.
         *
         * @param call The call.
         * @param sessionModificationState The new session modification state.
         */
        public void onSessionModificationStateChange(CallHelper call, int sessionModificationState) {
            final List<CallUpdateListener> listeners = mCallUpdateListenerMap.get(call.getId());
            if (listeners != null) {
                for (CallUpdateListener listener : listeners) {
                    listener.onSessionModificationStateChange(sessionModificationState);
                }
            }
        }
    
        /**
         * Called when the last forwarded number changes for a call.  With IMS, the last forwarded
         * number changes due to a supplemental service notification, so it is not pressent at the
         * start of the call.
         *
         * @param call The call.
         */
        public void onLastForwardedNumberChange(CallHelper call) {
            final List<CallUpdateListener> listeners = mCallUpdateListenerMap.get(call.getId());
            if (listeners != null) {
                for (CallUpdateListener listener : listeners) {
                    listener.onLastForwardedNumberChange();
                }
            }
        }
    
        /**
         * Called when the child number changes for a call.  The child number can be received after a
         * call is initially set up, so we need to be able to inform listeners of the change.
         *
         * @param call The call.
         */
        public void onChildNumberChange(CallHelper call) {
            final List<CallUpdateListener> listeners = mCallUpdateListenerMap.get(call.getId());
            if (listeners != null) {
                for (CallUpdateListener listener : listeners) {
                    listener.onChildNumberChange();
                }
            }
        }
    
        public void notifyCallUpdateListeners(CallHelper call) {
            final List<CallUpdateListener> listeners = mCallUpdateListenerMap.get(call.getId());
            if (listeners != null) {
                for (CallUpdateListener listener : listeners) {
                    listener.onCallChanged(call);
                }
            }
        }
    
        /**
         * Add a call update listener for a call id.
         *
         * @param callId The call id to get updates for.
         * @param listener The listener to add.
         */
        public void addCallUpdateListener(String callId, CallUpdateListener listener) {
            List<CallUpdateListener> listeners = mCallUpdateListenerMap.get(callId);
            if (listeners == null) {
                listeners = new CopyOnWriteArrayList<CallUpdateListener>();
                mCallUpdateListenerMap.put(callId, listeners);
            }
            listeners.add(listener);
        }
    
        /**
         * Remove a call update listener for a call id.
         *
         * @param callId The call id to remove the listener for.
         * @param listener The listener to remove.
         */
        public void removeCallUpdateListener(String callId, CallUpdateListener listener) {
            List<CallUpdateListener> listeners = mCallUpdateListenerMap.get(callId);
            if (listeners != null) {
                listeners.remove(listener);
            }
        }
    
        public void addListener(Listener listener) {
            Preconditions.checkNotNull(listener);
    
            mListeners.add(listener);
    
            // Let the listener know about the active calls immediately.
            listener.onCallListChange(this);
        }
    
        public void removeListener(Listener listener) {
            if (listener != null) {
                mListeners.remove(listener);
            }
        }
    
        /**
         * TODO: Change so that this function is not needed. Instead of assuming there is an active
         * call, the code should rely on the status of a specific CallHelper and allow the presenters to
         * update the CallHelper object when the active call changes.
         */
        public CallHelper getIncomingOrActive() {
            CallHelper retval = getIncomingCall();
            if (retval == null) {
                retval = getActiveCall();
            }
            return retval;
        }
    
        public CallHelper getOutgoingOrActive() {
            CallHelper retval = getOutgoingCall();
            if (retval == null) {
                retval = getActiveCall();
            }
            return retval;
        }
    
        /**
         * A call that is waiting for {@link PhoneAccount} selection
         */
        public CallHelper getWaitingForAccountCall() {
            return getFirstCallWithState(CallHelper.State.SELECT_PHONE_ACCOUNT);
        }
    
        public CallHelper getPendingOutgoingCall() {
            return getFirstCallWithState(CallHelper.State.CONNECTING);
        }
    
        public CallHelper getOutgoingCall() {
            CallHelper call = getFirstCallWithState(CallHelper.State.DIALING);
            if (call == null) {
                call = getFirstCallWithState(CallHelper.State.REDIALING);
            }
            return call;
        }
    
        public CallHelper getActiveCall() {
            return getFirstCallWithState(CallHelper.State.ACTIVE);
        }
    
        public CallHelper getBackgroundCall() {
            return getFirstCallWithState(CallHelper.State.ONHOLD);
        }
    
        public CallHelper getDisconnectedCall() {
            return getFirstCallWithState(CallHelper.State.DISCONNECTED);
        }
    
        public CallHelper getDisconnectingCall() {
            return getFirstCallWithState(CallHelper.State.DISCONNECTING);
        }
    
        public CallHelper getSecondBackgroundCall() {
            return getCallWithState(CallHelper.State.ONHOLD, 1);
        }
    
        public CallHelper getActiveOrBackgroundCall() {
            CallHelper call = getActiveCall();
            if (call == null) {
                call = getBackgroundCall();
            }
            return call;
        }
    
        public CallHelper getIncomingCall() {
            CallHelper call = getFirstCallWithState(CallHelper.State.INCOMING);
            if (call == null) {
                call = getFirstCallWithState(CallHelper.State.CALL_WAITING);
            }
    
            return call;
        }
    
        public CallHelper getFirstCall() {
            CallHelper result = getIncomingCall();
            if (result == null) {
                result = getPendingOutgoingCall();
            }
            if (result == null) {
                result = getOutgoingCall();
            }
            if (result == null) {
                result = getFirstCallWithState(CallHelper.State.ACTIVE);
            }
            if (result == null) {
                result = getDisconnectingCall();
            }
            if (result == null) {
                result = getDisconnectedCall();
            }
            return result;
        }
    
        public boolean hasLiveCall() {
            CallHelper call = getFirstCall();
            if (call == null) {
                return false;
            }
            return call != getDisconnectingCall() && call != getDisconnectedCall();
        }
    
    
        public CallHelper getVideoUpgradeRequestCall() {
            for(CallHelper call : mCallById.values()) {
                if (call.getSessionModificationState() ==
                        CallHelper.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
                    return call;
                }
            }
            return null;
        }
    
        public CallHelper getCallById(String callId) {
            return mCallById.get(callId);
        }
    
        public CallHelper getCallByTelecommCall(android.telecom.Call telecommCall) {
            return mCallByTelecommCall.get(telecommCall);
        }
    
        public List<String> getTextResponses(String callId) {
            return mCallTextReponsesMap.get(callId);
        }
    
        /**
         * Returns first call found in the call map with the specified state.
         */
        public CallHelper getFirstCallWithState(int state) {
            return getCallWithState(state, 0);
        }
    
        /**
         * Returns the [position]th call found in the call map with the specified state.
         * TODO: Improve this logic to sort by call time.
         */
        public CallHelper getCallWithState(int state, int positionToFind) {
            CallHelper retval = null;
            int position = 0;
            for (CallHelper call : mCallById.values()) {
                if (call.getState() == state) {
                    if (position >= positionToFind) {
                        retval = call;
                        break;
                    } else {
                        position++;
                    }
                }
            }
    
            return retval;
        }
    
        /**
         * This is called when the service disconnects, either expectedly or unexpectedly.
         * For the expected case, it's because we have no calls left.  For the unexpected case,
         * it is likely a crash of phone and we need to clean up our calls manually.  Without phone,
         * there can be no active calls, so this is relatively safe thing to do.
         */
        public void clearOnDisconnect() {
            for (CallHelper call : mCallById.values()) {
                final int state = call.getState();
                if (state != CallHelper.State.IDLE &&
                        state != CallHelper.State.INVALID &&
                        state != CallHelper.State.DISCONNECTED) {
    
                    call.setState(CallHelper.State.DISCONNECTED);
                    call.setDisconnectCause(new DisconnectCause(DisconnectCause.UNKNOWN));
                    updateCallInMap(call);
                }
            }
            notifyGenericListeners();
        }
    
        /**
         * Called when the user has dismissed an error dialog. This indicates acknowledgement of
         * the disconnect cause, and that any pending disconnects should immediately occur.
         */
        public void onErrorDialogDismissed() {
            final Iterator<CallHelper> iterator = mPendingDisconnectCalls.iterator();
            while (iterator.hasNext()) {
                CallHelper call = iterator.next();
                iterator.remove();
                finishDisconnectedCall(call);
            }
        }
    
        /**
         * Processes an update for a single call.
         *
         * @param call The call to update.
         */
        private void onUpdateCall(CallHelper call) {
    //        Log.d(this, "\t" + call);
            if (updateCallInMap(call)) {
    //            Log.i(this, "onUpdate - " + call);
            }
            updateCallTextMap(call, call.getCannedSmsResponses());
            notifyCallUpdateListeners(call);
        }
    
        /**
         * Sends a generic notification to all listeners that something has changed.
         * It is up to the listeners to call back to determine what changed.
         */
        private void notifyGenericListeners() {
            for (Listener listener : mListeners) {
                listener.onCallListChange(this);
            }
        }
    
        private void notifyListenersOfDisconnect(CallHelper call) {
            for (Listener listener : mListeners) {
                listener.onDisconnect(call);
            }
        }
    
        /**
         * Updates the call entry in the local map.
         * @return false if no call previously existed and no call was added, otherwise true.
         */
        private boolean updateCallInMap(CallHelper call) {
            Preconditions.checkNotNull(call);
    
            boolean updated = false;
    
            if (call.getState() == CallHelper.State.DISCONNECTED) {
                // update existing (but do not add!!) disconnected calls
                if (mCallById.containsKey(call.getId())) {
                    // For disconnected calls, we want to keep them alive for a few seconds so that the
                    // UI has a chance to display anything it needs when a call is disconnected.
    
                    // Set up a timer to destroy the call after X seconds.
                    final Message msg = mHandler.obtainMessage(EVENT_DISCONNECTED_TIMEOUT, call);
                    mHandler.sendMessageDelayed(msg, getDelayForDisconnect(call));
                    mPendingDisconnectCalls.add(call);
    
                    mCallById.put(call.getId(), call);
                    mCallByTelecommCall.put(call.getTelecommCall(), call);
                    updated = true;
                }
            } else if (!isCallDead(call)) {
                mCallById.put(call.getId(), call);
                mCallByTelecommCall.put(call.getTelecommCall(), call);
                updated = true;
            } else if (mCallById.containsKey(call.getId())) {
                mCallById.remove(call.getId());
                mCallByTelecommCall.remove(call.getTelecommCall());
                updated = true;
            }
    
            return updated;
        }
    
        private int getDelayForDisconnect(CallHelper call) {
            Preconditions.checkState(call.getState() == CallHelper.State.DISCONNECTED);
    
    
            final int cause = call.getDisconnectCause().getCode();
            final int delay;
            switch (cause) {
                case DisconnectCause.LOCAL:
                    delay = DISCONNECTED_CALL_SHORT_TIMEOUT_MS;
                    break;
                case DisconnectCause.REMOTE:
                case DisconnectCause.ERROR:
                    delay = DISCONNECTED_CALL_MEDIUM_TIMEOUT_MS;
                    break;
                case DisconnectCause.REJECTED:
                case DisconnectCause.MISSED:
                case DisconnectCause.CANCELED:
                    // no delay for missed/rejected incoming calls and canceled outgoing calls.
                    delay = 0;
                    break;
                default:
                    delay = DISCONNECTED_CALL_LONG_TIMEOUT_MS;
                    break;
            }
    
            return delay;
        }
    
        private void updateCallTextMap(CallHelper call, List<String> textResponses) {
            Preconditions.checkNotNull(call);
    
            if (!isCallDead(call)) {
                if (textResponses != null) {
                    mCallTextReponsesMap.put(call.getId(), textResponses);
                }
            } else if (mCallById.containsKey(call.getId())) {
                mCallTextReponsesMap.remove(call.getId());
            }
        }
    
        private boolean isCallDead(CallHelper call) {
            final int state = call.getState();
            return CallHelper.State.IDLE == state || CallHelper.State.INVALID == state;
        }
    
        /**
         * Sets up a call for deletion and notifies listeners of change.
         */
        private void finishDisconnectedCall(CallHelper call) {
            if (mPendingDisconnectCalls.contains(call)) {
                mPendingDisconnectCalls.remove(call);
            }
            call.setState(CallHelper.State.IDLE);
            updateCallInMap(call);
            notifyGenericListeners();
        }
    
        /**
         * Notifies all video calls of a change in device orientation.
         *
         * @param rotation The new rotation angle (in degrees).
         */
        public void notifyCallsOfDeviceRotation(int rotation) {
            for (CallHelper call : mCallById.values()) {
                // First, ensure a VideoCall is set on the call so that the change can be sent to the
                // provider (a VideoCall can be present for a call that does not currently have video,
                // but can be upgraded to video).
                // Second, ensure that the call videoState has video enabled (there is no need to set
                // device orientation on a voice call which has not yet been upgraded to video).
                if (call.getVideoCall() != null && CallUtils.isVideoCall(call)) {
                    call.getVideoCall().setDeviceOrientation(rotation);
                }
            }
        }
    
        /**
         * Handles the timeout for destroying disconnected calls.
         */
        private Handler mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case EVENT_DISCONNECTED_TIMEOUT:
    //                    Log.d(this, "EVENT_DISCONNECTED_TIMEOUT ", msg.obj);
                        finishDisconnectedCall((CallHelper) msg.obj);
                        break;
                    default:
    //                    Log.wtf(this, "Message not expected: " + msg.what);
                        break;
                }
            }
        };
    
        /**
         * Listener interface for any class that wants to be notified of changes
         * to the call list.
         */
        public interface Listener {
            /**
             * Called when a new incoming call comes in.
             * This is the only method that gets called for incoming calls. Listeners
             * that want to perform an action on incoming call should respond in this method
             * because {@link #onCallListChange} does not automatically get called for
             * incoming calls.
             */
            public void onIncomingCall(CallHelper call);
            /**
             * Called when a new modify call request comes in
             * This is the only method that gets called for modify requests.
             */
            public void onUpgradeToVideo(CallHelper call);
            /**
             * Called anytime there are changes to the call list.  The change can be switching call
             * states, updating information, etc. This method will NOT be called for new incoming
             * calls and for calls that switch to disconnected state. Listeners must add actions
             * to those method implementations if they want to deal with those actions.
             */
            public void onCallListChange(CallList callList);
    
            /**
             * Called when a call switches to the disconnected state.  This is the only method
             * that will get called upon disconnection.
             */
            public void onDisconnect(CallHelper call);
    
    
        }
    
        public interface CallUpdateListener {
            // TODO: refactor and limit arg to be call state.  Caller info is not needed.
            public void onCallChanged(CallHelper call);
    
            /**
             * Notifies of a change to the session modification state for a call.
             *
             * @param sessionModificationState The new session modification state.
             */
            public void onSessionModificationStateChange(int sessionModificationState);
    
            /**
             * Notifies of a change to the last forwarded number for a call.
             */
            public void onLastForwardedNumberChange();
    
            /**
             * Notifies of a change to the child number for a call.
             */
            public void onChildNumberChange();
        }
    }
    
    @Override
        public void onCallAdded(Call call) {
            super.onCallAdded(call);
            Log.d("MyConnectionService","onCallAdded");
    
            CallList.getInstance().onCallAdded(call);
            
        }
    
        @Override
        public void onCallRemoved(Call call) {
            super.onCallRemoved(call);
            Log.d("MyConnectionService","onCallRemoved"); 
    
            CallList.getInstance().onCallRemoved(call);
        }
    
    public void mergeCall() {
    
            final CallList calls = CallList.getInstance();
            CallHelper activeCall = calls.getActiveCall();
    
            if (activeCall != null) {
    
                final boolean canMerge = activeCall.can(
                        android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE);
                final boolean canSwap = activeCall.can(
                        android.telecom.Call.Details.CAPABILITY_SWAP_CONFERENCE);
                // (2) Attempt actions on conference calls
                if (canMerge) {
                    TelecomAdapter.getInstance().merge(activeCall.getId());
    
                } else if (canSwap) {
                    TelecomAdapter.getInstance().swap(activeCall.getId());
                }
            }
        }