İçeriğe geç
academia.sh

Ders 15 / 15

İzin Modeli

Kurulum izni ile çalışma zamanı izninin, normal ve tehlikeli iznin, kesin ve yaklaşık konumun, reddin ve bir daha sorma durumunun karşılama modeli ile kursun kapanış tablosu.

Yayın Güncelleme

Bu derste

  • Kurulum izni ile çalışma zamanı iznini ve normal izin ile tehlikeli izni ayırır
  • Kullanıcının henüz yanıtlamadığı tehlikeli iznin bekleyen, reddedilenin karşılıksız olduğunu ölçer
  • Kesin konum yerine verilen yaklaşık konumun ikame, bir daha sorma durumunun karşılıksız olduğunu ölçer
  • Kurs boyunca ölçülen on beş dersin karşılamasını tek tabloda okur
İçindekiler

Önceki ders uygulamanın aygıta kurulmasını ölçtü. Kurulum bir kez yapılır ve uygulama aygıtta durur. Sonra uygulama kullanıcının kamerasını, konumunu ya da rehberini ister. İzin (permission) uygulamanın korunan bir veriye ya da donanıma erişim hakkıdır. Bu ders, bir izin isteğine platformun ne verdiğini ölçer ve kursu kapatır.

Kursun ölçü ekseni değişmez: bu isteği platform nasıl karşılıyor. Karşılama dörttür: tam, ikame, bekleyen ve karşılıksız. Karşılamayı etiket değil kural verir: verilen değer istenene eşitse tam, eşit değilse ikame; bekleyen ve karşılıksız doğrudan sonuçtan çıkar. Aşağıdaki dosya o kuralı taşır ve her derste aynen yinelenir.

// Fulfillment.kt — bir istegin platformca nasil karsilandigini siniflandirir
sealed interface Outcome {
    data class Given(val value: String) : Outcome
    data class Waiting(val on: String) : Outcome
    data class Refused(val reason: String) : Outcome
}

class Ask(val label: String, val wanted: String, val outcome: Outcome) {
    val fulfillment: String = when (outcome) {
        is Outcome.Waiting -> "bekleyen"
        is Outcome.Refused -> "karsiliksiz"
        is Outcome.Given -> if (outcome.value == wanted) "tam" else "ikame"
    }

    fun report() {
        val detail = when (outcome) {
            is Outcome.Given -> "verilen=${outcome.value}"
            is Outcome.Waiting -> "bekledigi=${outcome.on}"
            is Outcome.Refused -> "neden=${outcome.reason}"
        }
        println("$label: istenen=$wanted | $detail -> $fulfillment")
    }
}

fun tally(asks: List<Ask>) {
    for (name in listOf("tam", "ikame", "bekleyen", "karsiliksiz")) {
        println("$name: ${asks.count { it.fulfillment == name }}")
    }
}

M30/K02’deki izin dersleri, istenen izin kümesiyle verilen kümenin buluşmasını ölçmüştü. Burada her izin ayrı bir istektir ve kullanıcının henüz yanıtlamadığı bir izin, kendi başına bir kategoridir: bekleyen. M12/K03’te karar kullanıcıya aitti, ama kategori kararın sahibini adlandırmıyordu; karşılama aynı kalıyordu ve yalnız neden= alanı sahibi adlandırabiliyordu. Burada da öyledir.

Kurulum İzni ve Çalışma Zamanı İzni

İzinler iki anda verilir. Kurulum izni (install-time permission) uygulama kurulurken, kullanıcıya soru sorulmadan verilir. Çalışma zamanı izni (runtime permission) uygulama çalışırken, kullanıcıya bir iletişim kutusu gösterilerek istenir. Hangisinin hangi yoldan geçeceğini izin türü belirler:

  • Normal izin (normal permission) kullanıcının gizliliğine az dokunur, örneğin ağa erişim. Bildirim dosyasında bildirilmesi yeterlidir ve kurulumda verilir.
  • Tehlikeli izin (dangerous permission) kullanıcının verisine ya da donanımına dokunur, örneğin kamera, ses kaydı ve konum. Bildirim dosyasında bildirilmesi gerekir, ama kullanıcı çalışma zamanında onaylamadıkça verilmez.

