<CarlosDev/>

Current

Android 17: adaptive-first, memory limits

API 37, June 2026. Large screens lost the orientation opt-out, the system started killing apps over a RAM-based memory limit, and two minor SDKs followed.

Published
Updated
Reading
40 min
Write-up
Stable
Series
Part 19 of 19 · Android, version by version
Android 17 Cinnamon Bun banner, with the version logo.
In this record14 sections

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

VersionAndroid 17
API level37 (37.1 from QPR1; 37.2 in the QPR2 beta)
NameCinnamon Bun, internal; visible in the SDK constant. Public name: Android 17
Build.VERSION_CODESCINNAMON_BUN (VERSION_CODES_FULL.CINNAMON_BUN_1 for QPR1)
ReleasePixel rollout began 16 June 2026
Launch devicesPixel 6 and newer by update; Samsung Galaxy Z Fold8 Ultra, Z Fold8 and Z Flip8, announced 22 July 2026
Google Play target-API deadlineNot 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.widget Views 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_KEYBOARD or CONFIG_COLOR_MODE no longer recreate the activity by default; it gets onConfigurationChanged() instead. android:recreateOnConfigChanges restores 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. ProfilingManager gains TRIGGER_TYPE_COLD_START, TRIGGER_TYPE_OOM, TRIGGER_TYPE_KILL_EXCESSIVE_CPU_USAGE and TRIGGER_TYPE_ANOMALY. For apps targeting 37, MessageQueue is 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_AID tells 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() throws UnsatisfiedLinkError unless 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, and MODE_BACKGROUND_ACTIVITY_START_ALLOWED gives way to granular modes such as MODE_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 the NEARBY_DEVICES group, 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 hides ACCOUNT_NAME, ACCOUNT_TYPE and ACCOUNT_TYPE_AND_DATA_SET in the data view, and queries without READ_CONTACTS get strict SQL checks.
  • Location button. USE_LOCATION_BUTTON lets an app embed the system-rendered button that grants precise location for the session only, with no dialog.
  • Pinned windows. USE_PINNED_WINDOWING_LAYER lets 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

ChangeWhat to do
A process over its RAM-based memory limit is killed with no stack traceRead 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 recipientUse SMS Retriever or SMS User Consent
Cross-profile loopback traffic is blockedDo not rely on localhost between work and personal profiles

Apps targeting API 37

ChangeWhat to do
On sw600dp displays, orientation, resizability and aspect ratio limits are ignored; the opt-out is goneBuild for any window size; games declaring android:appCategory="game" are exempt
static final fields cannot be changed by reflection or JNIUpdate libraries that rewrite them; JNI writes crash the process
MessageQueue is lock-free; its private fields are useless to reflectionUpdate Espresso and Robolectric; use TestLooperManager
Local network access is blocked by defaultUse a device picker, or request ACCESS_LOCAL_NETWORK
Background audio needs a foreground service with while-in-use capabilityStart the service from a user action
Standard SMS OTP messages are held for three hoursMove to SMS Retriever or SMS User Consent
setContentCaptureEnabled(false) no longer disables Content CaptureUse FLAG_SECURE
Widget RemoteViews over the bitmap memory limit throw IllegalArgumentExceptionDownscale 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 with CALL_PHONE alone, 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.java comment for the constant is two words: “Cinnamon Bun.” In VERSION_CODES_FULL it is just “Android 17.” Nothing like the Shakespeare line AOSP left on S_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 16f with 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_cmc flag was first enabled in the Android 16 QPR2 release configuration, and Android 17 inherits it.

Part 1 of 6

3 min

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 beta

Beta 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 date13 February 2026
BuildCP21.260116.011 (.A1 for Pixel 6, 6 Pro, 6a, 7 and 7 Pro; .B1 for the rest)
Security patch2026-01-05
DevicesSupported Pixel phones and tablets over the air; 64-bit Android Emulator images
Platform stabilityNot 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, minAspectRatio and maxAspectRatio are ignored there. Apps declaring android:appCategory="game" are exempt.
  • Fewer activity restarts. CONFIG_KEYBOARD, CONFIG_KEYBOARD_HIDDEN, CONFIG_NAVIGATION, CONFIG_TOUCHSCREEN, CONFIG_COLOR_MODE and some CONFIG_UI_MODE changes no longer recreate the activity; it gets onConfigurationChanged(). android:recreateOnConfigChanges opts back in.
  • Lock-free MessageQueue for apps targeting 37. Code that reflects on its private fields breaks.
  • static final is final. Changing one by reflection throws IllegalAccessException; 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:usesCleartextTraffic is 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

  • ProfilingManager triggers for cold start, OOM and excessive CPU use.
  • CameraCaptureSession.updateOutputConfigurations() switches between photo and video without closing the session, and LOGICAL_MULTI_CAMERA_ADDITIONAL_RESULTS returns 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, and setExtraPermissions() 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.

