Cambios en el comportamiento: apps orientadas a Android 17 o versiones posteriores

Al igual que las versiones anteriores, Android 17 incluye cambios de comportamiento que podrían afectar tu app. Los siguientes cambios se aplican exclusivamente a las apps que tienen como objetivo Android 17 o versiones posteriores. Si tu app está orientada a Android 17 o versiones posteriores, debes modificarla para que admita estos comportamientos, cuando corresponda.

Asegúrate de revisar también la lista de cambios en el comportamiento que afectan a todas las apps que se ejecutan en Android 17, independientemente de targetSdkVersion de la app.

Experiencia del usuario y la IU del sistema

Android 17 incluye los siguientes cambios que tienen como objetivo crear una experiencia del usuario más coherente e intuitiva.

Widget de límite de memoria

从 Android 17 开始,系统会对以 Android 17(API 级别 37)或更高版本为目标平台的应用的 RemoteViews 更新强制执行严格的内存限制。当应用将 RemoteViews 对象传递给 AppWidgetManager.updateAppWidget 时,system_server 会计算载荷中每个方向和大小存储桶中所有图片数据的总内存占用。如果总内存超出系统上限 (mMaxWidgetBitmapMemory),系统会拒绝更新并抛出 IllegalArgumentException:

java.lang.IllegalArgumentException: RemoteViews for widget update exceeds maximum bitmap memory usage (used: XXXXX, max: YYYYY)

对于以 API 级别 36 或更低级别为目标平台的应用,系统会将此内存违规行为记录为警告 (Slog.w),而不会抛出异常。

系统如何计算内存上限

系统会将活跃显示配置的 widget 位图内存预算设置为 6 * width * height 字节(1.5 * 4 * width * height,相当于全屏 ARGB_8888 缓冲区大小的 1.5 倍)。在 API 级别 37 及更高级别下,此计算包括以下两项:

  • 存储在 RemoteViews.mBitmapCache 中的内存中 Bitmap 和位图支持的 Icon 对象(Icon.TYPE_BITMAP 和 Icon.TYPE_ADAPTIVE_BITMAP),例如对 setImageViewBitmap、Icon.createWithBitmap、Icon.createWithAdaptiveBitmap 或 Jetpack Glance ImageProvider(bitmap) 的调用。mBitmapCache 中每个唯一的 Bitmap 或位图支持的 Icon 实例在每个 RemoteViews 层次结构中都会被计数一次。
  • URI 支持的图片(Icon.createWithContentUri、setImageViewUri 或 Glance ImageProvider(uri))不计入 system_server 中的 RemoteViews IPC 字节上限,但 widget 宿主进程会以完整分辨率将引用的 content:// 文件解码到其自己的堆中。在通过 FileProvider URI 公开降采样图片之前,请务必先将其写入 context.cacheDir。

缓解步骤

为了确保在手机、平板电脑和可折叠设备上,微件更新不会超出内存上限,请执行以下操作:

  1. 根据 WindowMetrics 计算运行时内存预算:在 API 级别 30 及更高级别,从 WindowManager.currentWindowMetrics.bounds 而不是 resources.displayMetrics 读取活动窗口边界。在多分辨率设备和可折叠设备上,displayMetrics 可能会报告非活动显示屏尺寸。将总微件图片预算估算为 6L * bounds.width() * bounds.height(),如以下示例所示:

    private const val FALLBACK_MAX_BYTES = 15_000_000L
    
    fun calculateWidgetMemoryCapBytes(context: Context): Long {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            val wm = context.getSystemService(WindowManager::class.java)
                ?: return FALLBACK_MAX_BYTES
            val bounds = wm.currentWindowMetrics.bounds
            return 6L * bounds.width().toLong() * bounds.height().toLong()
        }
        val dm = context.resources.displayMetrics
        return 6L * dm.widthPixels.toLong() * dm.heightPixels.toLong()
    }
    
  2. 将图片降采样到微件的可查看尺寸,并使用 RGB_565:使用 2 的幂 BitmapFactory.Options.inSampleSize 值(大小为微件的最大显示尺寸 [maxWidthDp x maxHeightDp])解码位图,并对不透明的照片和缩略图使用 Bitmap.Config.RGB_565(每个像素 2 字节)而不是 ARGB_8888(每个像素 4 字节)。

  3. 在 Jetpack Glance 中,将位图解码提升到 provideContent 之外:使用 SizeMode.Responsive 或 SizeMode.Exact 时,Glance 会针对每个尺寸分桶调用一次 provideContent lambda。在调用 provideContent 之前,在 provideGlance 中对每个共享的 Bitmap 解码一次,以便所有大小存储桶都引用 RemoteViews.mBitmapCache 中的同一 Bitmap 实例。

  4. 替换图片时使用完整的 updateAppWidget 调用:在传统 AppWidgetProvider 实现中,避免调用 AppWidgetManager.partiallyUpdateAppWidget 来交换 Bitmap 或 Icon 视图。部分更新会将新操作合并到 system_server 中的现有 RemoteViews 树中,而不会逐出之前缓存的映像操作。构建完整的 RemoteViews 布局,并改为调用 updateAppWidget。

  5. 提供错误后备布局:在 Jetpack Glance 中,替换 onCompositionError,以便在更新失败时呈现零位图 RemoteViews 后备。

