偵測 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 提供的 API 支援工作階段啟動協定 (SIP)。 這可讓你將 SIP 式網路電話功能加入應用程式。 Android 提供完整的 SIP 通訊協定堆疊和整合式通話管理功能 服務,以便輕鬆設定撥出電話和接聽語音電話; 不必管理工作階段、傳輸層級通訊或音訊 錄製或直接播放
以下列舉幾種可能使用 SIP API 的應用程式類型:
- 視訊會議
- 即時通訊軟體
規定和限制
以下是開發 SIP 應用程式的需求條件:
- 你的行動裝置必須搭載 Android 2.3 以上版本。
- SIP 透過無線數據連線執行,因此您的裝置必須有資料 連線 (透過行動數據服務或 Wi-Fi 連線)。這表示 您無法在 AVD 上測試,因此只能在實體裝置上測試。詳情請參閱 測試 SIP 應用程式。
- 應用程式通訊工作階段中的每位參與者都必須擁有 SIP 帳戶。許多不同的 SIP 供應商都提供 SIP 帳戶,
注意:android.net.sip
程式庫不支援影片
呼叫。如要使用如 VOIP 堆疊執行 VOIP 通話,例如:
android.net.sip
,來看看眾多現代開放原始碼軟體
替代選項。另外
您就能實作
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
確保應用程式只能安裝在已安裝 請將下列項目新增至應用程式的 資訊清單:
<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
,供意圖使用
篩選裝置 (請參閱設定
用於接聽來電的意圖篩選器)。以下是註冊步驟:
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
。用於追蹤「SipProfile
」是否已成功註冊您的 SIP 服務
供應商:
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 應用程式必須包含 BroadcastReceiver
的子類別,可回應意圖
表示是否有來電。因此,您必須在
應用程式:
- 在
AndroidManifest.xml
中宣告<receiver>
。在 SipDemo 中<receiver android:name=".IncomingCallReceiver" android:label="Call Receiver" />
。 - 實作接收器,也就是
BroadcastReceiver
的子類別。在 SipDemo 中IncomingCallReceiver
。 - 使用以下指令初始化本機設定檔 (
SipProfile
) 待處理意圖,在有人呼叫本機設定檔時觸發接收器。 - 設定意圖篩選器,以根據代表
。在 SipDemo 中操作:
android.SipDemo.INCOMING_CALL
。
將 BroadcastReceiver 設為子類別
如要接聽來電,SIP 應用程式必須將 BroadcastReceiver
設為子類別。
Android 系統會處理收到的 SIP 通話,並播送「收到來電」訊息
應用程式定義的「」意圖 (由應用程式定義) 時,
呼叫。以下是子類別的
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(); } } } }
設定意圖篩選器來接聽來電
SIP 服務收到新呼叫時,會發出符合下列條件的意圖:
應用程式提供的動作字串。在 SipDemo 中,這個動作字串是
android.SipDemo.INCOMING_CALL
。
這個 SipDemo 摘錄的程式碼顯示瞭如何根據以下條件,透過待處理意圖建立 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);
系統會將廣播訊息交由意圖篩選器攔截
接收端 (IncomingCallReceiver
)。您可以指定意圖
,或像 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 >Wi-Fi 設定)。
- 按照在裝置上開發的說明,設定用於測試的行動裝置。
- 按照在裝置上開發的說明,在行動裝置上執行您的應用程式。
- 如果您使用的是 Android Studio,可以透過下列方式查看應用程式記錄的輸出內容: 開啟「事件記錄」控制台 (依序點選「檢視」>「工具視窗」>「事件記錄」)。
- 請確認您的應用程式已設為在執行時自動啟動 Logcat:
- 選取 執行 >編輯設定。
- 選取「Run/Debug Configurations」視窗中的「Miscellaneous」分頁標籤。
- 在「Logcat」下方,選取「Show logcat 自動」,然後 選取「確定」。