Android Known Issues
This page provides a concise overview of platform-specific and device-specific known issues that may arise when integrating the Vonage Client SDK into your Android applications.
Voice SDK Integration
Handling isIncomingCallPermitted Exception
If you're using the isIncomingCallPermitted method from the TelecomManager, be aware that it may throw exceptions on certain devices, like Xiaomi. To ensure smooth operation, it's recommended to enclose this method call within a try-catch block and set a default value to true in case of an exception. Otherwise, the app might encounter issues while placing incoming calls. Here's how you can implement it:
// Get the TelecomManager instance
val telecomManager = context.getSystemService(AppCompatActivity.TELECOM_SERVICE) as TelecomManager
// Check permissions for incoming calls and handle exceptions
val isIncomingCallPermitted = try {
telecomManager.isIncomingCallPermitted(phoneAccountHandle)
} catch (_: Exception) {
// Exception occurred, default to true
true
}By adopting this approach, your application can gracefully handle potential exceptions, ensuring uninterrupted functionality, especially on devices where the isIncomingCallPermitted method may throw errors.
ConnectionService Unavailability Workaround
On certain devices, such as Samsung smartphones in airplane mode, the ConnectionService might not be available for handling outgoing calls, leading to potential issues. To address this, we present a workaround that enables making calls despite the unavailability of the ConnectionService.
Workaround for Placing Outgoing Calls
To overcome the unavailability of the ConnectionService, utilize the following code snippet in your application to place outgoing calls:
// Workaround for placing outgoing calls when ConnectionService is not available
private fun placeOutgoingCall(callId: CallId, callee: String) {
try {
coreContext.telecomHelper.startOutgoingCall(callId, callee)
// If ConnectionService does not respond within 3 seconds,
// we mock an outgoing connection
TimerManager.startTimer(TimerManager.CONNECTION_SERVICE_TIMER, 3000) {
mockOutgoingConnection(callId, callee)
}
} catch (e: Exception) {
abortOutboundCall(callId, e.message)
}
}
/**
* This method will mock `ConnectionService#onCreateOutgoingConnection`,
* allowing outgoing calls without direct interaction with the Telecom framework.
*/
private fun mockOutgoingConnection(callId: CallId, to: String): CallConnection {
showToast(context, "ConnectionService Not Available")
val connection = CallConnection(callId).apply {
setAddress(Uri.parse(to), TelecomManager.PRESENTATION_ALLOWED)
setCallerDisplayName(to, TelecomManager.PRESENTATION_ALLOWED)
setDialing()
}
return connection
}ConnectionService Implementation
Then, ensure your class implements the necessary ConnectionService methods as shown below:
import android.telecom.Connection
import android.telecom.ConnectionRequest
import android.telecom.PhoneAccountHandle
import android.telecom.ConnectionService
import android.telecom.TelecomManager
// Your class implementing ConnectionService
class YourConnectionService : ConnectionService() {
override fun onCreateOutgoingConnection(
connectionManagerPhoneAccount: PhoneAccountHandle?,
request: ConnectionRequest?
): Connection {
// Cancel the timer set in the workaround
TimerManager.cancelTimer(TimerManager.CONNECTION_SERVICE_TIMER)
// Your implementation of ConnectionService onCreateOutgoingConnection method
// ... (code snippet)
return connection
}
override fun onCreateOutgoingConnectionFailed(connectionManagerPhoneAccount: PhoneAccountHandle?, request: ConnectionRequest?) {
// Cancel the timer set in the workaround
TimerManager.cancelTimer(TimerManager.CONNECTION_SERVICE_TIMER)
// Your implementation of ConnectionService onCreateOutgoingConnectionFailed method
// ... (code snippet)
}
}TimerManager Utility
The TimerManager used in the workaround is just a custom utility class that allows you to set a timer for a callback function. It ensures that the function is executed if the timer is not canceled before the specified duration.
object TimerManager {
private val handler: Handler = Handler(Looper.getMainLooper())
private val timerMap: MutableMap<String, Runnable> = mutableMapOf()
const val CONNECTION_SERVICE_TIMER = "ConnectionServiceTimer"
fun startTimer(timerId: String, delayMillis: Long, callback: () -> Unit) {
val runnable = Runnable {
callback.invoke()
timerMap.remove(timerId)
}
timerMap[timerId] = runnable
handler.postDelayed(runnable, delayMillis)
}
fun cancelTimer(timerId: String) {
val runnable = timerMap[timerId]
if (runnable != null) {
handler.removeCallbacks(runnable)
timerMap.remove(timerId)
}
}
}By incorporating this workaround and the TimerManager utility, your application will effectively manage outgoing calls, even on devices where the ConnectionService may not function correctly.