Skip to content

Managed Telecom Integration

Since version 26.1, Libsoftphone manages its own Android Telecom integration. Your app no longer needs to implement a ConnectionService or register a PhoneAccount itself. The SDK also supports compatibility audio routing for devices where Android Telecom is unavailable or cannot handle a call reliably.

This guide covers strategy selection, audio endpoint selection, permission updates, and runtime diagnostics. The examples use the Instance API; call them on the main thread after loading the SDK.

Audio routing strategies

The defaultTelecomStrategy preference selects the preferred strategy for new calls:

Value Behavior
system (default) Integrates calls with Android Telecom and uses its audio routing support.
compat Manages call audio through AudioManager without using a TelecomManager.

Changing defaultTelecomStrategy does not immediately switch calls that are already in progress. To move the current call session to compatibility routing, use the explicit switch described below.

Automatic fallback

The SDK can switch strategies while calls are active. If system Telecom is unavailable or reporting a call to it fails, the SDK falls back to compatibility routing. It can also switch preemptively when a new call would require a call state that system Telecom cannot support.

Fallback applies to the current call session: existing calls and any additional calls started before the last call ends use compatibility routing. Once all calls have ended, the temporary override is cleared and subsequent calls use the configured default again. Fallback does not change the defaultTelecomStrategy preference.

You can observe the current routing mode through Instance.Telecom.isUsingCompatibilityAudioRouting(), which returns a StateFlow<Boolean>. This reports the current mode, including a configured compat strategy, rather than only indicating that an error occurred.

Switching active calls to compatibility routing

If your app detects a problem with system Telecom, request a switch without waiting for the calls to end:

Instance.Telecom.switchReportedCallsToCompatibilityAudioRouting();

This moves all currently reported calls to compatibility routing and keeps that strategy for additional calls during the same session. After the last call ends, the SDK returns to the configured default for future calls. Calling this method when there are no reported calls has no effect; use defaultTelecomStrategy to configure future calls.

Telecom errors are also delivered to Observer.onTelecomError(...). Your app can use this callback to record diagnostics or offer a compatibility routing option to the user.

Audio endpoints

Audio routes are now represented by cz.acrobits.libsoftphone.telecom.AudioEndpoint objects instead of only an enum of route types. This lets your app distinguish multiple endpoints of the same type, such as two Bluetooth devices.

Each endpoint exposes:

Property Meaning
id The endpoint's identifier. Use this to distinguish endpoints of the same type.
name An optional endpoint name, suitable for a device selection UI. Provide a fallback label when it is null.
type Unknown, Receiver, Speaker, Bluetooth, Headset, or Streaming.
isSynthetic Indicates an endpoint that cannot be associated with a specific physical device, for example because Bluetooth permission is missing.

Use endpoint instances supplied by the SDK; do not implement AudioEndpoint yourself. When building a route picker, keep separate entries for endpoints with different IDs even if their types match.

The Instance.Audio API provides the following operations:

Method Purpose
getAvailableCallAudioEndpoints() Returns a StateFlow<List<AudioEndpoint>> containing the available routes.
getCurrentAudioEndpoint() Returns a StateFlow containing the current route, or null while it is unknown.
setCallAudioEndpoint(endpoint) Requests a route using an endpoint from the available list.

Your existing SDK observer can also receive changes through onAudioAvailableAudioEndpointsChanged(...) and onAudioEndpointChanged(...). To choose a route when a call starts, implement Observer.getStartingAudioEndpoint(...) and return one of the supplied endpoints, or null to use the default.

Refresh your route picker when the available list changes, including after a strategy switch or permission update. Use the current endpoint updates to show the route actually in use after a selection request.

Updating permissions during a call

After your app receives a runtime permission grant, notify the SDK so it can refresh the affected audio components. Both methods below can be called while a call is active. See Android permissions for the required permissions and permission result handling.

Bluetooth access

After obtaining BLUETOOTH_CONNECT permission, reload the endpoints:

Instance.Audio.reloadAudioEndpoints();

This lets the SDK refresh Bluetooth device information and replace synthetic endpoints with endpoints for specific devices where available. Update your route picker from the resulting endpoint list.

Microphone access

After obtaining RECORD_AUDIO permission, refresh the recorder's permission state:

Instance.Audio.reloadAudioRecorderPermissions();

This allows the SDK to update recording after permission is granted during an ongoing call.

Testing system Telecom on a device

Some devices expose Android Telecom but fail when an app tries to use it. The SDK provides a runtime test that exercises simulated outgoing and incoming calls through the system strategy. These are local Telecom test calls; they do not place SIP or cellular calls.

Run the test only when no calls are active, and wait for it to finish before starting a call. The SDK rejects attempts to add calls while the test is running.

import cz.acrobits.commons.Disposable;

Disposable test = Instance.Telecom.testSystemAudioRoutingOnCurrentDevice(result -> {
    if (result.isSuccess()) {
        // System Telecom handled the simulated calls successfully.
    } else {
        // Inspect result.getFailureReasonOrNull() and update your UI.
    }
});

The test is asynchronous and returns an AudioRoutingTestResult to the callback. Possible failure reasons include ActiveCallsPresent, TestAlreadyRunning, SystemStrategyNotAvailable, TestTimedOut, SingleOutgoingCallFailed, SingleIncomingCallFailed, and UnknownError. The returned Disposable lets you cancel a test that is still running by calling dispose().

The test checks whether system Telecom can handle the simulated call lifecycle; success does not verify microphone input or audible playback. It does not change defaultTelecomStrategy automatically. Your app can use the result to help the user choose between system and compat.

Migrating from earlier SDK versions

When upgrading to 26.1 or later:

  1. Remove your app's custom ConnectionService implementation and its service declaration from the manifest.
  2. Remove the app code that registers the old service's PhoneAccount. You don't need to unregister them, Android Framework will do that automatically when the service is removed.
  3. Use defaultTelecomStrategy to select the preferred routing strategy.
  4. Update audio route selection code to use SDK-provided AudioEndpoint objects and respond to endpoint list changes.
  5. Notify the SDK after Bluetooth or microphone permission is granted at runtime.

The legacy ConnectionService integration guide applies to SDK versions before 26.1.