我尝试使用
android.net.sip.SipManager
将出站 SIP 呼叫添加到 Android 应用程序中,我接到了 onRingBack
的电话,但是当对方接听电话时,我接到了 onError
的电话,代码为 -4 并显示消息 android.system.ErrnoException: sendto failed: ENETUNREACH (Network is unreachable)
.
我怀疑这是某种防火墙或网络问题,因为根据我使用的网络,我在过程中的不同点收到 ENETUNREACH 错误。启用 VPN 后,我会在
onRingBack
回调之前收到它。使用公共 WiFi 网络,我根本不会收到 onError
回调,只是在 onRingBack
之后没有收到任何回调。通过我的个人 WiFi 网络,我得到了如上所述的 onError
。 但不知怎的,LinPhoneAndroid 应用程序没有这个问题,所以必须有一种方法来解决这个问题。
为了进行测试设置,我在 linphone.org 创建了两个帐户:
我在 iPhone 11 上使用第一个帐户和 LinPhone iOS 应用程序。我在装有 Android 12 的 Google Pixel 4a 上使用第二个帐户和 LinPhone Android 应用程序。我验证了我可以成功从 Android 到 iOS 拨打电话使用这些 LinPhone 应用程序。
然后我在 Android 上编写了自己的应用程序来调用 iPhone。它成功注册到 SIP 服务器:
sipProfile = SipProfile.Builder(username, domain).setPassword(password).build()
val intent = Intent()
intent.action = "android.SipDemo.INCOMING_CALL"
val pendingIntent = PendingIntent.getBroadcast(context, 0, intent, Intent.FILL_IN_DATA)
sipManager.open(sipProfile, pendingIntent, null)
sipManager.setRegistrationListener(sipProfile.uriString, object: SipRegistrationListener {
override fun onRegistering(localProfileUri: String?) {
Log.d(TAG, "Registering")
}
override fun onRegistrationDone(localProfileUri: String?, expiryTime: Long) {
registrationExpiration = Date(expiryTime)
...
Log.d(TAG, "Registration done. LocalProfileUri: $localProfileUri Expiry Time: ${registrationExpiration}")
}
override fun onRegistrationFailed(
localProfileUri: String?,
errorCode: Int,
errorMessage: String?
) {
Log.d(TAG, "Registration failed. LocalProfileUri: $localProfileUri, Error code: $errorCode, Error MessagE: $errorMessage")
}
})
然后,我可以使用
sipManager.makeAudioCall(sipProfile.getUriString(), sipAddress, audioCallListener, 30)
向 iPhone 发起出站呼叫,iPhone 实际上会响铃并可以应答呼叫,从而启动 LinPhone iOS 应用程序并显示正在进行的呼叫计时器。 **当 iPhone 开始响铃时,我的 Android 代码会回调 onRingingBack
。但是当 iPhone 接听电话时,它会接到一个拨打 onError
的电话,代码为 -4 并发送消息sendto failed: ENETUNREACH (Network is unreachable)
以下是发起调用并实现回调的完整代码:
audioCallListener = object: SipAudioCall.Listener() {
public override fun onError(
call: SipAudioCall?,
errorCode: Int,
errorMessage: String?
) {
super.onError(call, errorCode, errorMessage)
Log.d(TAG, "Error making call code: $errorCode, message: $errorMessage")
}
public override fun onReadyToCall(call: SipAudioCall?) {
Log.d(TAG, "ready to call")
super.onReadyToCall(call)
}
public override fun onRingingBack(call: SipAudioCall?) {
Log.d(TAG, "onRingingBack")
super.onRingingBack(call)
}
public override fun onCalling(call: SipAudioCall?) {
Log.d(TAG, "onCalling")
super.onCalling(call)
}
public override fun onCallHeld(call: SipAudioCall?) {
Log.d(TAG, "onCallHeld")
super.onCallHeld(call)
}
public override fun onCallBusy(call: SipAudioCall?) {
Log.d(TAG, "onCallBusy")
super.onCallBusy(call)
}
public override fun onChanged(call: SipAudioCall?) {
val state = call?.state ?: -1
Log.d(TAG, "onChanged state: ${state}")
super.onChanged(call)
}
public override fun onRinging(call: SipAudioCall?, caller: SipProfile?) {
Log.d(TAG, "Ringing...")
super.onRinging(call, caller)
}
public override fun onCallEstablished(call: SipAudioCall) {
mediaPlayer?.stop()
call.startAudio()
call.setSpeakerMode(true)
call.toggleMute()
Log.d(TAG, "Calls started")
super.onCallEstablished(call)
}
public override fun onCallEnded(call: SipAudioCall) {
Log.d(TAG, "Call ended")
super.onCallEnded(call)
}
}
GlobalScope.launch {
Log.d(TAG, "Making call from ${sipProfile.getUriString()} to ${sipAddress}")
try {
lastCall = sipManager.makeAudioCall(sipProfile.getUriString(), sipAddress, audioCallListener, 30);
}
catch (e: SipException) {
Log.e(TAG, "Call failed", e)
}
}
当我的电脑通过 VPN 连接时,端点可以访问,而我的手机却不能访问,这种情况就发生在我身上。将您的手机连接到 VPN 或使用反向网络共享,以便您的手机通过 PC 连接 - 请参阅 https://github.com/Genymobile/gnirehtet