Recursos e APIs

O Android 17 introduz ótimos novos recursos e APIs para desenvolvedores. As seções a seguir resumem esses recursos para ajudar você a começar a usar as APIs relacionadas.

Para uma lista detalhada das APIs novas, modificadas e removidas, leia o Relatório de diferenças da API. Para ver detalhes sobre as novas APIs, acesse a Referência da API do Android. As APIs novas estão em destaque para melhor visibilidade.

Você também precisa analisar as áreas em que as mudanças na plataforma podem afetar seus apps. Para mais informações, consulte as seguintes páginas:

Funcionalidade principal

O Android 17 adiciona os seguintes novos recursos relacionados à funcionalidade principal do Android.

Novos acionadores do ProfilingManager

Android 17 adds several new system triggers to ProfilingManager to help you collect in-depth data to debug performance issues.

The new triggers are:

To understand how to set up the system trigger, see the documentation on trigger-based profiling and how to retrieve and analyze profiling data documentation.

APIs JobDebugInfo

Android 17 introduces new JobDebugInfo APIs to help developers debug their JobScheduler jobs--why they aren't running, how long they ran for, and other aggregated information.

The first method of the expanded JobDebugInfo APIs is getPendingJobReasonStats(), which returns a map of reasons why the job was in a pending execution state and their respective cumulative pending durations. This method joins the getPendingJobReasonsHistory() and getPendingJobReasons() methods to give you insight into why a scheduled job is not running as expected, but simplifies information retrieval by making both duration and job reason available in a single method.

For example, for a specified jobId, the method might return PENDING_JOB_REASON_CONSTRAINT_CHARGING and a duration of 60000 ms, indicating the job was pending for 60000ms due to the charging constraint not being satisfied.

Reduza os bloqueios de ativação com suporte ao listener para alarmes allow-while-idle

O Android 17 introduz uma nova variante de AlarmManager.setExactAndAllowWhileIdle que aceita um OnAlarmListener em vez de um PendingIntent. Esse novo mecanismo baseado em callback é ideal para apps que dependem de wakelocks contínuos para realizar tarefas periódicas, como apps de mensagens que mantêm conexões de soquete.

Privacidade

O Android 17 inclui os seguintes novos recursos para melhorar a privacidade do usuário.

Suporte da plataforma para Encrypted Client Hello (ECH)

Android 17 introduces platform support for Encrypted Client Hello (ECH), a significant privacy enhancement for network communications. ECH is a TLS 1.3 extension that encrypts the Server Name Indication (SNI) during the initial TLS handshake. This encryption helps protect user privacy by making it more difficult for network intermediaries to identify the specific domain an app is connecting to.

The platform now includes the necessary APIs for networking libraries to implement ECH. This includes new capabilities in DnsResolver to query for HTTPS DNS records containing ECH configurations, and new methods in Conscrypt's SSLEngines and SSLSockets to enable ECH by passing in these configurations when connecting to a domain. Developers can configure ECH preferences, such as enabling it opportunistically or mandating its use, through the new <domainEncryption> element within the Network Security Configuration file, applicable globally or on a per-domain basis.

Popular networking libraries such as HttpEngine, WebView, and OkHttp are expected to integrate these platform APIs in future updates, making it easier for apps to adopt ECH and enhance user privacy.

For more information, see the Encrypted Client Hello documentation.

Seletor de contatos do Android

The Android Contact Picker is a standardized, browsable interface for users to share contacts with your app. Available on devices running Android 17 (API level 37) or higher, the picker offers a privacy-preserving alternative to the broad READ_CONTACTS permission. Instead of requesting access to the user's entire address book, your app specifies the data fields it needs, such as phone numbers or email addresses, and the user selects specific contacts to share. This grants your app read access to only the selected data, ensuring granular control while providing a consistent user experience with built-in search, profile switching, and multi-selection capabilities without having to build or maintain the UI.

