Tổng quan về giao thức khởi tạo phiên

Phát hiện eSIM và thẻ SIM

Phát hiện thẻ

Thiết bị Android có thẻ SIM và eSIM dùng các mã nhận dạng sau trong API điện thoại, bao gồm cả [`TelephonyManager`](/reference/android/telephony/TelephonyManager) và [`SubscriptionManager`](/reference/android/telephony/SubscriptionManager): * Mã gói thuê bao: mã nhận dạng duy nhất của một gói thuê bao trên thiết bị di động. * Chỉ mục hoặc mã vị trí logic: chỉ mục duy nhất tham chiếu đến vị trí SIM logic. Mã vùng logic bắt đầu từ 0 và tăng lên tuỳ thuộc vào số lượng khe đang hoạt động được hỗ trợ trên thiết bị. Ví dụ: thiết bị hai SIM thường có khe 0 và khe 1. Nếu có nhiều khe thực tế nhưng chỉ hỗ trợ một khe đang hoạt động, thì thiết bị sẽ chỉ có mã vùng logic là 0. * Chỉ mục vị trí thực tế hoặc mã nhận dạng: chỉ mục duy nhất đề cập đến vị trí lắp thẻ SIM thực. Mã vị trí thực bắt đầu từ 0 và tăng lên tuỳ thuộc vào số lượng khe thực tế trên thiết bị. Giá trị này khác với số lượng khe logic mà một thiết bị có, tương ứng với số lượng khe đang hoạt động mà một thiết bị có thể sử dụng. Ví dụ: một thiết bị chuyển đổi giữa chế độ 2 SIM và một SIM có thể luôn có 2 khe cắm vật lý, nhưng ở chế độ một SIM thì thiết bị sẽ chỉ có một khe logic. * Mã thẻ: mã nhận dạng duy nhất dùng để nhận dạng một thẻ UiccCard. ![Sơ đồ về cách sử dụng mã nhận dạng trong trường hợp có 2 khe logic và 3 khe vật lý](/images/guide/topics/connectivity/tel-ids.png) Trong sơ đồ trên: * Thiết bị có 2 khe logic. * Trong vị trí thực 0, có một thẻ UICC thực với hồ sơ đang hoạt động. * Trong vùng thực 2 là một eUICC có hồ sơ đang hoạt động. * Khe thực tế 1 hiện không được sử dụng. ![Sơ đồ về cách sử dụng mã nhận dạng trong trường hợp có 3 khe logic và 2 khe vật lý](/images/guide/topics/connectivity/tel-ids-2.png) Trong sơ đồ trên: * Thiết bị có 3 khe logic. * Trong vị trí thực 0, có một thẻ UICC thực với hồ sơ đang hoạt động. * Trong vùng thực 1 là một eUICC có hai cấu hình đã tải xuống, cả hai đều hoạt động bằng MEP (Nhiều cấu hình được bật).

Tổng quan về giao thức khởi tạo phiên

Android cung cấp một API hỗ trợ Giao thức khởi tạo phiên (SIP). Điều này cho phép bạn thêm các tính năng điện thoại Internet dựa trên SIP vào ứng dụng của mình. Android có một ngăn xếp giao thức SIP đầy đủ và các dịch vụ quản lý cuộc gọi tích hợp cho phép các ứng dụng dễ dàng thiết lập cuộc gọi thoại đi và đến mà không cần phải quản lý phiên hoạt động, giao tiếp ở cấp độ truyền tải hoặc trực tiếp ghi âm/phát âm thanh.

Dưới đây là ví dụ về các loại ứng dụng có thể sử dụng API SIP:

  • Hội nghị truyền hình
  • Nhắn tin tức thì

Yêu cầu và giới hạn

Dưới đây là các yêu cầu đối với việc phát triển ứng dụng SIP:

  • Bạn phải có thiết bị di động chạy Android 2.3 trở lên.
  • SIP chạy trên kết nối dữ liệu không dây, vì vậy, thiết bị của bạn phải có kết nối dữ liệu (với dịch vụ dữ liệu di động hoặc Wi-Fi). Tức là bạn không kiểm thử được trên AVD mà chỉ có thể kiểm thử trên thiết bị thực. Để biết thông tin chi tiết, vui lòng xem bài viết Kiểm thử ứng dụng SIP.
  • Mỗi người tham gia trong phiên giao tiếp của ứng dụng phải có tài khoản SIP. Có nhiều nhà cung cấp SIP khác nhau cung cấp tài khoản SIP.