您可以通过运行以下命令,在已连接的设备上检查微件当前的序列化位图内存用量:

adb shell dumpsys appwidget

Funcionalidad principal

Android 17 incluye los siguientes cambios que modifican o expanden varias funciones principales del sistema Android.

Nueva implementación sin bloqueos de MessageQueue

A partir de Android 17, las apps orientadas a Android 17 (nivel de API 37) o versiones posteriores reciben una nueva implementación sin bloqueos de android.os.MessageQueue. La nueva implementación mejora el rendimiento y reduce los fotogramas perdidos, pero puede interrumpir los clientes que reflejan los campos y métodos privados de MessageQueue.

Para obtener más información, incluidas las estrategias de mitigación, consulta la guía de cambio de comportamiento de MessageQueue.

Los campos finales estáticos ahora no se pueden modificar

Las apps que se ejecutan en Android 17 o versiones posteriores y que se orientan a Android 17 (nivel de API 37) o versiones posteriores no pueden cambiar los campos static final. Si una app intenta cambiar un campo static final con la reflexión, se producirá una IllegalAccessException. Si intentas modificar uno de estos campos a través de las APIs de JNI (como SetStaticLongField()), la app fallará.

Accesibilidad

Android 17 incluye los siguientes cambios para mejorar la accesibilidad.

Compatibilidad de accesibilidad con la escritura compleja con teclado físico del IME

Esta función presenta las nuevas APIs de AccessibilityEvent y TextAttribute para mejorar las descripciones por voz del lector de pantalla para la entrada de idiomas CJKV. Las apps de IME de CJKV ahora pueden indicar si se seleccionó un candidato de conversión de texto durante la composición de texto. Las apps con campos de edición pueden especificar tipos de cambios de texto cuando envían eventos de accesibilidad de texto modificado. Por ejemplo, las apps pueden especificar que se produjo un cambio de texto durante la composición de texto o que un cambio de texto se produjo a partir de una confirmación. Esto permite que los servicios de accesibilidad, como los lectores de pantalla, proporcionen comentarios más precisos según la naturaleza de la modificación del texto.

Adopción de apps

  • Apps de IME: Cuando se configura el texto de redacción en los campos de edición, los IME pueden usar TextAttribute.Builder.setTextSuggestionSelected() para indicar si se seleccionó un candidato de conversión específico.

  • Apps con campos de edición: Las apps que mantienen un InputConnection personalizado pueden recuperar datos de selección de candidatos llamando a TextAttribute.isTextSuggestionSelected(). Estas apps deben llamar a AccessibilityEvent.setTextChangeTypes() cuando se envían eventos TYPE_VIEW_TEXT_CHANGED. Las apps orientadas a Android 17 (nivel de API 37) que usen el TextView estándar tendrán esta función habilitada de forma predeterminada. (Es decir, TextView se encargará de recuperar datos del IME y establecer tipos de cambios de texto cuando se envíen eventos a los servicios de accesibilidad).

  • Servicios de accesibilidad: Los servicios de accesibilidad que procesan eventos TYPE_VIEW_TEXT_CHANGED pueden llamar a AccessibilityEvent.getTextChangeTypes() para identificar la naturaleza de la modificación y ajustar sus estrategias de comentarios según corresponda.

