Codelab sobre privacidad de Android

1. Introducción

Qué aprenderás

  • Por qué la privacidad es cada vez más importante para los usuarios
  • Prácticas recomendadas de privacidad de Android en las últimas versiones
  • Cómo integrar las prácticas recomendadas de privacidad en las apps existentes para que preserven más la privacidad

Qué compilarás

En este codelab, comenzarás con una app de ejemplo que les permite a los usuarios guardar sus recuerdos fotográficos.

Comenzará con las siguientes pantallas:

  • Pantalla de permisos: Es una pantalla que le solicita al usuario que otorgue todos los permisos antes de pasar a la pantalla principal.
  • Pantalla principal: Es una pantalla que muestra todos los registros de fotos existentes del usuario y que le permite agregar otros nuevos.
  • Pantalla para agregar registro: Es una pantalla que le permite al usuario crear un nuevo registro de fotos. En esta pantalla, los usuarios pueden navegar desde las fotos existentes en su biblioteca, tomar una nueva foto con la cámara y agregar su ciudad actual al registro de fotos.
  • Pantalla de cámara: Es una pantalla que le permite al usuario tomar una foto y guardarla en el registro de fotos.

La app está funcionando, pero tiene muchas deficiencias de privacidad que mejoraremos juntos.

A medida que avances en el codelab, sucederá lo siguiente:

  • Se te explicará por qué la privacidad es importante para tus apps.
  • Se te presentarán las funciones de privacidad de Android y las prácticas recomendadas clave.
  • Se te mostrará cómo implementar estas prácticas recomendadas en una app existente de la siguiente manera:
  • Solicitando permisos en contexto
  • Reduciendo el acceso a la ubicación de tu app
  • Usando el selector de fotos y otras mejoras de almacenamiento
  • Usando las APIs de auditoría de acceso a los datos

Cuando termines, tendrás una app que:

  • Implementará las prácticas recomendadas de privacidad antes mencionadas.
  • Preservará la privacidad y protegerá a los usuarios mediante el manejo cuidadoso de sus datos privados, lo que mejorará la experiencia del usuario.

Requisitos

Deseable

2. ¿Por qué es importante la privacidad?

Las investigaciones demuestran que las personas son cautelosas con su privacidad. En una encuesta que realizó Pew Research Institute, se descubrió que el 84% de los estadounidenses sienten que tienen poco o ningún control sobre los datos que recopilan las empresas y las apps. Su principal frustración es no saber qué sucede con los datos más allá del uso directo. Por ejemplo, les preocupa que los datos se usen para otros fines, como crear perfiles para anuncios segmentados o, incluso, que se vendan a terceros. Una vez que se recopilan los datos, parece que no hay forma de quitarlos.

Esta preocupación por la privacidad ya tiene un impacto significativo en las decisiones de las personas sobre qué servicios o apps usar. De hecho, en el mismo estudio de Pew Research Institute, se descubrió que más de la mitad (52%) de los adultos de EE.UU. decidieron no utilizar un producto o servicio debido a inquietudes sobre la privacidad, como la preocupación por la cantidad de datos que se recopilan sobre ellos.

Por lo tanto, mejorar y demostrar la privacidad de tu app es esencial para optimizar la experiencia que ofreces, y las investigaciones demuestran que es probable que también pueda ayudarte a expandir tu base de usuarios.

Muchas de las funciones y prácticas recomendadas que analizaremos en este codelab están directamente relacionadas con reducir la cantidad de datos a los que accede tu app o con expresar mejor la sensación de control que tienen tus usuarios sobre sus datos privados. Ambas mejoras abordan, de forma directa, las inquietudes que los usuarios compartieron en la investigación citada anteriormente.

3. Configura tu entorno

Para que comiences lo antes posible, preparamos un proyecto de partida sobre el cual puedes compilar. En este paso, descargarás el código para todo el codelab, incluido el proyecto inicial, y, luego, ejecutarás la app de partida en tu emulador o dispositivo.

