Fitur dan API

Android 17 memperkenalkan fitur dan API baru yang hebat untuk para developer. Bagian berikut merangkum fitur ini untuk membantu Anda mulai menggunakan API terkait.

Untuk melihat daftar mendetail tentang API yang baru, diubah, dan dihapus, baca laporan perbedaan API. Untuk mengetahui detail tentang API baru, buka referensi API Android — API baru ditandai agar lebih mudah dilihat.

Anda juga harus meninjau area tempat perubahan platform dapat memengaruhi aplikasi Anda. Untuk informasi selengkapnya, lihat halaman berikut:

Fungsi inti

Android 17 menambahkan fitur baru berikut yang terkait dengan fungsi Android inti.

Pemicu ProfilingManager baru

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.

Profiling trigger for app anomalies

Android 17 introduces an on-device anomaly detection service that monitors for resource-intensive behaviors and potential compatibility regressions. Integrated with ProfilingManager, this service allows your app to receive profiling artifacts triggered by specific system-detected events.

Use the TRIGGER_TYPE_ANOMALY trigger to detect system performance issues such as excessive binder calls and excessive memory usage. When an app breaches OS-defined memory limits, the anomaly trigger allows developers to receive app-specific heap dumps to help identify and fix memory issues. Additionally, for excessive binder spam, the anomaly trigger provides a stack sampling profile on binder transactions.

This API callback occurs prior to any system imposed enforcements. For example, it can help developers collect debug data before the app is terminated by the system for exceeding memory limits.

val profilingManager =
    applicationContext.getSystemService(ProfilingManager::class.java)
val triggers = ArrayList<ProfilingTrigger>()
triggers.add(ProfilingTrigger.Builder(ProfilingTrigger.TRIGGER_TYPE_ANOMALY))
val mainExecutor: Executor = Executors.newSingleThreadExecutor()
val resultCallback = Consumer<ProfilingResult> { profilingResult ->
    if (profilingResult.errorCode != ProfilingResult.ERROR_NONE) {
        // upload profile result to server for further analysis
        setupProfileUploadWorker(profilingResult.resultFilePath)
    }
    profilingManager.registerForAllProfilingResults(mainExecutor,
                                                    resultCallback)
    profilingManager.addProfilingTriggers(triggers)
}

JobDebugInfo API

Android 17 memperkenalkan JobDebugInfo API baru untuk membantu developer men-debug tugas JobScheduler mereka—alasan tugas tidak berjalan, durasi tugas berjalan, dan informasi gabungan lainnya.

Metode pertama dari JobDebugInfo API yang diperluas adalah getPendingJobReasonStats(), yang menampilkan peta alasan mengapa tugas berada dalam status eksekusi yang tertunda dan durasi tertunda kumulatifnya. Metode ini bergabung dengan metode getPendingJobReasonsHistory() dan getPendingJobReasons() untuk memberi Anda insight tentang alasan tugas terjadwal tidak berjalan seperti yang diharapkan, tetapi menyederhanakan pengambilan informasi dengan menyediakan durasi dan alasan tugas dalam satu metode.

Misalnya, untuk jobId yang ditentukan, metode ini dapat menampilkan PENDING_JOB_REASON_CONSTRAINT_CHARGING dan durasi 60.000 md, yang menunjukkan bahwa tugas tertunda selama 60.000 md karena batasan pengisian daya tidak terpenuhi.

Mengurangi penguncian layar saat aktif dengan dukungan pemroses untuk alarm yang diizinkan saat perangkat dalam kondisi tidak aktif

Android 17 introduces a new variant of AlarmManager.setExactAndAllowWhileIdle that accepts an OnAlarmListener instead of a PendingIntent. This new callback-based mechanism is ideal for apps that currently rely on continuous wakelocks to perform periodic tasks, such as messaging apps maintaining socket connections.

Privasi

Android 17 menyertakan fitur baru berikut untuk meningkatkan privasi pengguna.

Dukungan platform Encrypted Client Hello (ECH)

Android 17 memperkenalkan dukungan platform untuk Encrypted Client Hello (ECH), sebuah peningkatan privasi yang signifikan untuk komunikasi jaringan. ECH adalah ekstensi TLS 1.3 yang mengenkripsi Server Name Indication (SNI) selama handshake TLS awal. Enkripsi ini membantu melindungi privasi pengguna dengan mempersulit perantara jaringan mengidentifikasi domain tertentu yang terhubung ke aplikasi.

