Android Runtime İzin Yapısı

Bu yazı Android Marshmallow ile değişen runtime izin yapısı ve bu yapıya nasıl geçileceği ile ilgilidir.

Google Play yeni uygulamaların 1 Ağustos 2018’den, uygulama güncellemelerinin de 1 Kasım 2018’den itibaren Android 8.0 (API level 26) target’inde olmasını gereksiniyor.

Google bu bildirim ile uygulama geliştiricilerin önceliklerini değiştirdi. Şimdi çok sayıda uygulamanın güncellenmesi ve bununla birlikte izin yapılarının da değişmesi gerekiyor. Bu yazı geliştiricilere bu değişiklikleri nasıl yapacakları konusunda yol göstermesi amacındadır.

Yeni Runtime Yapısı vs. Eski Yapı

Android uygulama izinlerini yönetiminde API Level 23 ile birlikte yeni bir yönteme geçildi. Eski modelde bütün izinler uygulama yüklenirken alınırken Marshmallow’dan sonra normal seviyede olmayan izinler runtime sırasında kullanıcıdan istenecek.

İzinler dört seviyeye ayrıldı :

  1. Normal
  2. Signature
  3. Dangerous
  4. Special

Şimdi normal ve dangerous izin seviyelerine bakacağız. Eğer diğer izin seviyeleri olan Special ve Signature ile ilgili bilgi almak isterseniz Bonus Bölüm ‘e atlayabilirsiniz.

Normal izinlerin kullanıcı gizliliğine veya diğer uygulamaların işleyişine neredeyse etkileri yoktur. Örneğin, Internete bağlanmak için veya alarmları kurak için alınan izinler bu gruba girer.

Bu seviyedeki izinler uygulama kurulumu sırasında otomatik olarak verilir ve kullanıcıya ek bir ekran çıkarılmaz.

Normal izinlerin tam listesine Android’in sitesinden bakabiliriniz.

Dangerous izinler ise kullanıcının kişisel bilgilerini de içeren data kaçaklarına neden olabilir, kullanıcının kayıtlı bilgilerini değiştirebilir veya diğer programların işleyişini etkileyebilir. Örneğin, kamerayı açma ve fotoğraf çekme, sms gönderme, ses kaydetme yetkisi gibi.

Bu izinlerin kullanılabilmesi için uygulamanın runtime’ı sırasında kullanıcı ekrana çıkarılan izin promptundan veya sistem ayarlarından uygulamaya yetki verilmelidir. Ancak dangerous izinler sonradan kapatılabilirler.

Dangerous izinlerin tam listesine Android’in sitesinden bakabiliriniz.

Android Runtime’ında İzin İsteme

İzin modelini uygulamak için bu dört fonksiyondan yararlanılır :

  • ContextCompat.checkSelfPermission() : iznin alınıp alınmadığının kontrolü için.
  • ActivityCompat.requestPermissions() : izin istemek için. Birden çok izin birden çok izin grubu içerisinde istenebilir.
  • ActivityCompat.shouldShowRequestPermissionRationale() : iznin neden istendiğini belirten mesajın gösterilip gösterilmemesine karar vermek için.
  • onRequestPermissionsResult() : kullanıcının izin istemeye olan cevabını işlemek için.

Örnek olarak arama kayıtlarında değişiklik yapabilen izinleri ele alalım. Bu izinler dangerous seviyesinde olduğundan kullanmadan önce kontrol edilmesi gerekmektedir.

    private val REQUEST_CODE = 1234
    ...
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CALL_LOG) == PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_CALL_LOG) == PackageManager.PERMISSION_GRANTED) {
        // İzinler daha önceden alınmış. İşleyiş olması gerektiği gibi devam edebilir.
    } else {
        // İzinlere yetki verilmemiş; istenmesi gerekli.
        ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_CALL_LOG, Manifest.permission.WRITE_CALL_LOG), REQUEST_CODE)
    }
    
    private static final int REQUEST_CODE = 1234;
    ...
    if(ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CALL_LOG) == PackageManager.PERMISSION_GRANTED &&
                ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_CALL_LOG) == PackageManager.PERMISSION_GRANTED) {
        // İzinler daha önceden alınmış. İşleyiş olması gerektiği gibi devam edebilir.
    } else {
        // İzinlere yetki verilmemiş; istenmesi gerekli.
        ActivityCompat.requestPermissions(this, new String[] { Manifest.permission.READ_CALL_LOG, Manifest.permission.WRITE_CALL_LOG }, REQUEST_CODE);
    }

Kod bloğundaki REQUEST_CODE parametresi bu isteğin belirleyicidir. onRequestPermissionsResult fonksiyonunda yapılmış olunan aksiyonu belirlemek için kullanılır.

Bu aşamada izinler daha önceden alınmadıysa kullanıcıya prompt gösterilir. Yukarıdaki kod bloğunda iki ayrı izin istenmesine rağmen “make and manage phone calls” yazısıyla sadece bir tane prompt gösterilir. Bunun nedeni ise dangerous izinlerin kendi içerisinde gruplanmasıdır.

Dangerous izin gruplarını Android’in websitesinden görebilirsiniz.

Gruplama şu açılardan yardımcıdır :

  • Uygulamanın o izin grubunda almış olduğu bir izni yoksa sistem, kullanıcıya uygulamanın erişim istediği izin grubunu açıklayan bir prompt gösterir.
  • Uygulama aynı dangerous izin grubundan almış olduğu bir izni varsa sistem kullanıcıyla bir etkileşime geçmeden anında o izni verir.

Aynı izinler ikinci kere istendiğinde ise promptun içerisinde daha önce çıkmayan “Tekrar sorma” radiosu belirir.

Kullanıcının karar vermek için üç seçeneği bulunur :

  1. İzin ver – İzni verir.
  2. Reddet – İzni reddeder ancak tekrar istek yapılabilir.
  3. Tekrar sorma – İzni redderken tekrar istek yapılmasını da engeller.

Herhangi bir seçenek seçildiği takdirde prompt kaybolur ve onRequestPermissionsResult() fonksiyonu çağırılır.

override fun onRequestPermissionsResult(requestCode: Int, @NonNull permissions: Array<String>, @NonNull grantResults: IntArray) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)

    when (requestCode) {
        REQUEST_CODE -> {

            if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // İzinler alındı. İşleyiş olması gerektiği gibi devam edebilir.
            } else if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_CALL_LOG)) {
                // İzin reddedildi.
                // Kullanıcı "Tekrar sorma" seçeneğini seçti.
            } else {
                // İzin reddedildi.
                // Kullanıcı "Reddet" seçeneğini seçti.
                // Uygulama daha sonra tekrar izin isteyebilir.
            }

            // Her bir izin için kontrol yapmayı unutmayın.
        }
    }
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);

    switch (requestCode) {
        case REQUEST_CODE: {

            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // İzinler alındı. İşleyiş olması gerektiği gibi devam edebilir.
            } else if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_CALL_LOG)) {
                // İzin reddedildi.
                // Kullanıcı "Tekrar sorma" seçeneğini seçti.
            } else {
                // İzin reddedildi.
                // Kullanıcı "Reddet" seçeneğini seçti.
                // Uygulama daha sonra tekrar izin isteyebilir.
            }
            
            // Her bir izin için kontrol yapmayı unutmayın.
        }
    }
}

Bu kod bloğunda shouldShowRequestPermissionRationale() metodu kullanıcının reddediş seçeneğinin belirlenmesine yardımcı olur. Eğer “Tekrar sorma” seçeneği seçildiyse uygulamada kullanıcıya iznin neden istendiğini açıkça anlatılmalıdır.

Aşağıda bütün işleyişin diagramını bulabilirsiniz.


Bonus Bölüm

Google tarafından Special izin grubu aynı zamanda Signature olarak kabul edildiğinden bu iki grup birlikte değerlendirilmelidir.

Signature izinler

Signature izinler aynı normal izinler gibi uygulama yüklenmesi sırasında otomatik olarak verilir. Buradaki önemli fark her uygulamanın bu tip izinleri alamamasıdır. Ancak uygulama ve izin aynı sertifika ile imzalanmış ise uygulama izni alabilir.

Special izinler

Bazı signature izinler dangerous izinlerin de üzerindedir ve tüm telefonun işleyişine etki edebilirler. Bu izinler nadiren kullanılır. Uygulamalar izin isteğinde bulunamazlar fakat kullanıcıyı sistem ayarlarından manual bir şekilde izin vermesi için yönlendirebilirler.

En çok kullanılan iki special izin:

SYSTEM_ALERT_WINDOW

Bu izin uygulamanın “üstte görünebilen uygulamalar” arasında yer almasına izin verir; Facebook’un chatheadleri ve Turkcell UpCall’un ekran üzerine çıkardığı arama kartları gibi. Eğer uygulamanın bu izine ihtiyacı varsa normal istek yapısından farklı bir yapı kullanılmalıdır. Ayarlardan uygulamanın izin ekranını açmak için özel bir Intent ile çağrı yapılıp kullanıcı o ekrana götürülebilir.

    
    fun checkDrawOverlayPermission() {

        // İznin daha önce alınıp alınmadığı kontrol edilir
        if (Settings.canDrawOverlays(this)) {
            // İzinler daha önceden alınmış. İşleyiş olması gerektiği gibi devam edebilir.
        } else {
            // İzin isteği için intent
            val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:$packageName"))

            startActivityForResult(intent, REQUEST_CODE)
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {

        when (requestCode) {
            REQUEST_CODE -> {
                // İznin alınıp alınmadığı kontrol edilir
                if (Settings.canDrawOverlays(this)) {
                    // İzinler alındı. İşleyiş olması gerektiği gibi devam edebilir.
                } else {
                    // İzin verilmedi.
                    // Uygulama daha sonra tekrar izin isteyebilir.
                }
            }
        }
    }
    public void checkDrawOverlayPermission() {

        // İznin daha önce alınıp alınmadığı kontrol edilir
        if (Settings.canDrawOverlays(this)) {
            // İzinler daha önceden alınmış. İşleyiş olması gerektiği gibi devam edebilir.
        } else {
            // İzin isteği için intent
            Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));

            startActivityForResult(intent, REQUEST_CODE);

        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode,  Intent data) {

        switch (requestCode) {
            case REQUEST_CODE: {

                // İznin alınıp alınmadığı kontrol edilir
                if (Settings.canDrawOverlays(this)) {
                    // İzinler alındı. İşleyiş olması gerektiği gibi devam edebilir.
                } else {
                    // İzin verilmedi.
                    // Uygulama daha sonra tekrar izin isteyebilir.
                }
            }
        }
    }

WRITE_SETTINGS

Bu izin uygulamanın sistem ayarlarını değiştirmesine izin verir; yönetici admin uygulamaları ve bluetooth ayarlarını değiştiren uygulamalar gibi. Bu izni manual olarak isterken kullanılan yapı SYSTEM_ALERT_WINDOW’unki ile aynıdır, ancak ACTION_MANAGE_OVERLAY_PERMISSION and Settings.canDrawOverlays() yerine ACTION_MANAGE_WRITE_SETTINGS ve Settings.System.canWrite() kullanılmalıdır.

Happy coding 🙂

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir