build.prop
Also called: build properties, build fingerprint
build.prop is the Android system file holding a device’s build properties — manufacturer, brand, model, product, Android version, build ID and build fingerprint — written when the firmware is compiled. Apps read these through the Build class to know what device they are running on, which puts the file at the centre of device identity.
Located at /system/build.prop, the file is a plain list of key-value pairs. Since Android 10 the properties are split across several partitions — /vendor/build.prop, /odm/etc/build.prop, /product/build.prop and /system_ext/build.prop — all loaded into one namespace by the init property service at boot. An app never reads the files; it calls into the Build class, which reads the in-memory property area.
The keys that carry the identity
- ro.product.manufacturer, ro.product.brand, ro.product.model, ro.product.device, ro.product.name — the five that name the handset.
- ro.build.version.release and ro.build.version.sdk — the Android version and its API level.
- ro.build.id — the platform build ID, e.g. TQ3A.230805.001.
- ro.build.version.security_patch — a date such as 2024-03-05, always the 1st or 5th of a month in Google’s scheme.
- ro.build.tags and ro.build.type — should read release-keys and user on retail firmware.
- ro.product.cpu.abilist — the ABIs, e.g. arm64-v8a,armeabi-v7a,armeabi.
- ro.build.fingerprint — the concatenation of all of it into a single string.
How the fingerprint string is assembled
The format is fixed: brand/product/device:version.release/id/version.incremental:type/tags. A real Pixel reads google/raven/raven:13/TQ3A.230805.001/10316531:user/release-keys. Every field in that string is checkable against the firmware Google actually published, and the same is true of every major OEM. A fingerprint that parses correctly but names a build never released for that model is a fabrication that survives casual inspection and fails a table lookup.
The properties that announce a problem
Some keys should simply not be present, or should not hold the values they hold, on anything claiming to be a retail handset:
- ro.kernel.qemu=1 — the historical emulator flag, still the fastest single check.
- ro.hardware=goldfish or ranchu — the Android emulator’s virtual hardware platforms.
- ro.build.tags=test-keys — firmware signed with the public AOSP test keys, meaning it is not an OEM release.
- ro.build.type=userdebug or eng — engineering builds, never shipped to consumers.
- ro.debuggable=1 and ro.secure=0 — a debuggable, non-secure build.
- ro.product.model containing "Android SDK built for x86" or "sdk_gphone" — the SDK images, unmodified.
- ro.product.cpu.abilist listing x86_64 or x86 on a device claiming an ARM chipset.
Checking it yourself
All of it is one ADB session away, no root required, since ro properties are world-readable:
- adb shell getprop ro.build.fingerprint — then compare the string against the OEM’s published build list.
- adb shell getprop | grep ro.product — read all five naming properties together and check they agree.
- adb shell getprop ro.build.version.security_patch — a date in the future, or one that predates the build ID, is an invented value.
- adb shell getprop ro.kernel.qemu ro.hardware ro.build.tags — three answers that settle the emulator question quickly.
The consistency requirement
These values are read together, and they have to agree with each other and with the hardware. A build fingerprint naming a Samsung Galaxy S23 while ro.product.manufacturer says Google is broken on its face. Subtler: a build ID that never shipped for the model claimed, an Android version the model never received, or a security patch level from a month when that model was already end-of-life. Each is detectable by anyone maintaining a table of real firmware releases, and those tables are public.
They must also agree with the device fingerprint as a whole. A build.prop claiming a flagship on a device reporting a 720p screen, 2 GB of RAM, six sensors and a software GPU renderer contradicts itself four times over before anyone even looks at the network.
Why editing it is not enough
Rewriting build.prop changes what the device says about itself, not what it is. Sensors, baseband, GPU strings, codec lists and battery behaviour all continue reporting the real hardware underneath. Worse, the edit itself is detectable in ways that have nothing to do with the values: ro properties are read-only after init, so changing them means root and a remount, and root is its own signal. Runtime hooking frameworks that intercept the Java Build class leave a different tell — native code reading the property area directly gets the original value, so the two disagree inside the same process.
And above all of it sits attestation. Google’s Play Integrity checks the fingerprint against the set of Google-certified builds; a fingerprint that is not in that set fails device integrity no matter how plausible it reads. This is the ceiling every spoofing approach hits, and the reason real hardware with genuinely different build properties behaves differently from one device pretending to be many.
