Phát hiện eSIM và thẻ SIM
Phát hiện thẻ
Thiết bị Android có thẻ SIM và eSIM sử dụng các mã nhận dạng sau trong điện thoại API, bao gồm [`TelephonyManager`](/reference/android/telephony/TelephonyManager) và [`SubscriptionManager`](/reference/android/telephony/SubscriptionManager): * Mã nhận dạng gói thuê bao: mã nhận dạng duy nhất của một gói thuê bao di động. * Chỉ mục hoặc mã nhận dạng khe logic: chỉ mục duy nhất tham chiếu đến một khe cắm SIM logic. ID vị trí logic bắt đầu từ 0 và tăng lên tùy thuộc vào số lượng các vị trí đ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 thiết bị có nhiều khe cắm thực tế nhưng chỉ hỗ trợ một vị trí đang hoạt động, nó sẽ chỉ có ID vị trí logic 0. * Chỉ mục hoặc mã nhận dạng vị trí thực tế: chỉ mục duy nhất tham chiếu đến một khe SIM thực. Mã khung giờ thực tế bắt đầu từ 0 và tăng lên tuỳ thuộc vào số lượng đường liên kết vật lý các khe trên thiết bị. Số lượng 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ể đang sử dụng. Ví dụ: một thiết bị chuyển đổi giữa hai SIM và một SIM nhưng ở chế độ một SIM, chế độ này có thể luôn có 2 khe cắm vật lý một khe logic. * Mã thẻ: mã nhận dạng duy nhất dùng để nhận dạng một UiccCard. ![Sơ đồ về cách sử dụng mã nhận dạng trong trường hợp có 2 ô logic và 3 ô thực](/images/guide/topics/mạc/tel-ids.png) Trong biểu đồ trên: * Thiết bị có 2 khe logic. * Trong ô 0 thực có một thẻ UICC thực với hồ sơ đang hoạt động. * Trong ô thực tế 2 là một eUICC có hồ sơ đang hoạt động. * Khe thực 1 hiện không được sử dụng. ![Sơ đồ cách sử dụng mã nhận dạng trong trường hợp có 3 ô logic và 2 ô thực](/images/guide/topics/mạc/tel-ids-2.png) Trong biểu đồ trên: * Thiết bị có 3 khe logic. * Trong ô 0 thực có một thẻ UICC thực với hồ sơ đang hoạt động. * Trong ô thực tế 1 là một eUICC có 2 cấu hình đã tải xuống, cả hai đều hoạt động bằng MEP (Nhiều cấu hình đã 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). Việc này cho phép bạn thêm các tính năng của điện thoại Internet dựa trên SIP vào ứng dụng của mình. Android có ngăn xếp giao thức SIP đầy đủ và tính năng quản lý cuộc gọi tích hợp các dịch vụ cho phép ứng dụng dễ dàng thiết lập các cuộc gọi thoại đến và đi, mà không phải quản lý các phiên, thông tin giao tiếp cấp độ truyền tải hoặc âm thanh ghi hoặc phát trực tiếp.
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
Sau đây là các yêu cầu để 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 qua kết nối dữ liệu không dây, nên thiết bị của bạn phải có dữ liệu kết nối (qua dịch vụ dữ liệu di động hoặc Wi-Fi). Điều này có nghĩa là bạn không thể kiểm thử trên AVD – bạn chỉ có thể kiểm thử trên một thiết bị thực. Để biết thông tin chi tiết, hãy xem Thử nghiệm ứ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 cung cấp tài khoản SIP.
Lưu ý: Thư viện android.net.sip
không hỗ trợ video
cuộc gọi. Nếu bạn muốn triển khai tính năng gọi VOIP bằng cách sử dụng ngăn xếp SIP, chẳng hạn như
android.net.sip
, hãy xem xét một trong nhiều nguồn mở hiện đại
các phương án thay thế làm cơ sở cho bất kỳ việc triển khai lệnh gọi VOIP nào. Ngoài ra,
bạn có thể triển khai
ConnectionService
API để tích hợp chặt chẽ các lệnh gọi này vào Trình quay số của thiết bị
.
Các lớp và giao diện API SIP
Dưới đây là bản tóm tắt về các lớp và một giao diện
(SipRegistrationListener
) có trong Android SIP
API:
Lớp/Giao diện | Mô tả |
---|---|
SipAudioCall |
Xử lý cuộc gọi âm thanh trên 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 thực hiện đã nhận ("khi đổ chuông") hoặc một cuộc gọi đang đi ("khi gọi"). |
SipErrorCode |
Xác định mã lỗi được trả về trong các thao tác 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 sang các dịch vụ SIP có liên quan. |
SipProfile |
Xác định hồ sơ SIP, bao gồm thông tin tài khoản SIP, miền và máy chủ. |
SipProfile.Builder |
Lớp trợ giúp để tạo SipProfile. |
SipSession |
Biểu thị một phiên SIP liên kết với hộp thoại SIP hoặc một giao dịch độc lập không phải trong hộp thoại. |
SipSession.Listener |
Trình nghe các sự kiện liên quan đến một phiên SIP, chẳng hạn như khi một phiên đang được đăng ký ("khi đăng ký") hoặc là một cuộc gọi đi ("khi đang 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 |
Giao diện là trình nghe sự kiện đăng ký SIP. |
Đang 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) trở lên của nền tảng. 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 mọi thiết bị đều hỗ trợ SIP.
Để sử dụng SIP, hãy thêm các quyền sau đây 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ó khả năng hỗ trợ SIP, hãy thêm đoạn mã sau vào tệp kê khai:
<uses-sdk android:minSdkVersion="9" />
Điều này cho biết ứng dụng của bạn yêu cầu Android 2.3 trở lên. Cho
thông tin khác, xem
Cấp độ API
và tài liệu cho
<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 đoạn mã sau vào tệp kê khai:
<uses-feature android:name="android.software.sip.voip" />
Điều 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
thêm thuộc tính android:required
cho biết liệu bạn
muốn lọc ứng dụng khỏi các thiết bị không hỗ trợ SIP.
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
<uses-feature>
.
Nếu ứng dụng được thiết kế để nhận lệnh gọi, bạn cũng phải xác định trình nhận (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 đối tượng SipManager
. SipManager
sẽ lấy
hãy quan tâm đến những điều sau trong ứng dụng của bạn:
- Đang bắt đầu các phiên SIP.
- Thực hiện và nhận cuộc gọi.
- Đăng ký và huỷ đăng ký với nhà cung cấp SIP.
- Đang xác minh kết nối phiên.
Bạn tạo thực thể cho 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 điển hình liên quan đến một hoặc nhiều người dùng, mỗi người trong số họ
có 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 cả SIP
tài khoản, tên miền và thông tin máy chủ. Hồ sơ đã liên kết với SIP
trên thiết bị chạy ứng dụng được gọi là local
hồ sơ. Cấu hình mà phiên được 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ộ, điều này sẽ đăng ký hiệu quả
làm vị trí để gửi cuộc gọi SIP tới địa chỉ SIP của bạn.
Phần này trình bày 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 một đố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 cuộc gọi SIP chung. Người gọi có thể thực hiện các cuộc gọi tiếp theo thông qua
mSipManager.makeAudioCall
. Phần trích dẫn này cũng đặt hành động
android.SipDemo.INCOMING_CALL
(sẽ được một ý định sử dụng)
khi thiết bị nhận được cuộc gọi (xem phần Thiết lập
bộ lọc ý định để nhận cuộc 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 đặt SipRegistrationListener
trên SipManager
. Thao tác này sẽ theo dõi xem SipProfile
đã được đăng ký thành công với dịch vụ SIP của bạn hay chưa
nhà cung cấp:
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 ứng dụng của bạn hoàn tất quá trình sử dụng hồ sơ, hồ sơ sẽ đóng hồ sơ để giải phóng đối tượng được liên kết 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 thiết bị sau:
SipProfile
đang thực hiện cuộc gọi ( "local profile") và một địa chỉ SIP hợp lệ để nhận cuộc gọi ( "hồ sơ ngang hàng").- Đối tượng
SipManager
.
Để thực hiện cuộc 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 khách hàng với
ngăn xếp SIP diễn 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 cuộc gọi được thực hiện
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 cuộc gọi. Phương thức SipManager
makeAudioCall
nhận các tham số sau:
- Cấu hình SIP cục bộ (phương thức gọi).
- Hồ sơ SIP kết nối ngang hàng (người dùng được gọi).
SipAudioCall.Listener
để nghe cuộc gọi sự kiện từSipAudioCall
. Đây có thể lànull
, nhưng như đã trình bày ở trên, trình nghe được dùng để thiết lập mọi thứ sau khi cuộc 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 một ý định
cho biết có cuộc gọi đến. Do đó, bạn phải thực hiện những việc sau trong
ứng dụng của bạn:
- Trong
AndroidManifest.xml
, hãy khai báo<receiver>
Trong Sipdemo, đây là<receiver android:name=".IncomingCallReceiver" android:label="Call Receiver" />
. - Triển khai trình thu nhận, là một lớp con của
BroadcastReceiver
. Trong Sipdemo, đây làIncomingCallReceiver
. - Khởi chạy hồ sơ cục bộ (
SipProfile
) bằng một ý định đang chờ xử lý kích hoạt dịch vụ nhận của bạn khi ai đó gọi đến hồ sơ cục bộ. - Thiết lập bộ lọc ý định lọc theo hành động đại diện cho
cuộc gọi đến. Trong Sipdemo, thao tác này là
android.SipDemo.INCOMING_CALL
.
Phân loại con BroadcastReceiver
Để nhận cuộc gọi, ứng dụng SIP của bạn phải là lớp con BroadcastReceiver
. Các
Hệ thống Android xử lý các cuộc gọi SIP đến và truyền tin "cuộc gọi đến"
call" ý định (như được ứng dụng xác định) khi ứng dụng nhận được
một cuộc gọi. Đây là lớp con
BroadcastReceiver
mã từ mẫu SIP.
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 một bộ lọc ý định để nhận cuộc gọi
Khi nhận được một cuộc gọi mới, dịch vụ SIP sẽ gửi một ý định kèm theo
chuỗi hành động 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 trích từ Sipdemo cho thấy cách đối tượng SipProfile
được tạo với ý định đang chờ xử lý dựa trên
chuỗi hành động android.SipDemo.INCOMING_CALL
. Chiến lược phát hành đĩa đơn
Đối tượng PendingIntent
sẽ thực hiện thông báo truyền tin khi SipProfile
nhận được một cuộc 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);
Thông báo truyền tin sẽ bị chặn bởi bộ lọc ý định, sau đó bộ lọc này sẽ kích hoạt
trình nhận (IncomingCallReceiver
). Bạn có thể chỉ định một ý định
lọc trong tệp kê khai của ứng dụng hoặc thực hiện trong mã như trong Sipdemo
phương thức onCreate()
của ứng dụng mẫu
trong 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ử ứng dụng SIP
Để kiểm thử ứng dụng SIP, bạn cần có:
- Một thiết bị di động đang chạy Android 2.3 trở lên. SIP (Giao thức khởi tạo phiên) không dây, nên bạn phải thử nghiệm trên thiết bị thực tế. Bạn sẽ không kiểm thử được trên AVD.
- Tài khoản SIP. Có nhiều nhà cung cấp SIP cung cấp tài khoản SIP.
- Nếu bạn đang thực hiện cuộc gọi, cuộc gọi đó cũng phải thuộc một tài khoản SIP hợp lệ.
Cách kiểm thử ứng dụng SIP:
- Kết nối với mạng không dây (Cài đặt > Không dây và mạng) trên thiết bị của bạn > Wi-Fi > Cài đặt Wi-Fi).
- Thiết lập thiết bị di động để kiểm thử, như mô tả trong phần Phát triển trên thiết bị.
- Chạy ứng dụng trên thiết bị di động của bạn, như mô tả trong phần Phát triển trên thiết bị.
- Nếu đang sử dụng Android Studio, bạn có thể xem kết quả của 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).
- Đảm bảo ứng dụng của bạn được định cấu hình để tự động chạy Logcat khi chạy:
- Chọn Run > Edit Configurations (Chạy > Chỉnh sửa cấu hình).
- Chọn thẻ Miscellaneous (Khác) trong cửa sổ Run/Debug Configurations (Cấu hình chạy/gỡ lỗi).
- Trong mục Logcat, hãy chọn Show logcat tự động (Hiển thị logcat tự động) chọn OK.