Si tienes Git instalado, simplemente puedes ejecutar el comando que se indica abajo. Para comprobarlo, escribe git –version en la terminal o línea de comandos, y verifica que se ejecute de forma correcta.

git clone https://github.com/android/privacy-codelab

Si no tienes Git, puedes hacer clic en el vínculo para descargar todo el código de este codelab.

Para configurar el codelab, haz lo siguiente:

  1. Abre el proyecto en el directorio PhotoLog_Start de Android Studio.
  2. Ejecuta la configuración de ejecución PhotoLog_Start en un emulador o dispositivo que ejecute Android 12 (S) o versiones posteriores.

d98ce953b749b2be.png

Deberías ver una pantalla que te solicita que otorgues permisos para ejecutar la app. Eso significa que configuraste el entorno de forma correcta.

4. Práctica recomendada: Solicita permisos en contexto

Muchos desarrolladores saben que los permisos de tiempo de ejecución son esenciales a la hora de desbloquear muchas funcionalidades clave que son importantes para tener una excelente experiencia del usuario. Sin embargo, ¿sabías que el momento y la manera en que tu app solicita los permisos también tiene un impacto significativo en la experiencia del usuario?

Veamos cómo la app de PhotoLog_Start solicita permisos para mostrarte por qué no tiene un modelo de permisos óptimo:

  1. Inmediatamente después del inicio, el usuario recibe mensajes de permiso de inmediato y se le solicita que otorgue varios permisos. Es posible que esto confunda a los usuarios y logre que pierdan la confianza en la app o que, en el peor de los casos, la desinstalen.
  2. La app no permite que los usuarios procedan hasta que se otorguen todos los permisos. Es posible que los usuarios no confíen lo suficiente en nuestra app durante el inicio para otorgar acceso a toda esta información sensible.

Como probablemente adivinaste, la lista anterior representa el conjunto de mejoras que realizaremos juntos para mejorar el proceso de solicitud de permisos de la app. Comencemos.

Las prácticas recomendadas de Android indican que debemos solicitar permisos en contexto la primera vez que los usuarios comienzan a interactuar con una función. Esto se debe a que, si una app solicita permiso para habilitar una función con la que el usuario ya está interactuando, este no se sorprende con la solicitud. De esta manera, se mejora la experiencia del usuario. En la app de PhotoLog, debemos esperar hasta la primera vez que los usuarios hagan clic en los botones de cámara o ubicación antes de solicitar permisos.

Primero, quitemos la pantalla de permisos que obliga a los usuarios a aprobar todos los permisos antes de ir a la página principal. Esta lógica se define actualmente en MainActivity.kt, así que naveguemos hasta allí:

val startNavigation =
   if (permissionManager.hasAllPermissions) {
       Screens.Home.route
   } else {
       Screens.Permissions.route
   }

Comprueba si el usuario otorgó todos los permisos antes de permitirle ir a la página principal. Como mencionamos antes, esto no sigue nuestras prácticas recomendadas para la experiencia del usuario. Cambiémoslo por el siguiente código para permitir que los usuarios interactúen con la app sin que se les otorguen todos los permisos:

val startNavigation = Screens.Home.route

Ahora que ya no necesitamos la pantalla de permisos, también podemos borrar esta línea desde NavHost:

composable(Screens.Permissions.route) { PermissionScreen(navController) }

Luego, quita esta línea desde la clase Screens:

object Permissions : Screens("permissions")

Por último, también podemos borrar el archivo PermissionsScreen.kt.

Ahora borra y vuelve a instalar tu app, que es una manera de restablecer los permisos que se otorgaron anteriormente. Deberías poder ir a la pantalla principal de inmediato, pero cuando presionas los botones de cámara o ubicación en la pantalla "Agregar registro", no sucede nada porque la app ya no tiene la lógica para solicitarle permisos al usuario. Sin embargo, podemos solucionarlo.

Cómo agregar lógica para solicitar permisos de cámara

Comenzaremos con el permiso de cámara. Según las muestras de código que vimos en la documentación sobre la solicitud de permisos, primero debemos registrar la devolución de llamada de permisos para usar el contrato RequestPermission().

