Funktionen und APIs

Android 17 bietet viele neue Funktionen und APIs für Entwickler. In den folgenden Abschnitten werden diese Funktionen zusammengefasst, um Ihnen den Einstieg in die zugehörigen APIs zu erleichtern.

Eine detaillierte Liste der neuen, geänderten und entfernten APIs finden Sie im API-Diff-Bericht. Details zu neuen APIs finden Sie in der Android-API-Referenz. Neue APIs sind zur besseren Übersicht hervorgehoben.

Sie sollten auch Bereiche prüfen, in denen sich Plattformänderungen auf Ihre Apps auswirken könnten. Weitere Informationen finden Sie auf den folgenden Seiten:

Hauptfunktion

Android 17 bietet die folgenden neuen Funktionen im Zusammenhang mit der Android-Kernfunktionalität.

Neue ProfilingManager-Trigger

In Android 17 werden ProfilingManager mehrere neue Systemauslöser hinzugefügt, mit denen Sie detaillierte Daten zur Behebung von Leistungsproblemen erfassen können.

Die neuen Trigger sind:

  • TRIGGER_TYPE_COLD_START: Der Trigger wird beim Kaltstart der App ausgelöst. Die Antwort enthält sowohl ein Beispiel für einen Callstack als auch einen System-Trace.
  • TRIGGER_TYPE_OOM: Der Trigger wird ausgelöst, wenn eine App eine OutOfMemoryError auslöst und als Reaktion darauf einen Java-Heap-Dump bereitstellt.
  • TRIGGER_TYPE_KILL_EXCESSIVE_CPU_USAGE: Der Trigger wird ausgelöst, wenn eine App aufgrund einer ungewöhnlichen und übermäßigen CPU-Nutzung beendet wird. Als Reaktion wird eine Callstack-Stichprobe bereitgestellt.

Informationen zum Einrichten des Systemtriggers finden Sie in der Dokumentation zum triggerbasierten Profiling und zum Abrufen und Analysieren von Profiling-Daten.

JobDebugInfo APIs

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.

Wake Locks mit Listener-Unterstützung für „allow-while-idle“-Alarme reduzieren

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.

Datenschutz

Android 17 enthält die folgenden neuen Funktionen zur Verbesserung des Datenschutzes für Nutzer.

Android-Kontaktauswahl

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.

Sicherheit

Android 17 bietet die folgenden neuen Funktionen zur Verbesserung der Geräte- und App-Sicherheit.

