Skip to content

Add AdbRunner.EnrichDeviceAsync to populate Architecture/Manufacturer/Version via getprop #384

@rmarinho

Description

@rmarinho

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.

Metadata

Metadata

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions