Dangerous Permission е категория разрешения в Android, които изискват изричното съгласие на потребителя чрез runtime диалог по време на работа на приложението. Според Android Developer Guide, 2024, опасните разрешения имат ProtectionLevel dangerous и предоставят достъп до поверителни данни: камера, микрофон, геолокация и контакти. Без изричното съгласие на потребителя приложението не може да използва тези функции.
Основни точки
Dangerous Permission е категория системни разрешения на Android, които предоставят достъп до поверителни данни на потребителя. За разлика от нормалните разрешения, опасните разрешения не се издават автоматично при инсталиране — приложението трябва изрично да ги поиска по време на изпълнение чрез механизма runtime, представен в Android 6.0 Marshmallow (API 23).
Необходимостта от изрична заявка произтича от естеството на данните, които тези разрешения защитават: геолокация на потребителя, лични контакти, съдържание на камера и микрофон, история на обажданията и SMS. Android счита тези данни за чувствителни и изисква информирано съгласие на потребителя. Според Android Privacy Sandbox (2024), потребителите отхвърлят средно около 30 процента от runtime заявките.
Ключова характеристика на Dangerous Permission — възможност за оттегляне по всяко време. Потребителят може да отиде в Настройки — Приложения — Разрешения и да превключи ключа за всяко опасно разрешение. Приложението трябва да е готово, че разрешението, което е било предоставено, може да бъде оттеглено по всяко време без рестартиране.
Нивото на защита dangerous се задава в системните дефиниции на разрешенията на ниво OS. Когато приложението декларира uses-permission с такова protectionLevel, системата маркира разрешението като изискващо runtime заявка. За разлика от normal, dangerous разрешенията винаги се показват в системния UI за управление на разрешения и могат да бъдат оттеглени.
Всички опасни разрешения са групирани в Permission Group по функционален признак. Например CAMERA и CAMERA2 са в групата CAMERA, ACCESS_FINE_LOCATION и ACCESS_COARSE_LOCATION — в групата LOCATION. Ако потребителят е предоставил едно разрешение от групата, останалите разрешения от същата група се предоставят автоматично без допълнителен диалог.
Runtime заявката е механизмът, при който приложението извиква системния API за показване на диалог с искане за разрешение. Потребителят вижда модален прозорец с името на разрешението и бутоните Allow и Deny. След отговора системата извиква callback onRequestPermissionsResult с резултата.
Пълният цикъл включва три стъпки: проверка на статуса чрез checkSelfPermission, извикване на requestPermissions при липса на разрешение и обработка на резултата в onRequestPermissionsResult. Проверката на статуса е задължителна, тъй като потребителят може да оттегли разрешението по всяко време чрез настройки, а извикването на функцията без проверка ще доведе до 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
)
}
}
}
Обработката на резултата се извършва в ActivityResultLauncher или onRequestPermissionsResult. Препоръчителният модерен подход — използване на ActivityResultContracts.RequestPermission, който предоставя по-чист API без изрични кодове на заявка. Този договор връща Boolean — дали разрешението е предоставено или не.
Заявявайте опасните разрешения строго в контекста на използване на функцията, а не при стартиране на приложението. Ако потребителят е натиснал бутона на камерата — заявете CAMERA. Ако е отворил картата — заявете LOCATION. Контекстуалната заявка дава два пъти повече одобрения от заявката на всички разрешения при първото стартиране. Също така се препоръчва да не заявявате повече от едно разрешение наведнъж, за да разбере потребителят коя функция изисква достъп.
Android определя няколко групи опасни разрешения, всяка от които съдържа от една до няколко константи. Най-пълният списък е представен в класа Manifest.permission. По-долу са показани основните групи и разрешения, използвани в разработката.
| Група Permission Group | Разрешения | API за достъп |
|---|---|---|
| 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 |
От Android 12 нататък Google затегна изискванията за някои разрешения. Например BLUETOOTH_CONNECT и BLUETOOTH_SCAN станаха опасни и изискват runtime заявка. Също така се появи разрешението BODY_SENSORS_BACKGROUND за фон достъп до сензори. Разработчиците трябва да актуализират targetSdkVersion и да тестват заявките на актуалните версии на OS.
Android 13 (API 33) въведе нови разрешения за известия (POST_NOTIFICATIONS) и за медийни файлове (READ_MEDIA_IMAGES, READ_MEDIA_VIDEO, READ_MEDIA_AUDIO), заменяйки общото READ_EXTERNAL_STORAGE. Сега достъпът до снимки, видео и аудио се заявява отделно чрез специализирани разрешения без единен диалог.
Dangerous и Normal Permission се различават принципно по начина на предоставяне, възможността за оттегляне и UX. Normal се издава автоматично при инсталиране, Dangerous изисква изричен runtime диалог. Normal не може да бъде оттеглено чрез настройки, Dangerous може да бъде изключено по всяко време. Тази асиметрия създава различни модели на разработка.
От гледна точка на кода, опасните разрешения изискват повече работа: checkSelfPermission, requestPermissions, обработка на отказ. За Normal е достатъчен един ред в AndroidManifest.xml. В същото време Dangerous Permission дава контрол на потребителя, което повишава доверието, особено за чувствителни функции като камера или геолокация.
Изборът между категориите не стои пред разработчика — определя се от системата. Разработчикът само декларира uses-permission, а системата въз основа на protectionLevel определя категорията. Въпреки това стратегията за заявка на опасни разрешения влияе на потребителското изживяване: честите или неподходящи диалози намаляват рейтинга на приложението.
Съвременният начин за заявка на разрешения в Kotlin — използване на ActivityResultContracts.RequestMultiplePermissions или RequestPermission. Тези договори са част от библиотеката androidx.activity и предоставят чист API на базата на ламбда изрази, без необходимост от презаписване на 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
)
}
}
}
Когато приложението се нуждае от няколко опасни разрешения едновременно, използвайте RequestMultiplePermissions. Договорът връща Map<String, Boolean>, където ключът е името на разрешението, а стойността е резултатът. Това е удобно при първо стартиране, когато трябва да се заявят CAMERA и RECORD_AUDIO за видео запис.
Ако потребителят е отхвърлил заявката, методът shouldShowRequestPermissionRationale връща true. Това е сигнал да покажете обяснение защо разрешението е необходимо. Най-добрата практика — показване на персонализиран диалог с обяснение и бутон за Повторен опит. Ако потребителят отново е отхвърлил заявката с отметка Never Ask Again, shouldShowRequestPermissionRationale ще върне false и трябва да пренасочите към Настройки.
Never Ask Again е флаг, който потребителят може да зададе при повторно отхвърляне на runtime диалога. След това стандартният диалогов прозорец вече не се показва за това разрешение. Единственият начин за предоставяне на достъп — пренасочване на потребителя към системните настройки на приложенията.
Разработчикът трябва да различава два сценария на отказ: първи — когато shouldShowRequestPermissionRationale връща true (потребителят е отхвърлил, но диалогът все още може да бъде показан) и втори — когато методът връща false (Never Ask Again е активен или разрешението е блокирано от политика). Във втория случай трябва да се покаже бутон Отваряне на настройки.
fun handlePermissionDenied(permission: String) {
if (ActivityCompat.shouldShowRequestPermissionRationale(
this, permission
)) {
showRationaleDialog(permission)
} else {
showSettingsRedirectDialog(permission)
}
}
private fun showSettingsRedirectDialog(permission: String) {
AlertDialog.Builder(this)
.setTitle("Доступ запрещён")
.setMessage(
"Разрешение заблокировано. Откройте настройки."
)
.setPositiveButton("Настройки") { _, _ ->
val intent = Intent(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts(
"package", packageName, null
)
)
startActivity(intent)
}
.show()
}
Важно е да не заявявате разрешението отново, ако shouldShowRequestPermissionRationale е върнал false. Повторното извикване на requestPermissions в този случай няма да покаже диалог — резултатът ще дойде незабавно с DENIED без обяснение. Потребителят ще се сблъска с неразбираемо поведение, което ще се отрази негативно на изживяването при използване на приложението.
Често задавани въпроси
Към Dangerous Permission спадат разрешенията с ProtectionLevel dangerous: CAMERA, RECORD_AUDIO, ACCESS_FINE_LOCATION, READ_CONTACTS, READ_SMS, READ_CALENDAR и други. Пълният списък е достъпен в класа Manifest.permission.
Използвайте ContextCompat.checkSelfPermission, като предадете контекста и името на разрешението. Методът връща PERMISSION_GRANTED или PERMISSION_DENIED. Проверката трябва да се извършва преди всяко извикване на API, което изисква опасно разрешение.
Permission Group обединява свързани опасни разрешения. Ако потребителят е предоставил едно разрешение от групата, останалите се предоставят автоматично. Например LOCATION включва ACCESS_FINE_LOCATION и ACCESS_COARSE_LOCATION.
Проверете shouldShowRequestPermissionRationale след отказ. Ако методът е върнал false и разрешението все още не е предоставено — Never Ask Again е активиран. Пренасочете потребителя към Настройки чрез Intent с ACTION_APPLICATION_DETAILS_SETTINGS.
Да, те остават задължителни. На Android 13+ някои разрешения се промениха: POST_NOTIFICATIONS стана отделно runtime разрешение, а READ_EXTERNAL_STORAGE беше заменено от READ_MEDIA_IMAGES за точен достъп до медийни файлове.
Резюме
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също