From 5d0a69f276915ffa9aa135820da1e707644d1ee2 Mon Sep 17 00:00:00 2001 From: Victor Lyuboslavsky <2685025+getvictor@users.noreply.github.com> Date: Mon, 20 Apr 2026 19:23:13 -0500 Subject: [PATCH] Android agent to always send the `platform` field on enrollment (#43809) --- android/app/src/main/java/com/fleetdm/agent/ApiClient.kt | 5 +++++ .../test/java/com/fleetdm/agent/ApiClientReenrollTest.kt | 9 +++++++-- android/changes/43807-android-enroll-platform | 1 + 3 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 android/changes/43807-android-enroll-platform diff --git a/android/app/src/main/java/com/fleetdm/agent/ApiClient.kt b/android/app/src/main/java/com/fleetdm/agent/ApiClient.kt index 1c8ee0a03a..c149acf0a0 100644 --- a/android/app/src/main/java/com/fleetdm/agent/ApiClient.kt +++ b/android/app/src/main/java/com/fleetdm/agent/ApiClient.kt @@ -22,6 +22,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext +import kotlinx.serialization.EncodeDefault import kotlinx.serialization.KSerializer import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -438,6 +439,9 @@ object ApiClient : CertificateApiClient { ) } +// @EncodeDefault is marked @ExperimentalSerializationApi, but it has shipped in kotlinx.serialization since 1.3 (2022) +// and is widely used and reliable in production. The opt-in only acknowledges that the API shape could change in a future version. +@OptIn(kotlinx.serialization.ExperimentalSerializationApi::class) @Serializable data class EnrollRequest( @SerialName("enroll_secret") @@ -446,6 +450,7 @@ data class EnrollRequest( val hardwareUUID: String, @SerialName("hardware_serial") val hardwareSerial: String, + @EncodeDefault(EncodeDefault.Mode.ALWAYS) @SerialName("platform") val platform: String = "android", @SerialName("computer_name") diff --git a/android/app/src/test/java/com/fleetdm/agent/ApiClientReenrollTest.kt b/android/app/src/test/java/com/fleetdm/agent/ApiClientReenrollTest.kt index 97fed43533..584d3b4c31 100644 --- a/android/app/src/test/java/com/fleetdm/agent/ApiClientReenrollTest.kt +++ b/android/app/src/test/java/com/fleetdm/agent/ApiClientReenrollTest.kt @@ -100,10 +100,15 @@ class ApiClientReenrollTest { assertTrue("First call should succeed", firstResult.isSuccess) assertEquals(2, mockWebServer.requestCount) // enroll + config - // Verify first enrollment used the enroll secret + // Verify first enrollment used the enroll secret and sent platform="android" on the wire val firstEnroll = mockWebServer.takeRequest() assertEquals("/api/fleet/orbit/enroll", firstEnroll.path) - assertTrue(firstEnroll.body.readUtf8().contains("test-enroll-secret")) + val firstEnrollBody = firstEnroll.body.readUtf8() + assertTrue(firstEnrollBody.contains("test-enroll-secret")) + assertTrue( + "Expected platform=\"android\" in enroll body, got: $firstEnrollBody", + firstEnrollBody.contains("\"platform\":\"android\""), + ) // Verify first config used first-node-key val firstConfig = mockWebServer.takeRequest() diff --git a/android/changes/43807-android-enroll-platform b/android/changes/43807-android-enroll-platform new file mode 100644 index 0000000000..dc1b0b566e --- /dev/null +++ b/android/changes/43807-android-enroll-platform @@ -0,0 +1 @@ +- Fixed Android agent to always send the `platform` field on enrollment so the device is registered with the correct platform.