Add AdbRunner.EnrichDeviceAsync to populate Architecture/Manufacturer/Version via getprop
- Dominant language
- C#
- Stars
- 2.1k
- Forks
- 579
- Avg merge
- 1d 20h
- Merged PRs (30d)
- 257
Description
## 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](https://github.com/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`](https://github.com/dotnet/maui-labs/pull/302) 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 dotnet/android#12078 (`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`:
```csharp
public class AdbDeviceInfo
{
// ... existing properties ...
/// Primary CPU ABI from ro.product.cpu.abilist (e.g. "arm64-v8a", "x86_64").
public string? CpuAbi { get; set; }
/// Device manufacturer from ro.product.manufacturer (e.g. "Google", "Samsung").
public string? Manufacturer { get; set; }
/// Android release version from ro.build.version.release (e.g. "16", "14").
public string? ReleaseVersion { get; set; }
/// Android SDK level from ro.build.version.sdk (e.g. "36", "34").
public string? SdkVersion { get; set; }
}
```
```csharp
public partial class AdbRunner
{
///
/// 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.
///
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`](https://github.com/dotnet/maui-labs/blob/main/src/Cli/Microsoft.Maui.Cli/Providers/Android/AndroidDeviceEnricher.cs).
- `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.
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.