For more information, see the contact picker documentation.

Segurança

O Android 17 adiciona os seguintes novos recursos para melhorar a segurança de dispositivos e apps.

Modo de Proteção Avançada do Android (AAPM)

O modo de Proteção Avançada do Android oferece aos usuários do Android um novo conjunto de recursos de segurança poderosos, marcando uma etapa significativa na proteção dos usuários, principalmente aqueles com maior risco, contra ataques sofisticados. Projetado como um recurso de ativação, o AAPM é ativado com uma única configuração que os usuários podem ativar a qualquer momento para aplicar um conjunto de proteções de segurança.

Essas configurações principais incluem o bloqueio da instalação de apps de fontes desconhecidas (sideload), a restrição da sinalização de dados USB e a obrigatoriedade da verificação do Google Play Protect, o que reduz significativamente a área de superfície de ataque do dispositivo. Os desenvolvedores podem se integrar a esse recurso usando a API AdvancedProtectionManager para detectar o status do modo, permitindo que os aplicativos adotem automaticamente uma postura de segurança reforçada ou restrinjam funcionalidades de alto risco quando um usuário ativa o recurso.

Assinatura de APK PQC

O Android agora oferece suporte a um esquema híbrido de assinatura de APK para proteger a identidade de assinatura do seu app contra a possível ameaça de ataques que usam computação quântica. Esse recurso apresenta um novo esquema de assinatura de APK, que permite parear uma chave de assinatura clássica (como RSA ou EC) com um novo algoritmo de criptografia pós-quântica (PQC, na sigla em inglês) (ML-DSA).

Essa abordagem híbrida garante que seu app permaneça seguro contra futuros ataques quânticos, mantendo a compatibilidade com versões anteriores com versões mais antigas do Android e dispositivos que dependem da verificação de assinatura clássica.

Impacto nos desenvolvedores

  • Apps que usam a Assinatura de apps do Google Play:se você usa a Assinatura de apps do Google Play, aguarde que o Google Play ofereça a opção de fazer upgrade de uma assinatura híbrida usando uma chave PQC gerada pelo Google Play, garantindo que seu app esteja protegido sem exigir gerenciamento manual de chaves.
  • Apps que usam chaves autogerenciadas:os desenvolvedores que gerenciam as próprias chaves de assinatura podem usar ferramentas de build do Android atualizadas (como o apksigner) para alternar para uma identidade híbrida, combinando uma chave PQC com uma nova chave clássica. (Você precisa criar uma nova chave clássica. Não é possível reutilizar a antiga.)

Conectividade

O Android 17 adiciona os seguintes recursos para melhorar a conectividade de dispositivos e apps.

Redes de satélite restritas

Implements optimizations to enable apps to function effectively over low-bandwidth satellite networks.

Experiência do usuário e interface do sistema

O Android 17 inclui as seguintes mudanças para melhorar a experiência do usuário.

Fluxo de volume dedicado do Google Assistente

Android 17 introduces a dedicated Assistant volume stream for Assistant apps, for playback with USAGE_ASSISTANT. This change decouples Assistant audio from the standard media stream, providing users with isolated control over both volumes. This enables scenarios such as muting media playback while maintaining audibility for Assistant responses, and the other way around.

Assistant apps with access to the new MODE_ASSISTANT_CONVERSATION audio mode can further improve the volume control consistency. Assistant apps can use this mode to provide a hint to the system about an active Assistant session, ensuring the Assistant stream can be controlled outside of the active USAGE_ASSISTANT playback or with connected Bluetooth peripherals.

Handoff

O Handoff é um novo recurso e uma API que será lançada no Android 17. Os desenvolvedores de apps podem integrar esse recurso para oferecer continuidade entre dispositivos aos usuários. Ele permite que o usuário inicie uma atividade de app em um dispositivo Android e transfira para outro dispositivo Android. O Handoff é executado em segundo plano no dispositivo de um usuário e mostra as atividades disponíveis dos outros dispositivos próximos do usuário em vários pontos de entrada, como o iniciador e a barra de tarefas, no dispositivo de recebimento.

