Skip to main content

On 15 September, Pixel owners got Android 17 QPR1. The visible part was a feature drop: more blur in the system UI, a new media player carousel, custom ringtone vibration patterns. The part that matters to anyone shipping an Android app arrived without a blog post. QPR1 added new public APIs to the platform, and those APIs are not in the Android Open Source Project.

GrapheneOS flagged it on 16 September, noting that this is the first Android release since Honeycomb to add developer-facing APIs without a corresponding AOSP drop. The observation reached the Hacker News front page and was mostly read as an open-source story. It is not. It is a portability story, and it quietly changes what an API level tells you.

TL;DR

  • Android 17 QPR1 (API 37.1) shipped to Pixel devices on 15 September 2026 with new public APIs, including a brand new android.hardware.hid package. Other OEMs are expected to receive them in December via QPR2.
  • AOSP’s own compatibility table lists API 36.1 as “Android16 QPR2” and lists nothing at all for 37.1. The most recent AOSP tag is android-17.0.0_r1, at a June 2026 patch level.
  • Google’s minor SDK mechanism works exactly as designed. The problem is not the API surface, it is that availability is now scoped to a vendor rather than to a version.
  • Your toolchain cannot see this. Lint validates the version predicate and will confirm your gate is correct. It has no idea the true branch only executes on a Pixel.
  • If your CI device pool is Pixels and the Android Emulator, which is the default, you are testing the only hardware that has the API.

What actually shipped

The API difference report between 37 and 37.1 is public, and it is not a rounding error. One entirely new package appeared: android.hardware.hid, carrying peripheral customisation APIs (addPeripheralCustomization(), getAllPeripheralCustomizations(), a MANAGE_PERIPHERAL_CUSTOMIZATION permission and a set of trigger and action types for remapping buttons and keys). Alongside it: MediaStore.DeletedFiles with a queryDeletedFiles() entry point, embedded photo picker UI customisation, a KEYCODE_CONTEXTUAL_QUERY keycode, device UDI fields in Health Connect, and the full set of MADV_* constants behind a new Os.madvise() binding.

That last one is worth opening in the reference docs, because it contains the whole story in three words. Every method around it is annotated “Added in API level 21”, “Added in API level 30”. madvise reads “Added in version 37.1”. The unit of availability changed, and the documentation generator changed its wording to match.

None of this is an accident or a leak. Build.VERSION_CODES_FULL, added back in API 36, already declares CINNAMON_BUN, CINNAMON_BUN_1 and CINNAMON_BUN_2. The minor release after next has a constant waiting for it. This is a shipped, documented, deliberate cadence.

AOSP’s own table is the receipt

The clearest evidence is not the GrapheneOS thread, it is Google’s own build numbers page on source.android.com, last updated on 10 September. Its codename table reads:

  • Android17, version 17, API level 37, CTS 17, quarterly release 26Q2
  • Android16 QPR2, version 16, API level 36.1, CTS 16.1, quarterly release 25Q4
  • Android16, version 16, API level 36, CTS 16, quarterly release 25Q2

Two things follow. First, the previous minor API level, 36.1, reached AOSP through QPR2, which is the quarterly release Google still publishes. Second, there is no row for 37.1 at all, and no CTS version associated with it. The build ID list agrees: the newest tag is android-17.0.0_r1 (CP2A.260605.016), carrying a 2026-06-05 security patch level, months older than the release that added these APIs.

So an API level now exists on developer.android.com that does not exist in the compatibility definition other manufacturers build against. Google’s own developer page for Android 17 QPR1 is consistent with that: it offers the release on “select Pixel devices” and the Android Emulator. Those are the two places the API runs.

Your toolchain will tell you the gate is correct

The minor SDK mechanism itself is well designed, and it is worth being fair about that. Google’s documentation for SDK_INT_FULL states that minor releases “can add new APIs, and have stricter guarantees around backwards compatibility (e.g. no changes gated by targetSdkVersion)”. There is no compatibility cliff to walk off. You opt in through a clear build declaration:

