Giới thiệu
Lấy số điện thoại (MSISDN) của thiết bị qua TelephonyManager.getLine1Number() **không còn hoạt động** cho app thông thường từ **Android 10 (API 29)** do chính sách quyền riêng tư. Bài viết giải thích hạn chế và các giải pháp thay thế.
Hạn chế theo phiên bản Android
| Android Version | API Level | getLine1Number() | Quyền cần |
|---|---|---|---|
| < 6.0 (Marshmallow) | < 23 | Thường hoạt động | READ_PHONE_STATE |
| 6.0 - 9.0 | 23 - 28 | Có thể hoạt động | READ_PHONE_STATE (runtime permission) |
| 10 (Q) - 11 | 29 - 30 | Trả về null/empty | READ_PRIVILEGED_PHONE_STATE (system only) |
| 12+ | 31+ | Trả về null/empty | READ_PRIVILEGED_PHONE_STATE (system only) |
Kết luận: App thông thường (non-system) **không thể lấy số điện thoại** trên Android 10+.
Code tham khảo (Chỉ hoạt động Android < 10 hoặc System App)
AndroidManifest.xml
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!-- Chỉ system app được cấp -->
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
MainActivity.kt
package com.example.getphonenumber
import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.telephony.TelephonyManager
import android.widget.Button
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.example.getphonenumber.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val REQUEST_CODE = 101
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.btnGetPhoneNumber.setOnClickListener {
requestPhoneNumber()
}
}
private fun requestPhoneNumber() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_PHONE_STATE), REQUEST_CODE)
} else {
fetchPhoneNumber()
}
}
private fun fetchPhoneNumber() {
val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
try {
val phoneNumber: String?
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // API 29+
// getLine1Number() deprecated, dùng getLine1Number() vẫn compile nhưng trả về null
phoneNumber = telephonyManager.line1Number
} else {
@Suppress("DEPRECATION")
phoneNumber = telephonyManager.line1Number
}
if (phoneNumber.isNullOrBlank()) {
binding.tvPhoneNumber.text = "Số điện thoại: Không thể truy cập (Android 10+ hạn chế)"
showLimitationDialog()
} else {
binding.tvPhoneNumber.text = "Số điện thoại: $phoneNumber"
}
} catch (e: SecurityException) {
binding.tvPhoneNumber.text = "Lỗi bảo mật: ${e.message}"
}
}
private fun showLimitationDialog() {
androidx.appcompat.app.AlertDialog.Builder(this)
.setTitle("Hạn chế Android 10+")
.setMessage("""
Google đã hạn chế truy cập số điện thoại (IMEI, IMSI, MSISDN) cho app thông thường từ Android 10.
Giải pháp thay thế:
1. Nhập tay bởi người dùng (OTP verification)
2. Firebase Phone Auth (gửi OTP SMS)
3. Sử dụng AccountManager (Google account)
4. Custom UUID lưu trong SharedPreferences
""".trimIndent())
.setPositiveButton("OK", null)
.show()
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_CODE && grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
fetchPhoneNumber()
} else {
Toast.makeText(this, "Cần quyền READ_PHONE_STATE", Toast.LENGTH_SHORT).show()
}
}
}
Giải pháp thay thế cho App hiện đại
1. Firebase Phone Authentication (Khuyến nghị nhất)
- Gửi OTP SMS đến số điện thoại user nhập.
- Xác thực OTP → có số điện thoại verified.
- Hỗ trợ mọi Android version, không cần permission đặc biệt.
// build.gradle.kts
implementation("com.google.firebase:firebase-auth:22.3.0")
implementation("com.google.firebase:firebase-auth-ktx:22.3.0")
// Gửi OTP
FirebaseAuth.getInstance().verifyPhoneNumber(
phoneNumber, // User nhập: "+84901234567"
60, TimeUnit.SECONDS,
this,
object : PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
override fun onCodeSent(verificationId: String, token: PhoneAuthProvider.ForceResendingToken) {
// Lưu verificationId, chuyển màn hình nhập OTP
}
override fun onVerificationCompleted(credential: PhoneAuthCredential) {
// Auto-verify (một số thiết bị)
signInWithCredential(credential)
}
override fun onVerificationFailed(e: FirebaseException) {
// Xử lý lỗi
}
}
)
2. Hint Request (Google Play Services) - Deprecated
Credentials API và HintRequest đã deprecated. Thay bằng Credential Manager API (Android 14+) hoặc Firebase Phone Auth.
3. Tự nhập tay + OTP verification (Backend tự build)
- UI nhập số điện thoại → gửi OTP qua SMS gateway (Twilio, Vonage, local provider).
- Verify OTP → lưu số điện thoại vào server/user profile.
4. AccountManager (Google Account trên thiết bị)
// Lấy email Google account chính (không phải số điện thoại)
val accountManager = AccountManager.get(this)
val accounts = accountManager.getAccountsByType("com.google")
accounts.firstOrNull()?.name?.let { email ->
// email: user@gmail.com
}
5. Custom UUID (Identifier app install)
private fun getOrCreateDeviceId(): String {
val prefs = getSharedPreferences("device_id", MODE_PRIVATE)
return prefs.getString("device_id", "").let {
if (it.isNullOrBlank()) {
val newId = java.util.UUID.randomUUID().toString()
prefs.edit().putString("device_id", newId).apply()
newId
} else it
}
}
Best Practices
- Không假设 (assume) có thể lấy được số điện thoại - luôn có fallback.
- UI: Cho user **nhập số điện thoại** + **verify OTP** - cách chắc chắn nhất.
- Đừng dùng IMEI/Android ID/Phone number để track user cross-app (vi phạm Play Policy).
- Dùng **Firebase Installation ID** hoặc **Custom UUID** để identify app install.
- Nếu cần số điện thoại cho 2FA: dùng Firebase Phone Auth hoặc tự build OTP flow.