Platform kini menyertakan API yang diperlukan agar library jaringan dapat menerapkan ECH. Hal ini mencakup kemampuan baru di DnsResolver untuk mengkueri data DNS HTTPS yang berisi konfigurasi ECH, dan metode baru di SSLEngines dan SSLSockets Conscrypt untuk mengaktifkan ECH dengan meneruskan konfigurasi ini saat terhubung ke domain. Developer dapat mengonfigurasi preferensi ECH, seperti mengaktifkannya secara oportunistik atau mewajibkan penggunaannya, melalui elemen <domainEncryption> baru dalam file Konfigurasi Keamanan Jaringan, yang berlaku secara global atau per domain.

Library jaringan populer seperti HttpEngine, WebView, dan OkHttp diharapkan mengintegrasikan API platform ini dalam update mendatang, sehingga mempermudah aplikasi mengadopsi ECH dan meningkatkan privasi pengguna.

Untuk mengetahui informasi selengkapnya, lihat dokumentasi Encrypted Client Hello.

Pemilih kontak Android

Pemilih Kontak Android adalah antarmuka standar yang dapat dijelajahi bagi pengguna untuk membagikan kontak ke aplikasi Anda. Tersedia di perangkat yang menjalankan Android 17 (level API 37) atau yang lebih tinggi, pemilih ini menawarkan alternatif yang menjaga privasi untuk izin READ_CONTACTS yang luas. Daripada meminta akses ke seluruh buku alamat pengguna, aplikasi Anda menentukan kolom data yang diperlukan, seperti nomor telepon atau alamat email, dan pengguna memilih kontak tertentu untuk dibagikan. Hal ini memberi aplikasi Anda akses baca hanya ke data yang dipilih, sehingga memastikan kontrol terperinci sekaligus memberikan pengalaman pengguna yang konsisten dengan kemampuan penelusuran, pengalihan profil, dan pemilihan ganda bawaan tanpa harus membuat atau memelihara UI.

Untuk mengetahui informasi selengkapnya, lihat dokumentasi pemilih kontak.

Keamanan

Android 17 menambahkan fitur baru berikut untuk meningkatkan keamanan perangkat dan aplikasi.

Mode Perlindungan Lanjutan Android (AAPM)

Android Advanced Protection Mode offers Android users a powerful new set of security features, marking a significant step in safeguarding users—particularly those at higher risk—from sophisticated attacks. Designed as an opt-in feature, AAPM is activated with a single configuration setting that users can turn on at any time to apply an opinionated set of security protections.

These core configurations include blocking app installation from unknown sources (sideloading), restricting USB data signaling, and mandating Google Play Protect scanning, which significantly reduces the device's attack surface area. Developers can integrate with this feature using the AdvancedProtectionManager API to detect the mode's status, enabling applications to automatically adopt a hardened security posture or restrict high-risk functionality when a user has opted in.

Penandatanganan APK PQC

Android kini mendukung skema tanda tangan APK hybrid untuk melindungi identitas penandatanganan aplikasi Anda dari potensi ancaman serangan yang menggunakan komputasi kuantum. Fitur ini memperkenalkan Skema Tanda Tangan APK baru, yang memungkinkan Anda memasangkan kunci penandatanganan klasik (seperti RSA atau EC) dengan algoritma kriptografi pasca-kuantum (PQC) baru (ML-DSA).

Pendekatan hybrid ini memastikan aplikasi Anda tetap aman dari serangan kuantum di masa mendatang sekaligus mempertahankan kompatibilitas mundur penuh dengan perangkat dan versi Android lama yang mengandalkan verifikasi tanda tangan klasik.

Dampak bagi developer

  • Aplikasi yang menggunakan Penandatanganan Aplikasi Play: Jika menggunakan Penandatanganan Aplikasi Play, Anda dapat menunggu Google Play memberi Anda opsi untuk mengupgrade tanda tangan hybrid menggunakan kunci PQC yang dibuat oleh Google Play, sehingga aplikasi Anda terlindungi tanpa memerlukan pengelolaan kunci manual.
  • Aplikasi yang menggunakan kunci yang dikelola sendiri: Developer yang mengelola kunci penandatanganan mereka sendiri dapat menggunakan alat build Android yang diperbarui (seperti apksigner) untuk beralih ke identitas hybrid, yang menggabungkan kunci PQC dengan kunci klasik baru. (Anda harus membuat kunci klasik baru, Anda tidak dapat menggunakan kembali kunci yang lama.)

Konektivitas

Android 17 menambahkan fitur berikut untuk meningkatkan konektivitas perangkat dan aplikasi.

Jaringan satelit yang dibatasi

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

Pengalaman pengguna dan UI sistem

