All Articles
CategoryAndroid
Reading Time
15 min read
Published
2026-09-05
Word Count
3,782words

Grab a coffee — this one is a deep dive!

Android 17 (API 37): 6 Changes That Will Break Your App

Summary

Android 17 API 37 behavior changes: MemoryLimiter memory limit, background audio rejection, config-restart removal, cross-profile loopback block, 3-hour SMS OTP delay, and the new NPU permission.

  • Android 17's API level is 37; when the MemoryLimiter memory limit is exceeded, the exit reason is REASON_OTHER and ApplicationExitInfo.getDescription() contains the string 'MemoryLimiter:AnonSwap' along with other information.
  • The background-audio restriction's base rule applies to all apps regardless of targetSdk; apps targeting targetSdk 37 additionally need a while-in-use foreground service; the audio-playback and volume-change APIs fail silently with no exception, while the audio-focus API fails with the AUDIOFOCUS_REQUEST_FAILED code.
  • The Activity no longer restarts by default on keyboard, navigation, touchscreen, color-mode, and UI_MODE_TYPE_DESK transitions; the old behavior can be restored with android:recreateOnConfigChanges.
  • Cross-profile loopback traffic is blocked independently of targetSdk; standard SMS OTP messages are delayed 3 hours for targetSdk 37+ apps, and NPU access requires an android.hardware.npu declaration in the manifest.
Android 17 (API 37): 6 Changes That Will Break Your App

Android 17 (API level 37) has shipped, and four of its six behavior changes can break your app in production without throwing a single error: you won't see why the process died when the memory limit is exceeded, your background audio request comes back with AUDIOFOCUS_REQUEST_FAILED, and your SMS-based OTP flow gets delayed by three hours. This post walks through all six changes with code samples, based on developer.android.com's official "Android 17 is Here" announcement and its behavior-changes reference pages.

💡 Pro Tip: Before raising targetSdk to 37, turn these six items into a checklist and test each one on a real device (not an emulator) — the MemoryLimiter and cross-profile loopback changes in particular behave in hardware/profile-dependent ways.

Table of Contents

Android 17 = API Level 37: Version Clarity and Timeline

Android 17's API level is 37 (developer.android.com/about/versions/17). Google announced the release on the Android Developers Blog in a post titled "Android 17 is Here"; the beta program started with Beta 1 on February 13, 2026, reached Platform Stability with Beta 3 on March 26, 2026, and Beta 4.1 shipped on June 1, 2026. The stable release shipped on June 16, 2026, rolling out to most supported Pixel devices. As of writing, the release notes page was last updated September 2, 2026.

Raising targetSdk to 37 means facing all six changes below at once — some (like the cross-profile loopback block) kick in as soon as the device updates to Android 17, independent of targetSdk, while others only affect apps targeting targetSdk 37. That distinction matters for your test matrix.

Android 15 Developer Guide: Privacy Sandbox, Edge-to-Edge, Foreground Services covers the Privacy Sandbox and edge-to-edge requirements that are the natural predecessor to this post; start there if you want to follow the API-level jump (35 → 37) between the two releases.

MemoryLimiter: The New Memory Limit and Diagnosing It with ApplicationExitInfo

Android 17 introduces a memory limit calculated from the device's total RAM. When exceeded, the system terminates the process, and the first place to look when debugging this is the ApplicationExitInfo API. Per the official reference: if your app is terminated for this reason, the exit reason is REASON_OTHER, and ApplicationExitInfo.getDescription() contains the string "MemoryLimiter:AnonSwap" along with other information — so use a contains check, not equality.

Logging this alone isn't enough — Android 17 also offers on-device anomaly detection via ProfilingManager: registering a ProfilingTrigger.TRIGGER_TYPE_ANOMALY trigger captures profiling data at the moment of a memory anomaly.

Registering the anomaly trigger

kotlin
1import android.os.ProfilingManager
2import android.os.ProfilingTrigger
3 
4val profilingManager = applicationContext
5 .getSystemService(ProfilingManager::class.java)
6 
7val triggers = ArrayList<ProfilingTrigger>().apply {
8 add(ProfilingTrigger.Builder(
9 ProfilingTrigger.TRIGGER_TYPE_ANOMALY).build())
10}
11profilingManager.addProfilingTriggers(triggers)