Evaluemos la lógica que necesitamos:

  • Si el usuario acepta el permiso, se debe registrar el permiso con viewModel y también navegar a la pantalla de la cámara si el usuario aún no alcanzó el límite para la cantidad de fotos que ya se agregaron.
  • Si el usuario rechaza el permiso, podemos informarle que no se podrá usar la función debido a que se denegó el permiso.

Para ejecutar esta lógica, podemos agregar este bloque de código a: // TODO: Step 1. Register ActivityResult to request Camera permission

val requestCameraPermission =
   rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
       if (isGranted) {
           viewModel.onPermissionChange(CAMERA, isGranted)
           canAddPhoto {
               navController.navigate(Screens.Camera.route)
           }
       }
       else {
           coroutineScope.launch {
               snackbarHostState.showSnackbar("Camera currently disabled due to denied permission.")
           }
       }
   }

Ahora queremos verificar que la app tenga el permiso de cámara antes de navegar a la pantalla de la cámara y solicitarlo si el usuario aún no lo otorgó. Para implementar esta lógica, podemos agregar el siguiente bloque de código a: // TODO: Step 2. Check & request for Camera permission before navigating to the camera screen

canAddPhoto {
   when {
       state.hasCameraAccess -> navController.navigate(Screens.Camera.route)
       // TODO: Step 4. Trigger rationale screen for Camera if needed
       else -> requestCameraPermission.launch(CAMERA)
   }
}

Ahora, vuelve a ejecutar la app y haz clic en el ícono de la cámara en la pantalla "Agregar registro". Deberías ver un diálogo que solicita el permiso de cámara. ¡Felicitaciones! Esto es mucho mejor que solicitarles a los usuarios que aprueben todos los permisos antes de que prueben la app, ¿verdad?

Pero ¿podemos hacerlo mejor? ¡Sí! Podemos comprobar si el sistema recomienda que mostremos una justificación para explicar por qué nuestra app necesita acceder a la cámara. Esto ayuda a aumentar potencialmente las tasas de participación para el permiso y a conservar la capacidad de tus apps para solicitarlo de nuevo en momentos más oportunos.

Para ello, creemos una pantalla de justificación que explique por qué nuestra app necesita acceder a la cámara del usuario. Para crearla, agrega el siguiente bloque de código a: // TODO: Step 3. Add explanation dialog for Camera permission

var showExplanationDialogForCameraPermission by remember { mutableStateOf(false) }
if (showExplanationDialogForCameraPermission) {
   CameraExplanationDialog(
       
           requestCameraPermission.launch(CAMERA)
           showExplanationDialogForCameraPermission = false
       },
        showExplanationDialogForCameraPermission = false },
   )
}

Ahora que tenemos el diálogo en sí, solo tenemos que comprobar si debemos mostrar la justificación antes de solicitar el permiso de cámara. Para ello, llamamos a la API de shouldShowRequestPermissionRationale() de ActivityCompat. Si se muestra un valor verdadero, también debemos configurar showExplanationDialogForCameraPermission como verdadero para mostrar el diálogo de explicación.

Agreguemos el siguiente bloque de código entre el caso state.hasCameraAccess y el caso else, o en donde el siguiente TODO se agregó previamente en las instrucciones // TODO: Step 4. Add explanation dialog for Camera permission:

ActivityCompat.shouldShowRequestPermissionRationale(context.getActivity(),
           CAMERA) -> showExplanationDialogForCameraPermission = true

Ahora, tu lógica completa para el botón de la cámara debería verse de la siguiente manera:

canAddPhoto {
   when {
       state.hasCameraAccess -> navController.navigate(Screens.Camera.route)
       ActivityCompat.shouldShowRequestPermissionRationale(context.getActivity(),
           CAMERA) -> showExplanationDialogForCameraPermission = true
       else -> requestCameraPermission.launch(CAMERA)
   }
}

