Android 17 is the release that made adaptive layouts mandatory and gave every
app a memory ceiling. Apps targeting API 37 can no longer keep their
orientation or aspect ratio locks on tablets and foldables, and the system now
kills a process that outgrows a limit set by the device’s RAM. For developers,
most of what breaks waits until targetSdk reaches 37.
This is the overview. Six parts follow it, in order: the four betas, from Beta 1 to Beta 4, with Beta 3 covering every new API at platform stability; what breaks on targetSdk 37; and the memory limits.
At a glance
| Version | Android 17 |
| API level | 37 (37.1 from QPR1; 37.2 in the QPR2 beta) |
| Name | Cinnamon Bun, internal; visible in the SDK constant. Public name: Android 17 |
Build.VERSION_CODES | CINNAMON_BUN (VERSION_CODES_FULL.CINNAMON_BUN_1 for QPR1) |
| Release | Pixel rollout began 16 June 2026 |
| Launch devices | Pixel 6 and newer by update; Samsung Galaxy Z Fold8 Ultra, Z Fold8 and Z Flip8, announced 22 July 2026 |
| Google Play target-API deadline | Not announced as of October 2026 |
Google released Android 17 on 16 June 2026 for the Pixel 6 and newer, and published the source to AOSP the same day. The first
release build is CP2A.260605.016, tag android-17.0.0_r1, with the June 2026
security patch.
Google skipped developer previews this time: the Canary channel did that job, and Android 17 went straight to beta. Beta 1 came out on 13 February 2026 and Beta 2 on 26 February. The API surface locked at Beta 3, on 26 March. Beta 4, on 16 April, was the last scheduled beta; a bug-fix Beta 4.1 followed on 1 June.
Samsung announced the Galaxy Z Fold8 Ultra, Z Fold8 and Z Flip8 on 22 July 2026; their spec sheets list Android 17 with One UI 9.
The name Cinnamon Bun leaked in August 2025, when a Google source showed
Android Authority the codename for API level 37.0. The SDK constant confirms
it: CINNAMON_BUN = 37. Like Baklava for Android 16, it is internal, but
developers see it in code.
What it brought
- Adaptive-first. Google’s launch post calls Android 17 “an adaptive-first development standard”. Any app can become a floating App Bubble by long-pressing its icon, large screens get a Bubble Bar in the taskbar, and desktop mode gets interactive picture-in-picture windows.
- Compose-first. Google declared that all new UI APIs, libraries and
guidance will be built only for Jetpack Compose.
android.widgetViews and View-based Jetpack libraries such as Fragments and RecyclerView are now in maintenance mode: critical bug fixes only. - AppFunctions. Apps can expose their actions as tools that AI agents, such as Gemini, discover and run on the user’s behalf. The Jetpack library is in alpha; the Gemini integration was in private preview at launch.
- Fewer activity restarts. Changes such as
CONFIG_KEYBOARDorCONFIG_COLOR_MODEno longer recreate the activity by default; it getsonConfigurationChanged()instead.android:recreateOnConfigChangesrestores the old behaviour. - Continue On.
setHandoffEnabled()lets the tablet taskbar offer the app the user just had open on their phone, deep-linked to where they left off. - Performance tools.
ProfilingManagergainsTRIGGER_TYPE_COLD_START,TRIGGER_TYPE_OOM,TRIGGER_TYPE_KILL_EXCESSIVE_CPU_USAGEandTRIGGER_TYPE_ANOMALY. For apps targeting 37,MessageQueueis lock-free. - Media and camera.
ImageFormat.RAW14, vendor-defined camera extensions, camera device type queries, an Extended HE-AAC encoder, H.266 (VVC) support for OEMs, and constant-quality video recording. - Hearing aids.
AudioDeviceInfo.TYPE_BLE_HEARING_AIDtells hearing aids apart from headsets, and users can route notifications, ringtones and alarms to the hearing aid or the phone speaker. - Pickers instead of permissions. A system contact picker
(
ACTION_PICK_CONTACTS), a portrait photo picker grid, a system-rendered location button, and a screen-wide eyedropper (ACTION_OPEN_EYE_DROPPER).
The full API list at platform stability is in the Beta 3 part.
Security and privacy
- Post-quantum cryptography. Android Keystore can generate ML-DSA keys in secure hardware through the standard JCA APIs, and APK Signature Scheme v3.2 pairs a classical signature with an ML-DSA one.
- Encrypted Client Hello. For apps targeting 37, TLS uses ECH when the
networking library and the server both support it.
<domainEncryption>in the network security config turns it on or off per domain. - Certificate Transparency is on by default for apps targeting 37; on Android 16 it was opt-in.
- Safer native code loading. For apps targeting 37,
System.load()throwsUnsatisfiedLinkErrorunless the file is read-only, extending the Android 14 rule for DEX and JAR files. - SMS OTP. WebOTP messages are held for three hours from every app that is not the verified recipient. For apps targeting 37, the hold covers standard OTP messages too.
- Activity security. For apps targeting 37, background activity launch
restrictions extend to
IntentSender, andMODE_BACKGROUND_ACTIVITY_START_ALLOWEDgives way to granular modes such asMODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE. - Theft protection. Find Hub’s “Mark as lost” can require biometrics on top of the PIN, so a thief who saw the PIN cannot turn off tracking. Remote Lock and Theft Detection Lock are on by default on new, reset or upgraded Android 17 devices.
- Passwords on physical keyboards. For apps targeting 37, the last typed character is no longer shown when the user types on a physical keyboard; touch and physical input have separate settings.
- Keystore and profiles. Non-system apps targeting 37 can own at most 50,000 Keystore keys, and loopback traffic between profiles is blocked by default.
Permissions
ACCESS_LOCAL_NETWORK. Apps targeting 37 lose local network access unless they hold this runtime permission or use a system device picker. It belongs to theNEARBY_DEVICESgroup, so users who already granted another nearby devices permission are not asked again.- Contacts. The contact picker returns only the fields the user picks,
without
READ_CONTACTS. For apps targeting 37, Contacts Provider hidesACCOUNT_NAME,ACCOUNT_TYPEandACCOUNT_TYPE_AND_DATA_SETin the data view, and queries withoutREAD_CONTACTSget strict SQL checks. - Location button.
USE_LOCATION_BUTTONlets an app embed the system-rendered button that grants precise location for the session only, with no dialog. - Pinned windows.
USE_PINNED_WINDOWING_LAYERlets an app ask for an always-on-top, interactive window in desktop mode. - NPU access. Apps targeting 37 that use the neural processing unit
directly must declare
FEATURE_NEURAL_PROCESSING_UNIT, or they are blocked.
What changed for your app
All apps on Android 17
| Change | What to do |
|---|---|
| A process over its RAM-based memory limit is killed with no stack trace | Read ApplicationExitInfo for "MemoryLimiter:AnonSwap"; see the memory part |
Audio playback, focus and volume changes need a visible activity or a foreground service (not SHORT_SERVICE) | Start a mediaPlayback service while visible; Media3’s MediaSessionService does it |
| WebOTP SMS are held for three hours from apps that are not the recipient | Use SMS Retriever or SMS User Consent |
| Cross-profile loopback traffic is blocked | Do not rely on localhost between work and personal profiles |
Apps targeting API 37
| Change | What to do |
|---|---|
On sw600dp displays, orientation, resizability and aspect ratio limits are ignored; the opt-out is gone | Build for any window size; games declaring android:appCategory="game" are exempt |
static final fields cannot be changed by reflection or JNI | Update libraries that rewrite them; JNI writes crash the process |
MessageQueue is lock-free; its private fields are useless to reflection | Update Espresso and Robolectric; use TestLooperManager |
| Local network access is blocked by default | Use a device picker, or request ACCESS_LOCAL_NETWORK |
| Background audio needs a foreground service with while-in-use capability | Start the service from a user action |
| Standard SMS OTP messages are held for three hours | Move to SMS Retriever or SMS User Consent |
setContentCaptureEnabled(false) no longer disables Content Capture | Use FLAG_SECURE |
Widget RemoteViews over the bitmap memory limit throw IllegalArgumentException | Downscale bitmaps and icons in widgets |
The reasoning behind each change, and the order to tackle them in, is in Android 17: what breaks on targetSdk 37.
Android 17 QPR1, QPR2 and minor SDKs
Android 16 shipped one minor SDK, in its December release. Android 17 has two so far.
- QPR1 (API 37.1). Its release notes list API diffs from 37 to 37.1 and
say it reached platform stability at Beta 6. It reached Pixel phones on
15 September 2026, with the September Feature Drop. The API reference lists
VERSION_CODES_FULL.CINNAMON_BUN_1, “Android 17, minor release 1”, added in version 37.1. - QPR2 (API 37.2, beta). Google says it “includes a minor SDK release”
with “no planned behavior changes”. Beta 7.1 came out on 16 October 2026,
and the reference already lists
CINNAMON_BUN_2. Two changes still reach apps: call-forwarding USSD codes no longer work withCALL_PHONEalone, and self-broadcasts are delivered more efficiently, for every app.
The check stays the same as in Android 16: compare SDK_INT_FULL against the
VERSION_CODES_FULL constant.
What lasted
It is too early to say what later versions kept. Google has announced two
follow-ups: from Android 18, the system stops granting URI permissions
implicitly for some intents, and usesCleartextTraffic will be deprecated in a
future release in favour of a network security config. Google Play has not set
a date for API 37; the current rule asks for API 36 from 31 August 2026.
Worth knowing
- The
Build.javacomment for the constant is two words: “Cinnamon Bun.” InVERSION_CODES_FULLit is just “Android 17.” Nothing like the Shakespeare line AOSP left onS_V2. - The name keeps the order. The dessert alphabet restarted at Baklava, a B, for
Android 16; Cinnamon Bun is the C that follows. Android 17 build IDs start
with C as well:
CP2A.260605.016. - The Easter egg opens with 17 dots in a circle. Trace through all of them in one stroke and back to the first, and the logo appears; lift your finger and the path resets. Keyboard users skip it: the code says “skip the cute star-drawing minigame”.
- The starfield changed direction. The source comment reads “New in C: we’re
zooming out from the center this time. Classic.” The warp ceiling is still
16fwith the comment “must go faster”. - The generational garbage collector is in both Google’s Android 16 QPR2 post
and its Android 17 launch post. AOSP settles it: the
use_generational_cmcflag was first enabled in the Android 16 QPR2 release configuration, and Android 17 inherits it.
Android 17 Beta 1: straight to beta
13 February 2026. The first Android 17 build, with no Developer Preview before it, removed the large-screen orientation opt-out for apps targeting API 37.
Read on its own: Android 17 Beta 1: straight to betaBeta 1 was the first Android 17 build Google gave developers, on
13 February 2026. No Developer Preview came before it: Google replaced that
phase with the Android Canary channel. Most of what later breaks on
targetSdk 37 was already in this build.
| Release date | 13 February 2026 |
| Build | CP21.260116.011 (.A1 for Pixel 6, 6 Pro, 6a, 7 and 7 Pro; .B1 for the rest) |
| Security patch | 2026-01-05 |
| Devices | Supported Pixel phones and tablets over the air; 64-bit Android Emulator images |
| Platform stability | Not yet; Google targeted March |
Google calls the build “suitable for development, testing, and general use”, with the warning that the system and apps “might not always work as expected”. It reached Pixel phones two days later than planned: 9to5Google reported a two-day delay before the rollout began on the 13th.
Why there was no Developer Preview
Google’s Beta 1 post says Android “has replaced the traditional ‘Developer Preview’ with a continuous Canary channel”. Features and APIs land in Canary once they pass internal testing, Canary updates over the air, and the beta starts closer to final. The same post set the year’s plan: the Q2 release is the only one with planned app-breaking behaviour changes, and a minor SDK follows in Q4.
Behaviour changes in Beta 1
- Large screens. Apps targeting API 37 on displays with a smallest width
of 600dp or more can no longer opt out of resizing or orientation changes.
screenOrientation,setRequestedOrientation(),resizeableActivity,minAspectRatioandmaxAspectRatioare ignored there. Apps declaringandroid:appCategory="game"are exempt. - Fewer activity restarts.
CONFIG_KEYBOARD,CONFIG_KEYBOARD_HIDDEN,CONFIG_NAVIGATION,CONFIG_TOUCHSCREEN,CONFIG_COLOR_MODEand someCONFIG_UI_MODEchanges no longer recreate the activity; it getsonConfigurationChanged().android:recreateOnConfigChangesopts back in. - Lock-free
MessageQueuefor apps targeting 37. Code that reflects on its private fields breaks. static finalis final. Changing one by reflection throwsIllegalAccessException; doing it through JNI crashes the app.- Custom notification views get strict size limits for apps targeting 37, closing a loophole that used URIs to bypass the old ones.
- Background audio. Playback, focus requests and volume changes fail silently when the app is not in a valid lifecycle state. Beta 4 later narrowed this rule.
- Cleartext traffic. Google announced that
android:usesCleartextTrafficis deprecated; the release notes say the deprecation takes effect with a future SDK level, and apps should move to a network security config.
New APIs in Beta 1
ProfilingManagertriggers for cold start, OOM and excessive CPU use.CameraCaptureSession.updateOutputConfigurations()switches between photo and video without closing the session, andLOGICAL_MULTI_CAMERA_ADDITIONAL_RESULTSreturns metadata from every active physical camera.- H.266 (VVC) support for devices with a hardware decoder, and
MediaRecorder.setVideoEncodingQuality()for constant-quality recording. - A public Service Provider Interface for HPKE hybrid encryption.
- Medical device and fitness tracker profiles in
CompanionDeviceManager, andsetExtraPermissions()to fold nearby permissions into the pairing dialog.
Google’s post also listed ART’s generational garbage collector as an Android 17 change. AOSP shows it was already on in Android 16 QPR2; see “Worth knowing” in the overview above.
Android 17 Beta 2: pickers and local network
26 February 2026. App bubbles, the EyeDropper and Contacts Picker, cross-device handoff, the ACCESS_LOCAL_NETWORK permission and a three-hour hold on OTP messages.
Read on its own: Android 17 Beta 2: pickers and local networkBeta 2 arrived on 26 February 2026, thirteen days after Beta 1. It brought most of the system pickers that Android 17 uses in place of broad permissions, and the two privacy changes that reach apps through the network and the SMS inbox.
| Release date | 26 February 2026 |
| Build | CP21.260206.011 (.A1 for Pixel 6, 6 Pro, 6a, 7 and 7 Pro) |
| Security patch | 2026-02-05 |
| Devices | Supported Pixel phones and tablets over the air; 64-bit Android Emulator images |
| Platform stability | Not yet; still targeted for March |
New APIs in Beta 2
- App bubbles. Users can turn any app into a floating bubble by long-pressing its launcher icon; large screens get a bubble bar in the taskbar. Google’s post adds that bubbles “aren’t yet fully enabled in Beta 2”.
- EyeDropper.
Intent.ACTION_OPEN_EYE_DROPPERreturns the colour of any pixel on the display without a screen capture permission. - Contacts Picker.
ACTION_PICK_CONTACTSgrants temporary access to only the fields the user picks, from the personal or the work profile, withoutREAD_CONTACTS. - Touchpad pointer capture. A captured touchpad now reports relative,
mouse-like movement;
View.POINTER_CAPTURE_MODE_ABSOLUTEkeeps the raw finger positions. - Cross-device handoff. The Handoff API resumes an app’s state on a nearby
device, such as a tablet, through
CompanionDeviceManager, with a web fallback. - Ranging and data plans. UWB DL-TDOA (FiRa 4.0) for indoor navigation,
Wi-Fi Alliance proximity detection, and
getStreamingAppMaxDownlinkKbps()/getStreamingAppMaxUplinkKbps()for carrier-allocated streaming rates. - Time and text.
ACTION_TIMEZONE_OFFSET_CHANGEDfires on offset changes such as daylight saving time, and ICU 78 brings Unicode 17.
Behaviour changes in Beta 2
- Local network. Apps targeting API 37 lose LAN access unless they use a
system device picker or hold the new
ACCESS_LOCAL_NETWORKruntime permission. It belongs to theNEARBY_DEVICESgroup, so users who already granted a nearby devices permission are not asked again. - OTP messages held for three hours. WebOTP messages are held from every app that is not the verified recipient, whatever it targets. For apps targeting 37, the hold covers every SMS with an OTP. The default SMS app, the assistant and connected companion apps are exempt; Google points everyone else to SMS Retriever or SMS User Consent.
- NPU access. Apps targeting 37 that use the neural processing unit
directly, including through the LiteRT NPU delegate or the deprecated
NNAPI, must declare
FEATURE_NEURAL_PROCESSING_UNITor they are blocked.
The release notes list 16 fixed issues. The first is an Android 16 regression that made active apps restart or refresh on their own.
Android 17 Beta 3: platform stability reached
A full breakdown of API 37 at platform stability — every notable new API, breaking change, and what to do right now.
Read on its own: Android 17 Beta 3: platform stability reachedAndroid 17 Beta 3 dropped on March 26, 2026, and it carries a very specific signal for developers: platform stability has been reached. That means the API surface is now locked. No more additions, no more removals, no more surprises. If you’re a library author, SDK vendor, or app developer, the clock is ticking.
Build number CP21.260306.017, API level 37, security patch 2026-03-05. This is the real thing.
What “platform stability” means in practice
Google uses Beta 3 as the point at which the final API set is frozen. From here until the stable release:
- No new public APIs will be added or removed
- Apps submitted to Google Play can now target API 37
- SDK and library authors should publish compatibility updates immediately
- Game engines and developer tooling vendors need to validate against this build
The stable release is expected later in 2026. We don’t have an exact date yet, but the timeline mirrors previous years: Beta 3 in March → stable around Q3.
Update, 17 October 2026: that guess was wrong. Android 17 went stable on 16 June 2026, after one more beta (Beta 4, 16 April). See Android 17: what breaks on targetSdk 37.
Camera and media
Photo picker gets aspect ratio customization
A long-requested feature: the photo picker now supports portrait 9:16 grid aspect ratio alongside the existing 1:1 square. Use PhotoPickerUiCustomizationParams:
val params = PhotoPickerUiCustomizationParams.Builder()
.setAspectRatio(PhotoPickerUiCustomizationParams.ASPECT_RATIO_PORTRAIT_9_16)
.build()
Works with ACTION_PICK_IMAGES and the embedded photo picker variant. Useful for apps where vertical video or portrait photography is the primary use case.
RAW14: 14-bit single-channel RAW format
ImageFormat.RAW14 is a new constant for 14-bit per pixel, densely packed RAW images (4 pixels stored in every 7 bytes). Professional camera apps with access to compatible sensors can now capture maximum color depth without custom format hacks.
Camera device type queries
You can now query whether a camera is built-in hardware, an external USB webcam, or a virtual camera. No more guessing from CameraCharacteristics workarounds.
Vendor-defined camera extensions
Hardware partners can define custom extension modes — think Super Resolution, AI-driven night modes, or manufacturer-specific enhancements. Query support via isExtensionSupported(int) on CameraExtensionCharacteristics.
Bluetooth LE Audio hearing aid support
New AudioDeviceInfo.TYPE_BLE_HEARING_AID constant distinguishes hearing aids from generic LE Audio headsets:
val devices = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)
val hearingAidConnected = devices.any {
it.type == AudioDeviceInfo.TYPE_BLE_HEARING_AID
}
This also enables granular hearing aid audio routing — users can independently route notifications, ringtones, and alarms to the hearing aid or device speaker, with no app changes required.
Extended HE-AAC software encoder
New system-provided MediaCodec (c2.android.xheaac.encoder) for high-efficiency speech and audio encoding. Unified codec supporting both high and low bitrates, with mandatory loudness metadata for consistent volume in low-bandwidth conditions.
val encoder = MediaCodec.createByCodecName("c2.android.xheaac.encoder")
val format = MediaFormat.createAudioFormat(
MediaFormat.MIMETYPE_AUDIO_AAC,
sampleRate = 48000,
channelCount = 1
)
format.setInteger(
MediaFormat.KEY_AAC_PROFILE,
MediaCodecInfo.CodecProfileLevel.AACObjectXHE
)
Privacy and security
System-provided location button
A new system-rendered button (delivered via Jetpack) that grants one-time precise location for the current session only — with no system permission dialog. Requires the new USE_LOCATION_BUTTON permission.
This is a meaningful UX improvement for apps where users want to share their location for a single action (ordering food, sharing with a friend) without granting persistent location access.
Discrete password visibility
The “Show passwords” setting is now split in two:
- Touch/soft keyboard input: briefly echoes the last typed character (same as before)
- Physical keyboard input: hidden immediately by default
If your app uses custom text fields, use the new ShowSecretsSetting API:
val isPhysicalKeyboard =
event.source and InputDevice.SOURCE_KEYBOARD == InputDevice.SOURCE_KEYBOARD
val shouldShowPassword =
android.text.ShowSecretsSetting.shouldShowPassword(context, isPhysicalKeyboard)
Post-quantum cryptography APK signing
Android 17 introduces APK signature scheme v3.2 with a hybrid approach: classical RSA or Elliptic Curve signatures combined with ML-DSA (Module Lattice Digital Signature Algorithm — a NIST-standardized post-quantum algorithm). This future-proofs the ecosystem against quantum computing threats without breaking compatibility with current tooling.
Native dynamic code loading — read-only enforcement extended
Since Android 14, Java/DEX files loaded dynamically must be read-only. Android 17 extends this enforcement to native libraries for apps targeting API 37. If such an app calls System.load() on a native file that isn’t marked read-only, it now throws UnsatisfiedLinkError. Audit your native library loading paths.
Certificate transparency — on by default
What was an opt-in feature on Android 16 is now enabled by default for apps targeting API 37. No code changes needed for apps that use standard HTTPS — but if you’re doing manual certificate validation or certificate pinning, review your implementation.
Performance and battery
AlarmManager: exact alarms with a listener callback
New overload of setExactAndAllowWhileIdle() that accepts an OnAlarmListener instead of a PendingIntent. Intended for apps currently using continuous wakelocks for precise timing (messaging sockets, medical monitoring apps):
alarmManager.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
triggerAtMillis,
"com.example.MY_ALARM",
executor,
new AlarmManager.OnAlarmListener() {
@Override
public void onAlarm() {
// Handle the alarm
}
}
);
Note: CommonsWare flagged the API documentation as internally contradictory — it simultaneously claims to target apps “reliant on continuous wakelocks” but also “reduces wakelocks.” The intent appears to be a more targeted alternative to long-lived wakelocks, not a complete removal of wakelock semantics.
User experience and system UI
Widget support on external displays
RemoteViews.setViewPadding() now accepts complex units (DP and SP, not just pixels). Widgets can retrieve the display ID of the screen they’re rendering on via OPTION_APPWIDGET_DISPLAY_ID:
val displayId = appWidgetManager
.getAppWidgetOptions(appWidgetId)
.getInt(AppWidgetManager.OPTION_APPWIDGET_DISPLAY_ID)
This makes it possible to adapt widget layouts for external monitors with different pixel densities — critical for Desktop Mode.
Desktop interactive picture-in-picture
Apps can request a pinned windowing layer in desktop mode (now the default on external displays). The window is always-on-top and fully interactive. Requires the new USE_PINNED_WINDOWING_LAYER permission:
appTask.requestWindowingLayer(
ActivityManager.AppTask.WINDOWING_LAYER_PINNED,
context.mainExecutor,
object : OutcomeReceiver<Int, Exception> {
override fun onResult(result: Int) { /* success */ }
override fun onError(e: Exception) { /* handle */ }
}
)
Hidden app labels
Users can now hide app names on the home screen. Google’s guidance to developers: make sure your app icon is recognizable without its label.
Redesigned screen recording toolbar
New floating toolbar for recording controls. The toolbar itself is excluded from the final captured video — a simple but important fix that any screen recorder user will appreciate.
Breaking changes
For all apps (regardless of target API)
None of the changes Google highlighted at Beta 3 applies to every app. An earlier version of this part listed native DCL, Certificate Transparency and local network access here; Google’s Beta 3 post scopes all three to apps targeting API 37, so they are in the next table. The final list of changes for all apps is in the stable release part.
For apps targeting API 37
| Change | What to do |
|---|---|
| Large screen resizability enforced | Cannot opt out of orientation/resizability constraints on large screens — test on foldables and tablets |
| DCL native enforcement | Ensure native libs loaded via System.load() are on read-only paths |
| Certificate Transparency on by default | Review custom certificate validation code |
| Local Network Access blocked | Request ACCESS_LOCAL_NETWORK, or use a system device picker, to reach LAN devices |
API changes in SDK 37, which show up when you compile against it, whatever you target:
ACTION_TAG_DISCOVEREDis deprecated. Move toACTION_NDEF_DISCOVEREDorACTION_TECH_DISCOVERED.DnsResolver.getInstance()is deprecated, not removed. SDK 37 adds aDnsResolver(Context, Looper)constructor to use instead.String.getChars()is still there. The API diff lists it under removals, but the method is in the public API at theandroid-17.0.0_r1tag. What changed is thatCharSequencegained a defaultgetChars()from OpenJDK 25. No migration needed.
Notable underdocumented APIs
CommonsWare identified several new APIs in the API diff that lack documentation:
SerialManager— serial port access, scope unclearWebAppManager— relationship to the browser unknownFileManagersystem service — background disk I/O for privileged apps- Bridged notifications — from other connected devices
- ANR warning registration — graceful timeout callbacks before an ANR is triggered
- Alternative SMS transport framework — RCS and beyond
Treat these as internal or partner-restricted for now.
Stability fixes in Beta 3
Beta 3 included an unusually large number of stability fixes, suggesting that earlier betas had significant reliability issues:
- Process lifecycle regression (Android 16) causing random app restarts and screen flickering
- Camera: 5x telephoto lens switching failures, lens transition stuttering
- Android Auto: lock screen freeze after disconnection
- Spontaneous reboots: 40+ related issues, hangs during idle
- Bluetooth: 150-second pairing hang
- UI artifacts: status bar icons disappearing, notification dismiss failures
What you should do right now
- Library/SDK authors: publish your Android 17 compatibility release now. The API surface is frozen.
- App developers: run your app on the Beta 3 system image or emulator. Pay attention to the native DCL enforcement and local network access changes.
- Test on large screens: the resizability enforcement change is the most likely to surface new issues in foldable/tablet form factors.
- Check your NFC code: if you use
ACTION_TAG_DISCOVERED, start planning the migration. - Submit your app to Google Play targeting API 37 — you can do it now.
Resources
Android 17 Beta 4: the last scheduled beta
16 April 2026. The final scheduled beta added RAM-based app memory limits, an anomaly profiling trigger and ML-DSA keys, and relaxed the background audio rules.
Read on its own: Android 17 Beta 4: the last scheduled betaBeta 4 came out on 16 April 2026, three weeks after Beta 3. Google called it “the last scheduled beta of this release cycle”. The API surface was already locked, so it brought few new APIs and one change every app feels: memory limits.
| Release date | 16 April 2026; Beta 4.1 on 1 June 2026 |
| Build | CP21.260330.008; Beta 4.1 CP21.260330.011 |
| Security patch | 2026-04-05; Beta 4.1 2026-05-05 |
| Devices | Supported Pixel phones and tablets over the air; 64-bit Android Emulator images |
| Platform stability | Yes, since Beta 3 |
What Beta 4 added
- App memory limits. The system kills an app whose memory use passes a
limit derived from the device’s total RAM. Google set the limits
“conservatively” for Android 17, to catch extreme leaks and outliers. An
affected app finds
"MemoryLimiter"inApplicationExitInfo.getDescription(). The memory limits part below covers it in full. TRIGGER_TYPE_ANOMALY. A newProfilingManagertrigger hands the app a heap dump when it breaches the memory limit, or a binder stack sample when it makes excessive binder calls. The callback runs before the system enforces anything.- ML-DSA in Android Keystore. On supported devices, apps can generate
ML-DSA-65andML-DSA-87keys in secure hardware throughKeyPairGenerator,KeyFactoryandSignature. - LeakCanary in Android Studio. Android Studio Panda adds LeakCanary as a task in the Profiler.
What Beta 4 changed
- Background audio, relaxed. After developer feedback, Google limited the while-in-use foreground service requirement to apps targeting 37 and exempted alarm audio.
- Cross-profile loopback blocked. Loopback traffic between profiles is no longer allowed by default, for every app on Android 17. Traffic within one profile is unaffected.
Beta 4.1, a bug-fix update, followed on 1 June. Android 17 itself shipped on 16 June.
Android 17: what breaks on targetSdk 37
From Beta 3 to the June 16 release — the behaviour changes that stay dormant until you bump targetSdk to 37, and the two runtime rewrites underneath them.
Read on its own: Android 17: what breaks on targetSdk 37Android 17 — API 37, internal codename Cinnamon Bun — went stable on 16 June 2026, alongside the June Pixel Drop, on Pixel 6 and newer. Other OEMs have been joining the rollout since. The API surface locked at Beta 3, on 26 March, when Google declared platform stability; Beta 4, the last scheduled beta, shipped on 16 April. My Beta 3 write-up covers API 37 at platform stability; this covers what happened after it, and what actually costs you time.
Six weeks in, the useful framing is not the feature list. It is this: most of
what will break your app is dormant right now. It fires when you bump
targetSdk to 37, not when a user updates their phone. That means you choose the
day it happens, which is worth using deliberately.
The process changed, and that is the real news
For the first time in years, Google shipped no Developer Previews. Android 17 went straight to Beta.
That is not a shortcut. It is the Canary channel doing the job Developer Previews used to: APIs and features get tested continuously there, so a separate preview build stopped earning its place in the calendar. The consequence for planning is real — the window between “first buildable release” and “platform stability” is now shorter, and if your process assumed a DP phase to absorb surprises, that phase no longer exists.
What breaks on targetSdk 37
Large-screen resizability is no longer optional
Apps targeting Android 17 can no longer opt out of orientation, resizability and aspect-ratio constraints on displays whose smallest width is over 600dp. The manifest flags that used to pin an app to portrait stop being honoured, the app fills the whole display window, and there is no pillarboxing to hide behind.
This is the one to plan for, not patch. If your layout assumed a fixed orientation, it will be resized on a tablet or a foldable and you will find out where the assumptions live. Google frames Android 17 as the move to an “adaptive-first” standard, and this is the clause that enforces it.
The exemption is narrower than “games are fine”: it applies to apps that
declare android:appCategory="game". It is a manifest declaration, not a
vibe. Google has not announced the Play deadline for API 37 yet (as of
October 2026); the current rule asks for API 36 from 31 August 2026, and each
year so far has moved the bar one level. Plan as if API 37 follows — the
exemption buys layout work, not an escape.
System.load() requires read-only native libraries
All native files loaded using
System.load()must be marked as read-only. Otherwise, the system throwsUnsatisfiedLinkError.
If you ship native code that gets extracted, copied, or downloaded at runtime and then loaded from a writable location, that path now throws. This is a Dynamic Code Loading restriction: a library you can still write to is a library an attacker can still rewrite between the check and the load.
Worth grepping for System.load( — not loadLibrary, which resolves from the APK
and is unaffected.
Local network access is blocked by default
Apps targeting 17 have local network access blocked by default. mDNS, raw sockets, device discovery — anything that reaches a printer, a Chromecast, a smart bulb, a local dev server, or another phone on the same Wi-Fi.
There are two ways back in, and the order matters:
- A system-mediated device picker. The user chooses the device, the system grants the scope, and you never show a permission prompt at all.
- The new
ACCESS_LOCAL_NETWORKruntime permission, requested explicitly, for persistent LAN communication.
Reaching for the permission first is the reflex, and it is the worse option: you pay a prompt, a denial rate, and a justification you have to write. Try the picker before you decide you need blanket access.
Background audio needs a foreground service
On Android 17, every app — whatever it targets — needs a visible activity or a
running foreground service (not of type SHORT_SERVICE) to play audio,
request audio focus or change the volume. An app targeting 37 that does it from
the background must also meet one of two conditions:
- the foreground service has while-in-use (WIU) capabilities, or
- the app holds the exact alarm permission and is driving
USAGE_ALARMstreams.
Note what the platform actually demands — a foreground service with the right
capability. MediaSessionService is not in the rule; it is the practical way to
satisfy it. Media3 configures, starts and stops that service for you and publishes
the media notification, so an app built on MediaSessionService is mostly already
compliant.
Which makes this the deadline for ExoPlayer 2. It is unmaintained, it does not manage the foreground-service lifecycle, and the new rules are shaped around Media3’s session and focus handling. Nothing hard-blocks ExoPlayer 2 from running — you just get interrupted playback, lost focus and broken notifications, which is a worse failure than a crash because nobody files a bug for it.
Whatever the target, the failure mode is the same: audio APIs called outside a
valid lifecycle fail silently, and audio focus returns
AUDIOFOCUS_REQUEST_FAILED.
Reflection into the platform stops working
Two changes, one root cause, and they land on your dependencies rather than your code.
static final fields can no longer be changed. An app running on 17 and
targeting 37 that rewrites one by reflection gets an IllegalAccessException;
doing it through JNI (SetStaticLongField() and friends) crashes the process.
Test doubles, feature-flag shims and analytics SDKs do this more than you would
guess.
MessageQueue is a new lock-free implementation. The old one used a single
lock for the main thread’s task queue, so a background thread could block the main
one — contention that showed up as dropped frames. The rewrite removes it. The
cost is that anything reflecting into MessageQueue internals breaks: mMessages
still exists for binary compatibility, but it is always null, whether or not
messages are pending. Espresso and Robolectric are the ones you will hit first, so
update those libraries before you touch targetSdk.
Audit your dependency tree before the bump, not after. A silent null from a
field that used to hold data is the most expensive kind of failure to trace.
OTP SMS arrives three hours late
For most apps targeting 17, one-time-password SMS is not readable until three
hours after it arrives. During that window the SMS_RECEIVED_ACTION broadcast
is withheld and SMS provider queries are filtered. The default SMS app, the
assistant app and connected companion apps are exempt.
The same three-hour hold applies to WebOTP-format messages regardless of targetSdk, when the app is not the verified recipient by domain.
Three hours is not a delay you design around; it is a message that reading the inbox is over. If you still parse SMS directly, move to SMS Retriever or SMS User Consent — both hand you the code without the app reading the user’s messages, which is the point.
Certificate Transparency is on by default
CT was opt-in on Android 16. For apps targeting 17 it is enabled by default.
For almost everyone this is invisible and good. It bites in exactly one place: if you pin or inject a certificate that is not in a public CT log — a corporate proxy, a test harness, an internal CA — connections that used to work will fail. Check your staging environment before you assume production is fine.
Contacts get narrower
Two things moved, and only one of them breaks.
The breaking one: for apps targeting 37, Contacts Provider 2 hides PII columns
from the data view — ACCOUNT_NAME, ACCOUNT_TYPE, ACCOUNT_TYPE_AND_DATA_SET.
If you query them, plan for them to be gone.
The other is an opportunity. Android 17 ships a standardised Contact Picker: a
browsable system UI where you declare the fields you need — phone numbers, emails
— and the user hands you specific contacts. READ_CONTACTS is not deprecated, but
it is now the blunt option. If you asked for the whole address book because there
was no alternative, there is one now.
The garbage collector went generational
ART’s Concurrent Mark-Compact collector is now generational. Instead of treating every object the same and sweeping the whole heap, it runs frequent cheap young-generation collections and reserves full-heap passes for when they are actually needed.
It is not new in 17, though Google’s launch post presents it that way. Google
listed the generational CMC collector in the
Android 16 QPR2 release post
in December 2025, and AOSP agrees: the use_generational_cmc flag is first
ENABLED in the bp4a release configuration, the Android 16 QPR2 build
(BP4A.251205.006). Android 17’s configuration, cp1a, inherits from bp4a, so
17 keeps it on.
The premise is the oldest observation in garbage collection: most objects die young. Acting on it cuts GC CPU cost and collection duration, which shows up as fewer dropped frames and less battery burned doing bookkeeping.
You get this for free — no targetSdk, no code change. And because ART ships
through Google Play system updates, runtime improvements reach devices back to
API 31, not just the ones that took the OS upgrade.
Post-quantum signing is in Keystore
Android Keystore now supports ML-DSA — Module-Lattice-Based Digital Signature
Algorithm, the NIST post-quantum standard — in two variants, ML-DSA-65 and
ML-DSA-87.
The part that matters for adoption: it is exposed through the standard Java
Cryptographic Architecture APIs. KeyPairGenerator, KeyFactory, Signature.
No new surface to learn, no vendor SDK. If you generate signing keys in hardware
today, the shape of the code does not change — the algorithm string does.
Whether you need this yet is a separate question. Post-quantum signatures matter where a signature has to remain verifiable for years and an adversary can store today’s traffic to attack later. For a session token that expires in an hour, it does not. It is good that the platform is ready before the need is urgent, which is the correct order for cryptographic migrations.
Memory limits, briefly
Android 17 enforces per-app memory limits derived from device RAM, and a
process that exceeds them is killed with no stack trace. ApplicationExitInfo
is where you find out it happened.
This one deserves more than a paragraph, and it has its own part: Android 17 kills your process with no stack trace.
What users actually got
Two things worth knowing because support questions arrive about them:
App Bubbles for any app. Long-press an app on the home screen and it becomes a chat-head-style floating bubble. Previously bubbles were a messaging affordance; now any app can be one, which means your UI can be asked to render in a small floating window it was never designed for.
Find Hub “Mark as lost” can require biometrics. Locking a lost device can now demand biometric authentication in addition to the passcode, so someone who has watched you type your PIN still cannot disable tracking. A small change with a clear threat model behind it.
What I would do this week
- Compile against SDK 37 in CI, without changing
targetSdk. You get deprecation and API-surface signal at zero behavioural risk. - Audit your dependencies for reflection. Testing, feature-flag and analytics
libraries are the ones rewriting
static finalfields and readingMessageQueueinternals. Update Espresso and Robolectric first. - Grep for
System.load(. Fastest possible check for the breaking change most likely to be missed. - Test on a foldable or a tablet emulator in landscape. Not because it will look bad — because it will now happen whether or not you opted in.
- Inventory your permissions. Anything touching the LAN, the address book or the SMS inbox now has a narrower, system-mediated path that beats asking.
- Run your staging TLS path. CT-by-default fails loudly, but only where you have a non-public certificate, and that is usually not production.
- Then bump
targetSdk, deliberately, with time to react.
If you run a team, do not wait for the Play deadline to force the number. The audit is the slow part, not the bump.
The pattern in this release is consistent: the platform is closing paths that used to be optional — orientation opt-outs, writable native libraries, unrestricted LAN access, broad contact reads, SMS scraping, reflection into internals, unbounded memory. Each one was a place where an app could be careless and the cost landed somewhere else. That is a defensible direction. It also means “it still works” stops being evidence of anything the day you change one number.
Android 17 kills your process with no stack trace
Per-app memory limits are cgroup limits tied to device RAM, you cannot query them at runtime, and the kill leaves no crash log.
Read on its own: Android 17 kills your process with no stack traceAndroid 17 enforces per-app memory limits. When your process crosses one, the
system kills it — and there is no stack trace, no OutOfMemoryError, nothing
in your crash reporter that looks like a crash. From the inside it is
indistinguishable from the process simply ceasing to exist.
That is the whole problem. Not the limit. The silence.
What is actually being limited
Google’s developer blog says “memory limits based on device RAM”. The AOSP documentation says what that means, and it is more specific than the blog suggests.
The Memory Limiter sets two cgroup values per process:
| Value | Effect |
|---|---|
memory.high | Soft limit. The kernel throttles the process and reclaims |
memory.swap.max | Caps how much swap the process can take |
The metric is anonymous memory — heap plus native allocations not backed by a
file. File-backed pages get evicted; anonymous pages get swapped. Which is why the
exit description says AnonSwap.
Note what memory.high is: a soft limit. The kernel does not immediately kill
you for crossing it. It throttles you and tries to reclaim. The death comes later,
when reclaim cannot keep up and an allocation fails. That is why there is no clean
OutOfMemoryError to catch — by then you are being stopped, not asked.
Three facts that change how you plan
1. You cannot query the limit. As of Android 17 — SDK 37 — there is no runtime
API to ask what your limit is. Your app cannot read it, log it, or adapt to it.
Any strategy that involves “check the budget and stay under it” is not available.
On a test device you can see it: adb shell am memory-limiter status prints the
visible and not-visible limits, and am memory-limiter manual <pid> <MB> imposes
a tighter one so you can watch your app hit it.
2. Who sets the numbers depends on which source you read. The limits are
<limitSet> blocks in a memory-limiter-config.xml, declaring
minimumRequiredMemTotal, maximumAllowedMemTotal, memVisible,
memNotVisible, swapVisible and swapNotVisible in MiB. The two primary
sources disagree on where that file lives:
- The released code (
MemoryLimiter.javaatandroid-17.0.0_r1) reads/vendor/etc/memory-limiter-config.xml, the partition the device maker owns. AOSP’s documentation said the same in July 2026. - The current documentation (updated since August 2026) says
/system/etc/memory-limiter-config.xmland defines standard platform limits per RAM tier. It names “ecosystem consistency” as a principle and allows no per-app allowlists.
Both put visible processes at roughly 1/2 to 2/3 of physical RAM and not-visible
ones at 1/4 to 1/3. The standard tiers, by kernel MemTotal:
| Device RAM | MemTotal range (MiB) | Visible | Not visible |
|---|---|---|---|
| 16 GB | 14336 to 18431 | 10 GiB | 5 GiB |
| 12 GB | 9216 to 14335 | 8 GiB | 4 GiB |
| 8 GB | 6800 to 9215 | 5 GiB | 3 GiB |
| 6 GB | 4800 to 6799 | 4 GiB | 2 GiB |
| 4 GB | 3200 to 4799 | 2 GiB | 1 GiB |
Swap limits match the not-visible memory limit in every tier.
3. Some devices enforce nothing. In the released code, a missing or invalid
config file disables the limiter. Under the standard tiers, devices with 18,432
MiB of MemTotal or more, or less than 3,200, get no limits at all. “It works on
my phone” has never been weaker evidence than it is here.
Which processes are exempt
Only PERSISTENT and PERSISTENT_UI — system processes. Everything an app can
be is monitored: TOP, FOREGROUND_SERVICE, IMPORTANT_FOREGROUND,
TRANSIENT_BACKGROUND, BACKUP, SERVICE, RECEIVER, CACHED.
Cached processes get the harshest treatment: maximally reclaimed and frozen. Which is the point of the feature, stated plainly in Google’s own framing:
When an app becomes bloated or leaks memory while holding a privileged state, the LMK is forced to compensate by killing off dozens of smaller, well-behaved cached apps.
One greedy app used to cost every other app its warm state. The limiter makes the greedy app pay instead. Hard to argue with the direction.
How to detect it
This is the actionable part. The kill is silent, but it is recorded.
val activityManager = getSystemService(ActivityManager::class.java)
activityManager.getHistoricalProcessExitReasons(packageName, 0, 10)
.filter { it.reason == ApplicationExitInfo.REASON_OTHER }
.filter { it.description?.contains("MemoryLimiter") == true }
.forEach { exit ->
// description contains "MemoryLimiter:AnonSwap"
report("memory-limit-kill", exit.description, exit.timestamp)
}
Two details that matter:
- The reason is
REASON_OTHER, not a dedicated constant. You have to match on the description string, which means a platform wording change can silently break your detection. Match on"MemoryLimiter", not the full"MemoryLimiter:AnonSwap". - Read this on the next launch, not at the moment of death — there is no moment
of death you get to observe.
getHistoricalProcessExitReasonsis the only window.
If you do not add something like this, these kills arrive as an unexplained dip in session length and a rise in cold starts, with nothing in Crashlytics to connect them to.
Catching it before the platform does
ProfilingManager — introduced in Android 15, extended in 17 — gives you two
triggers worth wiring up:
TRIGGER_TYPE_OOMcollects a Java heap dump at the moment of anOutOfMemoryError. The dump you always wanted and never had.TRIGGER_TYPE_ANOMALYfires on severe performance problems including memory thresholds being approached — before the system enforces.
val profilingManager = getSystemService(ProfilingManager::class.java)
profilingManager.registerForAllProfilingResults(mainExecutor, resultCallback)
profilingManager.addProfilingTriggers(triggers)
TRIGGER_TYPE_ANOMALY is the one that changes the game: it is the closest thing to
the query API that does not exist. You cannot ask what your limit is, but you can
be told when you are approaching it.
What actually reduces the number
None of this is new advice. It is newly enforced advice, which is different.
Turn on R8 properly. isMinifyEnabled = true, isShrinkResources = true, and
proguard-android-optimize.txt rather than the non-optimising variant. A
surprising number of projects have the first two and not the third.
Downsample images at decode time. inSampleSize when you know the target
size, and RGB_565 when you do not need transparency — that is half the bytes per
pixel of ARGB_8888. A full-resolution bitmap for a thumbnail is the single most
common way an app wastes tens of megabytes.
Respond to onTrimMemory. Specifically TRIM_MEMORY_UI_HIDDEN — the user
left, your views are not visible, and whatever you cached for rendering is pure
cost — and TRIM_MEMORY_BACKGROUND. Most apps override this method and drop
nothing meaningful in it.
Find the leaks. LeakCanary now integrates into the Android Studio profiler directly in Panda 3. A leak under a memory limit is no longer a slow degradation; it is a hard kill on a timer.
What I would do this week about memory
- Ship the
ApplicationExitInfocheck first. It is a dozen lines and without it you are blind. You cannot fix a rate you cannot see. - Wire
TRIGGER_TYPE_ANOMALY. It is the only forward-looking signal available. - Check the third R8 flag. Two minutes, and often a real reduction.
- Audit bitmap decoding paths. Where the megabytes usually are.
- Do not tune to a number. Your app cannot read it, it depends on the device’s RAM tier, and on some devices it does not exist. Build headroom, not a target.
The honest summary: this is a good change with a bad failure mode. Making a memory-hungry app bear its own cost — instead of every other app on the phone paying for it — is the right call. Doing it with a kill that leaves no trace means the apps most affected are the least likely to find out. Which is why detection, not optimisation, is the first thing to ship.