diff --git a/build/ci/.azure-devops-stages.yml b/build/ci/.azure-devops-stages.yml
index 5606244df3d0..6c883c8666b6 100644
--- a/build/ci/.azure-devops-stages.yml
+++ b/build/ci/.azure-devops-stages.yml
@@ -18,9 +18,11 @@ stages:
README.md
README/**
build/**
+ src/Uno.Sdk/packages.json
- template: templates/gitversion-run.yml
- template: setup/.azure-devops-setup-commitsar.yml
+ - template: setup/.azure-devops-setup-validate-packages.yml
- template: setup/.azure-devops-setup-doc-validations.yml
- job: DetermineScope
diff --git a/build/ci/scripts/validate-packages-json.ps1 b/build/ci/scripts/validate-packages-json.ps1
new file mode 100644
index 000000000000..4bb05a26f952
--- /dev/null
+++ b/build/ci/scripts/validate-packages-json.ps1
@@ -0,0 +1,154 @@
+#!/usr/bin/env pwsh
+<#
+.SYNOPSIS
+ Validates that all package IDs and versions in src/Uno.Sdk/packages.json exist on NuGet.org.
+
+.DESCRIPTION
+ Parses packages.json, skips groups with placeholder versions (e.g. "DefaultUnoVersion"),
+ and verifies each package ID + version (including versionOverride entries) against NuGet.org.
+ Exits with code 1 if any package/version is missing.
+
+.PARAMETER PackagesJsonPath
+ Path to the packages.json file. Defaults to src/Uno.Sdk/packages.json relative to repo root.
+#>
+param(
+ [string]$PackagesJsonPath,
+ [switch]$WarningOnly
+)
+
+$ErrorActionPreference = 'Stop'
+
+# Auto-detect: on release/* branches, switch to warning-only mode
+# (stable builds test with versions not yet public on NuGet.org)
+if (-not $WarningOnly) {
+ $branch = $env:BUILD_SOURCEBRANCH
+ $targetBranch = $env:SYSTEM_PULLREQUEST_TARGETBRANCH
+ if (($branch -and $branch -like 'refs/heads/release/*') -or
+ ($targetBranch -and $targetBranch -like 'refs/heads/release/*')) {
+ $detected = if ($targetBranch -like 'refs/heads/release/*') { $targetBranch } else { $branch }
+ Write-Host "Detected release branch ($detected) - running in warning-only mode." -ForegroundColor Yellow
+ $WarningOnly = $true
+ }
+}
+
+# Resolve path
+if (-not $PackagesJsonPath) {
+ $repoRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot))
+ $PackagesJsonPath = Join-Path $repoRoot 'src/Uno.Sdk/packages.json'
+}
+
+if (-not (Test-Path $PackagesJsonPath)) {
+ Write-Error "packages.json not found at: $PackagesJsonPath"
+ exit 1
+}
+
+Write-Host "Validating packages.json: $PackagesJsonPath" -ForegroundColor Cyan
+
+$json = Get-Content $PackagesJsonPath -Raw | ConvertFrom-Json
+$errors = @()
+$checked = 0
+$validatedPairs = @{} # De-duplicate package+version pairs across groups
+
+# Placeholder versions that should not be validated against NuGet
+$placeholderVersions = @('DefaultUnoVersion')
+
+foreach ($group in $json) {
+ $groupName = $group.group
+ $baseVersion = $group.version
+ $packages = $group.packages
+
+ # Skip groups with placeholder versions
+ if ($placeholderVersions -contains $baseVersion) {
+ Write-Host " [SKIP] $groupName (placeholder version: $baseVersion)" -ForegroundColor DarkGray
+ continue
+ }
+
+ # Collect all version+package combinations to check
+ $versionsToCheck = @()
+
+ # Base version
+ $versionsToCheck += @{ Version = $baseVersion; Label = 'base' }
+
+ # Version overrides (TFM-specific)
+ if ($group.PSObject.Properties['versionOverride'] -and $null -ne $group.versionOverride) {
+ $overrides = $group.versionOverride
+ foreach ($prop in $overrides.PSObject.Properties) {
+ $versionsToCheck += @{ Version = $prop.Value; Label = "override($($prop.Name))" }
+ }
+ }
+
+ foreach ($pkg in $packages) {
+ foreach ($vEntry in $versionsToCheck) {
+ $version = $vEntry.Version
+ $label = $vEntry.Label
+
+ # Also skip overrides that reference a placeholder
+ if ($placeholderVersions -contains $version) {
+ continue
+ }
+
+ # De-duplicate: skip if we already validated this package+version
+ $pairKey = "$($pkg.ToLowerInvariant())|$($version.ToLowerInvariant())"
+ if ($validatedPairs.ContainsKey($pairKey)) {
+ Write-Host " [OK] $pkg $version ($label) - already validated" -ForegroundColor DarkGreen
+ continue
+ }
+ $validatedPairs[$pairKey] = $true
+
+ $checked++
+ $url = "https://api.nuget.org/v3-flatcontainer/$($pkg.ToLowerInvariant())/$($version.ToLowerInvariant())/$($pkg.ToLowerInvariant()).nuspec"
+
+ try {
+ $response = Invoke-WebRequest -Uri $url -Method Head -UseBasicParsing `
+ -TimeoutSec 30 -MaximumRetryCount 3 -RetryIntervalSec 2 -ErrorAction Stop
+ if ($response.StatusCode -eq 200) {
+ Write-Host " [OK] $pkg $version ($label)" -ForegroundColor Green
+ }
+ }
+ catch {
+ $httpResponse = $_.Exception.Response
+ if ($null -ne $httpResponse) {
+ $statusCode = $httpResponse.StatusCode.value__
+ if ($statusCode -eq 404) {
+ Write-Host " [MISSING] $pkg $version ($label) - NOT FOUND on NuGet" -ForegroundColor Red
+ $errors += "Group '$groupName': $pkg $version ($label) does not exist on NuGet.org"
+ }
+ else {
+ Write-Host " [ERROR] $pkg $version ($label) - HTTP $statusCode" -ForegroundColor Yellow
+ $errors += "Group '$groupName': $pkg $version ($label) - HTTP error $statusCode"
+ }
+ }
+ else {
+ Write-Host " [ERROR] $pkg $version ($label) - $($_.Exception.Message)" -ForegroundColor Yellow
+ $errors += "Group '$groupName': $pkg $version ($label) - network error: $($_.Exception.Message)"
+ }
+ }
+ }
+ }
+}
+
+Write-Host ""
+Write-Host "Checked $checked package/version combinations." -ForegroundColor Cyan
+
+if ($errors.Count -gt 0) {
+ Write-Host ""
+ if ($WarningOnly) {
+ Write-Host "VALIDATION WARNINGS - $($errors.Count) package(s) not found on NuGet.org (non-fatal):" -ForegroundColor Yellow
+ }
+ else {
+ Write-Host "VALIDATION FAILED - $($errors.Count) error(s):" -ForegroundColor Red
+ }
+ foreach ($err in $errors) {
+ Write-Host " - $err" -ForegroundColor $(if ($WarningOnly) { 'Yellow' } else { 'Red' })
+ }
+ if ($WarningOnly) {
+ Write-Host ""
+ Write-Host "Running in warning-only mode (stable branch) - not failing the build." -ForegroundColor Yellow
+ exit 0
+ }
+ exit 1
+}
+else {
+ Write-Host "All packages validated successfully." -ForegroundColor Green
+ exit 0
+}
diff --git a/build/ci/setup/.azure-devops-setup-validate-packages.yml b/build/ci/setup/.azure-devops-setup-validate-packages.yml
new file mode 100644
index 000000000000..2f492228c1be
--- /dev/null
+++ b/build/ci/setup/.azure-devops-setup-validate-packages.yml
@@ -0,0 +1,9 @@
+steps:
+- task: PowerShell@2
+ displayName: Validate packages.json (NuGet existence check)
+ inputs:
+ pwsh: true
+ filePath: build/ci/scripts/validate-packages-json.ps1
+ arguments: '-PackagesJsonPath "$(Build.SourcesDirectory)/src/Uno.Sdk/packages.json"'
+ env:
+ BUILD_SOURCEBRANCH: $(Build.SourceBranch)
diff --git a/doc/articles/features/android-auto.md b/doc/articles/features/android-auto.md
index 41e9551c13a2..b233006365ed 100644
--- a/doc/articles/features/android-auto.md
+++ b/doc/articles/features/android-auto.md
@@ -14,7 +14,7 @@ For projects using the `Uno.Sdk`, add the `AndroidAuto` feature to your project'
$(UnoFeatures);AndroidAuto
```
-This automatically adds a reference to the `Xamarin.AndroidX.Car.App` package. New projects created from the Uno Platform templates with the **Android Auto** option enabled will also have the required manifest entries and the `automotive_app_desc.xml` resource stamped automatically.
+This automatically adds a reference to the `Xamarin.AndroidX.Car.App.App` package. New projects created from the Uno Platform templates with the **Android Auto** option enabled will also have the required manifest entries and the `automotive_app_desc.xml` resource stamped automatically.
## What gets configured
diff --git a/src/Uno.Sdk/Sdk/Sdk.props.buildschema.json b/src/Uno.Sdk/Sdk/Sdk.props.buildschema.json
index c502976a54a4..c8c1be97c250 100644
--- a/src/Uno.Sdk/Sdk/Sdk.props.buildschema.json
+++ b/src/Uno.Sdk/Sdk/Sdk.props.buildschema.json
@@ -590,7 +590,7 @@
"helpUrl": "https://aka.platform.uno/feature-android-tv"
},
"AndroidAuto": {
- "description": "Configures the Android head for Android Auto / Automotive OS (adds Xamarin.AndroidX.Car.App).",
+ "description": "Configures the Android head for Android Auto / Automotive OS (adds Xamarin.AndroidX.Car.App.App).",
"helpUrl": "https://aka.platform.uno/feature-android-auto"
},
"AndroidWear": {
diff --git a/src/Uno.Sdk/packages.json b/src/Uno.Sdk/packages.json
index b5085b8e98e9..4190f78de795 100644
--- a/src/Uno.Sdk/packages.json
+++ b/src/Uno.Sdk/packages.json
@@ -281,7 +281,7 @@
"group": "AndroidXCarApp",
"version": "1.4.0.2",
"packages": [
- "Xamarin.AndroidX.Car.App"
+ "Xamarin.AndroidX.Car.App.App"
],
"versionOverride": {
"net10.0": "1.4.0.3"
@@ -294,17 +294,17 @@
"Xamarin.AndroidX.Wear"
],
"versionOverride": {
- "net10.0": "1.3.0.17"
+ "net10.0": "1.4.0.1"
}
},
{
"group": "AndroidXWearTiles",
- "version": "1.4.0.2",
+ "version": "1.4.0.1",
"packages": [
"Xamarin.AndroidX.Wear.Tiles"
],
"versionOverride": {
- "net10.0": "1.4.0.3"
+ "net10.0": "1.4.1"
}
},
{
diff --git a/src/Uno.Sdk/targets/Uno.Implicit.Packages.ProjectSystem.Android.targets b/src/Uno.Sdk/targets/Uno.Implicit.Packages.ProjectSystem.Android.targets
index 789df9cee89a..0e43b27dfa35 100644
--- a/src/Uno.Sdk/targets/Uno.Implicit.Packages.ProjectSystem.Android.targets
+++ b/src/Uno.Sdk/targets/Uno.Implicit.Packages.ProjectSystem.Android.targets
@@ -27,7 +27,7 @@
- <_UnoProjectSystemPackageReference Include="Xamarin.AndroidX.Car.App" ProjectSystem="true" />
+ <_UnoProjectSystemPackageReference Include="Xamarin.AndroidX.Car.App.App" ProjectSystem="true" />