会话发起协议概览

检测 eSIM 卡和 SIM 卡

检测卡

使用 SIM 卡和 eSIM 卡的 Android 设备在电话功能中使用以下 ID API,包括 [`TelephonyManager`](/reference/android/telephony/TelephonyManager) 和 [`SubscriptionManager`](/reference/android/telephony/SubscriptionManager): * 订阅 ID:移动订阅的唯一 ID。 * 逻辑插槽索引或 ID:引用逻辑 SIM 卡插槽的唯一索引。 逻辑插槽 ID 从 0 开始,并根据 支持的活跃槽位。例如,双 SIM 卡设备 具有槽位 0 和槽位 1 的会话。如果设备有多个物理插槽,但只有 支持一个活动槽位,但它只会拥有逻辑槽位 ID 0。 * 物理插槽索引或 ID:引用实体 SIM 卡插槽的唯一索引。 物理插槽 ID 从 0 开始,并根据物理插槽的数量 槽。这与设备的逻辑插槽数量不同 (对应于设备能够使用的有效槽数) 使用。例如,在双 SIM 卡和单 SIM 卡之间切换的设备 模式可能始终有两个实体插槽,但在单 SIM 卡模式下 一个逻辑插槽 * 卡片 ID:用于标识 UiccCard 的唯一 ID。 ![在具有两个逻辑槽和三个物理槽的情况下如何使用 ID 的示意图](/images/guide/topics/connectivity/tel-ids.png) 在上图中: * 设备有两个逻辑插槽。 * 在实体插槽 0 中,有一个具有有效配置文件的物理 UICC 卡。 * 实体槽位 2 中有一个具有有效配置文件的 eUICC。 * 物理槽位 1 当前未在使用。 ![在具有三个逻辑槽和两个物理槽的情况下如何使用 ID 的示意图](/images/guide/topics/connectivity/tel-ids-2.png) 在上图中: * 设备有三个逻辑插槽。 * 在实体插槽 0 中,有一个具有有效配置文件的物理 UICC 卡。 * 实体插槽 1 中有一个 eUICC,其中包含两个已下载的配置文件,这两个配置文件都使用 MEP(多个已启用的配置文件)处于有效状态。

会话发起协议概览

Android 提供了一个支持会话发起协议 (SIP) 的 API。 这样,您就可以为应用添加基于 SIP 的互联网电话功能。 Android 包含完整的 SIP 协议栈和集成的通话管理功能 可让应用轻松设置外拨和来电语音通话的服务, 而无需管理会话、传输层通信或 直接录制或回放。

以下是可能使用 SIP API 的应用类型示例:

  • 视频会议
  • 即时通讯

要求和限制

以下是开发 SIP 应用的要求:

  • 您必须拥有搭载 Android 2.3 或更高版本的移动设备。
  • SIP 通过无线数据连接运行,因此您的设备必须有数据网络 (通过移动数据服务或 Wi-Fi 连接)。这意味着 无法在 AVD 上进行测试,您只能在实体设备上进行测试。有关详情,请参阅 测试 SIP 应用
  • 应用通信会话中的每个参与者都必须拥有 SIP 账号。有很多不同的 SIP 提供商提供 SIP 账号。

注意android.net.sip 库不支持视频 调用。如果您想使用 SIP 堆栈实现 VOIP 通话,例如 android.net.sip,不妨了解一下 作为任何 VoIP 调用实现的基础。或者 您可以实现 ConnectionService 将这些调用紧密集成至设备的拨号器的 API 应用。

SIP API 类和接口

下面简要介绍了这些类和一个接口 (SipRegistrationListener) 包含在 Android SIP 中 API:

类/接口 说明
SipAudioCall 处理 SIP 互联网语音通话。
SipAudioCall.Listener 监听与 SIP 通话相关的事件,例如正在通话 收到来电(“正在响铃”)或外拨电话(“正在呼叫”)。
SipErrorCode 定义在 SIP 操作期间返回的错误代码。
SipManager 为 SIP 任务(例如启动 SIP 连接)提供 API,并提供访问权限 连接到相关 SIP 服务
SipProfile 定义 SIP 配置文件,包括 SIP 账号、网域和服务器信息。
SipProfile.Builder 用于创建 SipProfile 的辅助类。
SipSession 表示与 SIP 对话框或独立事务关联的 SIP 会话 而不是在对话框中显示
SipSession.Listener 监听与 SIP 会话相关的事件,例如注册会话时 (“注册时”)或已拨出电话(“通话中”)。
SipSession.State 定义 SIP 会话状态,例如“正在注册”、“去电”和“正在通话”。
SipRegistrationListener 作为 SIP 注册事件监听器的接口。

创建清单

如果您要开发使用 SIP API 的应用,请注意 仅 Android 2.3(API 级别 9)及更高版本的 平台。另外,在运行 Android 2.3(API 级别 9)或更高版本的设备上, 并非所有设备都会提供 SIP 支持。

要使用 SIP,请将以下权限添加到应用的清单中:

  • android.permission.USE_SIP
  • android.permission.INTERNET

为了确保您的应用只能安装在 支持 SIP,请将以下内容添加到您的应用的 清单:

<uses-sdk android:minSdkVersion="9" />

这表示您的应用需要安装 Android 2.3 或更高版本。对于 请参阅 API 级别 以及 <uses-sdk> 元素。

控制如何从不支持的设备中过滤您的应用 SIP(例如,在 Google Play 上),将以下内容添加到应用的 清单:

<uses-feature android:name="android.software.sip.voip" />

这说明您的应用使用的是 SIP API。该声明应该 请添加一个 android:required 属性,用于指明您是否 希望从不支持 SIP 的设备中滤除应用。 可能还需要其他 <uses-feature> 声明。 具体取决于您的实现情况如需了解详情,请参阅相关文档 的 <uses-feature> 元素。

如果应用可用于接听电话,您还必须在应用清单中定义接收器(BroadcastReceiver 子类):

<receiver android:name=".IncomingCallReceiver" android:label="Call Receiver" />

以下代码摘录自 SipDemo 清单:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.android.sip">
  ...
     <receiver android:name=".IncomingCallReceiver" android:label="Call Receiver" />
  ...
  <uses-sdk android:minSdkVersion="9" />
  <uses-permission android:name="android.permission.USE_SIP" />
  <uses-permission android:name="android.permission.INTERNET" />
  ...
  <uses-feature android:name="android.software.sip.voip" android:required="true" />
  <uses-feature android:name="android.hardware.wifi" android:required="true" />
  <uses-feature android:name="android.hardware.microphone" android:required="true" />
</manifest>

创建 SipManager

要使用 SIP API,您的应用必须创建 SipManager 对象。SipManager需要 您需要注意应用中的以下各项:

  • 发起 SIP 会话。
  • 发起和接听电话。
  • 向 SIP 提供商注册和取消注册。
  • 验证会话连接。

按照如下所示实例化一个新的 SipManager

Kotlin

val sipManager: SipManager? by lazy(LazyThreadSafetyMode.NONE) {
    SipManager.newInstance(this)
}

Java

public SipManager sipManager = null;
...
if (sipManager == null) {
    sipManager = SipManager.newInstance(this);
}

向 SIP 服务器注册

典型的 Android SIP 应用涉及一个或多个用户, 有 SIP 账号。在 Android SIP 应用中,每个 SIP 账号 由 SipProfile 对象表示。

SipProfile 定义 SIP 配置文件,包括 SIP 账号、网域和服务器信息。与 SIP 关联的配置文件 称为本地 个人资料。会话所关联的配置文件称为 对等配置文件。当您的 SIP 应用使用 本地 SipProfile,这实际上会注册 设备作为您 SIP 地址的 SIP 呼叫号码的目标发送位置。

本部分介绍了如何创建 SipProfile, 向 SIP 服务器注册,并跟踪注册事件。

按照如下所示创建一个 SipProfile 对象:

Kotlin

private var sipProfile: SipProfile? = null
...

val builder = SipProfile.Builder(username, domain)
        .setPassword(password)
sipProfile = builder.build()

Java

public SipProfile sipProfile = null;
...

SipProfile.Builder builder = new SipProfile.Builder(username, domain);
builder.setPassword(password);
sipProfile = builder.build();

以下代码摘录会打开本地配置文件,以便拨打电话和/或 接听一般 SIP 通话。来电者可以 mSipManager.makeAudioCall。这个片段还设定了 android.SipDemo.INCOMING_CALL,将由 intent 使用 在设备接到来电时进行过滤(请参阅设置 用于接听电话的 intent 过滤器)。以下是注册步骤:

Kotlin

val intent = Intent("android.SipDemo.INCOMING_CALL")
val pendingIntent: PendingIntent = PendingIntent.getBroadcast(this, 0, intent, Intent.FILL_IN_DATA)
sipManager?.open(sipProfile, pendingIntent, null)

Java

Intent intent = new Intent();
intent.setAction("android.SipDemo.INCOMING_CALL");
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, Intent.FILL_IN_DATA);
sipManager.open(sipProfile, pendingIntent, null);

最后,此代码在 SipManager 上设置了 SipRegistrationListener。这会跟踪是否已成功向您的 SIP 服务注册 SipProfile 提供商:

Kotlin

sipManager?.setRegistrationListener(sipProfile?.uriString, object : SipRegistrationListener {

    override fun onRegistering(localProfileUri: String) {
        updateStatus("Registering with SIP Server...")
    }

    override fun onRegistrationDone(localProfileUri: String, expiryTime: Long) {
        updateStatus("Ready")
    }

    override fun onRegistrationFailed(
            localProfileUri: String,
            errorCode: Int,
            errorMessage: String
    ) {
        updateStatus("Registration failed. Please check settings.")
    }
})

Java