Bildirim dosyasındaki bildirim şuna benzer. Platform araçlarının okuduğu bir dosyadır ve çalıştırılmaz.

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />

Model bir izni adı, tehlikeli olup olmadığı ve bildirim dosyasında bildirilip bildirilmediğiyle tanımlar. Kullanıcının yanıtı ayrı bir girdidir: yanıt yoksa null, onay ver, ret reddet. Başka bir metin, kullanıcının istenenden farklı bir şey verdiğini gösterir.

// Permission.kt — izin istegi modeli
class Perm(val name: String, val dangerous: Boolean, val declared: Boolean)

fun resolvePermission(label: String, perm: Perm, reply: String?): Ask {
    val outcome = when {
        !perm.declared -> Outcome.Refused("bildirilmedi")
        !perm.dangerous -> Outcome.Given(perm.name)
        reply == null -> Outcome.Waiting("kullanici-yaniti")
        reply == "reddet" -> Outcome.Refused("kullanici-reddetti")
        reply == "ver" -> Outcome.Given(perm.name)
        else -> Outcome.Given(reply)
    }
    return Ask(label, perm.name, outcome)
}

Ölçümde üç izin vardır. Normal bir izin ile iki tehlikeli izin sorulur ve kullanıcı hiçbirini henüz yanıtlamamıştır.

// Main.kt — normal ve tehlikeli izin
fun main() {
    val asks = listOf(
        resolvePermission("ag", Perm("internet", false, true), null),
        resolvePermission("kamera", Perm("kamera", true, true), null),
        resolvePermission("mikrofon", Perm("ses-kaydi", true, true), null),
    )
    for (ask in asks) ask.report()
    tally(asks)
}
ag: istenen=internet | verilen=internet -> tam
kamera: istenen=kamera | bekledigi=kullanici-yaniti -> bekleyen
mikrofon: istenen=ses-kaydi | bekledigi=kullanici-yaniti -> bekleyen
tam: 1
ikame: 0
bekleyen: 2
karsiliksiz: 0

Normal izin hiçbir yanıt beklemeden tam oldu. Tehlikeli iki izin reddedilmedi, bekliyor ve beklediği şeyin adı kullanici-yaniti. Bu dersin karşılaması bekleyendir.

AP6. Model yanıtı tek bir metin olarak kurar. Gerçek platformda yanıt, iletişim kutusunun sonucudur ve iznin hangi seçenekle verildiğine göre ayrılır; ölçüm iletişim kutusunu taklit etmez.

AP7. Model normal ile tehlikeli izin ayrımını izin başına bir mantıksal değer olarak alır. Gerçek platformda bir iznin koruma düzeyini sürüm belirler; hangi iznin hangi düzeyde olduğu ölçülmedi.

İzin İsteme Akışı

Çalışma zamanı izni kod içinde bir sözleşmeyle istenir. ActivityResultContracts.RequestPermission iznin adını alır ve kullanıcının onay verip vermediğini bir geri çağrıyla bildirir. Aşağıdaki parça akışın şeklini gösterir. Platform API’si kullanır, bu yüzden ölçülmez ve çıktısı iddia edilmez.

val launcher = registerForActivityResult(
    ActivityResultContracts.RequestPermission()
) { granted ->
    if (granted) openCamera() else showFallback()
}

launcher.launch(Manifest.permission.CAMERA)

Akışta iki geri dönüş yolu vardır ve model ikisini de ayırır. İletişim kutusu açıldığı anda uygulama yanıt beklemektedir: bekleyen. Kullanıcı yanıt verince aynı istek ya tam ya karşılıksız olur. Bunu ölçmek için aynı izin üç kullanıcı durumunda sorulur.