Reading the previous session's death reason

To check whether your app died for this reason in the previous session, run this at startup:

kotlin
1val am = getSystemService(ActivityManager::class.java)
2val exitInfos = am.getHistoricalProcessExitReasons(
3 packageName, 0, 10
4)
5 
6exitInfos.forEach { info ->
7 if (info.reason == ApplicationExitInfo.REASON_OTHER &&
8 info.description?.contains("MemoryLimiter:AnonSwap") == true
9 ) {
10 Log.w("MemoryWatch", "Terminated due to memory limit")
11 }
12}

You can also use this check while diagnosing recomposition-driven memory bloat as covered in our Jetpack Compose 1.7 Performance: Strong Skipping + Stability post — unnecessary object allocation can speed up how fast you approach the memory ceiling.

Background Audio Requests Are Silently Rejected

Android 17 restricts background apps' audio-playback, audio-focus-request, and volume-change APIs; this restriction has two layers. The base rule is independent of targetSdk: per the official reference, all apps with these interactions must have either a visible Activity or a foreground service that isn't of type SHORT_SERVICE, "regardless of whether the app targets API level 37." The second layer depends on targetSdk: if the app targets API 37 and is in the background, its foreground service must additionally have "while-in-use" (WIU) capability. Outside a valid lifecycle state, the audio-playback and volume-change APIs fail silently with no exception or error message; the audio-focus API fails with the AUDIOFOCUS_REQUEST_FAILED result code. The WIU requirement is lifted only if exact alarm permission has been granted and the audio streams being changed are attributed USAGE_ALARM.

kotlin
1val audioManager = getSystemService(AudioManager::class.java)
2val focusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
3 .setAudioAttributes(
4 AudioAttributes.Builder()
5 .setUsage(AudioAttributes.USAGE_MEDIA)
6 .build()
7 )
8 .setOnAudioFocusChangeListener { /* ... */ }
9 .build()
10 
11val result = audioManager.requestAudioFocus(focusRequest)
12if (result == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
13 // This can now happen SILENTLY in the background on Android 17 —
14 // check the foreground/WIU state before showing the user a notification
15}

Practical takeaway: for apps that play music/podcasts or run a background-download notification, review your foreground service type and WIU capability declaration before moving to targetSdk 37.

Activity Restart on Configuration Change Goes Away

Before Android 17, many configuration changes (keyboard attached, keyboard hidden, navigation mode change, touchscreen state, color mode) recreated the Activity from scratch. Starting with Android 17, the system no longer restarts the Activity by default for changes that don't require a full UI redraw: CONFIG_KEYBOARD, CONFIG_KEYBOARD_HIDDEN, CONFIG_NAVIGATION, CONFIG_TOUCHSCREEN, and CONFIG_COLOR_MODE. The release notes page adds a sixth item, CONFIG_UI_MODE, but only when the UI mode changes to UI_MODE_TYPE_DESK or from it to another type. Instead, the running Activity receives the update via the onConfigurationChanged() callback.

If your app explicitly depends on a full restart to reload resources on these changes, you can restore the old behavior with the new android:recreateOnConfigChanges manifest attribute:

xml
1<activity
2 android:name=".MainActivity"
3 android:recreateOnConfigChanges="keyboard|keyboardHidden|navigation|touchscreen|colorMode">
4</activity>

Note: the value table in the R.attr reference accepts no value for this attribute other than mcc, mnc, touchscreen, keyboard, keyboardHidden, navigation, and colorModeuiMode isn't in the list, so you can't re-enable the restart that's lifted for the DESK-mode transition this way.

This change matters especially on foldables and tablet-form devices with an external keyboard/mouse attached — the screen "flashing" and redrawing when the user plugs in a keyboard no longer happens by default.

Cross-Profile Loopback Traffic Is Blocked

This item has a critical difference from the others: it works independently of targetSdk. Per the official source, starting with Android 17, cross-profile loopback traffic is blocked by default; loopback traffic within the same profile is unaffected. This change applies to all apps regardless of which API level they target the moment the device updates to Android 17.

On enterprise devices using a work profile, local debug proxies, SDKs doing IPC over localhost, or test infrastructure assuming cross-profile loopback are directly affected. Even if you don't move to targetSdk 37, you'll hit this behavior once the user's device updates to Android 17 — include it in your test matrix.