Erweiterter Sicherheitsmodus für Android (Android Advanced Protection Mode, 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.

PQC-APK-Signierung

Android unterstützt jetzt ein hybrides APK-Signaturschema, um die Signaturidentität Ihrer App vor potenziellen Angriffen zu schützen, die Quantencomputer nutzen. Mit dieser Funktion wird ein neues APK-Signaturschema eingeführt, mit dem Sie einen klassischen Signaturschlüssel (z. B. RSA oder EC) mit einem neuen Algorithmus für die Post-Quantum-Kryptografie (PQC) (ML-DSA) kombinieren können.

Dieser hybride Ansatz sorgt dafür, dass Ihre App auch in Zukunft vor Quantenangriffen geschützt ist. Gleichzeitig wird die vollständige Abwärtskompatibilität mit älteren Android-Versionen und Geräten beibehalten, die auf der klassischen Signaturprüfung basieren.

Auswirkungen auf Entwickler

  • Apps mit der Google Play App-Signatur:Wenn Sie die Google Play App-Signatur verwenden, können Sie warten, bis Google Play Ihnen die Möglichkeit bietet, eine hybride Signatur mit einem von Google Play generierten PQC-Schlüssel zu aktualisieren. So ist Ihre App geschützt, ohne dass Sie die Schlüssel manuell verwalten müssen.
  • Apps mit selbst verwalteten Schlüsseln:Entwickler, die ihre eigenen Signaturschlüssel verwalten, können aktualisierte Android-Build-Tools (z. B. apksigner) verwenden, um zu einer hybriden Identität zu wechseln, indem sie einen PQC-Schlüssel mit einem neuen klassischen Schlüssel kombinieren. Sie müssen einen neuen klassischen Schlüssel erstellen. Der alte kann nicht wiederverwendet werden.

Konnektivität

Mit Android 17 werden die folgenden Funktionen hinzugefügt, um die Geräte- und App-Konnektivität zu verbessern.

Satellitennetzwerke mit eingeschränkter Bandbreite

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

Nutzererfahrung und System-UI

Android 17 enthält die folgenden Änderungen zur Verbesserung der Nutzerfreundlichkeit.

Eigener Lautstärkestream für Assistant

Mit Android 17 wird ein eigener Lautstärkestream für Assistant-Apps eingeführt, der für die Wiedergabe mit USAGE_ASSISTANT verwendet wird. Durch diese Änderung wird die Audioausgabe des Assistant vom Standard-Mediastream entkoppelt, sodass Nutzer die Lautstärke beider Streams unabhängig voneinander steuern können. So können sie beispielsweise die Medienwiedergabe stummschalten, während die Antworten des Assistant weiterhin hörbar sind, und umgekehrt.

Assistant-Apps mit Zugriff auf den neuen MODE_ASSISTANT_CONVERSATION Audio modus können die Konsistenz der Lautstärkeregelung weiter verbessern. Assistant-Apps können diesen Modus verwenden, um dem System einen Hinweis auf eine aktive Assistant-Sitzung zu geben. So kann der Assistant-Stream auch außerhalb der aktiven USAGE_ASSISTANT-Wiedergabe oder mit verbundenen Bluetooth-Peripheriegeräten gesteuert werden.

Übergabe

Handoff ist eine neue Funktion und API, die in Android 17 eingeführt wird. App-Entwickler können sie integrieren, um ihren Nutzern eine geräteübergreifende Kontinuität zu bieten. Damit kann der Nutzer eine App-Aktivität auf einem Android-Gerät starten und auf ein anderes Android-Gerät übertragen. Handoff wird im Hintergrund auf dem Gerät eines Nutzers ausgeführt und zeigt verfügbare Aktivitäten von den anderen Geräten des Nutzers in der Nähe über verschiedene Einstiegspunkte an, z. B. über den Launcher und die Taskleiste auf dem empfangenden Gerät.

Apps können Handoff so festlegen, dass dieselbe native Android-App gestartet wird, wenn sie auf dem empfangenden Gerät installiert und verfügbar ist. In diesem App-zu-App-Ablauf wird der Nutzer per Deeplink zur entsprechenden Aktivität weitergeleitet. Alternativ kann die Übergabe von Apps an das Web als Fallback-Option angeboten oder direkt mit der URL-Übergabe implementiert werden.

Die Unterstützung für den Wechsel zwischen Geräten wird pro Aktivität implementiert. Rufen Sie zum Aktivieren von Handoff die Methode setHandoffEnabled() für die Aktivität auf. Möglicherweise müssen zusätzliche Daten zusammen mit der Übergabe übergeben werden, damit der entsprechende Status der neu erstellten Aktivität auf dem empfangenden Gerät wiederhergestellt werden kann. Implementieren Sie den onHandoffActivityRequested()-Callback, um ein HandoffActivityData-Objekt zurückzugeben, das Details dazu enthält, wie Handoff die Aktivität auf dem empfangenden Gerät verarbeiten und neu erstellen soll.

Live-Update – Semantische Farb-API

With Android 17, Live Update launches the Semantic Coloring APIs to support colors with universal meaning.

The following classes support semantic coloring:

Coloring

  • Green: Associated with safety. This color should be used for the case where it lets people know you are in the safe situation.
  • Orange: For designating caution and marking physical hazards. This color should be used in the situation where users need to pay attention to set better protection setting.
  • Red: Generally indicates danger, stop. It should be presented for the case where need people's attention urgently.
  • Blue: Neutral color for content that is informational and should stand out from other content.

The following example shows how to apply semantic styles to text in a notification:

  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 für Android 17

Mit der DL-TDoA-Entfernungsmessung (Downlink Time Difference of Arrival) kann ein Gerät seine Position relativ zu mehreren Ankern ermitteln, indem es die relativen Ankunftszeiten von Signalen misst.

Das folgende Snippet zeigt, wie der Ranging Manager initialisiert, die Gerätefunktionen überprüft und eine DL-TDoA-Sitzung gestartet wird:

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
        }
    }
}

Out-of-Band-Konfigurationen (OOB)

Das folgende Snippet enthält ein Beispiel für DL-TDoA-OOB-Konfigurationsdaten für WLAN und 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
};

Wenn Sie keine OOB-Konfiguration verwenden können, weil sie fehlt, oder wenn Sie Standardwerte ändern müssen, die nicht in der OOB-Konfiguration enthalten sind, können Sie Parameter mit DlTdoaRangingParams.Builder erstellen, wie im folgenden Snippet gezeigt. Sie können diese Parameter anstelle von DlTdoaRangingParams.createFromFiraConfigPacket() verwenden:

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();