Google berkomitmen untuk mendorong terwujudnya keadilan ras bagi komunitas Kulit Hitam. Lihat caranya.

Ringkasan protokol pemulaian sesi

Android menyediakan API yang mendukung Protokol Pemulaian Sesi (SIP) Hal ini memungkinkan Anda menambahkan fitur telepon internet berbasis SIP ke aplikasi Anda. Android menyertakan stack protokol SIP penuh dan layanan pengelolaan panggilan terintegrasi yang memungkinkan aplikasi dengan mudah menyiapkan panggilan suara keluar dan masuk, tanpa harus mengelola sesi, komunikasi tingkat transport, atau rekaman/pemutaran audio secara langsung.

Berikut adalah contoh jenis aplikasi yang menggunakan SIP API:

  • Konferensi video
  • Instant messaging

Persyaratan dan batasan

Berikut adalah beberapa persyaratan untuk mengembangkan aplikasi SIP:

  • Anda harus memiliki perangkat seluler yang menjalankan Android 2.3 atau yang lebih baru.
  • SIP berjalan melalui koneksi data nirkabel, sehingga perangkat Anda harus memiliki koneksi data (dengan layanan data seluler atau Wi-Fi). Ini berarti, Anda tidak dapat menguji di AVD—Anda hanya dapat menguji di perangkat fisik. Untuk detailnya, lihat Menguji aplikasi SIP.
  • Setiap peserta dalam sesi komunikasi aplikasi harus memiliki akun SIP. Ada banyak penyedia SIP berbeda yang menawarkan akun SIP.

Antarmuka dan class SIP API

Berikut adalah ringkasan class dan satu antarmuka (SipRegistrationListener) yang termasuk dalam Android SIP API:

Class/Antarmuka Deskripsi
SipAudioCall Menangani panggilan audio Internet melalui SIP.
SipAudioCall.Listener Pemroses untuk peristiwa yang berkaitan dengan panggilan SIP, seperti ketika panggilan sedang diterima ("sedang berdering") atau panggilan sedang keluar ("sedang memanggil").
SipErrorCode Menentukan kode error yang ditampilkan selama tindakan SIP.
SipManager Menyediakan API untuk tugas SIP, seperti memulai koneksi SIP, dan memberikan akses ke layanan SIP terkait.
SipProfile Menentukan profil SIP, termasuk informasi akun, domain, dan server SIP.
SipProfile.Builder Class helper untuk membuat SipProfile.
SipSession Merepresentasikan sesi SIP yang terkait dengan dialog SIP atau transaksi mandiri tidak dalam dialog.
SipSession.Listener Pemroses untuk peristiwa yang berkaitan dengan sesi SIP, seperti saat sesi sedang didaftarkan ("sedang mendaftar") atau panggilan sedang keluar ("sedang memanggil").
SipSession.State Menentukan status sesi SIP, seperti "mendaftar", "panggilan keluar", dan "dalam panggilan".
SipRegistrationListener Antarmuka yang merupakan pemroses untuk peristiwa pendaftaran SIP.

Membuat manifes

Jika Anda mengembangkan aplikasi yang menggunakan SIP API, perhatikan bahwa fitur tersebut hanya didukung di Android 2.3 (API level 9) dan versi platform yang lebih baru. Selain itu, di antara perangkat yang menjalankan Android 2.3 (API level 9) atau yang lebih tinggi, tidak semua perangkat akan menawarkan dukungan SIP.

Untuk menggunakan SIP, tambahkan izin berikut ke manifes aplikasi:

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

Untuk memastikan bahwa aplikasi Anda hanya dapat diinstal di perangkat yang mampu mendukung SIP, tambahkan kode berikut ke manifes aplikasi Anda:

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

Ini akan mengindikasikan bahwa aplikasi Anda mewajibkan Android 2.3 atau yang lebih tinggi. Untuk informasi selengkapnya, lihat Tingkat API dan dokumentasi untuk elemen <uses-sdk>.

Untuk mengontrol cara aplikasi Anda difilter dari perangkat yang tidak mendukung SIP (misalnya, di Google Play), tambahkan kode berikut ke manifes aplikasi Anda:

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