Android 17 menyertakan perubahan berikut untuk meningkatkan pengalaman pengguna.

Aliran volume Asisten khusus

Android 17 memperkenalkan aliran volume Asisten khusus untuk aplikasi Asisten, untuk pemutaran dengan USAGE_ASSISTANT. Perubahan ini memisahkan audio Asisten dari aliran media standar, sehingga pengguna memiliki kontrol terpisah atas kedua volume. Hal ini memungkinkan skenario seperti membisukan pemutaran media sambil mempertahankan kemampuan mendengar untuk respons Asisten, dan sebaliknya.

Aplikasi Asisten dengan akses ke mode audio MODE_ASSISTANT_CONVERSATION baru dapat lebih meningkatkan konsistensi kontrol volume. Aplikasi Asisten dapat menggunakan mode ini untuk memberikan petunjuk kepada sistem tentang sesi Asisten yang aktif, sehingga memastikan aliran Asisten dapat dikontrol di luar pemutaran USAGE_ASSISTANT yang aktif atau dengan periferal Bluetooth yang terhubung.

Handoff

Penyerahan adalah fitur dan API baru yang akan hadir di Android 17 yang dapat diintegrasikan oleh developer aplikasi untuk memberikan kontinuitas lintas perangkat bagi pengguna mereka. Fitur ini memungkinkan pengguna memulai aktivitas aplikasi di satu perangkat Android dan mentransisikannya ke perangkat Android lain. Pengalihan berjalan di latar belakang perangkat pengguna dan menampilkan aktivitas yang tersedia dari perangkat terdekat pengguna lainnya melalui berbagai titik entri, seperti peluncur dan taskbar, di perangkat penerima.

Aplikasi dapat menetapkan Handoff untuk meluncurkan aplikasi Android native yang sama, jika aplikasi tersebut diinstal dan tersedia di perangkat penerima. Dalam alur aplikasi-ke-aplikasi ini, pengguna ditautkan secara mendalam ke aktivitas yang ditentukan. Atau, Penyerahan dari aplikasi ke web dapat ditawarkan sebagai opsi penggantian atau diterapkan langsung dengan Penyerahan URL.

Dukungan Handoff diimplementasikan per aktivitas. Untuk mengaktifkan Handoff, panggil metode setHandoffEnabled() untuk aktivitas. Data tambahan mungkin perlu diteruskan bersama dengan pengalihan sehingga aktivitas yang dibuat ulang di perangkat penerima dapat memulihkan status yang sesuai. Terapkan callback onHandoffActivityDataRequested() untuk menampilkan objek HandoffActivityData yang berisi detail yang menentukan cara Handoff harus menangani dan membuat ulang aktivitas di perangkat penerima.

Pembaruan Langsung - Semantic color API

Dengan Android 17, Update Langsung meluncurkan Semantic Coloring API untuk mendukung warna dengan makna universal.

Class berikut mendukung pewarnaan semantik:

Mewarnai

  • Hijau: Terkait dengan keselamatan. Warna ini harus digunakan untuk kasus yang memberi tahu orang lain bahwa Anda berada dalam situasi yang aman.
  • Oranye: Untuk menunjukkan kehati-hatian dan menandai bahaya fisik. Warna ini harus digunakan dalam situasi saat pengguna perlu memperhatikan untuk menyetel setelan perlindungan yang lebih baik.
  • Merah: Umumnya menunjukkan bahaya, berhenti. Harus ditampilkan untuk kasus yang memerlukan perhatian orang dengan segera.
  • Biru: Warna netral untuk konten yang bersifat informatif dan harus terlihat berbeda dari konten lainnya.

Contoh berikut menunjukkan cara menerapkan gaya semantik pada teks dalam notifikasi:

  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)

UWB Downlink-TDoA API untuk Android 17

Downlink Time Difference of Arrival (DL-TDoA) ranging lets a device determine its position relative to multiple anchors by measuring the relative arrival times of signals.

The following snippet demonstrates how to initialize the Ranging Manager, verify device capabilities, and start a DL-TDoA session:

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.RangingCapabilitiesCallback {
            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 = byteArrayOf(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.RangingCapabilitiesCallback() {
            @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());
        byte[] rangingRoundIndexes = new byte[] {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
        }
    }
}

Out-of-Band (OOB) Configurations

The following snippet provides an example of DL-TDoA OOB configuration data for Wi-Fi and 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
};

If you can't use an OOB configuration because it is missing, or if you need to change default values that aren't in the OOB config, you can build parameters with DlTdoaRangingParams.Builder as shown in the following snippet. You can use these parameters in place of 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();