android {
    compileSdk {
        version = release(37) {
            minorApiLevel = 1
        }
    }
}

Lint understands all of it. @RequiresApi and @ChecksSdkIntAtLeast accept full SDK integers, there is a check that catches you comparing SDK_INT against a VERSION_CODES_FULL constant, and gates that can never be true or false get flagged.

Which is precisely the trap. Static analysis verifies the predicate. It cannot verify the population. Write this:

if (Build.VERSION.SDK_INT_FULL >= Build.VERSION_CODES_FULL.CINNAMON_BUN_1) {
    // remap the scanner trigger button
}

and every tool in the chain will agree it is correct, because it is. What no tool states is that until December, and in practice well beyond it, the body of that branch is Pixel-only code. For fifteen years an API level was a portability contract: reach the number and the API is there, on any device that passed CTS. A minor API level is now closer to a vendor capability flag wearing a version number.

The irony is in which APIs shipped. Peripheral customisation is an enterprise and kiosk feature. The teams most likely to want it are building for retail handhelds, warehouse scanners and point-of-sale terminals, which is almost entirely non-Pixel hardware. The devices that need the API will be the last to get it.

The patch half of the story

GrapheneOS makes a second claim that deserves separate treatment, because we cannot verify it first-hand: that the September 2026 Pixel Update Bulletin contains fixes to standard Android platform components used by non-Pixel devices which did not appear in the corresponding Android Security Bulletin, and which other manufacturers will receive in December through QPR2.

Treat that as one well-placed party’s account rather than settled fact. Treat the structural point as settled, though, because the build tags already demonstrate it: two devices can report the same Android version and be a quarter apart on platform fixes. If you advise clients on device fleets, “running Android 17” has stopped being a sufficient answer in a risk register. We made a related argument about what verified boot actually attests to: the signal is accurate and narrower than people assume.

Six things worth doing this quarter

  1. Grep for SDK_INT_FULL and for any @RequiresApi carrying a minor level. In most codebases the answer is zero and you can stop reading. If it is not zero, you have vendor-scoped code paths and should know where they are.
  2. Name the gate honestly. A constant called SUPPORTS_PERIPHERAL_REMAPPING is a lie for the next three months. PIXEL_ONLY_UNTIL_QPR2 is not, and the person reading the diff in January will thank you.
  3. Prefer capability detection over version detection. PackageManager.hasSystemFeature(), a guarded first call, a reflective probe: anything that asks the device what it can do rather than asking a number what it implies.
  4. Audit the device matrix, not the API list. If your managed device tests run on Pixels and emulators, your coverage of a 37.1 gate is 100% positive and 0% negative. Add one non-Pixel handset on the same major version and the false branch gets exercised for the first time.
  5. Keep minor-level APIs out of shared code. A library, design system or white-label module that adopts a QPR1 API inherits its distribution limits to every consumer, including the client whose fleet is Samsung.
  6. Put the lag into procurement and MDM policy. Same version string, different patch reality, differing by OEM. That belongs in the device standard, next to the minimum supported version.

Openness is a schedule now, not a licence

Android is still open source. What changed is when. The licence says you may have the code; the release calendar decides whether you have it in September or the following December, and the answer now depends on whose logo is on the handset. That is a supply chain property, and like most supply chain properties it was invisible until a version number made it visible.

We keep meeting the same shape. A store review queue turned into an unversioned dependency with no SLA. A cross-platform framework turned out to be an amortisation decision with an unwritten expiry date. Now an API level is a distribution question. None of these appear in a dependency file, and all of them set delivery dates.

REPTILEHAUS builds and audits mobile and platform work for clients across Ireland and the EU, including device fleet strategy, release engineering and the unglamorous business of working out which of your dependencies are actually calendars. If you are not sure which parts of your stack are open on paper and gated in practice, get in touch.

📷 Photo by Liam Scotchmer on Unsplash