¡Felicitaciones! Terminamos de manejar los permisos de cámara, a la vez que seguimos todas las prácticas recomendadas de Android. Borra la app y vuelve a instalarla una vez más, y presiona el botón de la cámara en la página "Agregar registro". Si deniegas el permiso, la app no te impedirá usar otras funcionalidades, como abrir el álbum de fotos.

Sin embargo, la próxima vez que hagas clic en el ícono de la cámara después de denegar el permiso, deberías ver el mensaje de explicación que acabamos de agregar.* Ten en cuenta que el mensaje de permiso del sistema solo aparece después de que el usuario hace clic en "Continuar" en el mensaje de explicación y, si el usuario hace clic en "Ahora no", le permitimos utilizar la app sin más interrupciones. Esto ayuda a que la app evite las denegaciones de permisos adicionales del usuario y conserve nuestra capacidad de volver a solicitar el permiso en otro momento, cuando el usuario puede estar más listo para otorgarlo.

  • Nota: El comportamiento exacto de la API de shouldShowRequestPermissionRationale() es un detalle interno de la implementación y está sujeto a cambios.

Cómo agregar lógica para solicitar permiso de ubicación

Ahora, hagamos lo mismo con la ubicación. Primero, podemos registrar el ActivityResult para los permisos de ubicación agregando el siguiente bloque de código a: // TODO: Step 5. Register ActivityResult to request Location permissions

val requestLocationPermissions =
   rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
       if (isGranted) {
           viewModel.onPermissionChange(ACCESS_COARSE_LOCATION, isGranted)
           viewModel.onPermissionChange(ACCESS_FINE_LOCATION, isGranted)
           viewModel.fetchLocation()
       }
       else {
           coroutineScope.launch {
               snackbarHostState.showSnackbar("Location currently disabled due to denied permission.")
           }
       }
   }

Después de eso, podemos agregar el diálogo de explicación de los permisos de ubicación agregando el siguiente bloque de código a: // TODO: Step 6. Add explanation dialog for Location permissions

var showExplanationDialogForLocationPermission by remember { mutableStateOf(false) }
if (showExplanationDialogForLocationPermission) {
   LocationExplanationDialog(
       
           // TODO: Step 10. Change location request to only request COARSE location.
           requestLocationPermissions.launch(arrayOf(ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION))
           showExplanationDialogForLocationPermission = false
       },
        showExplanationDialogForLocationPermission = false },
   )
}

Luego, verifiquemos, expliquemos (si es necesario) y solicitemos los permisos de ubicación. Si se otorga el permiso, podemos recuperar la ubicación y propagar el registro de fotos. Agreguemos el siguiente bloque de código a: // TODO: Step 7. Check, request, and explain Location permissions

when {
   state.hasLocationAccess -> viewModel.fetchLocation()
   ActivityCompat.shouldShowRequestPermissionRationale(context.getActivity(),
       ACCESS_COARSE_LOCATION) ||
   ActivityCompat.shouldShowRequestPermissionRationale(
       context.getActivity(), ACCESS_FINE_LOCATION) ->
       showExplanationDialogForLocationPermission = true
   else -> requestLocationPermissions.launch(arrayOf(ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION))
}

Eso es todo. Ya terminamos con la sección sobre permisos de este codelab. Intenta restablecer la app y observa los resultados.

Resumen de cómo mejoramos la experiencia del usuario y los beneficios de tu app:

  • Solicita permisos en contexto (cuando los usuarios interactúen con la función) en lugar de hacerlo inmediatamente después de iniciar la app → Disminuye la confusión y el abandono de los usuarios.
  • Crea pantallas de explicación a fin de aclararles a los usuarios por qué la app necesita acceder a permisos → Aumenta la transparencia para los usuarios.
  • Aprovecha la API de shouldShowRequestPermissionRationale() para determinar cuándo el sistema considera que tu app debería mostrar una pantalla de explicación → Aumenta las tasas de aceptación de permisos y disminuye las probabilidades de denegación de permisos permanentes.

5. Práctica recomendada: Reduce el acceso a la ubicación de tu app

