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