Os apps podem designar o Handoff para iniciar o mesmo app Android nativo, se ele estiver instalado e disponível no dispositivo de recebimento. Nesse fluxo de app para app, o usuário é vinculado diretamente à atividade designada. Como alternativa, a transferência do app para a Web pode ser oferecida como uma opção de substituição ou implementada diretamente com a transferência de URL.

O suporte à transferência é implementado por atividade. Para ativar o Handoff, chame o método setHandoffEnabled() da atividade. Talvez seja necessário transmitir dados adicionais com a transferência para que a atividade recriada no dispositivo de recebimento possa restaurar o estado adequado. Implemente o callback onHandoffActivityRequested() para retornar um objeto HandoffActivityData que contenha detalhes especificando como o Handoff deve processar e recriar a atividade no dispositivo de recebimento.

Atualização em tempo real: API de cores semânticas

Com o Android 17, a Atualização em tempo real lança as APIs de coloração semântica para compatibilidade com cores de significado universal.

As seguintes classes são compatíveis com a coloração semântica:

Colorir

  • Verde: associado à segurança. Essa cor deve ser usada para informar que você está em uma situação segura.
  • Laranja: para designar cautela e marcar riscos físicos. Essa cor deve ser usada quando os usuários precisam prestar atenção para definir uma proteção melhor.
  • Vermelho: geralmente indica perigo, pare. Ele deve ser apresentado quando for necessário chamar a atenção das pessoas com urgência.
  • Azul: cor neutra para conteúdo informativo que precisa se destacar de outros conteúdos.

O exemplo a seguir mostra como aplicar estilos semânticos ao texto em uma notificação:

  val ssb = SpannableStringBuilder()
        .append("Colors: ")
        .append("NONE", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_UNSPECIFIED), 0)
        .append(", ")
        .append("INFO", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_INFO), 0)
        .append(", ")
        .append("SAFE", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_SAFE), 0)
        .append(", ")
        .append("CAUTION", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_CAUTION), 0)
        .append(", ")
        .append("DANGER", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_DANGER), 0)

    Notification.Builder(context, channelId)
          .setSmallIcon(R.drawable.ic_icon)
          .setContentTitle("Hello World!")
          .setContentText(ssb)
          .setOngoing(true)
              .setRequestPromotedOngoing(true)

API UWB Downlink-TDoA para Android 17

O intervalo de tempo de chegada da diferença de downlink (DL-TDoA, na sigla em inglês) permite que um dispositivo determine a posição dele em relação a várias âncoras medindo os tempos de chegada relativos dos sinais.

O snippet a seguir demonstra como inicializar o Gerenciador de alcance, verificar os recursos do dispositivo e iniciar uma sessão de DL-TDoA:

Kotlin

class RangingApp {

    fun initDlTdoa(context: Context) {
        // Initialize the Ranging Manager
        val rangingManager = context.getSystemService(RangingManager::class.java)

        // Register for device capabilities
        val capabilitiesCallback = object : RangingManager.CapabilitiesCallback {
            override fun onRangingCapabilities(capabilities: RangingCapabilities) {
                // Make sure Dl-TDoA is supported before starting the session
                if (capabilities.uwbCapabilities != null && capabilities.uwbCapabilities!!.isDlTdoaSupported) {
                    startDlTDoASession(context)
                }
            }
        }
        rangingManager.registerCapabilitiesCallback(Executors.newSingleThreadExecutor(), capabilitiesCallback)
    }