Lưu ý: Thư viện android.net.sip không hỗ trợ các cuộc gọi video. Nếu bạn muốn triển khai tính năng gọi VOIP bằng ngăn xếp SIP (chẳng hạn như android.net.sip), hãy xem một trong nhiều giải pháp thay thế nguồn mở hiện đại để làm cơ sở cho việc triển khai lệnh gọi VOIP. Ngoài ra, bạn có thể triển khai API ConnectionService để tích hợp chặt chẽ các lệnh gọi này vào ứng dụng Trình quay số của thiết bị.

Các lớp và giao diện của SIP API

Dưới đây là bản tóm tắt về các lớp và một giao diện (SipRegistrationListener) có trong API Android SIP:

Lớp/Giao diện Nội dung mô tả
SipAudioCall Xử lý cuộc gọi thoại qua Internet qua SIP.
SipAudioCall.Listener Trình nghe các sự kiện liên quan đến cuộc gọi SIP, chẳng hạn như khi một cuộc gọi đang được nhận ("khi đang đổ chuông") hoặc một cuộc gọi đang đi ("khi đang gọi").
SipErrorCode Xác định mã lỗi được trả về trong các hành động SIP.
SipManager Cung cấp API cho các tác vụ SIP, chẳng hạn như khởi tạo kết nối SIP và cung cấp quyền truy cập vào các dịch vụ SIP có liên quan.
SipProfile Xác định hồ sơ SIP, bao gồm tài khoản SIP, thông tin miền và máy chủ.
SipProfile.Builder Lớp trợ giúp để tạo SipProfile.
SipSession Đại diện cho một phiên SIP được liên kết với hộp thoại SIP hoặc một giao dịch độc lập không có trong hộp thoại.
SipSession.Listener Trình nghe các sự kiện liên quan đến phiên SIP, chẳng hạn như khi một phiên đang được đăng ký ("khi đăng ký") hoặc một cuộc gọi được gọi đi ("khi gọi").
SipSession.State Xác định trạng thái phiên SIP, chẳng hạn như "đăng ký", "cuộc gọi đi" và "đang gọi".
SipRegistrationListener Một giao diện làm trình nghe của các sự kiện đăng ký SIP.

Tạo tệp kê khai

Nếu bạn đang phát triển một ứng dụng sử dụng API SIP, hãy nhớ rằng tính năng này chỉ được hỗ trợ trên Android 2.3 (API cấp 9) và các phiên bản cao hơn của nền tảng này. Ngoài ra, trong số các thiết bị chạy Android 2.3 (API cấp 9) trở lên, không phải thiết bị nào cũng hỗ trợ SIP.

Để sử dụng SIP, hãy thêm các quyền sau vào tệp kê khai của ứng dụng:

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

Để đảm bảo rằng ứng dụng của bạn chỉ có thể được cài đặt trên các thiết bị có thể hỗ trợ SIP, hãy thêm thông tin sau vào tệp kê khai của ứng dụng:

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

Nhãn này cho biết ứng dụng của bạn yêu cầu Android 2.3 trở lên. Để biết thêm thông tin, hãy xem phần Cấp độ API và tài liệu cho phần tử <uses-sdk>.

Để kiểm soát cách lọc ứng dụng khỏi các thiết bị không hỗ trợ SIP (ví dụ: trên Google Play), hãy thêm thông tin sau vào tệp kê khai của ứng dụng:

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

Trạng thái này cho biết ứng dụng của bạn sử dụng API SIP. Nội dung khai báo phải bao gồm thuộc tính android:required cho biết liệu bạn có muốn lọc ứng dụng khỏi các thiết bị không hỗ trợ SIP hay không. Bạn cũng có thể cần khai báo <uses-feature> khác, tuỳ thuộc vào cách triển khai của bạn. Để biết thêm thông tin, hãy xem tài liệu cho phần tử <uses-feature>.

Nếu ứng dụng của bạn được thiết kế để nhận lệnh gọi, bạn cũng phải xác định receiver (lớp con BroadcastReceiver) trong tệp kê khai của ứng dụng:

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

