diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs
index 5b0f5111993..5a639c3e12e 100644
--- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs
@@ -69,6 +69,59 @@ public void AssertHasDevices (bool fail = true)
}
}
+ ///
+ /// Probes the Android package manager service the same way `bundletool build-apks
+ /// --connected-device` (which runs `pm list features`) and `adb install` do. On CI the
+ /// emulator's system_server sometimes wedges while `adb shell echo OK` still succeeds, so
+ /// reports the device as healthy even though every subsequent
+ /// build/install fails deep inside a test with a cryptic
+ /// "Failure calling service package: Broken pipe" (XABAS0000). Retries a few times so a
+ /// momentary hiccup does not trigger an unnecessary emulator restart.
+ ///
+ protected static bool IsPackageManagerResponsive (out string diagnostic)
+ {
+ string output = "";
+ for (int attempt = 0; attempt < 3; attempt++) {
+ output = (RunAdbCommand ("shell pm list features", timeout: 30) ?? "").Trim ();
+ bool broken = output.Contains ("Failure calling service", StringComparison.OrdinalIgnoreCase) ||
+ output.Contains ("Broken pipe", StringComparison.OrdinalIgnoreCase) ||
+ output.Contains ("Can't find service", StringComparison.OrdinalIgnoreCase);
+ // A responsive package service always lists the framework's own features.
+ if (!broken && output.Contains ("feature:", StringComparison.OrdinalIgnoreCase)) {
+ diagnostic = output;
+ return true;
+ }
+ WaitFor ((int) TimeSpan.FromSeconds (2).TotalMilliseconds);
+ }
+ diagnostic = output;
+ return false;
+ }
+
+ ///
+ /// Fails fast, with a clear reason, when the emulator's package manager service is
+ /// unusable — instead of letting the app build/install fail cryptically later and
+ /// reporting an emulator crash as N unrelated test failures. Attempts one emulator
+ /// restart first; if the service is still broken the run is marked inconclusive
+ /// (an infrastructure problem, not a product regression).
+ ///
+ protected void AssertPackageManagerResponsive ()
+ {
+ if (IsPackageManagerResponsive (out _)) {
+ return;
+ }
+
+ TestContext.Out.WriteLine ("Android package manager service is unresponsive; restarting the emulator.");
+ RestartDevice ();
+ AssertHasDevices ();
+
+ if (!IsPackageManagerResponsive (out string diagnostic)) {
+ Assert.Inconclusive (
+ "Android package manager service is unresponsive even after restarting the emulator " +
+ "(system_server likely crashed). Skipping so an emulator failure is not reported as a test failure. " +
+ $"Last 'adb shell pm list features' output: {diagnostic}");
+ }
+ }
+
[OneTimeSetUp]
public void DeviceSetup ()
{
@@ -126,6 +179,11 @@ public virtual void SetupTest ()
AssertHasDevices ();
}
+ // `adb shell echo OK` can succeed while the package manager service is wedged, so
+ // verify it is responsive before a test wastes time building/installing an app that
+ // would otherwise fail with a cryptic "Failure calling service package" error.
+ AssertPackageManagerResponsive ();
+
// We want to start with a clean logcat buffer for each test
ClearAdbLogcat ();
}