Troubleshooting

The failures you’ll actually hit, grouped by symptom.

Enforcement

The agent runs but nothing is enforced; observed state is “n/a”

The app isn’t a Device Owner, so isDeviceOwner() is false and the enforce/observe step is skipped. Set it on an emulator with no accounts:

adb shell dpm set-device-owner com.arganaemre.mdmcore/.MdmDeviceAdminReceiver

If that command itself fails, the device already has an account (or an existing owner). Use a fresh Google APIs emulator with nothing signed in. See agent.md.

SecurityException from a DevicePolicyManager call

The policy you’re invoking isn’t declared in device_admin.xml. Every policy a DPC calls must have a matching <uses-policies> tag. (Camera works because <disable-camera/> is declared.) See agent.md.

Networking

CLEARTEXT communication ... not permitted

targetSdk 36 blocks plain HTTP by default. Only 10.0.2.2 and localhost are re-permitted (network_security_config.xml). The agent must talk to http://10.0.2.2:8081 (the emulator’s alias for the host), not localhost and not a LAN IP. To use another host, add it to the network security config.

The agent can’t reach the server

  • From an emulator the host is 10.0.2.2, not 127.0.0.1/localhost.
  • The server binds 0.0.0.0:8081 — confirm it’s actually running (./gradlew :server:run) and the port matches.

CORS errors in the web console’s browser devtools

The server’s CORS is anyHost() (dev-only) and allows GET/POST/PUT + Content-Type. If you see CORS failures, the server probably isn’t running, or you changed the allowed methods/headers. A JSON body triggers a preflight, so Content-Type and PUT must stay allowed. See server.md.

Push (FCM doorbell)

Boot log says FCM doorbell: NOT configured

No service-account credential was found (or it failed to load). Push wake-ups are disabled, but everything else still works — agents sync over HTTP on their schedule. To enable push, configure the key per firebase-setup.md. The boot log flips to FCM doorbell: ready once it loads.

Policy changed, but the device didn’t wake

The doorbell is best-effort; the device will still catch up on its next sync. If you need the push specifically:

  • The server must log FCM doorbell: ready (a key is configured).
  • The agent must have registered a token (it does so every sync — make sure it has synced at least once after enrolling).
  • The emulator needs Google Play services (a Google APIs image); a bare AOSP image can’t receive FCM. See firebase-setup.md.
  • A force-stopped app won’t receive messages until it’s launched again.

Build

A ./gradlew command just hangs

A long-lived task (:server:run, a console dev-run) is holding the Gradle project lock. Stop it, or run the blocked command from a separate checkout. See development.md.

Agent build fails complaining about google-services.json

The google-services plugin requires agent/google-services.json. Download it from the Firebase console for application id com.arganaemre.mdmcore and place it there. It’s not secret. See firebase-setup.md.

Server state

A device gets 404 and re-enrolls with a new id

DeviceStore is in-memory — restarting the server forgets every device. The agent treats a 404 on policy-pull as “the server forgot me” and re-enrolls, receiving a fresh id (device-2, …). This is expected with the current non-persistent store. See server.md.

A device shows STALE and won’t clear

STALE means the last report applied an older policy version than the current desired one. It clears when the agent re-syncs (pull → enforce → report) on the new version. Trigger a sync (relaunch the agent, or let the doorbell/WorkManager fire). If it then shows NON_COMPLIANT, the device is on the right version but genuinely drifted. See the loop.