Como la ubicación es uno de los permisos más sensibles, Android la incluye en el Panel de privacidad.

A modo de resumen, en Android 12, les proporcionamos a los usuarios controles adicionales para la ubicación. Los usuarios ahora tienen una opción clara para compartir datos de ubicación menos precisos con las apps si seleccionan ubicación aproximada en lugar de ubicación precisa cuando las apps solicitan acceso a la ubicación.

La ubicación aproximada le proporciona a la app una estimación de la ubicación del usuario en un radio de 3 kilómetros cuadrados, lo que debería ser una precisión suficiente para muchas de las funciones de tu app. Les recomendamos a todos los desarrolladores cuyas apps necesiten acceso a la ubicación que revisen su caso de uso y solo soliciten ACCESS_FINE_LOCATION si el usuario interactúa activamente con una función que requiere su ubicación precisa.

ea5cc51fce3f219e.png

Gráfico que muestra el rango de estimación de la ubicación aproximada en el medio del centro de Los Ángeles, California.

El acceso a la ubicación aproximada es suficiente para nuestra app de PhotoLog, ya que solo necesitamos la ciudad del usuario para recordarle de dónde proviene el "recuerdo". Sin embargo, la app actualmente le solicita al usuario ACCESS_COARSE_LOCATION y ACCESS_FINE_LOCATION. Cambiemos eso.

Primero, necesitaremos editar el resultado de la actividad para la ubicación y proporcionar la función ActivityResultContracts.RequestPermission() como parámetro en lugar de ActivityResultContracts.RequestMultiplePermissions(), para reflejar el hecho de que solo solicitaremos ACCESS_COARSE_LOCATION.

Reemplacemos el objeto requestLocationsPermissions actual (indicado por // TODO: Step 8. Change activity result to only request Coarse Location) por el siguiente bloque de código:

val requestLocationPermissions =
   rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
       if (isGranted) {
           viewModel.onPermissionChange(ACCESS_COARSE_LOCATION, isGranted)
       }
   }

Luego, cambiaremos los métodos launch() para solicitar solo ACCESS_COARSE_LOCATION, en lugar de ambos permisos de ubicación.

Reemplacemos:

requestLocationPermissions.launch(arrayOf(ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION))

Por esto:

requestLocationPermissions.launch(ACCESS_COARSE_LOCATION)

Hay dos instancias de métodos launch() en PhotoLog que tendremos que cambiar: una está en la lógica onConfirm() en el LocationExplanationDialog indicado por // TODO: Step 9. Change location request to only request COARSE location y la otra en el elemento de la lista "Ubicación" indicado por // TODO: Step 10. Change location request to only request COARSE location.

Por último, ahora que ya no solicitamos el permiso ACCESS_FINE_LOCATION para PhotoLog, quitemos esta sección del método onPermissionChange() en AddLogViewModel.kt:

Manifest.permission.ACCESS_FINE_LOCATION -> {
   uiState = uiState.copy(hasLocationAccess = isGranted)
}

No olvides quitar también ACCESS_FINE_LOCATION del manifiesto de la app:

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

Ya terminamos con la sección sobre la ubicación de este codelab. Desinstala la app y vuelve a instalarla para ver los resultados.

6. Práctica recomendada: Minimiza el uso de permisos de almacenamiento

Es frecuente que las apps funcionen con fotos almacenadas en un dispositivo. Para permitir que los usuarios seleccionen las imágenes y los videos deseados, estas apps suelen implementar sus propios selectores de archivos, lo que requiere que soliciten permiso a fin de acceder a un almacenamiento amplio. A los usuarios no les gusta otorgar acceso a todas sus fotos, y a los desarrolladores no les gusta mantener un selector de archivos independiente.

Android 13 introduce el selector de fotos: una herramienta que le ofrece al usuario una manera de seleccionar archivos multimedia sin necesidad de otorgarle a una app acceso a toda su biblioteca. También se brinda portabilidad para Android 11 y Android 12 con la ayuda de las actualizaciones del sistema de Google Play.