Ini akan menegaskan bahwa aplikasi Anda menggunakan SIP API. Deklarasinya harus menyertakan atribut android:required yang menunjukkan apakah Anda ingin aplikasi tersebut difilter dari perangkat yang tidak menawarkan dukungan SIP. Deklarasi <uses-feature> lain mungkin juga diperlukan bergantung pada implementasi Anda. Untuk informasi selengkapnya, lihat dokumentasi untuk elemen <uses-feature>.

Jika aplikasi Anda dirancang untuk menerima panggilan, Anda juga harus menentukan penerima (subclass BroadcastReceiver) dalam manifes aplikasi:

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

Berikut adalah cuplikan manifes 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.hardware.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>
    

Membuat SipManager

Untuk menggunakan SIP API, aplikasi Anda harus membuat objek SipManager. SipManager menangani beberapa hal berikut dalam aplikasi Anda:

  • Memulai sesi SIP.
  • Memulai dan menerima panggilan.
  • Mendaftarkan dan membatalkan pendaftaran dengan penyedia SIP.
  • Memverifikasi konektivitas sesi.

Buat instance SipManager baru seperti berikut:

Kotlin

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

Java

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

Mendaftar dengan Server SIP

Aplikasi SIP Android umumnya melibatkan satu atau beberapa pengguna, yang masing-masing memiliki akun SIP. Dalam aplikasi SIP Android, setiap akun SIP diwakili oleh objek SipProfile.

SipProfile menentukan profil SIP, termasuk akun SIP, dan informasi server serta domain. Profil yang dikaitkan dengan akun SIP di perangkat yang menjalankan aplikasi tersebut dinamakan profil lokal. Profil yang sesinya terhubung dinamakan profil peer. Saat aplikasi SIP Anda login ke server SIP dengan SipProfile lokal, tindakan ini secara efektif akan mendaftarkan perangkat tersebut sebagai lokasi untuk mengirim panggilan SIP untuk alamat SIP Anda.

Bagian ini menunjukkan cara membuat SipProfile, mendaftarkannya dengan server SIP, dan melacak peristiwa pendaftaran.

Buat objek SipProfile seperti berikut:

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();
    

Cuplikan kode berikut membuka profil lokal untuk melakukan panggilan dan/atau menerima panggilan SIP umum. Pemanggil dapat melakukan panggilan berikutnya melalui mSipManager.makeAudioCall. Cuplikan ini juga menetapkan tindakan android.SipDemo.INCOMING_CALL, yang akan digunakan oleh filter intent ketika perangkat menerima panggilan (lihat Menyiapkan filter intent untuk menerima panggilan). Berikut adalah langkah pendaftarannya:

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);
    

Terakhir, kode ini akan menetapkan SipRegistrationListener di SipManager. Kode ini melacak apakah SipProfile berhasil didaftarkan dengan penyedia layanan SIP Anda:

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.");
        }
    }
    

Saat selesai menggunakan profil, aplikasi Anda harus menutupnya untuk membebaskan objek terkait ke dalam memori dan membatalkan pendaftaran perangkat dari server. Contoh:

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);
         }
    }
    

Melakukan panggilan audio

Untuk melakukan panggilan audio, Anda harus memiliki:

  • SipProfile yang membuat panggilan ("profil lokal"), dan alamat SIP yang valid untuk menerima panggilan ("profil peer").
  • Objek SipManager.

Untuk melakukan panggilan audio, Anda harus menyiapkan SipAudioCall.Listener. Sebagian besar interaksi klien dengan stack SIP terjadi melalui pemroses. Dalam cuplikan ini, Anda akan melihat cara SipAudioCall.Listener menyiapkan beberapa hal setelah panggilan dilakukan:

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.
       }
    };
    

Setelah menyiapkan SipAudioCall.Listener, Anda dapat melakukan panggilan. Metode SipManager makeAudioCall akan menggunakan beberapa parameter berikut:

  • Profil SIP lokal (pemanggil).
  • Profil SIP peer (pengguna yang dipanggil).
  • SipAudioCall.Listener untuk memproses peristiwa panggilan dari SipAudioCall. Ini dapat bernilai null, tetapi seperti yang ditampilkan di atas, pemroses tersebut digunakan untuk menyiapkan beberapa hal setelah panggilan dilakukan.
  • Nilai waktu tunggu, dalam detik.

Contoh:

Kotlin

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

Java

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

Menerima panggilan

Untuk menerima panggilan, aplikasi SIP harus menyertakan subclass BroadcastReceiver yang memiliki kemampuan untuk merespons intent yang mengindikasikan bahwa ada panggilan masuk. Dengan demikian, Anda harus melakukan beberapa hal berikut dalam aplikasi Anda:

  • Di AndroidManifest.xml, deklarasikan <receiver>. Di SipDemo, ini adalah <receiver android:name=".IncomingCallReceiver" android:label="Call Receiver" />.
  • Implementasikan penerima, yakni subclass BroadcastReceiver. Di SipDemo, ini adalah IncomingCallReceiver.
  • Inisialisasi profil lokal (SipProfile) dengan intent tertunda yang mengaktifkan penerima Anda ketika seseorang memanggil profil lokal.
  • Siapkan filter intent yang memfilter berdasarkan tindakan yang mewakili panggilan masuk. Di SipDemo, tindakan ini adalah android.SipDemo.INCOMING_CALL.

Memasukkan BroadcastReceiver ke subclass

Untuk menerima panggilan, aplikasi SIP Anda harus memasukkan BroadcastReceiver ke subclass. Sistem Android menangani panggilan masuk SIP dan menyiarkan intent "panggilan masuk" (seperti yang ditentukan oleh aplikasi) saat menerima panggilan. Berikut adalah kode BroadcastReceiver yang dimasukkan ke subclass dari Contoh 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();
                }
            }
        }
    }
    

Menyiapkan filter intent untuk menerima panggilan

Saat menerima panggilan baru, layanan SIP akan mengirimkan intent dengan string tindakan yang diberikan oleh aplikasi. Di SipDemo, string tindakan ini adalah android.SipDemo.INCOMING_CALL.

Cuplikan kode yang berasal SipDemo ini menunjukkan cara objek SipProfile dibuat dengan intent tertunda berdasarkan string tindakan android.SipDemo.INCOMING_CALL. Objek PendingIntent akan melakukan siaran saat SipProfile menerima panggilan:

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);
    

Siaran tersebut akan dicegat oleh filter intent, yang kemudian akan mengaktifkan penerima (IncomingCallReceiver). Anda dapat menentukan filter intent di file manifes aplikasi, atau melakukannya di kode seperti dalam metode onCreate() aplikasi contoh SipDemo pada Activity aplikasi tersebut:

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);
           ...
        }
        ...
    }
    

Menguji aplikasi SIP

Untuk menguji aplikasi SIP, Anda memerlukan beberapa hal berikut:

  • Perangkat seluler yang menjalankan Android 2.3 atau yang lebih tinggi. SIP berjalan melalui jaringan nirkabel, sehingga Anda harus menguji di perangkat yang sebenarnya. Pengujian pada AVD tidak akan bekerja.
  • Akun SIP. Ada banyak penyedia SIP berbeda yang menawarkan akun SIP.
  • Jika Anda melakukan panggilan, panggilan tersebut juga harus dilakukan ke akun SIP yang valid.

Untuk menguji aplikasi SIP:

  1. Di perangkat, sambungkan ke jaringan nirkabel ( Setelan > Nirkabel & jaringan > Wi-Fi > Setelan Wi-Fi).
  2. Siapkan perangkat seluler untuk pengujian, seperti yang dijelaskan dalam Melakukan Pengembangan di Perangkat.
  3. Jalankan aplikasi Anda di perangkat seluler, seperti yang dijelaskan dalam Melakukan Pengembangan di Perangkat.
  4. Jika menggunakan Android Studio, Anda dapat melihat output log aplikasi dengan membuka konsol Event Log (View > Tool Windows > Event Log).
  5. Pastikan aplikasi Anda dikonfigurasi untuk otomatis meluncurkan Logcat saat dijalankan:
    1. Pilih Run > Edit Configurations.
    2. Pilih tab Miscellaneous di jendela Run/Debug Configurations.
    3. Di bagian Logcat, pilih Show logcat automatically, lalu pilih OK.