AndroidManifest Permissions to deklaracje uprawnień w pliku AndroidManifest.xml, które określają, do jakich zasobów systemowych i danych ma dostęp aplikacja. Android wymaga określenia każdego uprawnienia w manifeście przed użyciem odpowiedniego API: od aparatu i geolokalizacji po wysyłanie SMS i dostęp do kontaktów. Według Android Developer Documentation każde uprawnienie dzieli się na jeden z czterech poziomów ochrony: normal, dangerous, signature i special.
Najważniejsze
AndroidManifest Permissions to mechanizm bezpieczeństwa Android, który kontroluje dostęp aplikacji do chronionych danych i funkcji systemowych. Każda aplikacja musi deklarować wymagane uprawnienia w pliku AndroidManifest.xml za pomocą elementu <uses-permission>. Bez deklaracji wywołanie odpowiedniego API zakończy się błędem bezpieczeństwa SecurityException.
Model uprawnień Android przeszedł kilka etapów ewolucji. Przed Android 6.0 (API 23) wszystkie uprawnienia były przyznawane podczas instalacji — użytkownik widział pełną listę i zgadzał się lub rezygnował z instalacji aplikacji. Od Android 6.0 uprawnienia poziomu dangerous są żądane w czasie wykonywania (Runtime Permissions), co daje użytkownikowi bardziej elastyczną kontrolę.
Uprawnienia dzielą się na cztery poziomy ochrony: normal (automatycznie przyznawane podczas instalacji), dangerous (wymagają żądania w czasie wykonywania), signature (dostępne tylko dla aplikacji podpisanych tym samym certyfikatem) i special (wymagają osobnego włączenia w ustawieniach). Każdy poziom ma swój mechanizm przyznawania i odwoływania.
Według Google I/O 2024, w Android 15 planowane jest wprowadzenie bardziej szczegółowych uprawnień — użytkownik będzie mógł udostępniać dostęp tylko do określonych plików w bibliotece medialnej, a nie do całej biblioteki. Kontynuuje to trend Androida do minimalizacji zakresu udostępnianych danych domyślnie.
W przeciwieństwie do iOS, gdzie wszystkie uprawnienia są żądane w czasie wykonywania (runtime), Android dzieli uprawnienia na instalacyjne (install-time) i wykonawcze (runtime). Poziom normal jest przyznawany automatycznie podczas instalacji bez powiadamiania użytkownika. Poziom dangerous wymaga jawnego okna dialogowego, podobnie jak w iOS.
Kolejna różnica: w Android uprawnienia są połączone w grupy (permission groups). Jeśli użytkownik zgodził się na dostęp do aparatu, aplikacja automatycznie otrzymuje dostęp do mikrofonu — są w tej samej grupie MICROPHONE. W iOS każde uprawnienie jest żądane osobno, niezależnie od grupy.
| Wersja Androida | Zmiana w modelu uprawnień |
|---|---|
| Android 1.0–5.x | Wszystkie uprawnienia przyznawane podczas instalacji (install-time) |
| Android 6.0 (API 23) | Wprowadzenie Runtime Permissions dla poziomu dangerous |
| Android 10 (API 29) | Scoped Storage — ograniczony dostęp do systemu plików |
| Android 11 (API 30) | Auto-reset uprawnień — nieużywane uprawnienia są resetowane |
| Android 14 (API 34) | Runtime uprawnienia dla dostępu do multimediów (zdjęcia, wideo, audio) |
Android określa cztery poziomy ochrony (protection levels) dla uprawnień, każdy z własnymi zasadami przyznawania. Omówmy szczegółowo każdy poziom.
Normal uprawnienia są przyznawane automatycznie podczas instalacji aplikacji bez powiadamiania lub żądania od użytkownika. Obejmują dostęp do funkcji niskiego ryzyka, które nie zagrażają prywatności użytkownika: INTERNET, ACCESS_NETWORK_STATE, VIBRATE, BLUETOOTH. Użytkownik nie widzi okna dialogowego zgody — uprawnienie jest uważane za przyznane z chwilą instalacji.
Deweloper nie musi obsługiwać żądania w kodzie dla normal uprawnień — wystarczy zadeklarować je w manifeście. Jednak w Android 12+ podczas instalacji z Google Play użytkownik widzi zakładkę „Uprawnienia” z listą wszystkich normal uprawnień, co zwiększa przejrzystość. Według Statista (2024) ponad 90% aplikacji w Google Play używa INTERNET jako najczęstszego normal uprawnienia.
Dangerous uprawnienia obejmują dostęp do danych i funkcji, które mogą naruszyć prywatność: aparat, mikrofon, geolokalizacja, kontakty, SMS, telefon, kalendarz, czujniki ciała. Uprawnienia te wymagają dwuetapowego mechanizmu: deklaracji w manifeście + żądania w czasie wykonywania przez ActivityCompat.requestPermissions().
Użytkownik może odmówić przyznania dangerous uprawnienia, a aplikacja musi poprawnie obsłużyć ten scenariusz. W Android 11+ jeśli użytkownik dwukrotnie odmówił, kolejne żądania nie pokazują systemowego okna dialogowego — system automatycznie zwraca DENIED. W takim przypadku należy skierować użytkownika do ustawień.
Poziom signature — uprawnienie jest przyznawane automatycznie, jeśli aplikacja jest podpisana tym samym certyfikatem co system lub inna aplikacja, która zdefiniowała uprawnienie. Używane w aplikacjach systemowych i korporacyjnych. Przykład: BIND_ACCESSIBILITY_SERVICE — dostępne tylko dla aplikacji systemowych.
Poziom special (SYSTEM_ALERT_WINDOW, WRITE_SETTINGS, REQUEST_INSTALL_PACKAGES, MANAGE_EXTERNAL_STORAGE) — wymaga osobnego działania użytkownika przez ustawienia systemowe. Aplikacja może otworzyć stronę ustawień za pomocą Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION). Google Play ogranicza użycie special uprawnień i wymaga uzasadnienia w formularzu podczas publikacji.
Od Android 6.0 wszystkie dangerous uprawnienia wymagają żądania w czasie wykonywania. Omówmy pełny cykl pracy z runtime permissions w Kotlin.
Przed wywołaniem API wymagającego dangerous uprawnienia zawsze sprawdzaj aktualny status przez ContextCompat.checkSelfPermission(). Jeśli status to PERMISSION_GRANTED — można wywołać API. Jeśli PERMISSION_DENIED — należy żądać uprawnienia przez ActivityResultContract RequestPermission (AndroidX) lub przestarzałe requestPermissions().
Zaleca się używanie ActivityResultContracts.RequestMultiplePermissions do żądania wielu uprawnień jednocześnie. Google zaleca grupowanie powiązanych uprawnień (np. aparat + mikrofon do nagrywania wideo) w jednym oknie dialogowym, aby użytkownik widział pełny kontekst żądania.
import android.Manifest
import android.content.pm.PackageManager
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
class CameraActivity : AppCompatActivity() {
private val requestPermissionLauncher =
registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted: Boolean ->
if (isGranted) {
openCamera()
} else {
explainWhyPermissionNeeded()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
checkCameraPermission()
}
private fun checkCameraPermission() {
when {
ContextCompat.checkSelfPermission(this,
Manifest.permission.CAMERA
) == PackageManager.PERMISSION_GRANTED -> {
openCamera()
}
shouldShowRequestPermissionRationale(
Manifest.permission.CAMERA
) -> {
showRationale()
}
else -> {
requestPermissionLauncher.launch(
Manifest.permission.CAMERA
)
}
}
}
}
Jeśli użytkownik dwukrotnie odmówił uprawnienia, Android przełącza żądanie w stan „Never ask again”. W takim przypadku shouldShowRequestPermissionRationale() zwraca false, a systemowe okno dialogowe nie zostanie pokazane. Aplikacja musi skierować użytkownika do ustawień systemowych przez Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).
Ważne: nie pokazuj okna dialogowego z propozycją otwarcia ustawień zaraz po pierwszej odmowie — jest to odbierane jako agresywne zachowanie. Użyj shouldShowRequestPermissionRationale() do określenia, czy należy pokazać wyjaśnienie. Material Design Guidelines zalecają pokazanie ekranu z wyjaśnieniem wartości dostępu, a nie tylko przycisku „Otwórz ustawienia”.
private fun openAppSettings() {
Intent(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts("package", packageName, null)
).also { intent ->
startActivity(intent)
}
}
private fun showPermissionSettings() {
AlertDialog.Builder(this)
.setTitle("Dostęp do kamery")
.setMessage("Zezwól na dostęp do kamery w Ustawieniach, "
+ "aby robić zdjęcia profilowe")
.setPositiveButton("Otwórz ustawienia") { _, _ ->
openAppSettings()
}
.setNegativeButton("Anuluj", null)
.show()
}
Plik AndroidManifest.xml zawiera element <uses-permission> dla każdego uprawnienia używanego przez aplikację. Uprawnienia są deklarowane na poziomie <manifest> przed elementem <application>.
Każde uprawnienie jest deklarowane osobnym elementem <uses-permission> z atrybutem android:name wskazującym pełną nazwę uprawnienia. Dla uprawnień, które pojawiły się w określonych wersjach Androida, użyj atrybutu maxSdkVersion, aby ograniczyć deklarację tylko do potrzebnych wersji — poprawia to kompatybilność.
Na przykład uprawnienie WRITE_EXTERNAL_STORAGE nie jest potrzebne na Android 10+ (Scoped Storage), więc określ maxSdkVersion=„28” (Android 9). Zapobiega to niepotrzebnym pytaniom od użytkowników na nowszych wersjach. Android Studio ostrzega o zalecanych maxSdkVersion przez Lint.
<!-- AndroidManifest.xml -->
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Normal permissions (install-time) -->
<uses-permission
android:name="android.permission.INTERNET" />
<uses-permission
android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- Dangerous permissions (runtime) -->
<uses-permission
android:name="android.permission.CAMERA" />
<uses-permission
android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- Legacy storage permission limited to API 28 and below -->
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<application>
<!-- ... -->
</application>
</manifest>
Element <uses-feature> określa, że aplikacja wymaga określonego sprzętu (aparat, GPS, NFC). Atrybut android:required=„false” umożliwia instalację aplikacji na urządzeniach bez tego sprzętu — sprawdzenie dostępności odbywa się w kodzie. Jeśli required=„true”, Google Play filtruje aplikację i staje się ona niedostępna dla nieodpowiednich urządzeń.
Zaleca się dla wszystkich funkcji sprzętowych określanie required=„false” i programowe sprawdzanie dostępności przez PackageManager.hasSystemFeature(). Zwiększa to grono odbiorców aplikacji. Jedynym wyjątkiem jest sytuacja, gdy funkcja jest krytyczna dla działania aplikacji (aplikacja do zamawiania taksówek bez GPS nie ma sensu).
Prawidłowa praca z uprawnieniami to kluczowy aspekt jakości aplikacji Android. Omówmy główne zalecenia i typowe błędy.
Żądaj tylko tych uprawnień, które są rzeczywiście niezbędne do działania aplikacji. Każde dodatkowe uprawnienie zmniejsza konwersję instalacji i zwiększa liczbę odmów. Google Play Console pokazuje, ilu użytkowników zrezygnowało z instalacji z powodu zestawu uprawnień. Według AppBrain (2024) aplikacje z 10+ dangerous uprawnieniami mają o 35% mniej instalacji.
Regularnie przeglądaj listę uprawnień. Usuwaj nieużywane, szczególnie przy przejściu na nowsze wersje Androida, gdzie niektóre uprawnienia stały się nieobowiązkowe. Na przykład z pojawieniem się selektora zdjęć (ActivityResultContracts.PickVisualMedia) w Android 13+ dostęp do biblioteki medialnej można uzyskać bez dangerous uprawnienia READ_MEDIA_IMAGES.
Przed żądaniem dangerous uprawnienia pokaż użytkownikowi ekran z wyjaśnieniem, dlaczego to uprawnienie jest potrzebne i jaką wartość daje. Material Design zaleca użycie bottom sheet lub okna dialogowego z ikoną, krótkim tekstem i przyciskiem „Kontynuuj”. Rationale zwiększa zgodę o 20–30% w porównaniu z bezpośrednim żądaniem.
Sprawdzaj shouldShowRequestPermissionRationale() przed wywołaniem launch(). Jeśli true — pokaż rationale. Jeśli false — albo uprawnienie już zostało przyznane, albo użytkownik odmówił na stałe (never ask again). W tym ostatnim przypadku pokaż przycisk „Otwórz ustawienia”, a nie powtarzaj żądania.
Przetestuj wszystkie możliwe scenariusze: przyznanie uprawnienia, odmowa, odmowa na stałe, cofnięcie uprawnienia w ustawieniach, reset uprawnień (Android 11+ auto-reset). Każdy scenariusz powinien być obsługiwany bez crasha i bez utraty danych. Android Testing Guide zaleca użycie biblioteki TestPermission do automatyzacji testów.
Szczególną uwagę poświęć scenariuszowi, gdy użytkownik cofnął uprawnienie podczas działania aplikacji (zminimalizowana aplikacja → Ustawienia → cofnięcie). Po powrocie do aplikacji sprawdź wszystkie uprawnienia ponownie przez onResume(). Nie polegaj na buforowaniu statusu uprawnień — użytkownik może je zmienić w każdej chwili.
Często zadawane pytania
Tak, jeśli SDK dołącza do swojego manifestu uprawnienie, zostaje ono połączone z manifestem aplikacji podczas budowania. Możesz wyłączyć niepotrzebne uprawnienie SDK za pomocą tools:node=„remove” w AndroidManifest.xml.
Wywołanie API bez uprawnienia spowoduje SecurityException, co doprowadzi do crasha aplikacji. Zawsze sprawdzaj status uprawnienia przed użyciem odpowiedniego API i obsługuj odmowę poprawnie.
W ustawieniach urządzenia: Ustawienia → Aplikacje → [twoja aplikacja] → Uprawnienia. Do zresetowania wszystkich uprawnień użyj komendy adb: adb shell pm reset-permissions.
Tak, za pomocą ActivityResultLauncher we Fragment lub Service. Jednak okno dialogowe żądania zawsze wymaga kontekstu UI Activity. Dla Service możesz pokazać Notification z Intent otwierającym Activity z żądaniem.
Na przykład WRITE_EXTERNAL_STORAGE nie jest potrzebny na Android 10+ (Scoped Storage). Określając android:maxSdkVersion=„28”, wykluczasz deklarację uprawnienia na nowszych wersjach, co poprawia kompatybilność i zmniejsza listę żądanych uprawnień.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również