對話啟動協定總覽

偵測 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 的子類別。在 SipDemoIncomingCallReceiver
  • 使用以下指令初始化本機設定檔 (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_CALLSipProfile 收到呼叫時,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 應用程式:

  1. 在裝置上,連線至無線網路 (依序前往「設定」>「無線與網路」) &gt;Wi-Fi >Wi-Fi 設定)。
  2. 按照在裝置上開發的說明,設定用於測試的行動裝置。
  3. 按照在裝置上開發的說明,在行動裝置上執行您的應用程式。
  4. 如果您使用的是 Android Studio,可以透過下列方式查看應用程式記錄的輸出內容: 開啟「事件記錄」控制台 (依序點選「檢視」>「工具視窗」>「事件記錄」)。
  5. 請確認您的應用程式已設為在執行時自動啟動 Logcat:
    1. 選取 執行 >編輯設定
    2. 選取「Run/Debug Configurations」視窗中的「Miscellaneous」分頁標籤。
    3. 在「Logcat」下方,選取「Show logcat 自動」,然後 選取「確定」