Privacidad

Android 17 incluye los siguientes cambios para mejorar la privacidad del usuario.

ECH (Encrypted Client Hello) habilitado

Android 17 引入了对加密客户端问候 (ECH) 的平台支持,这是一种 TLS 扩展,可通过在 TLS 握手中加密服务器名称指示 (SNI) 来增强用户隐私。这种加密有助于防止网络观察者轻易识别出应用连接到的特定网域。

对于以 Android 17(API 级别 37)或更高版本为目标平台的应用,ECH 用于 TLS 连接。只有在应用使用的网络库(例如 HttpEngine、WebView 或 OkHttp)集成了 ECH 支持,并且远程服务器也支持 ECH 协议时,ECH 才会处于活跃状态。如果无法协商 ECH,客户端会发送包含随机内容的 ECH 扩展程序(一种称为 ECH GREASE 的机制)。如需详细了解 ECH GREASE 的运作方式,请参阅 RFC 9849。

为了允许应用自定义此行为,Android 17 向网络安全配置文件添加了新的 <domainEncryption> 元素。开发者可以在 <base-config> 或 <domain-config> 标记中使用 <domainEncryption>,以全局或按网域选择 ECH 模式(例如 "enabled" 或 "disabled")。

如需了解详情,请参阅加密客户端问候文档。

Se requiere el permiso de red local para las apps que se orientan a Android 17

Android 17 presenta el permiso de tiempo de ejecución ACCESS_LOCAL_NETWORK para proteger a los usuarios del acceso no autorizado a la red local. Como esto se incluye en el grupo de permisos NEARBY_DEVICES existente, no se vuelve a solicitar a los usuarios que ya otorgaron otros permisos de NEARBY_DEVICES. Este nuevo requisito evita que las apps maliciosas aprovechen el acceso sin restricciones a la red local para realizar un seguimiento encubierto de los usuarios y crear huellas digitales. Si declaras y solicitas este permiso, tu app podrá descubrir dispositivos en la red de área local (LAN) y conectarse a ellos, como dispositivos inteligentes para la casa o receptores de transmisión.

Las apps que se orientan a Android 17 (nivel de API 37) o versiones posteriores ahora tienen dos formas de mantener la comunicación con los dispositivos de LAN: adoptar selectores de dispositivos que preserven la privacidad y estén mediados por el sistema para omitir el mensaje de permiso, o solicitar explícitamente este nuevo permiso durante el tiempo de ejecución para mantener la comunicación con la red local.

Para obtener más información, consulta la documentación sobre el permiso de red local.

Ocultar contraseñas de dispositivos físicos

Si una app se orienta a Android 17 (nivel de API 37) o una versión posterior, y el usuario utiliza un dispositivo de entrada físico (por ejemplo, un teclado externo), el sistema operativo Android aplica el nuevo parámetro de configuración show_passwords_physical a todos los caracteres del campo de contraseña. De forma predeterminada, ese parámetro de configuración oculta todos los caracteres de la contraseña.

El sistema Android muestra el último carácter de la contraseña que se escribió para ayudar al usuario a ver si la escribió de forma incorrecta. Sin embargo, esto es mucho menos necesario con teclados externos más grandes. Además, los dispositivos con teclados externos suelen tener pantallas más grandes, lo que aumenta el peligro de que alguien vea la contraseña que se escribe.

Si el usuario usa la pantalla táctil del dispositivo, el sistema aplica el nuevo parámetro de configuración show_passwords_touch.

Protección de OTP para mensajes SMS estándar

