Dangerous Permission là một loại quyền trong Android yêu cầu sự đồng ý rõ ràng của người dùng thông qua hộp thoại runtime trong khi ứng dụng đang chạy. Theo Hướng dẫn nhà phát triển Android, 2024, quyền nguy hiểm có ProtectionLevel dangerous và cấp quyền truy cập vào dữ liệu nhạy cảm: camera, micrô, vị trí và danh bạ. Nếu không có sự đồng ý rõ ràng của người dùng, ứng dụng không thể sử dụng các tính năng này.
Những điểm chính
Dangerous Permission là một loại quyền hệ thống Android cấp quyền truy cập vào dữ liệu nhạy cảm của người dùng. Không giống như quyền thông thường, quyền nguy hiểm không được cấp tự động khi cài đặt — ứng dụng phải yêu cầu chúng một cách rõ ràng trong thời gian chạy thông qua cơ chế runtime được giới thiệu trong Android 6.0 Marshmallow (API 23).
Sự cần thiết của yêu cầu rõ ràng xuất phát từ bản chất của dữ liệu mà các quyền này bảo vệ: vị trí người dùng, danh bạ cá nhân, nội dung camera và micrô, lịch sử cuộc gọi và SMS. Android coi dữ liệu này là nhạy cảm và yêu cầu người dùng cấp quyền truy cập một cách có ý thức. Theo Android Privacy Sandbox (2024), trung bình người dùng từ chối khoảng 30 phần trăm yêu cầu runtime.
Một tính năng chính của Dangerous Permission là khả năng thu hồi bất kỳ lúc nào. Người dùng có thể vào Cài đặt — Ứng dụng — Quyền và tắt bất kỳ quyền nguy hiểm nào. Ứng dụng phải sẵn sàng cho việc quyền đã được cấp trước đó có thể bị thu hồi bất kỳ lúc nào mà không cần khởi động lại.
Mức bảo vệ dangerous được đặt trong định nghĩa quyền hệ thống ở cấp độ OS. Khi ứng dụng khai báo uses-permission với protectionLevel này, hệ thống đánh dấu quyền là yêu cầu runtime. Không giống như normal, quyền dangerous luôn được hiển thị trong giao diện quản lý quyền của hệ thống và có thể bị thu hồi.
Tất cả quyền nguy hiểm được nhóm thành Permission Groups theo loại chức năng. Ví dụ: CAMERA và CAMERA2 nằm trong nhóm CAMERA, ACCESS_FINE_LOCATION và ACCESS_COARSE_LOCATION nằm trong nhóm LOCATION. Nếu người dùng đã cấp một quyền từ một nhóm, các quyền còn lại trong cùng nhóm sẽ được cấp tự động mà không cần hộp thoại bổ sung.
Yêu cầu runtime là cơ chế mà ứng dụng gọi API hệ thống để hiển thị hộp thoại yêu cầu quyền. Người dùng thấy cửa sổ phương thức với tên quyền và các nút Cho phép và Từ chối. Sau phản hồi, hệ thống gọi callback onRequestPermissionsResult với kết quả.
Chu trình đầy đủ bao gồm ba bước: kiểm tra trạng thái qua checkSelfPermission, gọi requestPermissions nếu quyền chưa được cấp và xử lý kết quả trong onRequestPermissionsResult. Kiểm tra trạng thái là bắt buộc vì người dùng có thể đã thu hồi quyền bất kỳ lúc nào qua cài đặt và gọi hàm mà không kiểm tra sẽ dẫn đến SecurityException.
fun checkAndRequestCameraPermission() {
when {
ContextCompat.checkSelfPermission(
this,
Manifest.permission.CAMERA
) == PackageManager.PERMISSION_GRANTED -> {
openCamera()
}
else -> {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.CAMERA),
REQUEST_CAMERA_CODE
)
}
}
}
Xử lý kết quả diễn ra trong ActivityResultLauncher hoặc onRequestPermissionsResult. Cách tiếp cận hiện đại được khuyến nghị là sử dụng ActivityResultContracts.RequestPermission, cung cấp API sạch hơn mà không cần mã yêu cầu rõ ràng. Hợp đồng này trả về Boolean — quyền đã được cấp hay chưa.
Yêu cầu quyền nguy hiểm một cách nghiêm ngặt trong bối cảnh sử dụng tính năng, không phải khi khởi động ứng dụng. Nếu người dùng đã nhấn nút camera — hãy yêu cầu CAMERA. Nếu họ mở bản đồ — hãy yêu cầu LOCATION. Yêu cầu theo ngữ cảnh mang lại số lần cấp quyền gấp đôi so với yêu cầu tất cả quyền khi khởi chạy lần đầu. Cũng nên không yêu cầu nhiều hơn một quyền cùng lúc để người dùng hiểu tính năng nào cần truy cập.
Android định nghĩa một số nhóm quyền nguy hiểm, mỗi nhóm chứa một hoặc nhiều hằng số. Danh sách đầy đủ nhất có sẵn trong lớp Manifest.permission. Dưới đây là các nhóm và quyền chính được sử dụng trong phát triển.
| Nhóm quyền | Quyền | API truy cập |
|---|---|---|
| CAMERA | CAMERA | Camera API, CameraX |
| LOCATION | ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION | FusedLocationProvider, Geofence |
| MICROPHONE | RECORD_AUDIO | MediaRecorder, AudioRecord |
| PHONE | READ_PHONE_STATE, CALL_PHONE, READ_CALL_LOG | TelephonyManager |
| CONTACTS | READ_CONTACTS, WRITE_CONTACTS, GET_ACCOUNTS | ContactsContract |
| SMS | READ_SMS, SEND_SMS, RECEIVE_SMS | SmsManager |
| STORAGE | READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE | MediaStore, File API |
| CALENDAR | READ_CALENDAR, WRITE_CALENDAR | CalendarContract |
Bắt đầu từ Android 12, Google đã thắt chặt yêu cầu đối với một số quyền. Ví dụ: BLUETOOTH_CONNECT và BLUETOOTH_SCAN đã trở nên nguy hiểm và yêu cầu yêu cầu runtime. Quyền BODY_SENSORS_BACKGROUND cũng được thêm vào để truy cập nền vào cảm biến. Các nhà phát triển cần cập nhật targetSdkVersion và kiểm tra yêu cầu trên các phiên bản OS hiện tại.
Android 13 (API 33) đã giới thiệu các quyền mới cho thông báo (POST_NOTIFICATIONS) và tệp phương tiện (READ_MEDIA_IMAGES, READ_MEDIA_VIDEO, READ_MEDIA_AUDIO), thay thế READ_EXTERNAL_STORAGE chung. Giờ đây, quyền truy cập vào ảnh, video và âm thanh được yêu cầu riêng biệt thông qua các quyền chuyên dụng mà không có hộp thoại đơn lẻ.
Quyền Dangerous và Normal khác nhau cơ bản về cách cấp, khả năng thu hồi và UX. Normal được cấp tự động khi cài đặt, Dangerous yêu cầu hộp thoại runtime rõ ràng. Normal không thể thu hồi qua cài đặt, Dangerous có thể bị vô hiệu hóa bất kỳ lúc nào. Sự bất đối xứng này tạo ra các mẫu phát triển khác nhau.
Từ góc độ mã, quyền nguy hiểm đòi hỏi nhiều công việc hơn: checkSelfPermission, requestPermissions, xử lý từ chối. Đối với quyền thông thường, một dòng trong AndroidManifest.xml là đủ. Tuy nhiên, Dangerous Permission trao quyền kiểm soát cho người dùng, điều này tăng sự tin cậy, đặc biệt đối với các tính năng nhạy cảm như camera hoặc vị trí.
Việc lựa chọn giữa các loại không phụ thuộc vào nhà phát triển — nó được xác định bởi hệ thống. Nhà phát triển chỉ khai báo uses-permission và hệ thống xác định loại dựa trên protectionLevel. Tuy nhiên, chiến lược yêu cầu quyền nguy hiểm ảnh hưởng đến trải nghiệm người dùng: hộp thoại thường xuyên hoặc không phù hợp làm giảm xếp hạng ứng dụng.
Cách hiện đại để yêu cầu quyền trong Kotlin là sử dụng ActivityResultContracts.RequestMultiplePermissions hoặc RequestPermission. Các hợp đồng này là một phần của thư viện androidx.activity và cung cấp API sạch dựa trên lambda, không cần ghi đè onRequestPermissionsResult.
class CameraActivity : AppCompatActivity() {
private val requestPermissionLauncher =
registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted: Boolean ->
if (isGranted) {
openCamera()
} else {
showPermissionDeniedMessage()
}
}
fun requestCamera() {
when {
ContextCompat.checkSelfPermission(
this,
Manifest.permission.CAMERA
) == PackageManager.PERMISSION_GRANTED ->
openCamera()
ActivityCompat.shouldShowRequestPermissionRationale(
this,
Manifest.permission.CAMERA
) ->
showRationaleDialog()
else ->
requestPermissionLauncher.launch(
Manifest.permission.CAMERA
)
}
}
}
Khi ứng dụng cần nhiều quyền nguy hiểm cùng lúc, hãy sử dụng RequestMultiplePermissions. Hợp đồng trả về Map<String, Boolean> trong đó khóa là tên quyền và giá trị là kết quả. Điều này hữu ích khi khởi chạy lần đầu khi bạn cần yêu cầu CAMERA và RECORD_AUDIO để ghi video.
Nếu người dùng từ chối yêu cầu, phương thức shouldShowRequestPermissionRationale trả về true. Điều này báo hiệu cần hiển thị giải thích lý do cần quyền. Thực hành tốt nhất là hiển thị hộp thoại tùy chỉnh với giải thích và nút Thử lại. Nếu người dùng từ chối yêu cầu lần nữa với hộp kiểm Never Ask Again được chọn, shouldShowRequestPermissionRationale sẽ trả về false và bạn cần chuyển hướng đến Cài đặt.
Never Ask Again là cờ mà người dùng có thể đặt khi từ chối hộp thoại runtime lần thứ hai. Sau đó, hộp thoại tiêu chuẩn không còn được hiển thị cho quyền đó nữa. Cách duy nhất để cấp quyền truy cập là chuyển hướng người dùng đến cài đặt ứng dụng hệ thống.
Nhà phát triển cần phân biệt hai kịch bản từ chối: thứ nhất, khi shouldShowRequestPermissionRationale trả về true (người dùng từ chối nhưng hộp thoại vẫn có thể hiển thị) và thứ hai, khi phương thức trả về false (Never Ask Again đang hoạt động hoặc quyền bị chặn bởi chính sách). Trong trường hợp thứ hai, bạn nên hiển thị nút Mở Cài đặt.
fun handlePermissionDenied(permission: String) {
if (ActivityCompat.shouldShowRequestPermissionRationale(
this, permission
)) {
showRationaleDialog(permission)
} else {
showSettingsRedirectDialog(permission)
}
}
private fun showSettingsRedirectDialog(permission: String) {
AlertDialog.Builder(this)
.setTitle("Truy cập bị từ chối")
.setMessage(
"Quyền bị chặn. Mở Cài đặt."
)
.setPositiveButton("Cài đặt") { _, _ ->
val intent = Intent(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts(
"package", packageName, null
)
)
startActivity(intent)
}
.show()
}
Điều quan trọng là không yêu cầu lại quyền nếu shouldShowRequestPermissionRationale đã trả về false. Việc gọi lại requestPermissions trong trường hợp này sẽ không hiển thị hộp thoại — kết quả sẽ trả về ngay lập tức với DENIED mà không có giải thích. Người dùng sẽ gặp phải hành vi không rõ ràng, ảnh hưởng tiêu cực đến trải nghiệm sử dụng ứng dụng.
Câu hỏi thường gặp
Quyền nguy hiểm bao gồm các quyền có ProtectionLevel dangerous: CAMERA, RECORD_AUDIO, ACCESS_FINE_LOCATION, READ_CONTACTS, READ_SMS, READ_CALENDAR và các quyền khác. Danh sách đầy đủ có sẵn trong lớp Manifest.permission.
Sử dụng ContextCompat.checkSelfPermission, truyền context và tên quyền. Phương thức trả về PERMISSION_GRANTED hoặc PERMISSION_DENIED. Việc kiểm tra nên được thực hiện trước mỗi lần gọi API yêu cầu quyền nguy hiểm.
Permission Group nhóm các quyền nguy hiểm liên quan lại với nhau. Nếu người dùng cấp một quyền từ một nhóm, các quyền còn lại được cấp tự động. Ví dụ: LOCATION bao gồm ACCESS_FINE_LOCATION và ACCESS_COARSE_LOCATION.
Kiểm tra shouldShowRequestPermissionRationale sau khi từ chối. Nếu phương thức trả về false và quyền vẫn chưa được cấp — Never Ask Again đang hoạt động. Chuyển hướng người dùng đến Cài đặt qua Intent với ACTION_APPLICATION_DETAILS_SETTINGS.
Có, chúng vẫn bắt buộc. Trên Android 13+, một số quyền đã thay đổi: POST_NOTIFICATIONS trở thành quyền runtime riêng biệt và READ_EXTERNAL_STORAGE được thay thế bằng READ_MEDIA_IMAGES để truy cập chi tiết vào tệp phương tiện.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm