android.os.FileUriExposedException: file:///... exposed beyond app through Intent.getData() significa que la app envió una URI file:// fuera de su proceso mediante un Intent o un flujo relacionado entre apps. Android genera esta excepción de diagnóstico en las apps orientadas a Android N, nivel de API 24, o una versión posterior. La primera medida más segura es revisar los datos del Intent, EXTRA_STREAM, ClipData y cualquier URI de salida de la cámara para localizar el valor file:// restante antes de modificar la configuración del proveedor.

Qué significa android.os.FileUriExposedException

La excepción se aplica a la exposición de archivos entre apps, no a todos los errores de acceso a archivos en Android. Suele aparecer en flujos de ACTION_VIEW, ACTION_SEND, ACTION_SEND_MULTIPLE, captura con la cámara, archivos adjuntos, impresión y uso compartido.

La excepción se incorporó en el nivel de API 24 y se genera en las apps orientadas a Android N o una versión posterior. La versión de Android del dispositivo y el targetSdkVersion de la app son condiciones relacionadas, pero no son equivalentes. La referencia de FileUriExposedException de Android identifica como alternativa compatible una URI content:// con acceso temporal para la app receptora.

Que la excepción no aparezca con otra configuración del SDK no significa que sea seguro compartir URI file://. Android describe esta excepción como una medida de diagnóstico, no como un mecanismo de seguridad por sí sola.

Localiza la URI file:// que sale de la app

Cuándo corresponde: Realiza esta revisión antes de configurar FileProvider, incluso cuando el mensaje termine en Intent.getData() o ClipData.Item.getUri().

Requisitos previos: El texto de la excepción y el código fuente del flujo del Intent saliente.

  1. Revisa la URI que aparece en la excepción y confirma que su esquema sea file.
  2. Examina los marcos de la aplicación inmediatamente anteriores a la excepción para localizar dónde se crea o adjunta la URI saliente.
  3. Busca usos de Uri.fromFile() y el análisis de valores que comiencen por file://.
  4. Revisa por separado los datos del Intent, EXTRA_STREAM, cada elemento de ClipData y cualquier URI de salida de la cámara.
  5. Registra el origen y la ubicación real del archivo. Estos datos determinan si corresponde usar una URI autorizada existente o FileProvider.

Resultado esperado: Identificas todas las URI file:// del flujo que falla y el punto en el que cada una entra en el Intent.

Riesgo y advertencia: Este paso de diagnóstico es de solo lectura. No supongas que basta con cambiar únicamente los datos del Intent si queda otro campo que contiene una URI. Debes confirmar si interviene ClipData revisando el Intent real.

Elige el origen correcto de la URI de contenido

Cuándo corresponde esta solución: Usa esta opción cuando el origen ya proporcione una URI content:// autorizada, como un documento seleccionado por el usuario o un elemento multimedia compartido.

Requisitos previos: Confirma que la URI existente siga autorizada para la operación prevista.

  1. Conserva la URI content:// existente en lugar de convertirla en una ruta del sistema de archivos.
  2. Usa el Marco de acceso al almacenamiento (Storage Access Framework) para los documentos seleccionados por el usuario.
  3. Usa una URI de MediaStore adecuada para el contenido multimedia compartido.
  4. Coloca esa URI autorizada en el campo necesario del Intent y continúa con las comprobaciones de permisos temporales que aparecen más adelante.

Resultado esperado: El flujo entre apps conserva una URI content:// autorizada sin introducir un nuevo FileProvider.

Riesgo y reversión: El riesgo es bajo. Si el origen no puede proporcionar una URI autorizada adecuada para la operación, usa FileProvider únicamente cuando la app sea propietaria del archivo. No pases todas las rutas por FileProvider de forma predeterminada.

Configura AndroidX FileProvider en el manifiesto

Cuándo corresponde esta solución: Usa AndroidX FileProvider cuando la app sea propietaria de un archivo y necesite exponerlo a otra app.

Requisitos previos: AndroidX FileProvider debe estar disponible, debes tener control sobre el manifiesto y debes poder crear un recurso XML de rutas del proveedor. La autoridad exacta depende de cada app.

  1. Declara un proveedor cuyo android:name sea FileProvider o la subclase de FileProvider de la app.
  2. Establece android:authorities en una autoridad específica de la app.
  3. Establece android:exported="false".
  4. Establece android:grantUriPermissions="true".
  5. Agrega la entrada de metadatos de FileProvider que haga referencia al recurso XML de rutas del proveedor.
  6. Confirma que la autoridad utilizada posteriormente para generar la URI coincida exactamente con la autoridad del manifiesto.

La documentación de AndroidX FileProvider admite este modelo de proveedor y sus permisos temporales para URI.

Resultado esperado: La app dispone de un proveedor no exportado capaz de generar URI content:// autorizadas para los archivos configurados.

Riesgo y reversión: El riesgo es bajo. Elimina el proveedor y sus metadatos si se retira la función para compartir. No publiques ni copies un valor de autoridad universal, ya que debe coincidir con la configuración de la app.

Limita el XML file_paths a los archivos que deban compartirse

Cuándo corresponde esta solución: Completa este paso siempre que se utilice FileProvider.