从 Android 17 开始,Android 将扩展其短信动态密码保护功能,使其适用于标准短信(包含动态密码但不使用 WebOTP 或短信检索器格式的短信)。对于大多数以 Android 17(API 级别 37)或更高版本为目标平台的应用,这些短信在收到 3 小时后才可供使用。此延迟旨在帮助防止动态密码被劫持。在这 3 小时的延迟期间,SMS_RECEIVED_ACTION 广播会被扣留,短信提供商数据库查询会被过滤。延迟后,这些应用便可收到短信。

某些应用(例如默认短信助理应用、已连接设备的配套应用等)不受此延迟的影响。所有依赖读取短信来提取动态密码的应用都应改用 SMS Retriever 或 SMS User Consent API,以确保功能正常运行。

Seguridad

Android 17 incluye las siguientes mejoras en la seguridad de los dispositivos y las apps.

Seguridad de la actividad

En Android 17, la plataforma continúa su cambio hacia una arquitectura "segura de forma predeterminada", con la introducción de un conjunto de mejoras diseñadas para mitigar los exploits de gravedad alta, como el phishing, el secuestro de interacciones y los ataques de diputado confundido. Esta actualización requiere que los desarrolladores habiliten de forma explícita los nuevos estándares de seguridad para mantener la compatibilidad de las apps y la protección de los usuarios.

Entre los impactos clave para los desarrolladores, se incluyen los siguientes:

  • Refuerzo de BAL y habilitación mejorada: Estamos perfeccionando las restricciones de inicio de actividades en segundo plano (BAL) extendiendo las protecciones a IntentSender. Los desarrolladores deben migrar de la constante heredada MODE_BACKGROUND_ACTIVITY_START_ALLOWED. En su lugar, debes adoptar controles detallados, como MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE, que restringe los inicios de actividades a situaciones en las que la app de llamadas es visible, lo que reduce significativamente la superficie de ataque.
  • Herramientas de adopción: Los desarrolladores deben usar el modo estricto y las verificaciones de lint actualizadas para identificar patrones heredados y garantizar la preparación para los requisitos futuros del SDK de destino.

Habilitar la CT de forma predeterminada

Si una app se orienta a Android 17 (nivel de API 37) o versiones posteriores, el certificado de transparencia (CT) está habilitado de forma predeterminada. (En Android 16, la CT está disponible, pero las apps debían habilitarla).

DCL nativo más seguro: C

如果您的应用以 Android 17(API 级别 37)或更高版本为目标平台,Android 14 中针对 DEX 和 JAR 文件引入的更安全的动态代码加载 (DCL) 保护机制现在也适用于原生库。

使用 System.load() 加载的所有原生文件都必须标记为只读。 否则,系统会抛出 UnsatisfiedLinkError。

我们建议应用尽可能避免动态加载代码,因为这样做会大大增加应用因代码注入或代码篡改而遭到入侵的风险。

Restringe los campos de PII en la vista de datos de CP2

En el caso de las apps que se orientan a Android 17 (nivel de API 37) y versiones posteriores, el proveedor de contactos 2 (CP2) restringe ciertas columnas que contienen información de identificación personal (PII) de la vista de datos. Cuando se habilita este cambio, estas columnas se quitan de la vista de datos para mejorar la privacidad del usuario. Las columnas restringidas incluyen las siguientes:

Las apps que usan estas columnas de ContactsContract.Data pueden extraerlas de ContactsContract.RawContacts en su lugar, uniéndolas con RAW_CONTACT_ID.

Aplica verificaciones estrictas de SQL en CP2

En el caso de las apps que se orientan a Android 17 (nivel de API 37) y versiones posteriores, el Proveedor de contactos 2 (CP2) aplica una validación estricta de consultas en SQL cuando se accede a la tabla ContactsContract.Data sin el permiso READ_CONTACTS.

Con este cambio, si una app no tiene el permiso READ_CONTACTS, se configuran las opciones StrictColumns y StrictGrammar cuando se consulta la tabla ContactsContract.Data. Si una consulta usa un patrón que no es compatible con estos, se rechazará y se generará una excepción.

Inteligencia

Android 17 incluye los siguientes cambios en la inteligencia del sistema.

Baja de setContentCaptureEnabled