Part 2 of 6

2 min

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 network

Beta 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 date26 February 2026
BuildCP21.260206.011 (.A1 for Pixel 6, 6 Pro, 6a, 7 and 7 Pro)
Security patch2026-02-05
DevicesSupported Pixel phones and tablets over the air; 64-bit Android Emulator images
Platform stabilityNot 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_DROPPER returns the colour of any pixel on the display without a screen capture permission.
  • Contacts Picker. ACTION_PICK_CONTACTS grants temporary access to only the fields the user picks, from the personal or the work profile, without READ_CONTACTS.
  • Touchpad pointer capture. A captured touchpad now reports relative, mouse-like movement; View.POINTER_CAPTURE_MODE_ABSOLUTE keeps 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_CHANGED fires 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_NETWORK runtime permission. It belongs to the NEARBY_DEVICES group, 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_UNIT or 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.

Part 3 of 6

8 min

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 reached

Android 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

ChangeWhat to do
Large screen resizability enforcedCannot opt out of orientation/resizability constraints on large screens — test on foldables and tablets
DCL native enforcementEnsure native libs loaded via System.load() are on read-only paths
Certificate Transparency on by defaultReview custom certificate validation code
Local Network Access blockedRequest 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_DISCOVERED is deprecated. Move to ACTION_NDEF_DISCOVERED or ACTION_TECH_DISCOVERED.
  • DnsResolver.getInstance() is deprecated, not removed. SDK 37 adds a DnsResolver(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 the android-17.0.0_r1 tag. What changed is that CharSequence gained a default getChars() 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 unclear
  • WebAppManager — relationship to the browser unknown
  • FileManager system 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

  1. Library/SDK authors: publish your Android 17 compatibility release now. The API surface is frozen.
  2. 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.
  3. Test on large screens: the resizability enforcement change is the most likely to surface new issues in foldable/tablet form factors.
  4. Check your NFC code: if you use ACTION_TAG_DISCOVERED, start planning the migration.
  5. Submit your app to Google Play targeting API 37 — you can do it now.

Resources

Part 4 of 6

2 min

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 beta

Beta 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 date16 April 2026; Beta 4.1 on 1 June 2026
BuildCP21.260330.008; Beta 4.1 CP21.260330.011
Security patch2026-04-05; Beta 4.1 2026-05-05
DevicesSupported Pixel phones and tablets over the air; 64-bit Android Emulator images
Platform stabilityYes, 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" in ApplicationExitInfo.getDescription(). The memory limits part below covers it in full.
  • TRIGGER_TYPE_ANOMALY. A new ProfilingManager trigger 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-65 and ML-DSA-87 keys in secure hardware through KeyPairGenerator, KeyFactory and Signature.
  • 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.

Part 5 of 6

10 min

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 37

Android 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 throws UnsatisfiedLinkError.

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:

  1. A system-mediated device picker. The user chooses the device, the system grants the scope, and you never show a permission prompt at all.
  2. The new ACCESS_LOCAL_NETWORK runtime 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_ALARM streams.

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

  1. Compile against SDK 37 in CI, without changing targetSdk. You get deprecation and API-surface signal at zero behavioural risk.
  2. Audit your dependencies for reflection. Testing, feature-flag and analytics libraries are the ones rewriting static final fields and reading MessageQueue internals. Update Espresso and Robolectric first.
  3. Grep for System.load(. Fastest possible check for the breaking change most likely to be missed.
  4. 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.
  5. Inventory your permissions. Anything touching the LAN, the address book or the SMS inbox now has a narrower, system-mediated path that beats asking.
  6. 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.
  7. 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.

Part 6 of 6

6 min

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 trace

Android 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:

ValueEffect
memory.highSoft limit. The kernel throttles the process and reclaims
memory.swap.maxCaps 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.java at android-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.xml and 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 RAMMemTotal range (MiB)VisibleNot visible
16 GB14336 to 1843110 GiB5 GiB
12 GB9216 to 143358 GiB4 GiB
8 GB6800 to 92155 GiB3 GiB
6 GB4800 to 67994 GiB2 GiB
4 GB3200 to 47992 GiB1 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. getHistoricalProcessExitReasons is 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_OOM collects a Java heap dump at the moment of an OutOfMemoryError. The dump you always wanted and never had.
  • TRIGGER_TYPE_ANOMALY fires 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

  1. Ship the ApplicationExitInfo check first. It is a dozen lines and without it you are blind. You cannot fix a rate you cannot see.
  2. Wire TRIGGER_TYPE_ANOMALY. It is the only forward-looking signal available.
  3. Check the third R8 flag. Two minutes, and often a real reduction.
  4. Audit bitmap decoding paths. Where the megabytes usually are.
  5. 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.

Filed under

Citations

42

External references