Para la función en la app de PhotoLog, usaremos el objeto ActivityResultContract de PickMultipleVisualMedia. Cuando esté presente en el dispositivo, usará el selector de fotos de Android y se basará en el intent ACTION_OPEN_DOCUMENT en los dispositivos más antiguos.

Primero, registraremos ActivityResultContract en el archivo AddLogScreen. Para ello, agrega el siguiente bloque de código después de esta línea: // TODO: Step 11. Register ActivityResult to launch the Photo Picker

val pickImage = rememberLauncherForActivityResult(
   PickMultipleVisualMedia(MAX_LOG_PHOTOS_LIMIT),
    viewModel::onPhotoPickerSelect
)

Nota: MAX_LOG_PHOTOS_LIMIT representa el límite máximo de fotos que elegimos establecer cuando las agregamos a un registro (en este caso, 3).

Ahora debemos reemplazar el selector interno que estaba en la app por el selector de fotos de Android. Agrega el siguiente código después del bloque: // TODO: Step 12. Replace the below line showing our internal UI by launching the Android Photo Picker instead

pickImage.launch(PickVisualMediaRequest(PickVisualMedia.ImageOnly))

Si agregas estas dos líneas de código, obtenemos una forma sin permiso de acceder a las fotos del dispositivo que proporciona una UX mucho más agradable y no requiere mantenimiento de código.

Como PhotoLog ya no requiere la cuadrícula de fotos heredada ni los permisos de almacenamiento para acceder a las fotos, ahora debemos quitar todo el código que contiene nuestra cuadrícula de fotos heredada, de la entrada de permisos de almacenamiento en el manifiesto a la lógica detrás de esta, ya que no es necesaria en nuestra app.

7. Recomendación: Usa las APIs de auditoría de acceso a los datos en compilaciones de depuración

¿Tienes una app grande con muchas funciones y colaboradores (o crees que la app lo será en el futuro) que dificulta el seguimiento de los datos de usuarios a los que accede la app? ¿Sabías que incluso si los accesos a los datos provienen de las APIs o los SDKs que se usaron en un momento determinado, pero que todavía permanecen en tu app, esta seguirá siendo responsable del acceso a los datos?

Sabemos que es difícil realizar un seguimiento de todos los lugares en los que tus apps acceden a datos privados, incluidos todos los SDKs y otras dependencias. Por lo tanto, para ayudarte a proporcionar más transparencia sobre cómo tu app y sus dependencias acceden a los datos privados de los usuarios, Android 11 incluye la auditoría de acceso a los datos. Esta API permite a los desarrolladores realizar acciones específicas, como mostrar contenido en un archivo de registro, cada vez que ocurre uno de los siguientes eventos:

  • El código de tu app accede a datos privados.
  • El código de una biblioteca dependiente o SDK accede a datos privados.

Primero, analicemos los conceptos básicos del funcionamiento de la API de auditoría de acceso a los datos en Android. Para adoptar la auditoría de acceso a los datos, registraremos una instancia de AppOpsManager.OnOpNotedCallback (requiere que se oriente a Android 11 o versiones posteriores).

También necesitamos anular los tres métodos en la devolución de llamada, que el sistema invocará cuando la app acceda a datos del usuario de diferentes maneras. Son los siguientes:

  • onNoted(): Se llama cuando una app invoca APIs síncronas (vinculación bidireccional) que acceden a los datos del usuario. Por lo general, son llamadas a la API que no requieren una devolución de llamada.
  • onAsyncNoted(): Se llama cuando una app invoca APIs asíncronas (vinculación unidireccional) que acceden a los datos del usuario. Por lo general, son llamadas a la API que requieren devoluciones de llamada, y el acceso a los datos se produce cuando se invoca la devolución de llamada.
  • onSelfNoted(): Es poco probable. Por ejemplo, se produce cuando una app pasa su propio UID a noteOp().