The 3-Hour Delay in SMS OTP and Migrating to SMS Retriever

To reduce OTP hijacking, Android 17 extends SMS-based one-time-password protection: SMS messages aren't accessible until three hours after receipt. The official reference (behavior-changes-17) splits this delay into two scenarios:

  • WebOTP format: delayed for all apps that aren't the intended recipient (domain mismatch).
  • Standard SMS OTP: delayed for most apps targeting SDK 37+.
  • Exemptions: the default SMS app, the assistant app, and connected companion device apps are exempt from this delay.

During this window, the SMS_RECEIVED_ACTION broadcast is held back and SMS provider database queries are filtered. Google recommends that all apps relying on reading SMS for an OTP move to the SMS Retriever or SMS User Consent APIs. The mechanism that lets Retriever escape the delay: per the official reference, delivery of messages containing the retriever hash is delayed three hours for most apps, but the app that owns the hash is exempt.

Beating the delay with SMS Retriever

kotlin
1val client = SmsRetriever.getClient(this)
2val task = client.startSmsRetriever()
3 
4task.addOnSuccessListener {
5 // The SMS Retriever API started listening; app-hash-signed SMS messages
6 // reach the BroadcastReceiver without hitting the 3-hour delay
7}
8 
9task.addOnFailureListener {
10 // Fallback: switch to the SMS User Consent API
11}

Moving to SMS Retriever doesn't just beat the delay — it also doesn't require your app to hold the READ_SMS permission.

New Mandatory Permission for NPU Access: FEATURE_NEURAL_PROCESSING_UNIT

Apps targeting targetSdk 37 that want direct NPU (Neural Processing Unit) access must now declare the FEATURE_NEURAL_PROCESSING_UNIT hardware feature in their manifest — otherwise NPU access is blocked. This also covers apps using the LiteRT NPU delegate, vendor-specific SDKs, and the deprecated NNAPI.

Note: what you write into the manifest is the constant's value, not its name. Per the PackageManager reference, FEATURE_NEURAL_PROCESSING_UNIT was added in API 37 and its constant value is "android.hardware.npu"; if you write the constant's name instead, the declaration silently has no effect.

xml
1<uses-feature
2 android:name="android.hardware.npu"
3 android:required="false" />

android:required="false" is recommended for most apps: with this setting, your app benefits from acceleration on devices that have an NPU, while not becoming invisible on the Play Store on devices without one. If you set required="true", your app becomes completely invisible on the Play Store on devices without an NPU.

If you're extending on-device AI features as covered in our Gemini Nano iOS + Android: Cross-platform On-Device AI post, skipping this manifest declaration can cause your NPU-accelerated model to silently fall back to the CPU (or not run at all).

Test Matrix Before targetSdk 37

Instead of reviewing all six changes at once, use the table below as a checklist:

Change
Depends on targetSdk
Scenario to test
MemoryLimiter
No (once device is on Android 17)
Long session + heap dump on a low-RAM device
Background audio
Partially — base rule for all apps, WIU for targetSdk 37+
Audio/focus request from a backgrounded app
Config restart removal
No targetSdk requirement stated in the source
Attach/detach external keyboard, fold/unfold
Cross-profile loopback
No (once device is on Android 17)
Work profile + localhost/loopback-dependent SDK
SMS OTP delay
Partially — WebOTP/Retriever format for all apps, standard SMS for targetSdk 37+
Test the OTP flow without SMS Retriever
NPU permission declaration
Yes (if using NPU access)
Check FEATURE_NEURAL_PROCESSING_UNIT in the manifest

If you combine this matrix with the background-task tests from our WorkManager 2.10 Coroutines: Modern Background Task Approach guide in CI, you can turn the background-audio and MemoryLimiter scenarios into automated regression tests.

The Six Changes at a Glance

Change
Source page
Does it fail silently
MemoryLimiter memory limit
Android 17 is Here + behavior-changes-all
Yes — process terminates without logging
Background audio rejection
changes/bg-audio
Yes — no exception; focus returns AUDIOFOCUS_REQUEST_FAILED
Config restart removal
Android 17 is Here
No — onConfigurationChanged() is called
Cross-profile loopback block
behavior-changes-all
Yes — connection is silently rejected
SMS OTP 3-hour delay
behavior-changes-17
Yes — broadcast is held, no error returned
NPU permission requirement
Android 17 is Here
No — NPU access is blocked (visible in the log)