在某些设备上,内容捕获功能默认处于启用状态,以便设备端 AI 功能分析屏幕内容,从而提供智能体验。

从 Android 17 开始,ContentCaptureManager.setContentCaptureEnabled(boolean) API 方法已被废弃。对于以 Android 17(API 级别 37)或更高版本为目标平台的应用,调用 setContentCaptureEnabled(false) 不再会停用内容捕获功能。

如果您的应用需要继续停用内容捕获功能或限制系统捕获屏幕内容,您必须改用 FLAG_SECURE 窗口布局参数。

如需停用内容捕获,请在窗口中设置 FLAG_SECURE 标志,如以下示例所示:

Kotlin

window.setFlags(
    WindowManager.LayoutParams.FLAG_SECURE,
    WindowManager.LayoutParams.FLAG_SECURE
)

Java

getWindow().setFlags(
    WindowManager.LayoutParams.FLAG_SECURE,
    WindowManager.LayoutParams.FLAG_SECURE
);

如需了解详情,请参阅 WindowManager.LayoutParams.FLAG_SECURE 参考文档。

Medios

Android 17 incluye los siguientes cambios en el comportamiento de los medios.

Refuerzo del audio en segundo plano

A partir de Android 17, el framework de audio aplica restricciones a las interacciones de audio en segundo plano, como la reproducción de audio, las solicitudes de foco de audio y las APIs de cambio de volumen, para garantizar que el usuario inicie estos cambios de forma intencional.

Algunas restricciones de audio se aplican a todas las apps. Sin embargo, las restricciones son más estrictas si una app se orienta a Android 17 (nivel de API 37). Si una de estas apps interactúa con el audio mientras está en segundo plano, debe tener un servicio en primer plano en ejecución. Además, la app debe cumplir con uno o ambos de estos requisitos:

  • El servicio en primer plano debe tener capacidades de uso en curso (WIU).
  • La app debe tener el permiso de alarma exacta y debe interactuar con las transmisiones de audio USAGE_ALARM.

Para obtener más información, incluidas las estrategias de mitigación, consulta Endurecimiento del audio en segundo plano.

Factores de forma de los dispositivos

Android 17 incluye los siguientes cambios para mejorar la experiencia del usuario en una variedad de tamaños y factores de forma de dispositivos.

Cambios en la API de la plataforma para ignorar las restricciones de orientación, cambio de tamaño y relación de aspecto en pantallas grandes (sw>=600 dp)

我们在 Android 16 中引入了平台 API 变更,以便针对以 API 级别 36 或更高级别为目标平台的应用,忽略大屏设备(sw >= 600dp)上的屏幕方向、尺寸调整和宽高比限制。开发者可以选择在 SDK 36 中选择退出这些变更,但对于以 Android 17(API 级别 37)或更高版本为目标平台的应用,此选择退出选项将不再可用。

如需了解详情,请参阅系统会忽略对屏幕方向和可调整大小性的限制。

Conectividad

Android 17 presenta el siguiente cambio para mejorar la coherencia y alinearse con el comportamiento estándar de Java InputStream para los sockets RFCOMM de Bluetooth.

Comportamiento coherente de read() de BluetoothSocket para RFCOMM

En el caso de las apps orientadas a Android 17 (nivel de API 37), el método read() de InputStream obtenido de un BluetoothSocket basado en RFCOMM ahora devuelve -1 cuando se cierra el socket o se interrumpe la conexión.

Este cambio hace que el comportamiento del socket de RFCOMM sea coherente con los sockets de LE CoC y se alinee con la documentación estándar de InputStream.read(), que indica que se devuelve -1 cuando se alcanza el final de la transmisión.

Es posible que este cambio afecte a las apps que dependen únicamente de detectar una IOException para salir de un bucle de lectura, por lo que deben actualizar los bucles de lectura de BluetoothSocket para verificar de forma explícita si se devuelve un valor de -1. Esto garantiza que el bucle finalice correctamente cuando el dispositivo remoto se desconecte o se cierre el socket. Para ver un ejemplo de la implementación recomendada, consulta el fragmento de código en la guía Transferencia de datos por Bluetooth.