    fun startDlTDoASession(context: Context) {

        // Initialize the Ranging Manager
        val rangingManager = context.getSystemService(RangingManager::class.java)

        // Create session and configure parameters
        val executor = Executors.newSingleThreadExecutor()
        val rangingSession = rangingManager.createRangingSession(executor, RangingSessionCallback())
        val rangingRoundIndexes = intArrayOf(0)
        val config: ByteArray = byteArrayOf() // OOB config data
        val params = DlTdoaRangingParams.createFromFiraConfigPacket(config, rangingRoundIndexes)

        val rangingDevice = RangingDevice.Builder().build()
        val rawTagDevice = RawRangingDevice.Builder()
            .setRangingDevice(rangingDevice)
            .setDlTdoaRangingParams(params)
            .build()

        val dtTagConfig = RawDtTagRangingConfig.Builder(rawTagDevice).build()

        val preference = RangingPreference.Builder(DEVICE_ROLE_DT_TAG, dtTagConfig)
            .setSessionConfig(SessionConfig.Builder().build())
            .build()

        // Start the ranging session
        rangingSession.start(preference)
    }
}

private class RangingSessionCallback : RangingSession.Callback {
    override fun onDlTdoaResults(peer: RangingDevice, measurement: DlTdoaMeasurement) {
        // Process measurement results here
    }
}

Java

public class RangingApp {

    public void initDlTdoa(Context context) {

        // Initialize the Ranging Manager
        RangingManager rangingManager = context.getSystemService(RangingManager.class);

        // Register for device capabilities
        RangingManager.CapabilitiesCallback capabilitiesCallback = new RangingManager.CapabilitiesCallback() {
            @Override
            public void onRangingCapabilities(RangingCapabilities capabilities) {
                // Make sure Dl-TDoA is supported before starting the session
                if (capabilities.getUwbCapabilities() != null && capabilities.getUwbCapabilities().isDlTdoaSupported) {
                    startDlTDoASession(context);
                }
            }
        };
        rangingManager.registerCapabilitiesCallback(Executors.newSingleThreadExecutor(), capabilitiesCallback);
    }

    public void startDlTDoASession(Context context) {
        RangingManager rangingManager = context.getSystemService(RangingManager.class);

        // Create session and configure parameters
        Executor executor = Executors.newSingleThreadExecutor();
        RangingSession rangingSession = rangingManager.createRangingSession(executor, new RangingSessionCallback());
        int[] rangingRoundIndexes = new int[] {0};
        byte[] config = new byte[0]; // OOB config data
        DlTdoaRangingParams params = DlTdoaRangingParams.createFromFiraConfigPacket(config, rangingRoundIndexes);

        RangingDevice rangingDevice = new RangingDevice.Builder().build();
        RawRangingDevice rawTagDevice = new RawRangingDevice.Builder()
                .setRangingDevice(rangingDevice)
                .setDlTdoaRangingParams(params)
                .build();

        RawDtTagRangingConfig dtTagConfig = new RawDtTagRangingConfig.Builder(rawTagDevice).build();

        RangingPreference preference = new RangingPreference.Builder(DEVICE_ROLE_DT_TAG, dtTagConfig)
                .setSessionConfig(new SessionConfig.Builder().build())
                .build();

        // Start the ranging session
        rangingSession.start(preference);
    }

    private static class RangingSessionCallback implements RangingSession.Callback {

        @Override
        public void onDlTdoaResults(RangingDevice peer, DlTdoaMeasurement measurement) {
            // Process measurement results here
        }
    }
}

Configurações fora da banda (OOB)

O snippet a seguir fornece um exemplo de dados de configuração OOB de DL-TDoA para Wi-Fi e BLE:

Java

