Problem
AdbRunner.ListDevicesAsync() returns AdbDeviceInfo populated only from adb devices -l output. Several fields that downstream tooling (IDE device pickers, dotnet/android's GetAvailableAndroidDevices MSBuild task, the new maui device list CLI in dotnet/maui-labs) need are not available via adb devices -l and require separate adb shell getprop calls per device:
| Field |
getprop key |
| CPU architecture |
ro.product.cpu.abilist (first entry) / ro.product.cpu.abi |
| Manufacturer |
ro.product.manufacturer / ro.product.brand |
| Android version (release) |
ro.build.version.release |
| Android SDK level |
ro.build.version.sdk |
| Real product model |
ro.product.model |
Every consumer ends up re-implementing the same orchestration:
- Legacy ServiceHub had
AndroidDeviceManager.PopulateDeviceAsync.
dotnet/android's GetAvailableAndroidDevices task does its own shelling.
- We just added
AndroidDeviceEnricher in maui-labs because adb devices -l on a USB-attached physical device returns essentially serial + model + product + device + transport_id — nothing actionable for an IDE picker.
This is the same shape of gap as #310 (AdbRunner.GetEmulatorAvdNameAsync needing a getprop fallback), so it feels like a natural sibling fix.
Proposed Fix
Add an opt-in enrichment API to AdbRunner plus new properties on AdbDeviceInfo:
public class AdbDeviceInfo
{
// ... existing properties ...
/// <summary>Primary CPU ABI from ro.product.cpu.abilist (e.g. "arm64-v8a", "x86_64").</summary>
public string? CpuAbi { get; set; }
/// <summary>Device manufacturer from ro.product.manufacturer (e.g. "Google", "Samsung").</summary>
public string? Manufacturer { get; set; }
/// <summary>Android release version from ro.build.version.release (e.g. "16", "14").</summary>
public string? ReleaseVersion { get; set; }
/// <summary>Android SDK level from ro.build.version.sdk (e.g. "36", "34").</summary>
public string? SdkVersion { get; set; }
}
public partial class AdbRunner
{
/// <summary>
/// Populates Architecture, Manufacturer, ReleaseVersion, SdkVersion (and refreshes Model)
/// for the given device by issuing the corresponding 'adb shell getprop' calls in parallel.
/// No-ops if the device is offline. Existing non-null values are preserved.
/// </summary>
public virtual Task EnrichDeviceAsync(AdbDeviceInfo device, CancellationToken cancellationToken = default);
}
Optionally a convenience overload on ListDevicesAsync(bool enrich, CancellationToken) that calls EnrichDeviceAsync for each online device in parallel.
Notes
EnrichDeviceAsync should be a no-op for Status != Online (Offline/Unauthorized/NoPermissions devices won't answer getprop).
- Per-property failures should not fail the whole enrichment — match the per-property tolerance in our
AndroidDeviceEnricher.
- Preserve
IsEmulator semantics: emulators are also valid enrichment targets when online (manufacturer = "Google", etc., is still useful).
- This is opt-in so unit tests and callers that only want what
adb devices -l provides keep today's behavior and don't pay the per-device round-trip cost.
Impact
Once upstream, downstream tooling can drop their bespoke enrichment paths:
dotnet/maui-labs can delete AndroidDeviceEnricher.
dotnet/android's GetAvailableAndroidDevices could potentially share the same code path.
- Future IDE/CLI integrations get a single canonical source of truth.
Happy to send a PR if the team is open to this direction.
Problem
AdbRunner.ListDevicesAsync()returnsAdbDeviceInfopopulated only fromadb devices -loutput. Several fields that downstream tooling (IDE device pickers,dotnet/android'sGetAvailableAndroidDevicesMSBuild task, the newmaui device listCLI in dotnet/maui-labs) need are not available viaadb devices -land require separateadb shell getpropcalls per device:getpropkeyro.product.cpu.abilist(first entry) /ro.product.cpu.abiro.product.manufacturer/ro.product.brandro.build.version.releasero.build.version.sdkro.product.modelEvery consumer ends up re-implementing the same orchestration:
AndroidDeviceManager.PopulateDeviceAsync.dotnet/android'sGetAvailableAndroidDevicestask does its own shelling.AndroidDeviceEnricherinmaui-labsbecauseadb devices -lon a USB-attached physical device returns essentiallyserial + model + product + device + transport_id— nothing actionable for an IDE picker.This is the same shape of gap as #310 (
AdbRunner.GetEmulatorAvdNameAsyncneeding agetpropfallback), so it feels like a natural sibling fix.Proposed Fix
Add an opt-in enrichment API to
AdbRunnerplus new properties onAdbDeviceInfo:Optionally a convenience overload on
ListDevicesAsync(bool enrich, CancellationToken)that callsEnrichDeviceAsyncfor each online device in parallel.Notes
EnrichDeviceAsyncshould be a no-op forStatus != Online(Offline/Unauthorized/NoPermissions devices won't answergetprop).AndroidDeviceEnricher.IsEmulatorsemantics: emulators are also valid enrichment targets when online (manufacturer ="Google", etc., is still useful).adb devices -lprovides keep today's behavior and don't pay the per-device round-trip cost.Impact
Once upstream, downstream tooling can drop their bespoke enrichment paths:
dotnet/maui-labscan deleteAndroidDeviceEnricher.dotnet/android'sGetAvailableAndroidDevicescould potentially share the same code path.Happy to send a PR if the team is open to this direction.