Ahora, determinemos cuál de estos métodos se aplica a los accesos a los datos de nuestra app de PhotoLog. PhotoLog accede principalmente a los datos del usuario en dos lugares: una cuando activamos la cámara y otra cuando accedemos a la ubicación del usuario. Ambas son llamadas a las APIs asíncronas, ya que requieren relativamente más recursos, por lo que se espera que el sistema invoque a onAsyncNoted() cuando accedemos a los datos respectivos del usuario.

Analicemos cómo adoptar las APIs de auditoría de acceso a los datos para PhotoLog.

Primero, necesitaremos crear una instancia de ​​AppOpsManager.OnOpNotedCallback() y anular los tres métodos anteriores.

Para los tres métodos del objeto, registremos la operación específica que accedió a los datos privados del usuario. Esta operación contendrá más información sobre el tipo de datos del usuario al que se accedió. Además, como esperamos que se llame a onAsyncNoted() cuando nuestra app acceda a la información de la cámara y la ubicación, hagamos algo especial y registremos un emoji de mapa para el acceso a la ubicación y un emoji de cámara para el acceso a la cámara. Para ello, podemos agregar el siguiente bloque de código a: // TODO: Step 1. Create Data Access Audit Listener Object

@RequiresApi(Build.VERSION_CODES.R)
object DataAccessAuditListener : AppOpsManager.OnOpNotedCallback() {
   // For the purposes of this codelab, we are just logging to console,
   // but you can also integrate other logging and reporting systems here to track
   // your app's private data access.
   override fun onNoted(op: SyncNotedAppOp) {
       Log.d("DataAccessAuditListener","Sync Private Data Accessed: ${op.op}")
   }

   override fun onSelfNoted(op: SyncNotedAppOp) {
       Log.d("DataAccessAuditListener","Self Private Data accessed: ${op.op}")
   }

   override fun onAsyncNoted(asyncNotedAppOp: AsyncNotedAppOp) {
       var emoji = when (asyncNotedAppOp.op) {
           OPSTR_COARSE_LOCATION -> "\uD83D\uDDFA"
           OPSTR_CAMERA -> "\uD83D\uDCF8"
           else -> "?"
       }

       Log.d("DataAccessAuditListener", "Async Private Data ($emoji) Accessed:
       ${asyncNotedAppOp.op}")
   }
}

Luego, tendremos que implementar la lógica de devolución de llamada que acabamos de crear. Para obtener mejores resultados, lo haremos lo antes posible, ya que el sistema solo comenzará a realizar un seguimiento del acceso a los datos después de que registremos la devolución de llamada. Para registrar la devolución de llamada, podemos agregar el siguiente bloque de código a: // TODO: Step 2. Register Data Access Audit Callback.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
   val appOpsManager = getSystemService(AppOpsManager::class.java) as AppOpsManager
   appOpsManager.setOnOpNotedCallback(mainExecutor, DataAccessAuditListener)
}

8. Finalización

A continuación, hagamos un repaso de los temas que abordamos. Hicimos lo siguiente:

  • Explicamos por qué la privacidad es importante para tus apps.
  • Presentamos las funciones de privacidad de Android.
  • Implementamos muchas prácticas recomendadas clave de privacidad para las apps, de la siguiente manera:
  • Solicitando permisos en contexto
  • Reduciendo el acceso a la ubicación de tu app
  • Usando el selector de fotos y otras mejoras de almacenamiento
  • Usando las APIs de auditoría de acceso a los datos
  • Implementamos estas prácticas recomendadas en una app existente para mejorar su privacidad.

Esperamos que hayas disfrutado de nuestro recorrido, ya que mejoramos la privacidad y la experiencia del usuario de PhotoLog, y aprendimos muchos conceptos durante el trayecto.

Encuentra nuestro código de referencia (opcional):

Si aún no lo hiciste, puedes consultar el código de solución para el codelab en la carpeta PhotoLog_End. Si seguiste las instrucciones de este codelab con detenimiento, el código de la carpeta PhotoLog_Start debería ser idéntico al de la carpeta PhotoLog_End.

Más información

Eso es todo. Para obtener más información sobre las prácticas recomendadas que mencionamos anteriormente, consulta la página de destino de privacidad de Android.