// Main.kt — yanit once, onay ve ret
fun main() {
    val camera = Perm("kamera", true, true)
    val asks = listOf(
        resolvePermission("yanit-yok", camera, null),
        resolvePermission("onay", camera, "ver"),
        resolvePermission("ret", camera, "reddet"),
    )
    for (ask in asks) ask.report()
    tally(asks)
}
yanit-yok: istenen=kamera | bekledigi=kullanici-yaniti -> bekleyen
onay: istenen=kamera | verilen=kamera -> tam
ret: istenen=kamera | neden=kullanici-reddetti -> karsiliksiz
tam: 1
ikame: 0
bekleyen: 1
karsiliksiz: 1

Aynı istek üç karşılama aldı ve fark izinden gelmedi, kullanıcının yanıtından geldi. Karşılama kategorisi sahibi söylemez; yalnız neden=kullanici-reddetti kullanıcının kararını adlandırır.

Kesin ve Yaklaşık Konum

Konum izni iki ayrıntıda istenir. Kesin konum (precise location) aygıtın bulunduğu yeri ayrıntılı verir. Yaklaşık konum (approximate location) yalnız çevreyi verir. Uygulama ikisini birlikte isteyebilir ve kullanıcı yaklaşık olanı seçerse uygulama kesin konum olmadan çalışmak zorunda kalır. Model bunu ikame sayar: istenen kesin konumdur, verilen yaklaşık konumdur.

// Main.kt — kesin konum istendi, kullanici ne verdi
fun main() {
    val location = Perm("kesin-konum", true, true)
    val asks = listOf(
        resolvePermission("kesin-ver", location, "ver"),
        resolvePermission("yaklasik-ver", location, "yaklasik-konum"),
        resolvePermission("yanit-yok", location, null),
        resolvePermission("ret", location, "reddet"),
    )
    for (ask in asks) ask.report()
    tally(asks)
}
kesin-ver: istenen=kesin-konum | verilen=kesin-konum -> tam
yaklasik-ver: istenen=kesin-konum | verilen=yaklasik-konum -> ikame
yanit-yok: istenen=kesin-konum | bekledigi=kullanici-yaniti -> bekleyen
ret: istenen=kesin-konum | neden=kullanici-reddetti -> karsiliksiz
tam: 1
ikame: 1
bekleyen: 1
karsiliksiz: 1

Dört durumun dördü ayrı bir kategoriye düştü. Kullanıcı yaklaşık konumu verince uygulama reddedilmedi, istediğinden azıyla çalışıyor.

AP8. Model yaklaşık konumu kesin konumun ikamesi olarak adlandırır. Gerçek platformda yaklaşık konumun doğruluğu aygıta ve ayara göre değişir; ölçüm bu doğruluğu bir sayıyla vermez.

Ret, Bir Daha Sorma ve Gerekçe

Kullanıcı bir isteği reddedince uygulama aynı iznin iletişim kutusunu hemen tekrar gösteremeyebilir. Platform iki durumu ayırır. İlk retten sonra sistem, iznin neden gerektiğini açıklamak için gerekçe göstermeyi önerir: shouldShowRequestPermissionRationale değeri true döner. Gerekçe (rationale) uygulamanın kendi ekranıdır, kullanıcıya iznin neye yaradığını söyler. Kullanıcı isteği “bir daha sorma” ile reddettiğinde ise sistem iletişim kutusunu hiç göstermez ve istek doğrudan reddedilmiş döner.

Gerekçe karşılamayı değiştirmez. Değişen, kullanıcının vereceği yanıtın olasılığıdır. Karşılamayı değiştiren, kullanıcıya ulaşılıp ulaşılamadığıdır. Model bunu tek bir girdiyle kurar: iletişim kutusu engellenmiş mi.

// Denial.kt — ret sonrasi istek modeli
fun askAfterDenial(label: String, perm: Perm, blocked: Boolean, reply: String?): Ask {
    val outcome = when {
        blocked -> Outcome.Refused("bir-daha-sorma")
        reply == null -> Outcome.Waiting("kullanici-yaniti")
        reply == "reddet" -> Outcome.Refused("kullanici-reddetti")
        else -> Outcome.Given(perm.name)
    }
    return Ask(label, perm.name, outcome)
}

Ölçümde kullanıcı kamerayı bir kez reddetmiştir. İlk iki satırda iletişim kutusu açıktır ve gerekçeden önce ve sonra sorulur. Son iki satırda kullanıcı “bir daha sorma” demiştir: biri yanıtsız, biri onaylı sorulur.

// Main.kt — gerekce ve bir daha sorma
fun main() {
    val camera = Perm("kamera", true, true)
    val asks = listOf(
        askAfterDenial("gerekce-oncesi", camera, false, null),
        askAfterDenial("gerekce-sonrasi", camera, false, "ver"),
        askAfterDenial("bir-daha-sorma", camera, true, null),
        askAfterDenial("bir-daha-sorma-onay", camera, true, "ver"),
    )
    for (ask in asks) ask.report()
    tally(asks)
}
gerekce-oncesi: istenen=kamera | bekledigi=kullanici-yaniti -> bekleyen
gerekce-sonrasi: istenen=kamera | verilen=kamera -> tam
bir-daha-sorma: istenen=kamera | neden=bir-daha-sorma -> karsiliksiz
bir-daha-sorma-onay: istenen=kamera | neden=bir-daha-sorma -> karsiliksiz
tam: 1
ikame: 0
bekleyen: 1
karsiliksiz: 2

Engellenmiş iletişim kutusunda onay yanıtı bile sonucu değiştirmedi, çünkü yanıt kullanıcıya hiç ulaşmadı. Bu durumdan çıkış uygulamanın içinde değildir: kullanıcı iznin kendisini sistem ayarlarından açar.

AP9. Model “bir daha sorma” durumunu tek bir mantıksal değer olarak alır. Gerçek platformda bu durumun hangi reddin kaç kez tekrarlanmasıyla oluştuğu sürüme göre değişir; ölçüm bunu belirlemez.

Sınırlayıcı Ölçüm

Karşılama kuralı sonucu ve eşitliği görür, nedeni görmez. İzinde bu sınırın bir yüzü vardır: bildirilmemiş bir izin hiçbir kullanıcı yanıtıyla verilemez. Model karşılaştırmayı sırayla yapar. Önce izin bildirim dosyasında var mı, sonra tehlikeli mi, sonra kullanıcı ne dedi. İlk uyuşmayan alan sonucu belirler ve adını verir.

Ölçümde aynı kamera izni bildirimde yazılı olmadan ve kullanıcı onay vermişken sorulur. Karşılaştırma için normal bir izin de bildirilmeden sorulur ve bildirilmiş hali eklenir.

// Main.kt — bildirilmemis izin
fun main() {
    val asks = listOf(
        resolvePermission("bildirimsiz-kamera", Perm("kamera", true, false), "ver"),
        resolvePermission("bildirimsiz-ag", Perm("internet", false, false), null),
        resolvePermission("bildirimli-kamera", Perm("kamera", true, true), "ver"),
    )
    for (ask in asks) ask.report()
    tally(asks)
}
bildirimsiz-kamera: istenen=kamera | neden=bildirilmedi -> karsiliksiz
bildirimsiz-ag: istenen=internet | neden=bildirilmedi -> karsiliksiz
bildirimli-kamera: istenen=kamera | verilen=kamera -> tam
tam: 1
ikame: 0
bekleyen: 0
karsiliksiz: 2

Kullanıcının onayı bildirilmemiş kamera iznini kurtarmadı. Normal izin de bildirilmeden verilmedi: kurulum izni de bildirim dosyasından başlar.

Kapanış Tablosu

Kurs on beş ders boyunca aynı soruyu sordu. Aşağıdaki tablo her dersin ölçtüğü isteği, o dersin karşılamasını ve dersin sınırlayıcı ölçümünün gösterdiğini bir arada verir. Karşılama hücresi her dersin Özet’indeki “Bu dersin karşılaması” maddesinden alınmıştır. Seçim kuralı tektir: dersin karşılaması, “Ölçülen istek” sütunundaki odak isteğin karşılamasıdır; dersteki ölçümlerin çoğunluğu değildir.

Ders Ölçülen istek Karşılama Sınırlayıcı ölçüm
Android Mimari Katmanları bildirilen activity, normal izin tam aynı tam iki ayrı nedenden gelir, kural nedeni görmez; uyumluluk karşılığı ikame
Activity’ler bildirilmemiş ya da dışa kapalı activity karşılıksız android:exported: aynı activity çağırana göre tam ya da karşılıksız
Activity Yaşam Döngüsü yapılandırma değişikliğinde durum ikame durdurulmuş activity’ye arayüz güncellemesi bekler, yok edilmişe karşılıksız
Fragment’lar ev sahibi durmuşken fragment işlemi bekleyen durum kaydedildikten sonra commit karşılıksız, commitAllowingStateLoss ikame
Servisler arka plandan servis başlatma karşılıksız ön plan servisi ikame, koşullu zamanlanmış iş bekleyen
Yayın Alıcıları örtük sistem yayını karşılıksız aynı alıcı sınıfı üç yolda iki sonuç
İçerik Sağlayıcılar sağlayıcıdan satır sorgusu tam izinsiz sorgu karşılıksız, URI izni yalnız tek satıra
Intent’ler ve Intent Süzgeçleri örtük intent bekleyen kurulu uygulama sayısı: sıfır karşılıksız, bir tam, iki bekleyen
Görevler ve Geri Yığını yeni örnek isteği ikame aynı kip, yığıtın durumuna göre iki sonuç
Bildirim Dosyası birleştirilen bildirim öğesi tam yer tutucu çözülmeden sonuç belli değil, bekleyen
Kaynak Sistemi niteleyicili kaynak seçimi ikame bölge dizini yoksa dil dizini; niteleyici çakışması
Derleme Sistemi geçişli bağımlılık sürümü ikame 1.10 sürümü 1.9 sürümünden yüksek; katı sürüm çakışması
Ürün Çeşitleri kod kümesinden istenen dosya ikame aynı iki kümede bulunan .kt sınıfı çakışır, karşılıksız
Geliştirme Ortamı uygulamanın aygıta kurulması bekleyen işlemci mimarisi uyuşmazlığı API düzeyi yeterli olsa da karşılıksız
İzin Modeli tehlikeli çalışma zamanı izni bekleyen bildirilmemiş izin hiçbir yanıtla verilmez

Dağılım: tam 3, ikame 5, bekleyen 4, karşılıksız 3; toplam 15. Açılış dersindeki çekirdek 3/2/2/1 dağılımını verdi. İki dağılım aynı şey değildir. Çekirdek sekiz isteğin tek bir ölçümüydü; tablo on beş dersin her birinin kendi karşılamasını sayar. Aralarında bir tahmin köprüsü kurulmaz: biri bir modelin çıktısı, öteki on beş ayrı modelin tek tek seçilmiş satırıdır.

Özet

  • Bu dersin ölçütü: karşılama; iznin kullanıcıdan yanıt bekleyip beklemediği, kimin karar verdiği değil. Bu dersin karşılaması: bekleyen.
  • Kurulum izni kurulumda, kullanıcıya sorulmadan verilir; çalışma zamanı izni çalışırken, kullanıcıya sorularak verilir.
  • Normal izin bildirilince tam olur; tehlikeli izin kullanıcı yanıtlayana kadar bekleyendir, ret karşılıksızdır.
  • Kesin konum istenip yaklaşık konum verilirse sonuç ikamedir.
  • Gerekçe karşılamayı değiştirmez; “bir daha sorma” durumunda sistem kullanıcıya ulaşmaz ve istek karşılıksız döner.
  • Bildirim dosyasında bildirilmemiş izin, kullanıcı onay verse de verilmez.

Sonraki Adım

Kurs burada kapanıyor. Sıradaki kurs arayüzün bildirimsel biçimde kurulmasını ele alır.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ ↵ Esc gezin · aç · kapat