Dưới đây là các phần trích dẫn từ tệp kê khai 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>

Tạo SipManager

Để sử dụng API SIP, ứng dụng của bạn phải tạo một đối tượng SipManager. SipManager sẽ xử lý những vấn đề sau trong ứng dụng của bạn:

  • Đang khởi động phiên SIP.
  • Đang bắt đầu và nhận cuộc gọi.
  • Đăng ký và huỷ đăng ký với nhà cung cấp SIP.
  • Đang xác minh khả năng kết nối của phiên.

Bạn tạo thực thể cho một SipManager mới như sau:

Kotlin

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

Java

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

Đăng ký bằng máy chủ SIP

Một ứng dụng Android SIP thông thường bao gồm một hoặc nhiều người dùng, mỗi người đều có một tài khoản SIP. Trong ứng dụng Android SIP, mỗi tài khoản SIP được biểu thị bằng một đối tượng SipProfile.

SipProfile xác định hồ sơ SIP, bao gồm tài khoản SIP, thông tin miền và máy chủ. Hồ sơ liên kết với tài khoản SIP trên thiết bị đang chạy ứng dụng được gọi là hồ sơ cục bộ. Hồ sơ mà phiên kết nối được gọi là hồ sơ ngang hàng. Khi ứng dụng SIP của bạn đăng nhập vào máy chủ SIP bằng SipProfile cục bộ, thao tác này sẽ đăng ký thiết bị làm vị trí để gửi các lệnh gọi SIP đến địa chỉ SIP của bạn một cách hiệu quả.

Phần này cho biết cách tạo SipProfile, đăng ký với máy chủ SIP và theo dõi các sự kiện đăng ký.

Bạn sẽ tạo đối tượng SipProfile như sau:

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

Phần trích dẫn mã sau đây sẽ mở hồ sơ cục bộ để thực hiện cuộc gọi và/hoặc nhận các lệnh gọi SIP chung. Phương thức gọi có thể thực hiện các lệnh gọi tiếp theo thông qua mSipManager.makeAudioCall. Phần trích dẫn này cũng đặt thao tác android.SipDemo.INCOMING_CALL. Thao tác này sẽ được bộ lọc ý định sử dụng khi thiết bị nhận được lệnh gọi (xem phần Thiết lập bộ lọc ý định để nhận lệnh gọi). Đây là bước đăng ký:

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

Cuối cùng, mã này sẽ đặt SipRegistrationListener trên SipManager. Thông tin này sẽ theo dõi xem SipProfile có được đăng ký thành công với nhà cung cấp dịch vụ SIP của bạn hay không:

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

Khi sử dụng xong một hồ sơ, ứng dụng của bạn sẽ đóng hồ sơ đó để các đối tượng liên kết giải phóng vào bộ nhớ và huỷ đăng ký thiết bị khỏi máy chủ. Ví dụ:

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

Gọi thoại

Để thực hiện cuộc gọi thoại, bạn phải có sẵn những thứ sau:

  • SipProfile đang thực hiện cuộc gọi ("hồ sơ cục bộ") và địa chỉ SIP hợp lệ để nhận cuộc gọi ("hồ sơ ngang hàng").
  • Đối tượng SipManager.

Để gọi thoại, bạn nên thiết lập SipAudioCall.Listener. Phần lớn hoạt động tương tác của máy khách với ngăn xếp SIP xảy ra thông qua trình nghe. Trong đoạn mã này, bạn sẽ thấy cách SipAudioCall.Listener thiết lập mọi thứ sau khi lệnh gọi được thiết lập:

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

Sau khi thiết lập SipAudioCall.Listener, bạn có thể thực hiện lệnh gọi. Phương thức SipManager makeAudioCall nhận các thông số sau:

  • Một hồ sơ SIP cục bộ (người gọi).
  • Hồ sơ SIP ngang hàng (người dùng đang được gọi).
  • SipAudioCall.Listener để theo dõi các sự kiện gọi từ SipAudioCall. Giá trị này có thể là null, nhưng như đã trình bày ở trên, trình nghe được dùng để thiết lập sau khi lệnh gọi được thiết lập.
  • Giá trị thời gian chờ, tính bằng giây.

Ví dụ:

Kotlin

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

Java

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

Đang nhận cuộc gọi

