Document Android biometric unlock behavior on BYOD work profiles (#50265)

<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #49655

Documentation-only change. No server behavior changes.

## What's happening

On a personally-owned work profile (BYOD), Android applies the biometric
values of `keyguardDisabledFeatures` to the work profile lock. By
default the end user has one lock for both the work profile and the host
("Use one lock"), so there is no separate work profile lock to restrict,
and Android restricts the host's lock instead. Fingerprint and face
unlock turn off for the whole host, including the end user's personal
apps.

This is documented Android behavior, per
[`setKeyguardDisabledFeatures`](https://developer.android.com/reference/android/app/admin/DevicePolicyManager#setKeyguardDisabledFeatures(android.content.ComponentName,%20int)):

> `KEYGUARD_DISABLE_FINGERPRINT`, `KEYGUARD_DISABLE_FACE` or
`KEYGUARD_DISABLE_IRIS` which affects the managed profile challenge if
there is one, **or the parent user otherwise**.

Fleet never sets `keyguardDisabledFeatures` itself. Fleet's default
Android policy sets only `StatusReportingSettings`, and admin-authored
AMAPI policy is passed through as-is. The reason this still lands on
Fleet is that the profile in the bug report is byte-for-byte the example
Fleet publishes at
`docs/solutions/android/configuration-profiles/disable-face-and-biometrics-unlock.json`,
with no note about the BYOD side effect.

## What changed

- `articles/custom-os-settings.md`: new "Biometric unlock on
personally-owned (BYOD) hosts" subsection under "Special Android
behavior", covering the behavior and the configuration that scopes the
restriction to work.
- `docs/solutions/android/configuration-profiles/README.md`: entries for
the biometrics profile (carrying the caveat) and for the new example.
-
`docs/solutions/android/configuration-profiles/require-separate-work-profile-lock.json`:
new example using `passwordScope: SCOPE_PROFILE` and
`unifiedLockSettings: REQUIRE_SEPARATE_WORK_LOCK`.

The remedy needs no Fleet change: `passwordPolicies` is already in the
Android policy field mask
(`server/mdm/android/service/androidmgmt/policy_field_mask_test.go`), so
Fleet already delivers it.

## Testing

- [ ] QA'd all new/changed functionality manually

Docs-only, so no automated tests were added. Verification done:

- Both the new example profile and the combined snippet in the guide
decode into the real `androidmanagement.Policy` struct with
`DisallowUnknownFields`, confirming every key and nesting level matches
the AMAPI schema Fleet ships.
- The documented status flow (`USER_ACTION` non-compliance on
`passwordPolicies` marks the profile "Failed", then "Verified" once the
end user sets the work lock) is confirmed against
`server/mdm/android/service/pubsub_test.go`.

**Not yet verified on hardware.** I have not run the repro on a physical
BYOD Android host. Before merge, this is worth confirming:

1. On a BYOD Android host with fingerprint unlock configured and a work
profile enrolled, apply `disable-face-and-biometrics-unlock.json`.
Confirm fingerprint disappears as an unlock method device-wide.
2. Add the `passwordPolicies` block from
`require-separate-work-profile-lock.json` to that profile and re-upload.
Confirm Android prompts for a separate work profile lock, and that Fleet
shows the profile "Failed" with `USER_ACTION` on **Host > OS settings**
until the end user sets it.
3. After the end user sets the work lock, confirm the profile moves to
"Verified", fingerprint unlock works again on the personal side, and the
work profile still requires PIN/password.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added an Android configuration profile option that requires a separate
lock for the work profile.
* Applies password policy settings specifically to the work profile
scope.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Dante Catalfamo
2026-08-05 13:18:26 -04:00
committed by GitHub
parent 5ec4c650d3
commit c9001b4e46
3 changed files with 55 additions and 0 deletions
+34
View File
@@ -178,6 +178,40 @@ The error message will provide the reason from the Android Management API (AMAPI
Note that the "Resend" button is only available for certificates. Fleet pushes certificates via Fleet's Android app. Other configuration profiles don't have the "Resend" button because they are sent via a different mechanism: the host checks in for these profiles periodically similarly to Apple declaration (DDM) profiles, rather than Fleet pushing them.
#### Biometric unlock on personally-owned (BYOD) hosts
Android applies the biometric values of `keyguardDisabledFeatures` to the work profile lock. By default, the end user has one lock for both the work profile and the host ("Use one lock"). There is no separate work profile lock to restrict, so Android restricts the host's lock instead. The profile below turns off fingerprint and face unlock for the whole host, including the end user's personal apps:
```json
{
"keyguardDisabledFeatures": [
"FACE",
"BIOMETRICS"
]
}
```
To restrict biometric unlock on the work profile only, require a separate work profile lock in the same profile:
```json
{
"keyguardDisabledFeatures": [
"FACE",
"BIOMETRICS"
],
"passwordPolicies": [
{
"passwordScope": "SCOPE_PROFILE",
"unifiedLockSettings": "REQUIRE_SEPARATE_WORK_LOCK"
}
]
}
```
Android then asks the end user to set a work profile lock. Until they set it, Android reports `passwordPolicies` with a reason of `USER_ACTION`, and Fleet shows the profile as "Failed" on **Host > OS settings**. The profile moves to "Verified" after the end user sets the lock.
`unifiedLockSettings` requires Android 9 or later, and Android rejects the policy unless `passwordScope` is `SCOPE_PROFILE`.
## Broken profiles
If one or more labels included in the profile's scope are deleted, the profile will not apply to new hosts that enroll.
@@ -7,3 +7,16 @@
- Google limits how the complexity requirements function on BYOD devices. For BYOD:
- `passwordMinimumLength` set to 8 makes the length requirement for PIN 8, password 6.
- `passwordMinimumLength` set to 6 makes the length requirement for PIN 6, password 4.
## [Disable face and biometrics unlock](disable-face-and-biometrics-unlock.json)
- Android applies these values to the work profile lock. On BYOD hosts, the end user has one lock for both the work profile and the host by default. There is no separate work profile lock to restrict, so Android restricts the host's lock instead. This turns off fingerprint and face unlock for the whole host, including the end user's personal apps.
- To restrict biometric unlock on the work profile only, add the settings from [Require separate work profile lock](require-separate-work-profile-lock.json) to this profile.
## [Require separate work profile lock](require-separate-work-profile-lock.json)
- This stops the end user from using one lock for both the work profile and the host.
- Until the end user sets a work profile lock, Android reports `passwordPolicies` with a reason of `USER_ACTION`, and Fleet shows the profile as "Failed" on **Host > OS settings**. The profile moves to "Verified" after the end user sets the lock.
- `unifiedLockSettings` requires Android 9 or later, and Android rejects the policy unless `passwordScope` is `SCOPE_PROFILE`.
@@ -0,0 +1,8 @@
{
"passwordPolicies": [
{
"passwordScope": "SCOPE_PROFILE",
"unifiedLockSettings": "REQUIRE_SEPARATE_WORK_LOCK"
}
]
}