Requisitos previos: Debes conocer el directorio exacto que contiene el archivo que se compartirá.

  1. Define únicamente el directorio o los directorios necesarios para la función de uso compartido.
  2. Da preferencia al directorio más específico que todavía contenga el archivo previsto.
  3. Evita raíces amplias que incluyan archivos no relacionados de la app o del almacenamiento compartido.
  4. Comprueba que el archivo se resuelva dentro de una de las raíces configuradas antes de generar su URI de contenido.

No puede proporcionarse una entrada XML universal exacta porque depende de dónde almacene el archivo cada app. Los cambios de Android 7.0 relacionados con la exposición de URI de archivo recomiendan migrar a FileProvider en lugar de seguir exponiendo URI de archivo.

Resultado esperado: FileProvider puede resolver el archivo previsto, mientras que las ubicaciones no relacionadas permanecen fuera del ámbito configurado del proveedor.

Riesgo y reversión: El riesgo es medio porque una raíz demasiado amplia puede exponer más archivos de los previstos. Restringe el XML de rutas si una revisión o las pruebas muestran una cobertura excesiva; no lo amplíes solo para eludir un fallo relacionado con una raíz configurada.

Sustituye file:// y concede únicamente el acceso temporal necesario

Crea y adjunta la URI de contenido de FileProvider

Cuándo corresponde esta solución: Úsala después de configurar la autoridad del manifiesto y rutas restringidas del proveedor para un archivo propiedad de la app.

Requisitos previos: El archivo debe encontrarse dentro de una raíz permitida del proveedor, y la autoridad utilizada para generar la URI debe coincidir con la del manifiesto.

  1. Crea la URI con FileProvider en lugar de usar Uri.fromFile() o un valor file:// construido manualmente.
  2. Coloca la URI content:// resultante en los datos del Intent, EXTRA_STREAM o el campo de salida de la cámara que requiera el flujo existente.
  3. Si hay varios archivos, sustituye todas las URI expuestas, no solo el primer elemento.
  4. Revisa ClipData y cualquier otro campo que pueda contener una URI para evitar que la URI de archivo anterior permanezca en otro lugar.

Resultado esperado: El Intent saliente transporta URI content:// de FileProvider en lugar de URI file://.

Riesgo y reversión: El riesgo es bajo y esta operación no elimina el archivo. No restaures en producción el uso compartido de URI file:// entre apps; el origen anterior de la URI solo es adecuado para una depuración local aislada que no exponga la URI.

Concede permisos temporales para la URI

Cuándo corresponde esta solución: Usa permisos temporales cuando la app receptora necesite leer o modificar el contenido al que hace referencia el Intent.

Requisitos previos: El Intent debe contener una URI content:// válida y debe conocerse el nivel de acceso que necesita el receptor.

  1. Agrega FLAG_GRANT_READ_URI_PERMISSION cuando el receptor deba leer el archivo.
  2. Agrega FLAG_GRANT_WRITE_URI_PERMISSION solo cuando el receptor deba modificar el archivo.
  3. Adjunta la URI mediante ClipData cuando sea necesario para propagar el permiso en el flujo compatible.
  4. No agregues permisos más amplios para el sistema de archivos ni acceso de escritura que no esté relacionado con la operación.

Resultado esperado: El receptor obtiene el acceso temporal mínimo necesario mientras el proveedor permanece sin exportar.

Riesgo y reversión: El riesgo es bajo cuando el acceso está limitado. Elimina los indicadores de permiso de escritura o lectura cuando el receptor ya no necesite esa capacidad. La propagación mediante ClipData depende del flujo y debe probarse en las versiones de Android compatibles.

Comprueba que la app receptora pueda acceder a la URI de contenido

Cuándo corresponde: Comprueba el flujo completo después de sustituir la URI y aplicar los permisos temporales.

Requisitos previos: El componente receptor previsto debe estar disponible para las pruebas.

  1. Confirma que todas las URI salientes usen el esquema content.
  2. Confirma que la autoridad de la URI generada coincida exactamente con la autoridad del proveedor declarada en el manifiesto.
  3. Confirma que el archivo se encuentre dentro de una raíz configurada del proveedor.
  4. Vuelve a revisar los datos del Intent, EXTRA_STREAM, ClipData y las URI de salida para detectar cualquier valor file:// restante.
  5. Prueba la app receptora real con permiso de lectura y agrega permiso de escritura solo si es necesario modificar el archivo.
  6. Confirma que el receptor pueda realizar la operación prevista sin ampliar la raíz del proveedor ni los permisos.

Resultado esperado: El flujo saliente deja de exponer una URI de archivo y el componente receptor puede acceder a la URI de contenido autorizada con el permiso mínimo necesario. Esto debe confirmarse mediante pruebas; cambiar únicamente el esquema no garantiza que el receptor pueda acceder al contenido.

Riesgo y advertencia: La comprobación tiene un riesgo bajo. Si la excepción original desaparece, pero el acceso sigue fallando, revisa manualmente la autoridad, los metadatos del proveedor, las rutas configuradas, los permisos para la URI y la ubicación real del archivo. No desactives la detección mediante StrictMode.VmPolicy, reduzcas el targetSdkVersion, expongas raíces amplias ni concedas acceso de escritura de forma predeterminada. Esas medidas ocultan o debilitan el problema en lugar de corregir el diseño del uso compartido entre apps.