How to merge Call programmatically while other call is running (Conference call)

Learn how to merge call programmatically while other call is running (conference call) with practical examples, diagrams, and best practices. Covers android, merge development techniques with visua...

Programmatic Conference Call Merging on Android

Hero image for How to merge Call programmatically while other call is running (Conference call)

Learn how to programmatically merge an incoming call into an active call to create a conference call experience on Android devices.

Creating a conference call programmatically on Android involves managing multiple Call objects and utilizing the device's telephony capabilities. This article will guide you through the process, focusing on the android.telecom framework, which is essential for handling in-call experiences on modern Android versions. We'll cover the necessary permissions, how to detect and manage calls, and the specific API calls required to merge them into a conference.

Understanding Android's Telephony Framework

Before diving into the code, it's crucial to understand how Android handles calls. The android.telecom package provides classes for managing calls, including Call, Call.Details, and InCallService. An InCallService is a specialized service that an application can implement to provide its own in-call user interface and manage calls. For programmatic merging, your application will typically need to interact with the active Call objects provided by the system's TelecomManager.

flowchart TD
    A[Incoming Call Detected] --> B{Is there an Active Call?}
    B -->|Yes| C[Active Call Exists]
    B -->|No| D[Handle as New Call]
    C --> E[Accept Incoming Call]
    E --> F[Hold Active Call (Optional)]
    F --> G[Merge Calls]
    G --> H[Conference Call Established]

Flowchart illustrating the process of merging an incoming call into an active call.

Permissions and Setup

To manage calls, your application needs specific permissions. These must be declared in your AndroidManifest.xml file. Additionally, your app needs to be set as the default dialer or have appropriate InCallService capabilities to fully control the call state. For basic merging functionality, you'll primarily need READ_PHONE_STATE and MANAGE_OWN_CALLS.

<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
<uses-permission android:name="android.permission.CALL_PHONE" />

<application ...>
    <service android:name=".YourInCallService"
        android:permission="android.permission.BIND_INCALL_SERVICE"
        android:exported="true">
        <intent-filter>
            <action android:name="android.telecom.InCallService" />
        </intent-filter>
    </service>
</application>

Required permissions and InCallService declaration in AndroidManifest.xml

Implementing Call Merging Logic

The core logic for merging calls resides within your InCallService implementation. When a new incoming call arrives while another call is active, your service will receive updates. You'll need to identify the active call and the new incoming call, then use the Call.mergeCalls() method to combine them. This method is available on the Call object that represents the active call.

import android.telecom.Call;
import android.telecom.InCallService;

public class MyInCallService extends InCallService {

    private Call activeCall = null;
    private Call incomingCall = null;

    @Override
    public void onCallAdded(Call call) {
        super.onCallAdded(call);
        if (call.getState() == Call.STATE_ACTIVE) {
            activeCall = call;
            // Register a callback to monitor the active call
            activeCall.registerCallback(activeCallCallback);
        } else if (call.getState() == Call.STATE_RINGING) {
            incomingCall = call;
            // Register a callback to monitor the incoming call
            incomingCall.registerCallback(incomingCallCallback);
            
            // Attempt to merge if an active call already exists
            if (activeCall != null && activeCall.can(Call.Details.CAPABILITY_MERGE_CONFERENCE)) {
                // Answer the incoming call first
                incomingCall.answer(0);
            }
        }
    }

    @Override
    public void onCallRemoved(Call call) {
        super.onCallRemoved(call);
        if (call.equals(activeCall)) {
            activeCall = null;
        } else if (call.equals(incomingCall)) {
            incomingCall = null;
        }
    }

    private Call.Callback activeCallCallback = new Call.Callback() {
        @Override
        public void onStateChanged(Call call, int state) {
            if (state == Call.STATE_ACTIVE && incomingCall != null && incomingCall.getState() == Call.STATE_ACTIVE) {
                // Both calls are active, now merge them
                if (call.can(Call.Details.CAPABILITY_MERGE_CONFERENCE)) {
                    call.mergeCalls();
                    // Clear incomingCall reference after merging
                    incomingCall = null;
                } else {
                    // Handle cases where merging is not supported
                    // e.g., show a toast or log an error
                    Log.w("MyInCallService", "Active call cannot merge conference.");
                }
            }
        }
    };

    private Call.Callback incomingCallCallback = new Call.Callback() {
        @Override
        public void onStateChanged(Call call, int state) {
            if (state == Call.STATE_ACTIVE && activeCall != null && activeCall.getState() == Call.STATE_ACTIVE) {
                // Both calls are active, now merge them
                if (activeCall.can(Call.Details.CAPABILITY_MERGE_CONFERENCE)) {
                    activeCall.mergeCalls();
                    // Clear incomingCall reference after merging
                    incomingCall = null;
                } else {
                    Log.w("MyInCallService", "Active call cannot merge conference.");
                }
            }
        }
    };
}

Example InCallService logic for detecting and merging calls.

1. Declare Permissions

Add READ_PHONE_STATE, MANAGE_OWN_CALLS, and CALL_PHONE permissions to your AndroidManifest.xml.

2. Implement InCallService

Create a class extending android.telecom.InCallService and declare it in your manifest. This service will receive callbacks for call state changes.

3. Track Active and Incoming Calls

Within your InCallService, maintain references to the current active call and any new incoming calls using onCallAdded() and onCallRemoved().

4. Answer Incoming Call

When an incoming call is detected and an active call exists, answer the incoming call using incomingCall.answer(0).

5. Check Merge Capability

Before merging, verify that the active call supports conferencing using activeCall.can(Call.Details.CAPABILITY_MERGE_CONFERENCE).

6. Merge Calls

Once both calls are active and merge capability is confirmed, call activeCall.mergeCalls() to combine them into a conference.

7. Handle Callbacks

Use Call.Callback to monitor state changes of both calls, triggering the merge operation at the appropriate time (e.g., when both calls are STATE_ACTIVE).