Policy-key compatibility: stock Android vs GrapheneOS
The HardenedOS DPC uses standard Android
DevicePolicyManager / UserManager
APIs, so the policy capability set works on any Android
Enterprise Device Owner — Pixel, Samsung, and other OEMs — not just on
the Custom OS tier. This page is the per-key
reference for what behaves identically everywhere and the handful of
keys that don't.
Bottom line: of the full key catalogue, exactly
one key is GrapheneOS-only
(block_os_updates) and one is its inverse
(ota_install_window, which works on stock Android but not
GrapheneOS). A few radio controls are an effective block on the
managed tier rather than a hardware radio-off. Everything else is
identical across OEMs.
See policy-tiers.md for
the authoritative key catalogue (types, defaults, value shapes); this
page only classifies portability.
Summary — the keys that differ
| Key | Stock Android (e.g. Samsung) | GrapheneOS | Note |
|---|---|---|---|
block_os_updates |
❌ unsupported | ✅ works | Verifiably hides and/or suspends
app.seamlessupdate.client; enforcement fails closed when
that package is absent. |
ota_install_window |
✅ works (AOSP SystemUpdatePolicy) |
❌ no effect | GrapheneOS's Updater ignores SystemUpdatePolicy. |
disable_wifi |
⚠️ effective block | ⚠️ effective block | Software block (config restriction + match-nothing SSID allowlist). True radio-off needs the Custom OS tier on either OEM. |
disable_nfc |
⚠️ beam/share off | ⚠️ beam/share off | DISALLOW_OUTGOING_BEAM; no Device-Owner API for a full
NFC radio-off on any OEM. |
disable_location |
✅ | ✅ | Device Owner forces global location off and locks the Location
Services control (setLocationEnabled(false) + location user
restrictions). |
To control OS updates portably: use
ota_install_window on stock Android and
block_os_updates on GrapheneOS. Do not set
block_os_updates on a stock device: the GrapheneOS Updater
is absent, so the DPC reports the policy as unverifiable instead of
claiming the OS update freeze succeeded.
Everything below is cross-platform — identical behavior on any Android Enterprise Device Owner.
Full matrix
Lock screen + biometric — all cross-platform
| Key | Mechanism |
|---|---|
screen_lock_required |
setRequiredPasswordComplexity() |
screen_lock_max_attempts |
setMaximumFailedPasswordsForWipe() |
require_biometric_after_seconds |
setMaximumTimeToLock() |
biometric_only |
setKeyguardDisabledFeatures() (face/iris bits) |
disable_biometric_unlock |
setKeyguardDisabledFeatures()
(KEYGUARD_DISABLE_BIOMETRICS) |
screen_timeout_seconds |
setSystemSetting(SCREEN_OFF_TIMEOUT) |
lockscreen_message |
setDeviceOwnerLockScreenInfo() |
lockscreen_notification_redaction |
setKeyguardDisabledFeatures() (notification bits) |
Apps + sideloading — all cross-platform
| Key | Mechanism |
|---|---|
allow_sideloading |
DISALLOW_INSTALL_UNKNOWN_SOURCES |
allow_developer_mode |
DISALLOW_DEBUGGING_FEATURES |
allow_safe_mode |
DISALLOW_SAFE_BOOT |
allow_personal_accounts |
DISALLOW_MODIFY_ACCOUNTS |
force_installed_apps |
setUninstallBlocked() + install |
blocked_apps |
setApplicationHidden() |
disabled_system_apps |
setApplicationHidden() — the package names are
OEM-specific (e.g. Samsung ships different built-ins than
GrapheneOS/AOSP); the mechanism is identical. |
permitted_input_methods |
setPermittedInputMethods() |
Privacy + capture — all cross-platform
| Key | Mechanism |
|---|---|
allow_screenshots |
setScreenCaptureDisabled() |
disable_camera |
setCameraDisabled() |
disable_location |
setLocationEnabled(false) +
DISALLOW_CONFIG_LOCATION +
DISALLOW_SHARE_LOCATION |
disable_microphone |
DISALLOW_UNMUTE_MICROPHONE |
disable_usb_transfer |
DISALLOW_USB_FILE_TRANSFER |
Network + radios — cross-platform (radio caveats below)
| Key | Mechanism |
|---|---|
always_on_vpn |
setAlwaysOnVpnPackage(lockdown) |
allowed_wifi_networks |
setWifiSsidPolicy() (API 33+) |
block_other_wifi |
DISALLOW_ADD_WIFI_CONFIG |
disable_wifi |
DISALLOW_CONFIG_WIFI + match-nothing
WifiSsidPolicy — effective block, see
below |
disable_bluetooth |
DISALLOW_BLUETOOTH |
allow_bluetooth_config |
DISALLOW_CONFIG_BLUETOOTH |
disable_nfc |
DISALLOW_OUTGOING_BEAM — beam/share only, see
below |
disable_tethering |
DISALLOW_CONFIG_TETHERING |
disable_data_roaming |
DISALLOW_DATA_ROAMING |
esim_lock |
DISALLOW_CONFIG_MOBILE_NETWORKS — blocks the whole SIMs
settings page (eSIM erase/disable, roaming, mobile-data, APN), not just
removal |
disable_sms |
DISALLOW_SMS — SS7 hardening (no SMS ⇒ no OTP to
intercept); see ss7-hardening.md |
disable_calls |
DISALLOW_OUTGOING_CALLS — SS7 hardening (voice → E2E
VoIP); emergency calls still allowed |
disable_2g |
DISALLOW_CELLULAR_2G — Android 14+ on any
OEM; no-op below 14. Cuts 2G-downgrade + many
IMSI-catchers |
Branding + time + misc — cross-platform
| Key | Mechanism |
|---|---|
wallpaper_url / wallpaper_sha256 |
WallpaperManager — covers home + lock; home only when a
lock image is also set (DPC v107+) |
lock_wallpaper_url /
lock_wallpaper_sha256 |
WallpaperManager FLAG_LOCK — optional
dedicated lock-screen image (DPC v107+; older DPCs ignore it) |
auto_time |
setAutoTimeEnabled() |
auto_timezone |
setAutoTimeZoneEnabled() |
disable_backup_service |
setBackupServiceEnabled() |
disable_status_bar |
setStatusBarDisabled() |
ota_channel |
persisted config (honored only by the Custom OS Updater) |
High-restriction — cross-platform
| Key | Mechanism |
|---|---|
kiosk_mode_enabled / kiosk_apps |
lock-task / single-app DPM calls |
geofence |
persisted (server/heartbeat-enforced) |
remote_attestation_required |
persisted flag; attestation works on any device with StrongBox |
audit_log_enabled |
setSecurityLoggingEnabled() |
tamper_response |
persisted; mechanism OEM-agnostic |
hardware_locked_settings |
addUserRestriction() over a validated list |
The OS-update keys
block_os_updatesuses two Device Owner barriers against the GrapheneOS Updater (app.seamlessupdate.client): hidden and suspended. The DPC verifies the resulting state on every sync and leaves the manifest unapplied if neither barrier is active. On stock Android that package doesn't exist, so the policy fails verification rather than pretending to freeze a Samsung/Pixel-stock OTA. An OTA already staged before the block may still finish on reboot.ota_install_windowuses AOSPsetSystemUpdatePolicy(), which stock Android honours but GrapheneOS ignores (its Updater is independent of the system OTA mechanism).
So they're complementary, not redundant, but they must be targeted by
OS. For a fleet spanning OEMs, use ota_install_window for
stock Android policies and block_os_updates for GrapheneOS
policies.
The radio caveat (both tiers)
disable_wifi and disable_nfc are an
effective block at the managed tier, not a hardware
radio-off:
disable_wifi=DISALLOW_CONFIG_WIFI+ a match-nothing SSID allowlist, so the device can't associate with any network or change Wi-Fi settings. A non-system Device Owner has no API to power the Wi-Fi radio off (setWifiEnabledis a no-op for non-system apps since Android 10).disable_nfc=DISALLOW_OUTGOING_BEAM(blocks NFC beam/share); there is no Device-Owner API for a full NFC radio-off.
A true hardware radio-off requires system-app privileges — i.e. the Custom OS tier — and even there it is Pixel-only. This caveat is not GrapheneOS-specific; it's a managed-Device-Owner limitation that applies equally on Samsung and Pixel-stock.