// Wifi Configuration
byte[] wifiConfig = {
    (byte) 0xDD, (byte) 0x2D, (byte) 0x5A, (byte) 0x18, (byte) 0xFF, // Header
    (byte) 0x5F, (byte) 0x19, // FiRa Sub-Element
    (byte) 0x02, (byte) 0x00, // Profile ID
    (byte) 0x06, (byte) 0x02, (byte) 0x20, (byte) 0x08, // MAC Address
    (byte) 0x14, (byte) 0x01, (byte) 0x0C, // Preamble Index
    (byte) 0x27, (byte) 0x02, (byte) 0x08, (byte) 0x07, // Vendor ID
    (byte) 0x28, (byte) 0x06, (byte) 0xCA, (byte) 0xC8, (byte) 0xA6, (byte) 0xF7, (byte) 0x6F, (byte) 0x08, // Static STS IV
    (byte) 0x08, (byte) 0x02, (byte) 0x60, (byte) 0x09, // Slot Duration
    (byte) 0x1B, (byte) 0x01, (byte) 0x0A, // Slots per RR
    (byte) 0x09, (byte) 0x04, (byte) 0xE8, (byte) 0x03, (byte) 0x00, (byte) 0x00, // Duration
    (byte) 0x9F, (byte) 0x04, (byte) 0x67, (byte) 0x45, (byte) 0x23, (byte) 0x01  // Session ID
};

// BLE Configuration
byte[] bleConfig = {
    (byte) 0x2D, (byte) 0x16, (byte) 0xF4, (byte) 0xFF, // Header
    (byte) 0x5F, (byte) 0x19, // FiRa Sub-Element
    (byte) 0x02, (byte) 0x00, // Profile ID
    (byte) 0x06, (byte) 0x02, (byte) 0x20, (byte) 0x08, // MAC Address
    (byte) 0x14, (byte) 0x01, (byte) 0x0C, // Preamble Index
    (byte) 0x27, (byte) 0x02, (byte) 0x08, (byte) 0x07, // Vendor ID
    (byte) 0x28, (byte) 0x06, (byte) 0xCA, (byte) 0xC8, (byte) 0xA6, (byte) 0xF7, (byte) 0x6F, (byte) 0x08, // Static STS IV
    (byte) 0x08, (byte) 0x02, (byte) 0x60, (byte) 0x09, // Slot Duration
    (byte) 0x1B, (byte) 0x01, (byte) 0x0A, // Slots per RR
    (byte) 0x09, (byte) 0x04, (byte) 0xE8, (byte) 0x03, (byte) 0x00, (byte) 0x00, // Duration
    (byte) 0x9F, (byte) 0x04, (byte) 0x67, (byte) 0x45, (byte) 0x23, (byte) 0x01  // Session ID
};

Se você não puder usar uma configuração OOB porque ela está faltando ou se precisar mudar valores padrão que não estão na configuração OOB, crie parâmetros com DlTdoaRangingParams.Builder, como mostrado no snippet a seguir. É possível usar estes parâmetros no lugar de DlTdoaRangingParams.createFromFiraConfigPacket():

Kotlin

val dlTdoaParams = DlTdoaRangingParams.Builder(1)
    .setComplexChannel(UwbComplexChannel.Builder()
            .setChannel(9).setPreambleIndex(10).build())
    .setDeviceAddress(deviceAddress)
    .setSessionKeyInfo(byteArrayOf(0x01, 0x02, 0x03, 0x04))
    .setRangingIntervalMillis(240)
    .setSlotDuration(UwbRangingParams.DURATION_2_MS)
    .setSlotsPerRangingRound(20)
    .setRangingRoundIndexes(byteArrayOf(0x01, 0x05))
    .build()

Java

DlTdoaRangingParams dlTdoaParams = new DlTdoaRangingParams.Builder(1)
    .setComplexChannel(new UwbComplexChannel.Builder()
            .setChannel(9).setPreambleIndex(10).build())
    .setDeviceAddress(deviceAddress)
    .setSessionKeyInfo(new byte[]{0x01, 0x02, 0x03, 0x04})
    .setRangingIntervalMillis(240)
    .setSlotDuration(UwbRangingParams.DURATION_2_MS)
    .setSlotsPerRangingRound(20)
    .setRangingRoundIndexes(new byte[]{0x01, 0x05})
    .build();