Other Important Changes Outside the Scope of This Post

The six items above focus on the changes that will break the most work during the targetSdk 37 transition; but the Android 17 announcement covers a broader platform update, and we recommend noting these too as you move to targetSdk 37. Per the official announcement, on large-screen devices whose smallest width (sw) exceeds 600dp (including mobile devices running in desktop mode), the system now ignores legacy manifest attributes and runtime APIs like screenOrientation, setRequestedOrientation(), resizeableActivity=false, and minAspectRatio/maxAspectRatio for apps targeting targetSdk 37 — the app must adapt to any window size. Apps in the games category on Google Play are exempt.

This change matters especially for apps using the design-system updates that came with Material 3 Expressive, covered in Material 3 Expressive: Android 16 Design System — layouts written with fixed-orientation assumptions can break outright on targetSdk 37. Also, with Android 17 the platform's source code was published via the Android Open Source Project (AOSP); that's another option if you want to examine the implementation details of these behavior changes.

Read-only requirement for loading native libraries

The Safer Dynamic Code Loading (DCL) protection that came for DEX and JAR files in Android 14 now extends to native libraries as well, with Android 17. The documentation states it verbatim: "the Safer Dynamic Code Loading (DCL) protection introduced in Android 14 for DEX and JAR files now extends to native libraries." The practical implication fits in one sentence — if you target targetSdk 37 or higher, every native file you load with System.load() must be marked read-only: "All native files loaded using System.load() must be marked as read-only. Otherwise, the system throws UnsatisfiedLinkError."

Unlike the silent breakages in this post's main body, this item behaves noisily: you get an exception, so it's visible in crash-reporting tools and caught during QA. Still, add it to your test matrix separately; if you load a native library from a runtime-downloaded directory rather than from inside the APK, you may never have thought about that file's permissions before. Google's recommendation goes a step further than hardening: "We recommend that apps avoid dynamically loading code whenever possible, as doing so greatly increases the risk that an app can be compromised by code injection or code tampering." So often the correct fix isn't making the file read-only — it's eliminating dynamic loading entirely.

Local network access now requires a runtime permission

Android 17 introduces the ACCESS_LOCAL_NETWORK runtime permission against unauthorized local network access. The permission sits under the existing NEARBY_DEVICES permission group; per the documentation, "users who have already granted other NEARBY_DEVICES permissions aren't prompted again." The rationale is explicit: the requirement prevents malicious apps from exploiting unrestricted local network access for "covert user tracking and fingerprinting." Once declared and granted, your app can discover and connect to devices on the local network (LAN), such as smart home devices or casting receivers.

For apps targeting targetSdk 37, the documentation describes two paths: adopt privacy-preserving system device pickers to skip the permission prompt entirely, or explicitly request the permission at runtime and continue local network communication. This protection was optional on Android 16; the page states the difference verbatim: "In Android 16, apps could opt in to local network permissions. Beginning with Android 17, enforcement is mandatory for apps that target Android 17 (API level 37) or higher." If your app discovers LAN devices, connects to a casting receiver, or talks to a local device, treat this item with the same priority as the six changes in the main body — the documentation requires one of these two paths to continue local network communication.

Certificate transparency now comes enabled by default

For apps targeting targetSdk 37 or higher, certificate transparency (CT) is now enabled by default. The documentation notes the difference from Android 16 in parentheses: "On Android 16, CT is available but apps had to opt in." So a verification layer you previously had to enable consciously now kicks in automatically the moment you raise your target API level.

It's hard to estimate this item's impact at your desk, since it was opt-in on Android 16 and you may never have exercised this path before. When testing the network layer during the targetSdk 37 transition, list every TLS endpoint your app connects to and re-verify all of them after raising the target level — include internal-network and corporate-infrastructure services, not just public APIs. These three items all live on the behavior-changes-17 page, concerning only apps targeting targetSdk 37+; they kick in when you raise your target level, not the moment the device updates to Android 17.

GOLDEN TIP

The most valuable insight in this article

This tip holds the article's most important takeaway.

Easter Egg

You found a hidden gem!

