Android R Permission Değişiklikleri
Yıllardır Android’in en büyük problemi hep güvenlik konusu oldu. İzin sorunlarından tutun da malware, ransomware (kötü amaçlı yazılımlar) ve güvenliği ihlal edilmiş ROM’lara kadar birçok problem.. Tabi problemler ortaya çıktıkça her sürümde farklı farklı çözüm yollarına başvuruldu. Android R ile birlikte yine önceden beri varolan birkaç gizlilik ve güvenlik sorunuyla ilgili bazı çözümler geliştirildi.
Genel hatlarıyla baktığımızda izinler konusunda iki temel değişiklik yapılmış gibi görünse de detaylara indiğimiz zaman daha fazlası olduğunu görüyoruz. Yapılan değişiklikler genel olarak kullanıcıların kamera, mikrofon, konum gibi izinleri daha detaylı olarak belirlemelerine olanak sağlıyor. Sistem, kullanılmayan uygulamaların izinlerini otomatik olarak resetliyor. Bunun yanında kullanıcıların system alert penceresini kullanmaları ya da rehberle ilgili bilgileri okumaları durumunda mevcutta bildirdikleri izinleri güncellemeleri gerekiyor.
- Tek seferlik izin verme (One-time Permissions)
- İzinlerin otomatik olarak sıfırlanması (Auto-reset)
1. One-time Permissions:
Android 11'den itibaren uygulamanızın konum, kamera, mikrofon gibi izinleri istemesi durumunda, kullanıcıya gösterdiğiniz permission dialog ekranında “Only this time” seçeneği de yer alır. Kullanıcının bu seçeneği seçmesi durumunda uygulamanıza geçici olarak (yalnızca bir seferlik) izin vermiş olur.
Kullanıcınızın ve uygulamanızın davranışına göre uygulamanız ilgili verilere belli bir süre erişebilir.
Örneğin activity foregrounddayken uygulamanız ilgili verilere erişebilir. Uygulamanız background moda geçtiğinde kısa bir süre verilere erişmeye devam eder. Eğer activityniz visibleken bir foreground service çalıştırmaya başlarsanız ve kullanıcı uygulamanızı backgrounda gönderirse ilgili foreground servis duruncaya kadar dataya erişmeye devam edersiniz. Ancak kullanıcınız one-time permissionı revoke ederse, (örneğin ayarlardan) uygulamanız foreground bir servis başlatmış olsa bile uygulamanızın bu izni kullanımı sona erer.
Kullanıcınız uygulamanızı tekrar açtığında ilgili feature için lokasyon, kamera vs izni tekrar istenir.
Kullanıcınız permission dialog ekranını kapatmak için back tuşuna bastıysa bu bir deny eylemi sayılmıyor. Ancak kullanıcınızı system settings ekranına gönderdiğiniz zaman back tuşuna basarsa bu bir deny eylemi sayılıyor.
Uygulamanız runtime permission best practicelerine uygun olarak yazılmışsa one-time permissions için ayrıca bir değişiklik yapmanıza gerek yoktur.
Runtime permission requestleri nasıl olmalıdır?
Her android uygulaması belli bir güvenli alanda çalışır. Uygulamanızda bu alanın dışına çıkmanızı gerektiren kaynaklara ya da bilgiye ulaşmanız gerekmesi durumunda permission talep etmeniz gerekir.
Bildiğimiz gibi eğer uygulamamız dangerous diye sınıflandırılan izinlerden birini istiyorsa Android 6.0 (API Level 23) ve üzeri bir cihaza yüklendiğinde bu dangerous izinleri runtime permission olarak istediğimiz bir akış çizmeliyiz. Herhangi bir dangerous izin istemiyorsanız bahsedeceğim adımların yapılmasına gerek yok.
İzinlerin protection level bilgilerine buradan ulaşabilirsiniz.
Temel Prensipler
Runtime permission isterken UX açısından dikkat etmemiz gereken temel prensiplerden bahsedelim.
- İzni tam olarak kullanıcının ihtiyacı olduğu anda istemeliyiz. Normal kullanım akışını bozacak ve kullanımı engelleyecek bir durum oluşturmamalıyız.
- İstemiş olduğunuz izin uygulamanızdaki bir özelliğin kullanımı için gerekliyse kullanıcınızın bu izni reddetmesi durumunda ilgili özelliği devre dışı bırakabilir ve uygulamanızı kullanmaya devam etmesini sağlayabilirsiniz.
- Herhangi bir sistem davranışı için varsayımda bulunmamalısınız. Örneğin aynı izin grubunda olmayan izinlerin gruplandığını varsaymamalısınız. Bu grupların amacı birbirine çok yakın olan izinlerin kullanıcıya aynı dialog içinde gösterilerek kullanıcıya sunulan dialog kutusu kalabalığını önlemektir.
Permission Request Akışı
Öncelikle uygulamanızda isteyeceğiniz izinlerin runtime permission gerektirip gerektirmediğine bir bakmalısınız. Örneğin fotoğraf çekme, medya oynatma, reklam görüntüleme gibi birçok kullanım durumu herhangi bir izin bildirimi gerektirmez.
Runtime permission istemenizi gerektiren durumlar mevcutsa aşağıdaki adımları tamamlamalısınız:
- Öncelikle manifest dosyanızda talep etmeniz gereken izinleri belirtmelisiniz.
- Uygulamanızın iyi bir user experience sağlaması için hangi noktalarda izin istemesi gerektiğine karar vermelisiniz.
- İzin isterken ilgili izni ne için kullanacağınıza dair açıklama yapmayı es geçmemelisiniz.
- Kullanıcınızın daha önce uygulamanıza bu izni verip vermediğini kontrol edin. Bu izni gerektirecek eylemi her yapmaya çalıştığınızda bunu kontrol etmelisiniz.
- Uygulamanızın kullanıcınıza neden bu izni vermesi gerektiğini bildiren dialogu göstermesine gerek olup olmadığını kontrol edin. (show rationale) Eğer sistem rationale göstermenizin gerekli olmadığına dair bilgi döndürüyorsa bu açıklamayı göstermeden bir sonraki adıma geçebilirsiniz. Ancak gösterilmesi gerektiğine dair bir cevap dönerse kullanıcıya bir diyalogla bu izni neden istediğinize dair açıklama sunmalısınız. Kullanıcı kendisine açıklama yapıldıktan sonra isteğinizi kabul ederse bir sonraki adıma geçebilirsiniz.
- Kullanıcınıza runtime permission isteği yapın. Sistem runtime permission request diyalogu gösterdikten sonra kullanıcınızın izin verip vermediğini kontrol edin.
- Eğer kullanıcınız izin verdiyse ilgili veri ya da kaynaklara ulaşarak ilgili özelliği kullanmasını sağlayabilirsiniz. İzin vermemesi durumunda bu özellik olmaksızın kullanıcınızın uygulamanızı kullanmaya devam edebileceği bir akış çizmeniz gerekir.
Aşağıda akışı çok güzel özetleyen bir diyagram bulunuyor.
Kullanıcınızın uygulamanıza izin verip vermediğini kontrol etmek için ContextCompat.checkSelfPermission() metodundan yararlanabilirsiniz. Bu metod izin verip vermemesi durumuna bağlı olarak PERMISSION_GRANTED veya PERMISSION_DENIED döndürür.
Uygulamanızın neden bu izne ihtiyacı olduğunu açıklayın. Bunun için öncelikle ContextCompat.checkSelfPermission() PERMISSION_DENIED döndürürse shouldShowRequestPermissionRationale() metodunu çağırmalıyız. Bu metod true döndürürse kullanıcıya açıklama metni göstermelisiniz.
Permission Request:
Genel olarak izin isteme aşamasını kendiniz de yönetebilirsiniz. Ancak ActivityResultContracts içerisinde gelen RequestPermission contract işleri oldukça kolaylaştırıyor.
- Request Permission ile yönetme:
App dizinindeki gradle dosyanızda dependency scope’u altına androidx.activity librarysini eklemelisiniz.
androidx.activity:activity-ktx:1.2.0-rc01
Tek bir izin isteyecekseniz RequestPermission
, aynı anda birden fazla izin isteyecekseniz RequestMultiplePermissions
kullanabilirsiniz. Her ikisinin de kullanımı hemen hemen aynı.
Activity ya da Fragment’ınızın init aşamasında registerForActivityResult()
çağırarak ActivityResultCallback
implementasyonunu yapıyoruz. ActivityResultCallback
uygulamanızın kullanıcınızın izin isteğine verdiği yanıtı yönetmenize yarar.
registerForActivityResult()
dan dönen ActivityResultLauncher
tipindeki değeri bir değişkende tutalım.
Bu değişkenin launch metodunu call ederek ihtiyaç olduğunda system permission diyalogunun gösterilmesini sağlıyoruz. Kullanıcınız seçim yaptıktan sonra sistem asenkron olarak daha önce implemente ettiğimiz ActivityResultCallback
sürecini başlatıyor.
launch’ı call ettiğinizde gelen diyalogu customize edemezsiniz. Ancak kullanıcıya ilgili izini neden istediğinize dair yaptığınız açıklamayı biraz daha detaylı sunabilirsiniz.
Permission isteğinize karşılık dönen cevabı handle etmenize yarayan kod parçası:
Aşağıda ise iznin verilip verilmediğini kontrol etmek ve gerekiyorsa izin istemek için kullanılması tavsiye edilen kod parçasını görüyoruz.
2. Permission request kodunu custom olarak yönetme:
requestPermissions()
kullanarak permission kodunu kendiniz yönetebilirsiniz.
Aşağıda bir request kodu tanımlamayarak permission requestini nasıl yapabileceğimizi görebilirsiniz.
Kullanıcı izin diyaloguna bir yanıt verdikten sonra, sistem bu defa onRequestPermissionsResult()
implementasyonunu başlatır. Request kodunu ve kullanıcının verdiği yanıtı geri döndürür.
İznin reddedilmesi sürecini yönetme:
Kullanıcı bir izni reddettiği zaman uygulamanızdaki ilgili feature’ı bu izin olmadan kullanamayacağı konusunda onu bilgilendirmeniz gerekir.
Best practice için spesifik olarak hangi feature’ı kullanamayacağını anlatmalı ve izin vermemesi halinde UI’ı bloke etmemeli uygulamayı normal seyrinde kullanabilmesine izin vermelisiniz.
Bu noktada Android 11 ile gelen bir diğer değişiklik ise, kullanıcınızın uygulamanızı kullandığı süre içerisinde izin diyalogunda bir defadan fazla reddet butonunu tıklamasıyla birlikte sistem bundan sonra izin istediğiniz zaman diyalog göstermez. Daha önceki versiyonlardaki “Bir daha gösterme” seçeneğini seçili hale getirdiğimiz davranışa sahip olur.
2. Kullanılmayan uygulamalarda otomatik olarak izinleri resetleme:
Eğer uygulamanız Android 11 ve sonrasına destek veriyorsa kullanıcınızın daha önceden verdiği izinler birkaç ay uygulamanızı kullanmaması durumunda otomatik olarak resetleniyor. Bu kullanıcınızın settings sayfası üzerinden direk olarak uygulamanızla ilgili izinleri reddetmesiyle aynı davranışa sahip. Uygulamanız runtime permission için yukarda söz edilen best practiceleri takip ediyorsa herhangi bir değişiklik yapmanıza gerek yok.
Kullanıcınızdan auto-reset özelliğini disable etmesini isteyebilirsiniz.
Kullanıcılarınız uygulamanızla etkileşimde bulunmadığı anlarda bile uygulamanızın arka planda çalışmasının beklendiği bazı senaryolar olabilir.
- Family safety uygulamaları
- Data sync etmeniz gereken uygulamalar
- Smart devicelarla iletişim kurmaya yarayan uygulamalar
- Cihazlarla eşleştirme yapılmasını sağlayan uygulamalar
Intent.ACTION_AUTO_REVOKE_PERMISSIONS
ile bir intent başlatarak kullanıcınızı uygulamanızın system settings altındaki sayfasına yönlendirerek auto-reset ayarını disable ettirebilirsiniz.
Bunu yapmak için Permission -> App Permissions ekranına girdiğimizde karşımıza çıkan “Remove permissions if app isn’t used” optionının seçili olmaması gerekiyor.
Peki uygulamamızda biz bu optionın seçili olup olmadığını nasıl kontrol edeceğiz. isAutoRevokeWhitelisted()
metodunu call ederek true dönmesi durumunda auto-reset’in disable olduğunu anlıyoruz.
Gerekli olduğu durumlarda Default Handler request:
Android, telefon görüşmeleri yapma, SMS mesajları gönderme gibi temel kullanım durumları için varsayılan işleyiciler (default handler) içerir. Bazı uygulamaların sundukları hizmete bağlı olarak arama kayıtları ve SMS mesajlarıyla ilgili hassas kullanıcı bilgilerine erişmek gibi izinlerde bulunması gerekebiliyor. Kullanıcılara böyle hassas bilgiler içeren izin grupları üzerinde daha fazla kontrol olanağı vermek adına bazı kısıtlamalar getirilmiştir.
Bu tarz bir uygulamayı Play Store’da yayınlamak istiyorsanız runtime permission istemeden önce kullanıcınızdan uygulamanızı core sistem için Default handler olarak ayarlamasını istemelisiniz. Aksi takdirde uygulamanızı yayınlamaya çalıştığınız zaman exception case olarak bir hata alacaksınız ve uygulamanız release olmayacak.
Örneğin aramalarla ilişkili izinlere erişmek için kullanıcınızın uygulamanızı default Phone ya da Assistant handler olarak ayarlaması gerekir. (Bakınız Restricted permissions)
Default handler görüntüleme ve değiştirme:
Android’deki settings uygulamasında hangi uygulamaların device’ın core functionlarını gerçekleştirmek için varsayılan olarak ayarlandığını bize gösteren bir ekran mevcuttur. Bu ekran üzerinden kullanıcı başka bir appi seçerek değişiklik yapabilir.
Default handler için gereksinimler:
- Uygulamanızın varsayılan işleyici olarak tanımlandığı fonksiyonu gerçekleştirebilmesi gerekir. Örneğin, varsayılan bir SMS işleyicisi ise metin mesajları gönderebilmesi gerekir.
- Uygulamanızın gizlilik politikası bulunmalıdır.
- Uygulamanızın store’daki açıklamasında temel işlevi net olarak belirtilmelidir.
- Uygulamanız, söz konusu handlerla ilişkili izinleri istemeden önce default handler olmayı istemelidir. Örneğin, bir uygulama READ_SMS iznini istemeden önce default SMS handler olmayı istemelidir.
Uygulamanız bütün bu gereksinimleri sağlıyorsa aşağıdaki gibi bir dialog ile kullancınıza default handler için istekte bulunabilirsiniz.
val setSmsAppIntent = Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT)
setSmsAppIntent.putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME, packageName)
startActivityForResult(setSmsAppIntent, your-result-code)
Not: Uygulamanızın öncelikle default handler prompt ile kullanıcıdan bu istek için onay alması ve sonrasında READ_SMS gibi izinleri istemesi gerekir. Burdaki sıraya dikkat etmelisiniz.
System alert window için gelen değişiklikler:
Bazı uygulamalara otomatik olarak System Alert Window izni verilir. Örneğin:
ROLE_CALL_SCREENING rolüne sahip olan ve SYSTEM_ALERT_WINDOW requesti yapan herhangi bir uygulamaya otomatik olarak izin verilir. Uygulama ROLE_CALL_SCREENING’i kaybederse, izni kaybeder.
Bir MediaProjection aracılığıyla ekran kaydeden ve SYSTEM_ALERT_WINDOW requesti yapan herhangi bir uygulamaya, kullanıcı reddetmedikçe, otomatik olarak izin verilir. Uygulama ekranı kaydetmeyi durdurduğunda, izni kaybeder. Bu usecase öncelikle game livestreaming uygulamaları için tasarlanmıştır.
Bu uygulamaların SYSTEM_ALERT_WINDOW iznini almak için ACTION_MANAGE_OVERLAY_PERMISSION göndermesine gerek yoktur, doğrudan SYSTEM_ALERT_WINDOW talep edebilirler.
MANAGE_OVERLAY_PERMISSION intentleri kullanıcıyı her zaman system permissions ekranına getirir
Android 11'le birlikte ACTION_MANAGE_OVERLAY_PERMISSION
kullanıcıları appler için SYSTEM_ALERT_WINDOW iznini verebilecekleri ya da reddedebilecekleri setting sayfasının ilk ekranına yönlendirir. Intent içerisindeki herhangi bir package: datası ignore edilir.
Android 11'den önceki sürümlerde bu intent, içerisinde package datası belirtilerek kullanıcıyı direk belirtilen appin permission sayfasına yönlendiriyordu. Android 11'le birlikte bu özellik kaldırıldı. Kullanıcının manuel olarak settings sayfası içerisinde ilgili appi seçerek ona ait olan permission ekranına gitmesi gerekiyor.
Phone numbers:
Android 11 kullanıcının telefon numarasını okumaya yarayan phone-related izinlerle ilgili değişiklikler de içeriyor.
Android 11 ve sonrası için artık telefon numarasına erişmek için READ_PHONE_STATE
yerine phone number API kullanarak READ_PHONE_NUMBERS
requesti yapmak gerekiyor.
getLine1Number()
metodu hemTelephonyManager
hem deTelecomManager
classı içerisinde mevcut.- Unsupported getMsisdn() metodu ise
TelephonyManager
classı içerisinde.
Uygulamanız bunlar dışındaki metodları call ediyorsa tüm android versiyonları için READ_PHONE_STATE
kullanmaya devam edebilirsiniz.
READ_PHONE_STATE
iznini yalnızca yukarıdaki metodları kullanmak için istiyorsanız manifest dosyanızı aşağıdaki gibi update etmelisiniz:
READ_PHONE_STATE
permissionı eklediğiniz satıra yalnızca Android 10 (API 29) ve altı için bu izni istediğinizi belirtikten sonra READ_PHONE_NUMBERS
iznini de aşağıdaki gibi permission olarak eklemelisiniz.
<manifest>
<!-- Grants the READ_PHONE_STATE permission only on devices that run
Android 10 (API level 29) and lower. -->
<uses-permission android:name="android.permission.READ_PHONE_STATE"
android:maxSdkVersion="29" />
<uses-permission android:name="android.permission.READ_PHONE_NUMBERS" />
</manifest>
Android 11'le gelen gelişmelerle ile ilgili aşağıdaki videoya da göz atabilirsiniz.
Henüz ActivityResultContracts.RequestPermission
kullanmadıysanız hemen bir göz atabilirsiniz. Permission flowunu çizerken bizim için işleri kolaylaştırıyor.
Android 11'le birlikte gelen Data access auditing ise bir başka yazının konusu olsun:)