Irbisa · Шпаргалка

Разрешения и приватность

Runtime-разрешения, purpose strings, ATT, геолокация, фото, privacy manifest, согласия, аудит SDK

Middle-разработчик · 19 вопросов

  1. 01

    Чем запрос разрешения на Android отличается от iOS?

    На Android разрешение объявляют в манифесте и запрашивают самостоятельно в рантайме; в iOS общего вызова для запроса нет: система спрашивает при первом обращении к API и завершает приложение, если в Info.plist нет purpose string.

  2. 02

    Что означают варианты «Только в этот раз» и «Приблизительное» в диалоге разрешений Android для вашего кода?

    С Android 11 пользователь может выдать одноразовое разрешение, которое отзывается вскоре после ухода приложения с экрана, а с Android 12 запрос геолокации может вернуться только с грубой точностью.

  3. 03

    Онбординг на первом экране просит геолокацию, контакты и уведомления, и большинство пользователей отказывает во всех трёх. Когда спрашивать правильно?

    Спрашивайте в момент, когда пользователь запускает фичу, которой нужно разрешение, и сначала показывайте своё короткое объяснение.

    @Composablefun AddToCalendarButton(vm: BookingViewModel) {    val context = LocalContext.current    var showPrimer by remember { mutableStateOf(false) }    val launcher = rememberLauncherForActivityResult(        ActivityResultContracts.RequestPermission()…
  4. 04

    Приложение падает, когда пользователь нажимает «Сканировать». Что не так с этим запросом разрешения?

    class ScanFragment : Fragment(R.layout.fragment_scan) {    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {        view.findViewById<Button>(R.id.scan).setOnClickListener {            val request = registerForActivityResult(                ActivityResultContracts.RequestPermission()            ) { granted ->                if (granted) startScanner()            }            request.launch(Manifest.permission.CAMERA)        }    }}

    registerForActivityResult вызывается внутри обработчика клика, когда фрагмент уже создан, поэтому бросается IllegalStateException: «Fragments must call registerForActivityResult() before they are created».

  5. 05

    shouldShowRequestPermissionRationale возвращает false и на свежей установке, и после того, как пользователь заблокировал разрешение. Как различить эти состояния?

    По одному этому вызову это никак не определить: он возвращает true только после отказа, который ещё можно пересмотреть, и false как до первого запроса, так и после окончательного отказа.

    class PermissionGate(private val prefs: DataStore<Preferences>) {    private fun blockedKey(permission: String) = booleanPreferencesKey("blocked_$permission")     suspend fun state(activity: Activity, permission: String): PermissionState = when {        ContextCompat.checkSelfPermission(activity, permission) ==            PackageManager.PERMISSION_GRANTED -> PermissionState.Granted…
  6. 06

    Какие фичи можно сделать вообще без запроса dangerous-разрешения, переложив работу на системный компонент?

    Многие: когда системный UI действует по явному выбору пользователя, приложение получает только результат (одно фото, один контакт, одну SMS) и широкое разрешение ему не нужно.

    // Photo: the camera app takes it and writes to your FileProvider Uri. No CAMERA permission.private val takePhoto = registerForActivityResult(ActivityResultContracts.TakePicture()) { saved ->    if (saved) upload(photoUri)}// takePhoto.launch(photoUri)…
  7. 07

    Что такое Photo Picker и частичный доступ к медиа в Android 14 и почему пикер лучше, чем READ_MEDIA_IMAGES?

    Photo Picker представляет собой системный UI, который возвращает только выбранные пользователем файлы, вообще без разрешения.

    // Attaching an avatar: no permission, no manifest entry, nothing to denyval pickAvatar = registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->    if (uri != null) upload(uri)}fun chooseAvatar() = pickAvatar.launch(    PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)…
  8. 08

    Найдите баг: выбранная аватарка отображается, но после перезапуска приложения она пропадает, а в логе исключение.

    class ProfileFragment : Fragment(R.layout.fragment_profile) {    private val prefs by lazy { requireContext().getSharedPreferences("profile", Context.MODE_PRIVATE) }     private val pickAvatar = registerForActivityResult(PickVisualMedia()) { uri ->        if (uri == null) return@registerForActivityResult        prefs.edit { putString("avatar_uri", uri.toString()) }        showAvatar(uri) // reads it with contentResolver.openInputStream(uri)    }     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {        prefs.getString("avatar_uri", null)?.let { showAvatar(it.toUri()) }        view.findViewById<Button>(R.id.change_avatar).setOnClickListener {            pickAvatar.launch(PickVisualMediaRequest(PickVisualMedia.ImageOnly))        }    }}

    Приложение сохраняет content://-URI из пикера, но не право на его чтение.

  9. 09

    Приложению курьера нужна геолокация водителя при выключенном экране во время доставки. Какие разрешения запрашивать и нужна ли фоновая геолокация вообще?

    Запрашивайте точную и приблизительную геолокацию, пока приложение на экране, и ведите поездку в foreground-сервисе с типом location; фоновая геолокация обычно не нужна.

    private val requestLocation = registerForActivityResult(    ActivityResultContracts.RequestMultiplePermissions()) { result ->    when {        result[Manifest.permission.ACCESS_FINE_LOCATION] == true -> startTrip(precise = true)        result[Manifest.permission.ACCESS_COARSE_LOCATION] == true -> startTrip(precise = false)…
  10. 10

    После повышения targetSdk этот код всегда показывает «No maps app installed», хотя Google Maps на телефоне стоит. Почему и как исправить?

    fun openInMaps(context: Context, lat: Double, lng: Double) {    val intent = Intent(Intent.ACTION_VIEW, Uri.parse("geo:$lat,$lng"))    if (intent.resolveActivity(context.packageManager) != null) {        context.startActivity(intent)    } else {        Toast.makeText(context, "No maps app installed", Toast.LENGTH_SHORT).show()    }}

    Видимость пакетов (package visibility): с Android 11 приложение видит лишь отфильтрованный список установленных приложений, поэтому resolveActivity возвращает null для невидимого.

  11. 11

    Что меняют для приложения индикаторы и переключатели камеры и микрофона в Android 12?

    Зелёная точка появляется, пока приложение использует камеру или микрофон, а пользователь может выключить любой из сенсоров для всего устройства.

  12. 12

    Минорное обновление SDK добавило READ_PHONE_STATE в ваш релизный манифест. Как найти, откуда оно взялось, и какие есть варианты?

    Манифесты библиотек мёржатся в ваш, поэтому любая зависимость может добавить <uses-permission>, которого никто в команде не писал.

    // build.gradle.kts (:app): run ./gradlew checkReleasePermissions in CI// import com.android.build.api.artifact.SingleArtifactabstract class CheckPermissions : DefaultTask() {    @get:InputFile abstract val mergedManifest: RegularFileProperty    @get:InputFile abstract val allowList: RegularFileProperty…
  13. 13

    Что требует форма Data safety в Play и как на ответы влияют сторонние SDK?

    Это публичная декларация того, какие данные приложение собирает и передаёт, зачем и как защищает; она покрывает и данные ваших SDK: Play считает их трафик вашим.

  14. 14

    Что такое App Tracking Transparency и что происходит с IDFA и атрибуцией рекламы, когда пользователь отказывает?

    ATT представляет собой диалог iOS, обязательный перед трекингом: объединением своих данных с данными других компаний для рекламы или передачей брокерам данных.

    import AppTrackingTransparencyimport AdSupport // Call when the first screen is visible, e.g. when scenePhase becomes .active.// Called from didFinishLaunching, the alert is never shown.@MainActor…
  15. 15

    Загрузку в App Store отклонили с ITMS-91053 из-за SDK, который писали не вы. Что декларирует privacy manifest и что проверяет Apple?

    PrivacyInfo.xcprivacy представляет собой property list, где приложение и каждый SDK декларируют трекинг, собираемые данные и причину для каждого вызываемого API из списка «required reason».

    <plist version="1.0"><dict>  <key>NSPrivacyTracking</key>  <false/>  <key>NSPrivacyTrackingDomains</key>  <array/>…
  16. 16

    Каждый вызов аналитики закрыт флагом согласия, но вендор получает запрос ещё до ответа на диалог согласия. Откуда берётся этот запрос?

    Из автоинициализации: Firebase и многие SDK стартуют через ContentProvider или инициализатор androidx.startup, который выполняется до Application.onCreate, поэтому они начинают собирать данные раньше, чем ваш флаг вообще прочитан.

    // AndroidManifest.xml: read before any of your code runs// <meta-data android:name="firebase_analytics_collection_enabled" android:value="false" />// <meta-data android:name="firebase_crashlytics_collection_enabled" android:value="false" />// <meta-data android:name="google_analytics_default_allow_analytics_storage" android:value="false" /> data class Consent(val analytics: Boolean, val ads: Boolean, val crashes: Boolean)…
  17. 17

    Продукт хочет открыть приложение для детей младше 13 лет. Что меняется в декларациях сторов, в рекламном и аналитическом стеке и в том, какие данные вообще можно собирать?

    Детская аудитория выключает почти всю стороннюю телеметрию: нет рекламного ID и персонализации, только SDK, сертифицированные для семейных приложений, и проверяемое согласие родителей до сбора данных.

    enum class Audience { CHILD, TEEN, ADULT } // Applied once, before MobileAds.initialize()// AndroidManifest.xml: <meta-data android:name="firebase_analytics_collection_enabled" android:value="false" />fun configureForAudience(app: Application, audience: Audience) {    val child = audience == Audience.CHILD…
  18. 18

    Пользователь нажимает «Удалить аккаунт». Что должно произойти помимо удаления строки пользователя и что вы обещаете насчёт бэкапов?

    Оба стора требуют, чтобы удаление начиналось внутри приложения, а строка в базе оказывается самой простой частью: сервер рассылает удаление по всем системам и вендорам, у которых есть данные пользователя, устройство очищается, а бэкапы вычищаются по заявленному сроку хранения.

  19. 19

    Ревьюер просит доказать, что приложение отправляет только то, что заявлено в форме Data safety. Какие доказательства вы можете предъявить?

    Три вещи: инвентарь данных, который ревьюят как код, запись трафика со свежей установки на реальном устройстве и проверку в CI, которая падает, если приложение обращается к хосту, которого нет в инвентаре.