Để nhận cuộc gọi, ứng dụng SIP phải bao gồm một lớp con của BroadcastReceiver có khả năng phản hồi ý định cho biết có cuộc gọi đến. Do đó, bạn phải làm những việc sau trong ứng dụng:

  • Trong AndroidManifest.xml, hãy khai báo <receiver>. Trong Sipdemo, đây là <receiver android:name=".IncomingCallReceiver" android:label="Call Receiver" />.
  • Triển khai receiver (là lớp con của BroadcastReceiver). Trong Sipdemo, đây là IncomingCallReceiver.
  • Khởi động hồ sơ cục bộ (SipProfile) với ý định đang chờ xử lý sẽ kích hoạt dịch vụ nhận khi có người gọi hồ sơ cục bộ.
  • Thiết lập bộ lọc ý định lọc theo thao tác đại diện cho lệnh gọi đến. Trong Sipdemo, thao tác này là android.SipDemo.INCOMING_CALL.

Phân lớp BroadcastReceiver

Để nhận cuộc gọi, ứng dụng SIP của bạn phải phân lớp con BroadcastReceiver. Hệ thống Android xử lý các cuộc gọi SIP đến và thông báo ý định "cuộc gọi đến" (do ứng dụng xác định) khi nhận được một cuộc gọi. Dưới đây là mã BroadcastReceiver phân lớp con từ mẫu 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();
            }
        }
    }
}

Thiết lập bộ lọc ý định để nhận lệnh gọi

Khi nhận được một lệnh gọi mới, dịch vụ SIP sẽ gửi một ý định bằng chuỗi thao tác do ứng dụng cung cấp. Trong Sipdemo, chuỗi hành động này là android.SipDemo.INCOMING_CALL.

Đoạn mã này được trích từ Sipdemo cho thấy cách đối tượng SipProfile được tạo bằng ý định đang chờ xử lý dựa trên chuỗi hành động android.SipDemo.INCOMING_CALL. Đối tượng PendingIntent sẽ thực hiện một thông báo truyền tin khi SipProfile nhận được một lệnh gọi:

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

Tin truyền sẽ bị chặn bởi bộ lọc ý định, sau đó sẽ kích hoạt receiver (IncomingCallReceiver). Bạn có thể chỉ định bộ lọc ý định trong tệp kê khai của ứng dụng hoặc thực hiện việc này trong mã như trong phương thức onCreate() của ứng dụng mẫu Sipdemo của Activity của ứng dụng:

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

Kiểm thử các ứng dụng SIP

Để kiểm thử các ứng dụng SIP, bạn cần có:

  • Thiết bị di động đang chạy Android 2.3 trở lên. SIP chạy qua mạng không dây, vì vậy, bạn phải kiểm thử trên một thiết bị thực. Bạn không thể kiểm thử trên AVD.
  • Tài khoản SIP. Có nhiều nhà cung cấp SIP khác nhau cung cấp tài khoản SIP.
  • Nếu bạn đang thực hiện cuộc gọi, thì cuộc gọi đó cũng phải đến một tài khoản SIP hợp lệ.

Cách kiểm thử ứng dụng SIP:

  1. Trên thiết bị của bạn, hãy kết nối với mạng không dây (Cài đặt > Không dây và mạng > Wi-Fi > Cài đặt Wi-Fi).
  2. Thiết lập thiết bị di động để kiểm thử, như mô tả trong Phát triển trên thiết bị.
  3. Chạy ứng dụng trên thiết bị di động của bạn, như mô tả trong Phát triển trên thiết bị.
  4. Nếu sử dụng Android Studio, bạn có thể xem đầu ra nhật ký ứng dụng bằng cách mở bảng điều khiển Nhật ký sự kiện (View > Tool Windows > Event Log (Xem > Cửa sổ công cụ > Nhật ký sự kiện)).
  5. Đảm bảo ứng dụng của bạn được định cấu hình để tự động chạy Logcat khi ứng dụng chạy:
    1. Chọn Run > Edit Configurations (Chạy > Chỉnh sửa cấu hình).
    2. Chọn thẻ Miscellaneous (Khác) trong cửa sổ Run/Debug Configurations (Cấu hình chạy/gỡ lỗi).
    3. Trong Logcat, hãy chọn Show logcat Automatically (Tự động hiện logcat) rồi chọn OK.