sipManager.setRegistrationListener(sipProfile.getUriString(), new SipRegistrationListener() {

    public void onRegistering(String localProfileUri) {
        updateStatus("Registering with SIP Server...");
    }

    public void onRegistrationDone(String localProfileUri, long expiryTime) {
        updateStatus("Ready");
    }

    public void onRegistrationFailed(String localProfileUri, int errorCode,
        String errorMessage) {
        updateStatus("Registration failed.  Please check settings.");
    }
}

当应用使用完配置文件后,应将其关闭以释放空间 关联对象到内存中并从服务器取消注册设备。例如:

Kotlin

fun closeLocalProfile() {
    try {
        sipManager?.close(sipProfile?.uriString)
    } catch (ee: Exception) {
        Log.d("WalkieTalkieActivity/onDestroy", "Failed to close local profile.", ee)
    }
}

Java

public void closeLocalProfile() {
    if (sipManager == null) {
       return;
    }
    try {
       if (sipProfile != null) {
          sipManager.close(sipProfile.getUriString());
       }
     } catch (Exception ee) {
       Log.d("WalkieTalkieActivity/onDestroy", "Failed to close local profile.", ee);
     }
}

进行语音通话

要进行语音通话,您必须具备以下条件:

  • 正在进行调用的 SipProfile( “本地配置文件”)和有效的 SIP 地址( “对等配置文件”)。
  • 一个 SipManager 对象。

要进行语音通话,您应设置 SipAudioCall.Listener。客户与 SIP 堆栈通过监听器发送。在此代码段中,您可以看到 SipAudioCall.Listener 如何在调用 已建立:

Kotlin

var listener: SipAudioCall.Listener = object : SipAudioCall.Listener() {

    override fun onCallEstablished(call: SipAudioCall) {
        call.apply {
            startAudio()
            setSpeakerMode(true)
            toggleMute()
        }
    }

    override fun onCallEnded(call: SipAudioCall) {
        // Do something.
    }
}

Java

SipAudioCall.Listener listener = new SipAudioCall.Listener() {

   @Override
   public void onCallEstablished(SipAudioCall call) {
      call.startAudio();
      call.setSpeakerMode(true);
      call.toggleMute();
         ...
   }

   @Override

   public void onCallEnded(SipAudioCall call) {
      // Do something.
   }
};

设置 SipAudioCall.Listener 后,您可以 拨打电话。SipManager 方法 makeAudioCall 采用以下参数:

  • 本地 SIP 配置文件(来电者)。
  • 对等 SIP 配置文件(被呼叫的用户)。
  • 用于监听通话的 SipAudioCall.Listener 来自 SipAudioCall 的活动。可以是 null、 但如上所示,在调用 。
  • 超时值,以秒为单位。

例如:

Kotlin

val call: SipAudioCall? = sipManager?.makeAudioCall(
        sipProfile?.uriString,
        sipAddress,
        listener,
        30
)

Java

call = sipManager.makeAudioCall(sipProfile.getUriString(), sipAddress, listener, 30);

接听电话

如需接听电话,SIP 应用必须包含能够响应 intent 的 BroadcastReceiver 子类 表示有来电。因此,您必须执行以下操作 您的应用:

  • AndroidManifest.xml 中,声明 <receiver>。在 SipDemo 中, <receiver android:name=".IncomingCallReceiver" android:label="Call Receiver" />
  • 实现接收器,它是 BroadcastReceiver 的子类。在 SipDemo 中, IncomingCallReceiver
  • 使用SipProfile 待处理 intent,在用户调用本地个人资料时触发接收器。
  • 设置一个 intent 过滤器,用于按表示 来电。在 SipDemo 中,此操作为 android.SipDemo.INCOMING_CALL

创建 BroadcastReceiver 的子类

要接听电话,您的 SIP 应用必须创建 BroadcastReceiver 的子类。 Android 系统处理 SIP 来电并广播“来电” 在收到“” intent(由应用定义)时调用 通话。以下是子类化的 BroadcastReceiver SipDemo 示例中的.

Kotlin

/**
 * Listens for incoming SIP calls, intercepts and hands them off to WalkieTalkieActivity.
 */
class IncomingCallReceiver : BroadcastReceiver() {

    /**
     * Processes the incoming call, answers it, and hands it over to the
     * WalkieTalkieActivity.
     * @param context The context under which the receiver is running.
     * @param intent The intent being received.
     */
    override fun onReceive(context: Context, intent: Intent) {
        val wtActivity = context as WalkieTalkieActivity

        var incomingCall: SipAudioCall? = null
        try {
            incomingCall = wtActivity.sipManager?.takeAudioCall(intent, listener)
            incomingCall?.apply {
                answerCall(30)
                startAudio()
                setSpeakerMode(true)
                if (isMuted) {
                    toggleMute()
                }
                wtActivity.call = this
                wtActivity.updateStatus(this)
            }
        } catch (e: Exception) {
            incomingCall?.close()
        }
    }

    private val listener = object : SipAudioCall.Listener() {

        override fun onRinging(call: SipAudioCall, caller: SipProfile) {
            try {
                call.answerCall(30)
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    }
}

Java

/**
 * Listens for incoming SIP calls, intercepts and hands them off to WalkieTalkieActivity.
 */
public class IncomingCallReceiver extends BroadcastReceiver {
    /**
     * Processes the incoming call, answers it, and hands it over to the
     * WalkieTalkieActivity.
     * @param context The context under which the receiver is running.
     * @param intent The intent being received.
     */
    @Override
    public void onReceive(Context context, Intent intent) {
        SipAudioCall incomingCall = null;
        try {
            SipAudioCall.Listener listener = new SipAudioCall.Listener() {
                @Override
                public void onRinging(SipAudioCall call, SipProfile caller) {
                    try {
                        call.answerCall(30);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            };
            WalkieTalkieActivity wtActivity = (WalkieTalkieActivity) context;
            incomingCall = wtActivity.sipManager.takeAudioCall(intent, listener);
            incomingCall.answerCall(30);
            incomingCall.startAudio();
            incomingCall.setSpeakerMode(true);
            if(incomingCall.isMuted()) {
                incomingCall.toggleMute();
            }
            wtActivity.call = incomingCall;
            wtActivity.updateStatus(incomingCall);
        } catch (Exception e) {
            if (incomingCall != null) {
                incomingCall.close();
            }
        }
    }
}

设置 Intent 过滤器以接听电话

当 SIP 服务收到新来电时,它会发出一个包含 操作字符串。在 SipDemo 中,此操作字符串为 android.SipDemo.INCOMING_CALL

这段代码摘录自 SipDemo,展示如何根据以下条件使用待处理 intent 创建 SipProfile 对象: 操作字符串 android.SipDemo.INCOMING_CALL。通过 当 SipProfile 收到调用时,PendingIntent 对象将执行广播:

Kotlin

val sipManager: SipManager? by lazy(LazyThreadSafetyMode.NONE) {
    SipManager.newInstance(this)
}

var sipProfile: SipProfile? = null
...

val intent = Intent("android.SipDemo.INCOMING_CALL")
val pendingIntent: PendingIntent = PendingIntent.getBroadcast(this, 0, intent, Intent.FILL_IN_DATA)
sipManager?.open (sipProfile, pendingIntent, null)

Java

public SipManager sipManager = null;
public SipProfile sipProfile = null;
...

Intent intent = new Intent();
intent.setAction("android.SipDemo.INCOMING_CALL");
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, Intent.FILL_IN_DATA);
sipManager.open(sipProfile, pendingIntent, null);

广播将会被 intent 过滤器拦截,然后会触发 intent 过滤器 接收器 (IncomingCallReceiver)。您可以指定 intent 过滤器,也可以像 SipDemo 中所示,在代码中执行此操作 示例应用的 onCreate() 方法 的 Activity

Kotlin

class WalkieTalkieActivity : Activity(), View.OnTouchListener {
    ...
    lateinit var callReceiver: IncomingCallReceiver
    ...

    override fun onCreate(savedInstanceState: Bundle) {
        val filter = IntentFilter().apply {
            addAction("android.SipDemo.INCOMING_CALL")
        }
        callReceiver = IncomingCallReceiver()
        this.registerReceiver(callReceiver, filter)
        ...
    }
    ...
}

Java

public class WalkieTalkieActivity extends Activity implements View.OnTouchListener {
...
    public IncomingCallReceiver callReceiver;
    ...

    @Override
    public void onCreate(Bundle savedInstanceState) {

       IntentFilter filter = new IntentFilter();
       filter.addAction("android.SipDemo.INCOMING_CALL");
       callReceiver = new IncomingCallReceiver();
       this.registerReceiver(callReceiver, filter);
       ...
    }
    ...
}

测试 SIP 应用

要测试 SIP 应用,您需要具备以下条件:

  • 搭载 Android 2.3 或更高版本的移动设备。SIP 通过 因此您必须在实际设备上进行测试。在 AVD 上进行测试无效。
  • SIP 账号。有很多不同的 SIP 提供商提供 SIP 账号。
  • 如果您要拨打电话,它还必须是有效的 SIP 账号。

要测试 SIP 应用,请按以下步骤操作:

  1. 在您的设备上,连接到无线网络(设置 > 无线和网络) &gt;Wi-Fi >WLAN 设置)。
  2. 面向测试设置您的移动设备,详见在设备上进行开发
  3. 按照在设备上进行开发中所述,在移动设备上运行您的应用。
  4. 如果您使用的是 Android Studio,则可以通过 打开事件日志控制台(View > Tool Windows > Event Log)。
  5. 确保您的应用已配置为在运行时自动启动 Logcat: <ph type="x-smartling-placeholder">
      </ph>
    1. 选择 Run >修改配置
    2. Run/Debug Configurations 窗口中选择 Miscellaneous 标签页。
    3. Logcat 下,选择 Show logcat automated,然后 选择确定