There's a hidden detail in this section. Want to uncover it?

Reader Reward

Here's a short prep checklist covering all six changes in this post, to follow before you raise your targetSdk to 37. Every item you check off reduces the risk of a surprise silent failure in production.

FAQ

Why doesn't my app play audio in the background on Android 17?

For all apps on Android 17 — targeting targetSdk 37 or not — background audio interactions require either a visible Activity or a foreground service that isn't SHORT_SERVICE; if unmet, the audio-focus request fails with AUDIOFOCUS_REQUEST_FAILED, while the audio-playback and volume-change APIs fail silently. Targeting API 37 additionally requires while-in-use (WIU) authorization on the foreground service; this extra requirement is lifted only for apps granted exact alarm permission that change USAGE_ALARM streams.

What does ApplicationExitInfo 'MemoryLimiter' mean?

When Android 17's new memory limit is exceeded, the system terminates the app; ApplicationExitInfo.getDescription() is called to detect this. If affected, the exit reason is REASON_OTHER and the description field contains the string "MemoryLimiter:AnonSwap" along with other information; do a contains check, not an equality check. You can also register a ProfilingTrigger.TRIGGER_TYPE_ANOMALY trigger with ProfilingManager.

Why isn't the Activity restarted on Android 17?

Starting with API 37, the system no longer restarts the Activity by default for keyboard, keyboard-hiding, navigation, touchscreen, and color-mode changes that don't require a full UI redraw, nor when the UI mode switches to UI_MODE_TYPE_DESK or from it to another type; instead, onConfigurationChanged() is called. If you want the old restart behavior, you need to explicitly declare the android:recreateOnConfigChanges manifest attribute.

Why is reading SMS OTP delayed on API 37?

For most apps targeting targetSdk 37, standard SMS messages containing an OTP aren't accessible until three hours after receipt; during that time the SMS_RECEIVED_ACTION broadcast is held back and SMS provider database queries are filtered. The default SMS app, the assistant app, and connected companion device apps are exempt. The permanent fix is moving to the SMS Retriever or SMS User Consent API.

Am I affected by these changes if I don't raise my targetSdk to 37 right away?

Partially. The cross-profile loopback block and the MemoryLimiter memory limit apply independently of targetSdk the moment the device updates to Android 17. The base rule for the background-audio restriction (a visible Activity or a foreground service that isn't SHORT_SERVICE) is also independent of targetSdk; only the additional WIU requirement depends on targetSdk 37. On the SMS side, the three-hour delay for WebOTP- and SMS-Retriever-format messages also applies to all apps independently of targetSdk; it's the extension of this protection to standard SMS messages, and the NPU permission requirement, that affect apps targeting targetSdk 37+.

Conclusion

Android 17 (API 37) arrives with a "quieter" set of breakages than previous releases: four of the six items — the memory limit, the background-audio restriction, the cross-profile loopback block, and the SMS OTP delay — operate without throwing an error, affecting the user experience even if you don't notice. Testing these six items on real devices before raising your targetSdk is the most reliable way to catch the problem before Google Play reviews start saying "the app doesn't work in the background" or "OTP isn't arriving."

If you're following the Android release history, our Android 15 Developer Guide: Privacy Sandbox, Edge-to-Edge, Foreground Services post shows you what's changed since API 35. To go deeper on memory and performance, our Jetpack Compose 1.7 Performance: Strong Skipping + Stability guide will help, and for background task testing, our WorkManager 2.10 Coroutines: Modern Background Task Approach post. On the NPU and on-device AI side, check out our Gemini Nano iOS + Android: Cross-platform On-Device AI post, and for design-system updates, our Material 3 Expressive: Android 16 Design System guide.

Sources

Tags

#Android 17#API 37#MemoryLimiter#targetSdk#ApplicationExitInfo#SMS Retriever#NPU
Muhittin Çamdalı

Muhittin Çamdalı

Lead Mobile Engineer

Lead Mobile Engineer with 12+ years of experience. Expert in iOS, Android and cross-platform architectures with Swift, SwiftUI, Kotlin and Flutter. I build performant, user-friendly mobile apps.

iOS Development News

Weekly Swift tips, SwiftUI tricks and iOS best practices. No spam, only valuable content.

We respect your privacy. You can unsubscribe at any time.

Share